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