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