2023-11-13 08:57:14 +00:00
|
|
|
// 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.
|
|
|
|
|
|
2022-10-18 16:07:36 -07:00
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-09 14:29:49 -08:00
|
|
|
type derived struct {
|
|
|
|
|
name string // name for alloc/free functions
|
|
|
|
|
typ string // the type they return/accept
|
|
|
|
|
base string // underlying allocator
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-18 16:07:36 -07:00
|
|
|
func genAllocators() {
|
|
|
|
|
allocators := []allocator{
|
|
|
|
|
{
|
|
|
|
|
name: "ValueSlice",
|
|
|
|
|
typ: "[]*Value",
|
|
|
|
|
capacity: "cap(%s)",
|
|
|
|
|
mak: "make([]*Value, %s)",
|
|
|
|
|
resize: "%s[:%s]",
|
2025-04-17 07:49:35 +00:00
|
|
|
clear: "clear(%s)",
|
2022-10-18 16:07:36 -07:00
|
|
|
minLog: 5,
|
|
|
|
|
maxLog: 32,
|
|
|
|
|
},
|
|
|
|
|
{
|
2024-05-18 08:01:27 -07:00
|
|
|
name: "LimitSlice",
|
|
|
|
|
typ: "[]limit", // the limit type is basically [4]uint64.
|
2022-10-18 16:07:36 -07:00
|
|
|
capacity: "cap(%s)",
|
2024-05-18 08:01:27 -07:00
|
|
|
mak: "make([]limit, %s)",
|
2022-10-18 16:07:36 -07:00
|
|
|
resize: "%s[:%s]",
|
2025-04-17 07:49:35 +00:00
|
|
|
clear: "clear(%s)",
|
2024-05-18 08:01:27 -07:00
|
|
|
minLog: 3,
|
|
|
|
|
maxLog: 30,
|
2022-10-18 16:07:36 -07:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
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,
|
|
|
|
|
},
|
|
|
|
|
}
|
2023-01-09 14:29:49 -08:00
|
|
|
deriveds := []derived{
|
|
|
|
|
{
|
|
|
|
|
name: "BlockSlice",
|
|
|
|
|
typ: "[]*Block",
|
|
|
|
|
base: "ValueSlice",
|
|
|
|
|
},
|
2024-05-18 08:01:27 -07:00
|
|
|
{
|
|
|
|
|
name: "Int64",
|
|
|
|
|
typ: "[]int64",
|
|
|
|
|
base: "LimitSlice",
|
|
|
|
|
},
|
2023-01-09 14:29:49 -08:00
|
|
|
{
|
|
|
|
|
name: "IntSlice",
|
|
|
|
|
typ: "[]int",
|
2024-05-18 08:01:27 -07:00
|
|
|
base: "LimitSlice",
|
2023-01-09 14:29:49 -08:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "Int32Slice",
|
|
|
|
|
typ: "[]int32",
|
2024-05-18 08:01:27 -07:00
|
|
|
base: "LimitSlice",
|
2023-01-09 14:29:49 -08:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "Int8Slice",
|
|
|
|
|
typ: "[]int8",
|
2024-05-18 08:01:27 -07:00
|
|
|
base: "LimitSlice",
|
2023-01-09 14:29:49 -08:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "BoolSlice",
|
|
|
|
|
typ: "[]bool",
|
2024-05-18 08:01:27 -07:00
|
|
|
base: "LimitSlice",
|
2023-01-09 14:29:49 -08:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "IDSlice",
|
|
|
|
|
typ: "[]ID",
|
2024-05-18 08:01:27 -07:00
|
|
|
base: "LimitSlice",
|
2023-01-09 14:29:49 -08:00
|
|
|
},
|
|
|
|
|
}
|
2022-10-18 16:07:36 -07:00
|
|
|
|
|
|
|
|
w := new(bytes.Buffer)
|
2022-05-14 22:45:05 -04:00
|
|
|
fmt.Fprintf(w, "// Code generated from _gen/allocators.go using 'go generate'; DO NOT EDIT.\n")
|
2022-10-18 16:07:36 -07:00
|
|
|
fmt.Fprintln(w)
|
|
|
|
|
fmt.Fprintln(w, "package ssa")
|
|
|
|
|
|
|
|
|
|
fmt.Fprintln(w, "import (")
|
2023-01-09 14:29:49 -08:00
|
|
|
fmt.Fprintln(w, "\"internal/unsafeheader\"")
|
2022-10-18 16:07:36 -07:00
|
|
|
fmt.Fprintln(w, "\"math/bits\"")
|
|
|
|
|
fmt.Fprintln(w, "\"sync\"")
|
2023-01-09 14:29:49 -08:00
|
|
|
fmt.Fprintln(w, "\"unsafe\"")
|
2022-10-18 16:07:36 -07:00
|
|
|
fmt.Fprintln(w, ")")
|
|
|
|
|
for _, a := range allocators {
|
|
|
|
|
genAllocator(w, a)
|
|
|
|
|
}
|
2023-01-09 14:29:49 -08:00
|
|
|
for _, d := range deriveds {
|
|
|
|
|
for _, base := range allocators {
|
|
|
|
|
if base.name == d.base {
|
|
|
|
|
genDerived(w, d, base)
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-10-18 16:07:36 -07:00
|
|
|
// gofmt result
|
|
|
|
|
b := w.Bytes()
|
|
|
|
|
var err error
|
|
|
|
|
b, err = format.Source(b)
|
|
|
|
|
if err != nil {
|
|
|
|
|
fmt.Printf("%s\n", w.Bytes())
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-10 14:15:46 -04:00
|
|
|
if err := os.WriteFile(outFile("allocators.go"), b, 0666); err != nil {
|
2022-10-18 16:07:36 -07:00
|
|
|
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<<a.minLog, 1<<a.minLog)
|
|
|
|
|
fmt.Fprintf(w, "b := bits.Len(uint(n2-1))\n")
|
|
|
|
|
fmt.Fprintf(w, "v := poolFree%s[b-%d].Get()\n", a.name, a.minLog)
|
|
|
|
|
fmt.Fprintf(w, "if v == nil {\n")
|
|
|
|
|
fmt.Fprintf(w, " s = %s\n", fmt.Sprintf(a.mak, "1<<b"))
|
|
|
|
|
fmt.Fprintf(w, "} else {\n")
|
|
|
|
|
if a.typ[0] == '*' {
|
|
|
|
|
fmt.Fprintf(w, "s = v.(%s)\n", a.typ)
|
|
|
|
|
} else {
|
|
|
|
|
fmt.Fprintf(w, "sp := v.(*%s)\n", a.typ)
|
|
|
|
|
fmt.Fprintf(w, "s = *sp\n")
|
|
|
|
|
fmt.Fprintf(w, "*sp = nil\n")
|
|
|
|
|
fmt.Fprintf(w, "c.hdr%s = append(c.hdr%s, sp)\n", a.name, a.name)
|
|
|
|
|
}
|
|
|
|
|
fmt.Fprintf(w, "}\n")
|
|
|
|
|
if a.resize != "" {
|
|
|
|
|
fmt.Fprintf(w, "s = %s\n", fmt.Sprintf(a.resize, "s", "n"))
|
|
|
|
|
}
|
|
|
|
|
fmt.Fprintf(w, "return s\n")
|
|
|
|
|
fmt.Fprintf(w, "}\n")
|
|
|
|
|
fmt.Fprintf(w, "func (c *Cache) free%s(s %s) {\n", a.name, a.typ)
|
|
|
|
|
fmt.Fprintf(w, "%s\n", fmt.Sprintf(a.clear, "s"))
|
|
|
|
|
fmt.Fprintf(w, "b := bits.Len(uint(%s) - 1)\n", fmt.Sprintf(a.capacity, "s"))
|
|
|
|
|
if a.typ[0] == '*' {
|
|
|
|
|
fmt.Fprintf(w, "poolFree%s[b-%d].Put(s)\n", a.name, a.minLog)
|
|
|
|
|
} else {
|
|
|
|
|
fmt.Fprintf(w, "var sp *%s\n", a.typ)
|
|
|
|
|
fmt.Fprintf(w, "if len(c.hdr%s) == 0 {\n", a.name)
|
|
|
|
|
fmt.Fprintf(w, " sp = new(%s)\n", a.typ)
|
|
|
|
|
fmt.Fprintf(w, "} else {\n")
|
|
|
|
|
fmt.Fprintf(w, " sp = c.hdr%s[len(c.hdr%s)-1]\n", a.name, a.name)
|
|
|
|
|
fmt.Fprintf(w, " c.hdr%s[len(c.hdr%s)-1] = nil\n", a.name, a.name)
|
|
|
|
|
fmt.Fprintf(w, " c.hdr%s = c.hdr%s[:len(c.hdr%s)-1]\n", a.name, a.name, a.name)
|
|
|
|
|
fmt.Fprintf(w, "}\n")
|
|
|
|
|
fmt.Fprintf(w, "*sp = s\n")
|
|
|
|
|
fmt.Fprintf(w, "poolFree%s[b-%d].Put(sp)\n", a.name, a.minLog)
|
|
|
|
|
}
|
|
|
|
|
fmt.Fprintf(w, "}\n")
|
|
|
|
|
}
|
2023-01-09 14:29:49 -08:00
|
|
|
func genDerived(w io.Writer, d derived, base allocator) {
|
|
|
|
|
fmt.Fprintf(w, "func (c *Cache) alloc%s(n int) %s {\n", d.name, d.typ)
|
|
|
|
|
if d.typ[:2] != "[]" || base.typ[:2] != "[]" {
|
|
|
|
|
panic(fmt.Sprintf("bad derived types: %s %s", d.typ, base.typ))
|
|
|
|
|
}
|
|
|
|
|
fmt.Fprintf(w, "var base %s\n", base.typ[2:])
|
|
|
|
|
fmt.Fprintf(w, "var derived %s\n", d.typ[2:])
|
|
|
|
|
fmt.Fprintf(w, "if unsafe.Sizeof(base)%%unsafe.Sizeof(derived) != 0 { panic(\"bad\") }\n")
|
|
|
|
|
fmt.Fprintf(w, "scale := unsafe.Sizeof(base)/unsafe.Sizeof(derived)\n")
|
|
|
|
|
fmt.Fprintf(w, "b := c.alloc%s(int((uintptr(n)+scale-1)/scale))\n", base.name)
|
|
|
|
|
fmt.Fprintf(w, "s := unsafeheader.Slice {\n")
|
|
|
|
|
fmt.Fprintf(w, " Data: unsafe.Pointer(&b[0]),\n")
|
|
|
|
|
fmt.Fprintf(w, " Len: n,\n")
|
|
|
|
|
fmt.Fprintf(w, " Cap: cap(b)*int(scale),\n")
|
|
|
|
|
fmt.Fprintf(w, " }\n")
|
|
|
|
|
fmt.Fprintf(w, "return *(*%s)(unsafe.Pointer(&s))\n", d.typ)
|
|
|
|
|
fmt.Fprintf(w, "}\n")
|
|
|
|
|
fmt.Fprintf(w, "func (c *Cache) free%s(s %s) {\n", d.name, d.typ)
|
|
|
|
|
fmt.Fprintf(w, "var base %s\n", base.typ[2:])
|
|
|
|
|
fmt.Fprintf(w, "var derived %s\n", d.typ[2:])
|
|
|
|
|
fmt.Fprintf(w, "scale := unsafe.Sizeof(base)/unsafe.Sizeof(derived)\n")
|
|
|
|
|
fmt.Fprintf(w, "b := unsafeheader.Slice {\n")
|
|
|
|
|
fmt.Fprintf(w, " Data: unsafe.Pointer(&s[0]),\n")
|
|
|
|
|
fmt.Fprintf(w, " Len: int((uintptr(len(s))+scale-1)/scale),\n")
|
|
|
|
|
fmt.Fprintf(w, " Cap: int((uintptr(cap(s))+scale-1)/scale),\n")
|
|
|
|
|
fmt.Fprintf(w, " }\n")
|
|
|
|
|
fmt.Fprintf(w, "c.free%s(*(*%s)(unsafe.Pointer(&b)))\n", base.name, base.typ)
|
|
|
|
|
fmt.Fprintf(w, "}\n")
|
|
|
|
|
}
|