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