1
2
3
4
5
6
7 package main
8
9 import (
10 "bytes"
11 "flag"
12 "fmt"
13 "go/format"
14 "log"
15 "math/bits"
16 "os"
17 "path"
18 "regexp"
19 "runtime"
20 "runtime/pprof"
21 "runtime/trace"
22 "slices"
23 "sort"
24 "strings"
25 "sync"
26 )
27
28
29
30
31 type arch struct {
32 name string
33 pkg string
34 genfile string
35 ops []opData
36 blocks []blockData
37 regnames []string
38 ParamIntRegNames string
39 ParamFloatRegNames string
40 gpregmask regMask
41 fpregmask regMask
42 fp32regmask regMask
43 fp64regmask regMask
44 specialregmask regMask
45 framepointerreg int8
46 linkreg int8
47 generic bool
48 imports []string
49 }
50
51 type opData struct {
52 name string
53 reg regInfo
54 asm string
55 typ string
56 aux string
57 rematerializeable bool
58 argLength int32
59 commutative bool
60 resultInArg0 bool
61 resultNotInArgs bool
62 clobberFlags bool
63 needIntTemp bool
64 call bool
65 tailCall bool
66 nilCheck bool
67 faultOnNilArg0 bool
68 faultOnNilArg1 bool
69 hasSideEffects bool
70 zeroWidth bool
71 unsafePoint bool
72 symEffect string
73 scale uint8
74 }
75
76 type blockData struct {
77 name string
78 controls int
79 aux string
80 }
81
82 type regInfo struct {
83
84
85 inputs []regMask
86
87
88 clobbers regMask
89
90 outputs []regMask
91 }
92
93 type regMask uint64
94
95 func (a arch) regMaskComment(r regMask) string {
96 var buf strings.Builder
97 for i := uint64(0); r != 0; i++ {
98 if r&1 != 0 {
99 if buf.Len() == 0 {
100 buf.WriteString(" //")
101 }
102 buf.WriteString(" ")
103 buf.WriteString(a.regnames[i])
104 }
105 r >>= 1
106 }
107 return buf.String()
108 }
109
110 var archs []arch
111
112 var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to `file`")
113 var memprofile = flag.String("memprofile", "", "write memory profile to `file`")
114 var tracefile = flag.String("trace", "", "write trace to `file`")
115
116 func main() {
117 flag.Parse()
118 if *cpuprofile != "" {
119 f, err := os.Create(*cpuprofile)
120 if err != nil {
121 log.Fatal("could not create CPU profile: ", err)
122 }
123 defer f.Close()
124 if err := pprof.StartCPUProfile(f); err != nil {
125 log.Fatal("could not start CPU profile: ", err)
126 }
127 defer pprof.StopCPUProfile()
128 }
129 if *tracefile != "" {
130 f, err := os.Create(*tracefile)
131 if err != nil {
132 log.Fatalf("failed to create trace output file: %v", err)
133 }
134 defer func() {
135 if err := f.Close(); err != nil {
136 log.Fatalf("failed to close trace file: %v", err)
137 }
138 }()
139
140 if err := trace.Start(f); err != nil {
141 log.Fatalf("failed to start trace: %v", err)
142 }
143 defer trace.Stop()
144 }
145
146 slices.SortFunc(archs, func(a, b arch) int {
147 return strings.Compare(a.name, b.name)
148 })
149
150
151
152
153
154
155
156
157
158
159 tasks := []func(){
160 genOp,
161 genAllocators,
162 }
163 for _, a := range archs {
164 a := a
165 tasks = append(tasks, func() {
166 genRules(a)
167 genSplitLoadRules(a)
168 genLateLowerRules(a)
169 })
170 }
171 var wg sync.WaitGroup
172 for _, task := range tasks {
173 task := task
174 wg.Add(1)
175 go func() {
176 task()
177 wg.Done()
178 }()
179 }
180 wg.Wait()
181
182 if *memprofile != "" {
183 f, err := os.Create(*memprofile)
184 if err != nil {
185 log.Fatal("could not create memory profile: ", err)
186 }
187 defer f.Close()
188 runtime.GC()
189 if err := pprof.WriteHeapProfile(f); err != nil {
190 log.Fatal("could not write memory profile: ", err)
191 }
192 }
193 }
194
195 func genOp() {
196 w := new(bytes.Buffer)
197 fmt.Fprintf(w, "// Code generated from _gen/*Ops.go using 'go generate'; DO NOT EDIT.\n")
198 fmt.Fprintln(w)
199 fmt.Fprintln(w, "package ssa")
200
201 fmt.Fprintln(w, "import (")
202 fmt.Fprintln(w, "\"cmd/internal/obj\"")
203 for _, a := range archs {
204 if a.pkg != "" {
205 fmt.Fprintf(w, "%q\n", a.pkg)
206 }
207 }
208 fmt.Fprintln(w, ")")
209
210
211 fmt.Fprintln(w, "const (")
212 fmt.Fprintln(w, "BlockInvalid BlockKind = iota")
213 for _, a := range archs {
214 fmt.Fprintln(w)
215 for _, d := range a.blocks {
216 fmt.Fprintf(w, "Block%s%s\n", a.Name(), d.name)
217 }
218 }
219 fmt.Fprintln(w, ")")
220
221
222 fmt.Fprintln(w, "var blockString = [...]string{")
223 fmt.Fprintln(w, "BlockInvalid:\"BlockInvalid\",")
224 for _, a := range archs {
225 fmt.Fprintln(w)
226 for _, b := range a.blocks {
227 fmt.Fprintf(w, "Block%s%s:\"%s\",\n", a.Name(), b.name, b.name)
228 }
229 }
230 fmt.Fprintln(w, "}")
231 fmt.Fprintln(w, "func (k BlockKind) String() string {return blockString[k]}")
232
233
234 fmt.Fprintln(w, "func (k BlockKind) AuxIntType() string {")
235 fmt.Fprintln(w, "switch k {")
236 for _, a := range archs {
237 for _, b := range a.blocks {
238 if b.auxIntType() == "invalid" {
239 continue
240 }
241 fmt.Fprintf(w, "case Block%s%s: return \"%s\"\n", a.Name(), b.name, b.auxIntType())
242 }
243 }
244 fmt.Fprintln(w, "}")
245 fmt.Fprintln(w, "return \"\"")
246 fmt.Fprintln(w, "}")
247
248
249 fmt.Fprintln(w, "const (")
250 fmt.Fprintln(w, "OpInvalid Op = iota")
251 for _, a := range archs {
252 fmt.Fprintln(w)
253 for _, v := range a.ops {
254 if v.name == "Invalid" {
255 continue
256 }
257 fmt.Fprintf(w, "Op%s%s\n", a.Name(), v.name)
258 }
259 }
260 fmt.Fprintln(w, ")")
261
262
263 fmt.Fprintln(w, "var opcodeTable = [...]opInfo{")
264 fmt.Fprintln(w, " { name: \"OpInvalid\" },")
265 for _, a := range archs {
266 fmt.Fprintln(w)
267
268 pkg := path.Base(a.pkg)
269 for _, v := range a.ops {
270 if v.name == "Invalid" {
271 continue
272 }
273 fmt.Fprintln(w, "{")
274 fmt.Fprintf(w, "name:\"%s\",\n", v.name)
275
276
277 if v.aux != "" {
278 fmt.Fprintf(w, "auxType: aux%s,\n", v.aux)
279 }
280 fmt.Fprintf(w, "argLen: %d,\n", v.argLength)
281
282 if v.rematerializeable {
283 if v.reg.clobbers != 0 {
284 log.Fatalf("%s is rematerializeable and clobbers registers", v.name)
285 }
286 if v.clobberFlags {
287 log.Fatalf("%s is rematerializeable and clobbers flags", v.name)
288 }
289 fmt.Fprintln(w, "rematerializeable: true,")
290 }
291 if v.commutative {
292 fmt.Fprintln(w, "commutative: true,")
293 }
294 if v.resultInArg0 {
295 fmt.Fprintln(w, "resultInArg0: true,")
296
297
298 if v.name != "Convert" && v.reg.inputs[0] != v.reg.outputs[0] {
299 log.Fatalf("%s: input[0] and output[0] must use the same registers for %s", a.name, v.name)
300 }
301 if v.name != "Convert" && v.commutative && v.reg.inputs[1] != v.reg.outputs[0] {
302 log.Fatalf("%s: input[1] and output[0] must use the same registers for %s", a.name, v.name)
303 }
304 }
305 if v.resultNotInArgs {
306 fmt.Fprintln(w, "resultNotInArgs: true,")
307 }
308 if v.clobberFlags {
309 fmt.Fprintln(w, "clobberFlags: true,")
310 }
311 if v.needIntTemp {
312 fmt.Fprintln(w, "needIntTemp: true,")
313 }
314 if v.call {
315 fmt.Fprintln(w, "call: true,")
316 }
317 if v.tailCall {
318 fmt.Fprintln(w, "tailCall: true,")
319 }
320 if v.nilCheck {
321 fmt.Fprintln(w, "nilCheck: true,")
322 }
323 if v.faultOnNilArg0 {
324 fmt.Fprintln(w, "faultOnNilArg0: true,")
325 if v.aux != "Sym" && v.aux != "SymOff" && v.aux != "SymValAndOff" && v.aux != "Int64" && v.aux != "Int32" && v.aux != "" {
326 log.Fatalf("faultOnNilArg0 with aux %s not allowed", v.aux)
327 }
328 }
329 if v.faultOnNilArg1 {
330 fmt.Fprintln(w, "faultOnNilArg1: true,")
331 if v.aux != "Sym" && v.aux != "SymOff" && v.aux != "SymValAndOff" && v.aux != "Int64" && v.aux != "Int32" && v.aux != "" {
332 log.Fatalf("faultOnNilArg1 with aux %s not allowed", v.aux)
333 }
334 }
335 if v.hasSideEffects {
336 fmt.Fprintln(w, "hasSideEffects: true,")
337 }
338 if v.zeroWidth {
339 fmt.Fprintln(w, "zeroWidth: true,")
340 }
341 if v.unsafePoint {
342 fmt.Fprintln(w, "unsafePoint: true,")
343 }
344 needEffect := strings.HasPrefix(v.aux, "Sym")
345 if v.symEffect != "" {
346 if !needEffect {
347 log.Fatalf("symEffect with aux %s not allowed", v.aux)
348 }
349 fmt.Fprintf(w, "symEffect: Sym%s,\n", strings.Replace(v.symEffect, ",", "|Sym", -1))
350 } else if needEffect {
351 log.Fatalf("symEffect needed for aux %s", v.aux)
352 }
353 if a.name == "generic" {
354 fmt.Fprintln(w, "generic:true,")
355 fmt.Fprintln(w, "},")
356
357 continue
358 }
359 if v.asm != "" {
360 fmt.Fprintf(w, "asm: %s.A%s,\n", pkg, v.asm)
361 }
362 if v.scale != 0 {
363 fmt.Fprintf(w, "scale: %d,\n", v.scale)
364 }
365 fmt.Fprintln(w, "reg:regInfo{")
366
367
368
369
370 var s []intPair
371 for i, r := range v.reg.inputs {
372 if r != 0 {
373 s = append(s, intPair{countRegs(r), i})
374 }
375 }
376 if len(s) > 0 {
377 sort.Sort(byKey(s))
378 fmt.Fprintln(w, "inputs: []inputInfo{")
379 for _, p := range s {
380 r := v.reg.inputs[p.val]
381 fmt.Fprintf(w, "{%d,%d},%s\n", p.val, r, a.regMaskComment(r))
382 }
383 fmt.Fprintln(w, "},")
384 }
385
386 if v.reg.clobbers > 0 {
387 fmt.Fprintf(w, "clobbers: %d,%s\n", v.reg.clobbers, a.regMaskComment(v.reg.clobbers))
388 }
389
390
391 s = s[:0]
392 for i, r := range v.reg.outputs {
393 s = append(s, intPair{countRegs(r), i})
394 }
395 if len(s) > 0 {
396 sort.Sort(byKey(s))
397 fmt.Fprintln(w, "outputs: []outputInfo{")
398 for _, p := range s {
399 r := v.reg.outputs[p.val]
400 fmt.Fprintf(w, "{%d,%d},%s\n", p.val, r, a.regMaskComment(r))
401 }
402 fmt.Fprintln(w, "},")
403 }
404 fmt.Fprintln(w, "},")
405 fmt.Fprintln(w, "},")
406 }
407 }
408 fmt.Fprintln(w, "}")
409
410 fmt.Fprintln(w, "func (o Op) Asm() obj.As {return opcodeTable[o].asm}")
411 fmt.Fprintln(w, "func (o Op) Scale() int16 {return int16(opcodeTable[o].scale)}")
412
413
414 fmt.Fprintln(w, "func (o Op) String() string {return opcodeTable[o].name }")
415
416 fmt.Fprintln(w, "func (o Op) SymEffect() SymEffect { return opcodeTable[o].symEffect }")
417 fmt.Fprintln(w, "func (o Op) IsCall() bool { return opcodeTable[o].call }")
418 fmt.Fprintln(w, "func (o Op) IsTailCall() bool { return opcodeTable[o].tailCall }")
419 fmt.Fprintln(w, "func (o Op) HasSideEffects() bool { return opcodeTable[o].hasSideEffects }")
420 fmt.Fprintln(w, "func (o Op) UnsafePoint() bool { return opcodeTable[o].unsafePoint }")
421 fmt.Fprintln(w, "func (o Op) ResultInArg0() bool { return opcodeTable[o].resultInArg0 }")
422
423
424 for _, a := range archs {
425 if a.generic {
426 continue
427 }
428 fmt.Fprintf(w, "var registers%s = [...]Register {\n", a.name)
429 var gcRegN int
430 num := map[string]int8{}
431 for i, r := range a.regnames {
432 num[r] = int8(i)
433 pkg := a.pkg[len("cmd/internal/obj/"):]
434 var objname string
435 switch r {
436 case "SB":
437
438 objname = "0"
439 case "SP":
440 objname = pkg + ".REGSP"
441 case "g":
442 objname = pkg + ".REGG"
443 default:
444 objname = pkg + ".REG_" + r
445 }
446
447
448 gcRegIdx := -1
449 if a.gpregmask&(1<<uint(i)) != 0 {
450 gcRegIdx = gcRegN
451 gcRegN++
452 }
453 fmt.Fprintf(w, " {%d, %s, %d, \"%s\"},\n", i, objname, gcRegIdx, r)
454 }
455 parameterRegisterList := func(paramNamesString string) []int8 {
456 paramNamesString = strings.TrimSpace(paramNamesString)
457 if paramNamesString == "" {
458 return nil
459 }
460 paramNames := strings.Split(paramNamesString, " ")
461 var paramRegs []int8
462 for _, regName := range paramNames {
463 if regName == "" {
464
465 continue
466 }
467 if regNum, ok := num[regName]; ok {
468 paramRegs = append(paramRegs, regNum)
469 delete(num, regName)
470 } else {
471 log.Fatalf("parameter register %s for architecture %s not a register name (or repeated in parameter list)", regName, a.name)
472 }
473 }
474 return paramRegs
475 }
476
477 paramIntRegs := parameterRegisterList(a.ParamIntRegNames)
478 paramFloatRegs := parameterRegisterList(a.ParamFloatRegNames)
479
480 if gcRegN > 32 {
481
482 log.Fatalf("too many GC registers (%d > 32) on %s", gcRegN, a.name)
483 }
484 fmt.Fprintln(w, "}")
485 fmt.Fprintf(w, "var paramIntReg%s = %#v\n", a.name, paramIntRegs)
486 fmt.Fprintf(w, "var paramFloatReg%s = %#v\n", a.name, paramFloatRegs)
487 fmt.Fprintf(w, "var gpRegMask%s = regMask(%d)\n", a.name, a.gpregmask)
488 fmt.Fprintf(w, "var fpRegMask%s = regMask(%d)\n", a.name, a.fpregmask)
489 if a.fp32regmask != 0 {
490 fmt.Fprintf(w, "var fp32RegMask%s = regMask(%d)\n", a.name, a.fp32regmask)
491 }
492 if a.fp64regmask != 0 {
493 fmt.Fprintf(w, "var fp64RegMask%s = regMask(%d)\n", a.name, a.fp64regmask)
494 }
495 fmt.Fprintf(w, "var specialRegMask%s = regMask(%d)\n", a.name, a.specialregmask)
496 fmt.Fprintf(w, "var framepointerReg%s = int8(%d)\n", a.name, a.framepointerreg)
497 fmt.Fprintf(w, "var linkReg%s = int8(%d)\n", a.name, a.linkreg)
498 }
499
500
501 b := w.Bytes()
502 var err error
503 b, err = format.Source(b)
504 if err != nil {
505 fmt.Printf("%s\n", w.Bytes())
506 panic(err)
507 }
508
509 if err := os.WriteFile("../opGen.go", b, 0666); err != nil {
510 log.Fatalf("can't write output: %v\n", err)
511 }
512
513
514
515
516
517
518
519 for _, a := range archs {
520 if a.genfile == "" {
521 continue
522 }
523
524 pattern := fmt.Sprintf(`\Wssa\.Op%s([a-zA-Z0-9_]+)\W`, a.name)
525 rxOp, err := regexp.Compile(pattern)
526 if err != nil {
527 log.Fatalf("bad opcode regexp %s: %v", pattern, err)
528 }
529
530 src, err := os.ReadFile(a.genfile)
531 if err != nil {
532 log.Fatalf("can't read %s: %v", a.genfile, err)
533 }
534 seen := make(map[string]bool, len(a.ops))
535 for _, m := range rxOp.FindAllSubmatch(src, -1) {
536 seen[string(m[1])] = true
537 }
538 for _, op := range a.ops {
539 if !seen[op.name] {
540 log.Fatalf("Op%s%s has no code generation in %s", a.name, op.name, a.genfile)
541 }
542 }
543 }
544 }
545
546
547 func (a arch) Name() string {
548 s := a.name
549 if s == "generic" {
550 s = ""
551 }
552 return s
553 }
554
555
556 func countRegs(r regMask) int {
557 return bits.OnesCount64(uint64(r))
558 }
559
560
561 type intPair struct {
562 key, val int
563 }
564 type byKey []intPair
565
566 func (a byKey) Len() int { return len(a) }
567 func (a byKey) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
568 func (a byKey) Less(i, j int) bool { return a[i].key < a[j].key }
569
View as plain text