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