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