github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/compile/ssa/_gen/allocators.go (about) 1 // Copyright 2022 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package main 6 7 // TODO: should we share backing storage for similarly-shaped types? 8 // e.g. []*Value and []*Block, or even []int32 and []bool. 9 10 import ( 11 "bytes" 12 "fmt" 13 "go/format" 14 "io" 15 "log" 16 "os" 17 ) 18 19 type allocator struct { 20 name string // name for alloc/free functions 21 typ string // the type they return/accept 22 mak string // code to make a new object (takes power-of-2 size as fmt arg) 23 capacity string // code to calculate the capacity of an object. Should always report a power of 2. 24 resize string // code to shrink to sub-power-of-two size (takes size as fmt arg) 25 clear string // code for clearing object before putting it on the free list 26 minLog int // log_2 of minimum allocation size 27 maxLog int // log_2 of maximum allocation size 28 } 29 30 type derived struct { 31 name string // name for alloc/free functions 32 typ string // the type they return/accept 33 base string // underlying allocator 34 } 35 36 func genAllocators() { 37 allocators := []allocator{ 38 { 39 name: "ValueSlice", 40 typ: "[]*Value", 41 capacity: "cap(%s)", 42 mak: "make([]*Value, %s)", 43 resize: "%s[:%s]", 44 clear: "for i := range %[1]s {\n%[1]s[i] = nil\n}", 45 minLog: 5, 46 maxLog: 32, 47 }, 48 { 49 name: "Int64Slice", 50 typ: "[]int64", 51 capacity: "cap(%s)", 52 mak: "make([]int64, %s)", 53 resize: "%s[:%s]", 54 clear: "for i := range %[1]s {\n%[1]s[i] = 0\n}", 55 minLog: 5, 56 maxLog: 32, 57 }, 58 { 59 name: "SparseSet", 60 typ: "*sparseSet", 61 capacity: "%s.cap()", 62 mak: "newSparseSet(%s)", 63 resize: "", // larger-sized sparse sets are ok 64 clear: "%s.clear()", 65 minLog: 5, 66 maxLog: 32, 67 }, 68 { 69 name: "SparseMap", 70 typ: "*sparseMap", 71 capacity: "%s.cap()", 72 mak: "newSparseMap(%s)", 73 resize: "", // larger-sized sparse maps are ok 74 clear: "%s.clear()", 75 minLog: 5, 76 maxLog: 32, 77 }, 78 { 79 name: "SparseMapPos", 80 typ: "*sparseMapPos", 81 capacity: "%s.cap()", 82 mak: "newSparseMapPos(%s)", 83 resize: "", // larger-sized sparse maps are ok 84 clear: "%s.clear()", 85 minLog: 5, 86 maxLog: 32, 87 }, 88 } 89 deriveds := []derived{ 90 { 91 name: "BlockSlice", 92 typ: "[]*Block", 93 base: "ValueSlice", 94 }, 95 { 96 name: "IntSlice", 97 typ: "[]int", 98 base: "Int64Slice", 99 }, 100 { 101 name: "Int32Slice", 102 typ: "[]int32", 103 base: "Int64Slice", 104 }, 105 { 106 name: "Int8Slice", 107 typ: "[]int8", 108 base: "Int64Slice", 109 }, 110 { 111 name: "BoolSlice", 112 typ: "[]bool", 113 base: "Int64Slice", 114 }, 115 { 116 name: "IDSlice", 117 typ: "[]ID", 118 base: "Int64Slice", 119 }, 120 } 121 122 w := new(bytes.Buffer) 123 fmt.Fprintf(w, "// Code generated from _gen/allocators.go using 'go generate'; DO NOT EDIT.\n") 124 fmt.Fprintln(w) 125 fmt.Fprintln(w, "package ssa") 126 127 fmt.Fprintln(w, "import (") 128 fmt.Fprintln(w, "\"github.com/go-asm/go/unsafeheader\"") 129 fmt.Fprintln(w, "\"math/bits\"") 130 fmt.Fprintln(w, "\"sync\"") 131 fmt.Fprintln(w, "\"unsafe\"") 132 fmt.Fprintln(w, ")") 133 for _, a := range allocators { 134 genAllocator(w, a) 135 } 136 for _, d := range deriveds { 137 for _, base := range allocators { 138 if base.name == d.base { 139 genDerived(w, d, base) 140 break 141 } 142 } 143 } 144 // gofmt result 145 b := w.Bytes() 146 var err error 147 b, err = format.Source(b) 148 if err != nil { 149 fmt.Printf("%s\n", w.Bytes()) 150 panic(err) 151 } 152 153 if err := os.WriteFile("../allocators.go", b, 0666); err != nil { 154 log.Fatalf("can't write output: %v\n", err) 155 } 156 } 157 func genAllocator(w io.Writer, a allocator) { 158 fmt.Fprintf(w, "var poolFree%s [%d]sync.Pool\n", a.name, a.maxLog-a.minLog) 159 fmt.Fprintf(w, "func (c *Cache) alloc%s(n int) %s {\n", a.name, a.typ) 160 fmt.Fprintf(w, "var s %s\n", a.typ) 161 fmt.Fprintf(w, "n2 := n\n") 162 fmt.Fprintf(w, "if n2 < %d { n2 = %d }\n", 1<<a.minLog, 1<<a.minLog) 163 fmt.Fprintf(w, "b := bits.Len(uint(n2-1))\n") 164 fmt.Fprintf(w, "v := poolFree%s[b-%d].Get()\n", a.name, a.minLog) 165 fmt.Fprintf(w, "if v == nil {\n") 166 fmt.Fprintf(w, " s = %s\n", fmt.Sprintf(a.mak, "1<<b")) 167 fmt.Fprintf(w, "} else {\n") 168 if a.typ[0] == '*' { 169 fmt.Fprintf(w, "s = v.(%s)\n", a.typ) 170 } else { 171 fmt.Fprintf(w, "sp := v.(*%s)\n", a.typ) 172 fmt.Fprintf(w, "s = *sp\n") 173 fmt.Fprintf(w, "*sp = nil\n") 174 fmt.Fprintf(w, "c.hdr%s = append(c.hdr%s, sp)\n", a.name, a.name) 175 } 176 fmt.Fprintf(w, "}\n") 177 if a.resize != "" { 178 fmt.Fprintf(w, "s = %s\n", fmt.Sprintf(a.resize, "s", "n")) 179 } 180 fmt.Fprintf(w, "return s\n") 181 fmt.Fprintf(w, "}\n") 182 fmt.Fprintf(w, "func (c *Cache) free%s(s %s) {\n", a.name, a.typ) 183 fmt.Fprintf(w, "%s\n", fmt.Sprintf(a.clear, "s")) 184 fmt.Fprintf(w, "b := bits.Len(uint(%s) - 1)\n", fmt.Sprintf(a.capacity, "s")) 185 if a.typ[0] == '*' { 186 fmt.Fprintf(w, "poolFree%s[b-%d].Put(s)\n", a.name, a.minLog) 187 } else { 188 fmt.Fprintf(w, "var sp *%s\n", a.typ) 189 fmt.Fprintf(w, "if len(c.hdr%s) == 0 {\n", a.name) 190 fmt.Fprintf(w, " sp = new(%s)\n", a.typ) 191 fmt.Fprintf(w, "} else {\n") 192 fmt.Fprintf(w, " sp = c.hdr%s[len(c.hdr%s)-1]\n", a.name, a.name) 193 fmt.Fprintf(w, " c.hdr%s[len(c.hdr%s)-1] = nil\n", a.name, a.name) 194 fmt.Fprintf(w, " c.hdr%s = c.hdr%s[:len(c.hdr%s)-1]\n", a.name, a.name, a.name) 195 fmt.Fprintf(w, "}\n") 196 fmt.Fprintf(w, "*sp = s\n") 197 fmt.Fprintf(w, "poolFree%s[b-%d].Put(sp)\n", a.name, a.minLog) 198 } 199 fmt.Fprintf(w, "}\n") 200 } 201 func genDerived(w io.Writer, d derived, base allocator) { 202 fmt.Fprintf(w, "func (c *Cache) alloc%s(n int) %s {\n", d.name, d.typ) 203 if d.typ[:2] != "[]" || base.typ[:2] != "[]" { 204 panic(fmt.Sprintf("bad derived types: %s %s", d.typ, base.typ)) 205 } 206 fmt.Fprintf(w, "var base %s\n", base.typ[2:]) 207 fmt.Fprintf(w, "var derived %s\n", d.typ[2:]) 208 fmt.Fprintf(w, "if unsafe.Sizeof(base)%%unsafe.Sizeof(derived) != 0 { panic(\"bad\") }\n") 209 fmt.Fprintf(w, "scale := unsafe.Sizeof(base)/unsafe.Sizeof(derived)\n") 210 fmt.Fprintf(w, "b := c.alloc%s(int((uintptr(n)+scale-1)/scale))\n", base.name) 211 fmt.Fprintf(w, "s := unsafeheader.Slice {\n") 212 fmt.Fprintf(w, " Data: unsafe.Pointer(&b[0]),\n") 213 fmt.Fprintf(w, " Len: n,\n") 214 fmt.Fprintf(w, " Cap: cap(b)*int(scale),\n") 215 fmt.Fprintf(w, " }\n") 216 fmt.Fprintf(w, "return *(*%s)(unsafe.Pointer(&s))\n", d.typ) 217 fmt.Fprintf(w, "}\n") 218 fmt.Fprintf(w, "func (c *Cache) free%s(s %s) {\n", d.name, d.typ) 219 fmt.Fprintf(w, "var base %s\n", base.typ[2:]) 220 fmt.Fprintf(w, "var derived %s\n", d.typ[2:]) 221 fmt.Fprintf(w, "scale := unsafe.Sizeof(base)/unsafe.Sizeof(derived)\n") 222 fmt.Fprintf(w, "b := unsafeheader.Slice {\n") 223 fmt.Fprintf(w, " Data: unsafe.Pointer(&s[0]),\n") 224 fmt.Fprintf(w, " Len: int((uintptr(len(s))+scale-1)/scale),\n") 225 fmt.Fprintf(w, " Cap: int((uintptr(cap(s))+scale-1)/scale),\n") 226 fmt.Fprintf(w, " }\n") 227 fmt.Fprintf(w, "c.free%s(*(*%s)(unsafe.Pointer(&b)))\n", base.name, base.typ) 228 fmt.Fprintf(w, "}\n") 229 }