// Copyright 2022 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package main // TODO: should we share backing storage for similarly-shaped types? // e.g. []*Value and []*Block, or even []int32 and []bool. import ( "bytes" "fmt" "go/format" "io" "log" "os" ) type allocator struct { name string // name for alloc/free functions typ string // the type they return/accept mak string // code to make a new object (takes power-of-2 size as fmt arg) capacity string // code to calculate the capacity of an object. Should always report a power of 2. resize string // code to shrink to sub-power-of-two size (takes size as fmt arg) clear string // code for clearing object before putting it on the free list minLog int // log_2 of minimum allocation size maxLog int // log_2 of maximum allocation size } type derived struct { name string // name for alloc/free functions typ string // the type they return/accept base string // underlying allocator } func genAllocators() { allocators := []allocator{ { name: "ValueSlice", typ: "[]*Value", capacity: "cap(%s)", mak: "make([]*Value, %s)", resize: "%s[:%s]", clear: "for i := range %[1]s {\n%[1]s[i] = nil\n}", minLog: 5, maxLog: 32, }, { name: "LimitSlice", typ: "[]limit", // the limit type is basically [4]uint64. capacity: "cap(%s)", mak: "make([]limit, %s)", resize: "%s[:%s]", clear: "for i := range %[1]s {\n%[1]s[i] = limit{}\n}", minLog: 3, maxLog: 30, }, { name: "SparseSet", typ: "*sparseSet", capacity: "%s.cap()", mak: "newSparseSet(%s)", resize: "", // larger-sized sparse sets are ok clear: "%s.clear()", minLog: 5, maxLog: 32, }, { name: "SparseMap", typ: "*sparseMap", capacity: "%s.cap()", mak: "newSparseMap(%s)", resize: "", // larger-sized sparse maps are ok clear: "%s.clear()", minLog: 5, maxLog: 32, }, { name: "SparseMapPos", typ: "*sparseMapPos", capacity: "%s.cap()", mak: "newSparseMapPos(%s)", resize: "", // larger-sized sparse maps are ok clear: "%s.clear()", minLog: 5, maxLog: 32, }, } deriveds := []derived{ { name: "BlockSlice", typ: "[]*Block", base: "ValueSlice", }, { name: "Int64", typ: "[]int64", base: "LimitSlice", }, { name: "IntSlice", typ: "[]int", base: "LimitSlice", }, { name: "Int32Slice", typ: "[]int32", base: "LimitSlice", }, { name: "Int8Slice", typ: "[]int8", base: "LimitSlice", }, { name: "BoolSlice", typ: "[]bool", base: "LimitSlice", }, { name: "IDSlice", typ: "[]ID", base: "LimitSlice", }, } w := new(bytes.Buffer) fmt.Fprintf(w, "// Code generated from _gen/allocators.go using 'go generate'; DO NOT EDIT.\n") fmt.Fprintln(w) fmt.Fprintln(w, "package ssa") fmt.Fprintln(w, "import (") fmt.Fprintln(w, "\"internal/unsafeheader\"") fmt.Fprintln(w, "\"math/bits\"") fmt.Fprintln(w, "\"sync\"") fmt.Fprintln(w, "\"unsafe\"") fmt.Fprintln(w, ")") for _, a := range allocators { genAllocator(w, a) } for _, d := range deriveds { for _, base := range allocators { if base.name == d.base { genDerived(w, d, base) break } } } // gofmt result b := w.Bytes() var err error b, err = format.Source(b) if err != nil { fmt.Printf("%s\n", w.Bytes()) panic(err) } if err := os.WriteFile("../allocators.go", b, 0666); err != nil { log.Fatalf("can't write output: %v\n", err) } } func genAllocator(w io.Writer, a allocator) { fmt.Fprintf(w, "var poolFree%s [%d]sync.Pool\n", a.name, a.maxLog-a.minLog) fmt.Fprintf(w, "func (c *Cache) alloc%s(n int) %s {\n", a.name, a.typ) fmt.Fprintf(w, "var s %s\n", a.typ) fmt.Fprintf(w, "n2 := n\n") fmt.Fprintf(w, "if n2 < %d { n2 = %d }\n", 1<