github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/cmd/compile/internal/ssa/gen/main.go (about) 1 // Copyright 2015 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // +build ignore 6 7 // The gen command generates Go code (in the parent directory) for all 8 // the architecture-specific opcodes, blocks, and rewrites. 9 package main 10 11 import ( 12 "bytes" 13 "flag" 14 "fmt" 15 "go/format" 16 "io/ioutil" 17 "log" 18 "os" 19 "path" 20 "regexp" 21 "runtime" 22 "runtime/pprof" 23 "runtime/trace" 24 "sort" 25 "strings" 26 "sync" 27 ) 28 29 type arch struct { 30 name string 31 pkg string // obj package to import for this arch. 32 genfile string // source file containing opcode code generation. 33 ops []opData 34 blocks []blockData 35 regnames []string 36 gpregmask regMask 37 fpregmask regMask 38 fp32regmask regMask 39 fp64regmask regMask 40 specialregmask regMask 41 framepointerreg int8 42 linkreg int8 43 generic bool 44 imports []string 45 } 46 47 type opData struct { 48 name string 49 reg regInfo 50 asm string 51 typ string // default result type 52 aux string 53 rematerializeable bool 54 argLength int32 // number of arguments, if -1, then this operation has a variable number of arguments 55 commutative bool // this operation is commutative on its first 2 arguments (e.g. addition) 56 resultInArg0 bool // (first, if a tuple) output of v and v.Args[0] must be allocated to the same register 57 resultNotInArgs bool // outputs must not be allocated to the same registers as inputs 58 clobberFlags bool // this op clobbers flags register 59 call bool // is a function call 60 nilCheck bool // this op is a nil check on arg0 61 faultOnNilArg0 bool // this op will fault if arg0 is nil (and aux encodes a small offset) 62 faultOnNilArg1 bool // this op will fault if arg1 is nil (and aux encodes a small offset) 63 usesScratch bool // this op requires scratch memory space 64 hasSideEffects bool // for "reasons", not to be eliminated. E.g., atomic store, #19182. 65 zeroWidth bool // op never translates into any machine code. example: copy, which may sometimes translate to machine code, is not zero-width. 66 unsafePoint bool // this op is an unsafe point, i.e. not safe for async preemption 67 symEffect string // effect this op has on symbol in aux 68 scale uint8 // amd64/386 indexed load scale 69 } 70 71 type blockData struct { 72 name string // the suffix for this block ("EQ", "LT", etc.) 73 controls int // the number of control values this type of block requires 74 auxint string // the type of the AuxInt value, if any 75 } 76 77 type regInfo struct { 78 // inputs[i] encodes the set of registers allowed for the i'th input. 79 // Inputs that don't use registers (flags, memory, etc.) should be 0. 80 inputs []regMask 81 // clobbers encodes the set of registers that are overwritten by 82 // the instruction (other than the output registers). 83 clobbers regMask 84 // outputs[i] encodes the set of registers allowed for the i'th output. 85 outputs []regMask 86 } 87 88 type regMask uint64 89 90 func (a arch) regMaskComment(r regMask) string { 91 var buf bytes.Buffer 92 for i := uint64(0); r != 0; i++ { 93 if r&1 != 0 { 94 if buf.Len() == 0 { 95 buf.WriteString(" //") 96 } 97 buf.WriteString(" ") 98 buf.WriteString(a.regnames[i]) 99 } 100 r >>= 1 101 } 102 return buf.String() 103 } 104 105 var archs []arch 106 107 var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to `file`") 108 var memprofile = flag.String("memprofile", "", "write memory profile to `file`") 109 var tracefile = flag.String("trace", "", "write trace to `file`") 110 111 func main() { 112 flag.Parse() 113 if *cpuprofile != "" { 114 f, err := os.Create(*cpuprofile) 115 if err != nil { 116 log.Fatal("could not create CPU profile: ", err) 117 } 118 defer f.Close() 119 if err := pprof.StartCPUProfile(f); err != nil { 120 log.Fatal("could not start CPU profile: ", err) 121 } 122 defer pprof.StopCPUProfile() 123 } 124 if *tracefile != "" { 125 f, err := os.Create(*tracefile) 126 if err != nil { 127 log.Fatalf("failed to create trace output file: %v", err) 128 } 129 defer func() { 130 if err := f.Close(); err != nil { 131 log.Fatalf("failed to close trace file: %v", err) 132 } 133 }() 134 135 if err := trace.Start(f); err != nil { 136 log.Fatalf("failed to start trace: %v", err) 137 } 138 defer trace.Stop() 139 } 140 141 sort.Sort(ArchsByName(archs)) 142 143 // The generate tasks are run concurrently, since they are CPU-intensive 144 // that can easily make use of many cores on a machine. 145 // 146 // Note that there is no limit on the concurrency at the moment. On a 147 // four-core laptop at the time of writing, peak RSS usually reaches 148 // ~200MiB, which seems doable by practically any machine nowadays. If 149 // that stops being the case, we can cap this func to a fixed number of 150 // architectures being generated at once. 151 152 tasks := []func(){ 153 genOp, 154 } 155 for _, a := range archs { 156 a := a // the funcs are ran concurrently at a later time 157 tasks = append(tasks, func() { 158 genRules(a) 159 genSplitLoadRules(a) 160 }) 161 } 162 var wg sync.WaitGroup 163 for _, task := range tasks { 164 task := task 165 wg.Add(1) 166 go func() { 167 task() 168 wg.Done() 169 }() 170 } 171 wg.Wait() 172 173 if *memprofile != "" { 174 f, err := os.Create(*memprofile) 175 if err != nil { 176 log.Fatal("could not create memory profile: ", err) 177 } 178 defer f.Close() 179 runtime.GC() // get up-to-date statistics 180 if err := pprof.WriteHeapProfile(f); err != nil { 181 log.Fatal("could not write memory profile: ", err) 182 } 183 } 184 } 185 186 func genOp() { 187 w := new(bytes.Buffer) 188 fmt.Fprintf(w, "// Code generated from gen/*Ops.go; DO NOT EDIT.\n") 189 fmt.Fprintln(w) 190 fmt.Fprintln(w, "package ssa") 191 192 fmt.Fprintln(w, "import (") 193 fmt.Fprintln(w, "\"github.com/gagliardetto/golang-go/cmd/internal/obj\"") 194 for _, a := range archs { 195 if a.pkg != "" { 196 fmt.Fprintf(w, "%q\n", a.pkg) 197 } 198 } 199 fmt.Fprintln(w, ")") 200 201 // generate Block* declarations 202 fmt.Fprintln(w, "const (") 203 fmt.Fprintln(w, "BlockInvalid BlockKind = iota") 204 for _, a := range archs { 205 fmt.Fprintln(w) 206 for _, d := range a.blocks { 207 fmt.Fprintf(w, "Block%s%s\n", a.Name(), d.name) 208 } 209 } 210 fmt.Fprintln(w, ")") 211 212 // generate block kind string method 213 fmt.Fprintln(w, "var blockString = [...]string{") 214 fmt.Fprintln(w, "BlockInvalid:\"BlockInvalid\",") 215 for _, a := range archs { 216 fmt.Fprintln(w) 217 for _, b := range a.blocks { 218 fmt.Fprintf(w, "Block%s%s:\"%s\",\n", a.Name(), b.name, b.name) 219 } 220 } 221 fmt.Fprintln(w, "}") 222 fmt.Fprintln(w, "func (k BlockKind) String() string {return blockString[k]}") 223 224 // generate block kind auxint method 225 fmt.Fprintln(w, "func (k BlockKind) AuxIntType() string {") 226 fmt.Fprintln(w, "switch k {") 227 for _, a := range archs { 228 for _, b := range a.blocks { 229 if b.auxint == "" { 230 continue 231 } 232 fmt.Fprintf(w, "case Block%s%s: return \"%s\"\n", a.Name(), b.name, b.auxint) 233 } 234 } 235 fmt.Fprintln(w, "}") 236 fmt.Fprintln(w, "return \"\"") 237 fmt.Fprintln(w, "}") 238 239 // generate Op* declarations 240 fmt.Fprintln(w, "const (") 241 fmt.Fprintln(w, "OpInvalid Op = iota") // make sure OpInvalid is 0. 242 for _, a := range archs { 243 fmt.Fprintln(w) 244 for _, v := range a.ops { 245 if v.name == "Invalid" { 246 continue 247 } 248 fmt.Fprintf(w, "Op%s%s\n", a.Name(), v.name) 249 } 250 } 251 fmt.Fprintln(w, ")") 252 253 // generate OpInfo table 254 fmt.Fprintln(w, "var opcodeTable = [...]opInfo{") 255 fmt.Fprintln(w, " { name: \"OpInvalid\" },") 256 for _, a := range archs { 257 fmt.Fprintln(w) 258 259 pkg := path.Base(a.pkg) 260 for _, v := range a.ops { 261 if v.name == "Invalid" { 262 continue 263 } 264 fmt.Fprintln(w, "{") 265 fmt.Fprintf(w, "name:\"%s\",\n", v.name) 266 267 // flags 268 if v.aux != "" { 269 fmt.Fprintf(w, "auxType: aux%s,\n", v.aux) 270 } 271 fmt.Fprintf(w, "argLen: %d,\n", v.argLength) 272 273 if v.rematerializeable { 274 if v.reg.clobbers != 0 { 275 log.Fatalf("%s is rematerializeable and clobbers registers", v.name) 276 } 277 if v.clobberFlags { 278 log.Fatalf("%s is rematerializeable and clobbers flags", v.name) 279 } 280 fmt.Fprintln(w, "rematerializeable: true,") 281 } 282 if v.commutative { 283 fmt.Fprintln(w, "commutative: true,") 284 } 285 if v.resultInArg0 { 286 fmt.Fprintln(w, "resultInArg0: true,") 287 // OpConvert's register mask is selected dynamically, 288 // so don't try to check it in the static table. 289 if v.name != "Convert" && v.reg.inputs[0] != v.reg.outputs[0] { 290 log.Fatalf("%s: input[0] and output[0] must use the same registers for %s", a.name, v.name) 291 } 292 if v.name != "Convert" && v.commutative && v.reg.inputs[1] != v.reg.outputs[0] { 293 log.Fatalf("%s: input[1] and output[0] must use the same registers for %s", a.name, v.name) 294 } 295 } 296 if v.resultNotInArgs { 297 fmt.Fprintln(w, "resultNotInArgs: true,") 298 } 299 if v.clobberFlags { 300 fmt.Fprintln(w, "clobberFlags: true,") 301 } 302 if v.call { 303 fmt.Fprintln(w, "call: true,") 304 } 305 if v.nilCheck { 306 fmt.Fprintln(w, "nilCheck: true,") 307 } 308 if v.faultOnNilArg0 { 309 fmt.Fprintln(w, "faultOnNilArg0: true,") 310 if v.aux != "SymOff" && v.aux != "SymValAndOff" && v.aux != "Int64" && v.aux != "Int32" && v.aux != "" { 311 log.Fatalf("faultOnNilArg0 with aux %s not allowed", v.aux) 312 } 313 } 314 if v.faultOnNilArg1 { 315 fmt.Fprintln(w, "faultOnNilArg1: true,") 316 if v.aux != "SymOff" && v.aux != "SymValAndOff" && v.aux != "Int64" && v.aux != "Int32" && v.aux != "" { 317 log.Fatalf("faultOnNilArg1 with aux %s not allowed", v.aux) 318 } 319 } 320 if v.usesScratch { 321 fmt.Fprintln(w, "usesScratch: true,") 322 } 323 if v.hasSideEffects { 324 fmt.Fprintln(w, "hasSideEffects: true,") 325 } 326 if v.zeroWidth { 327 fmt.Fprintln(w, "zeroWidth: true,") 328 } 329 if v.unsafePoint { 330 fmt.Fprintln(w, "unsafePoint: true,") 331 } 332 needEffect := strings.HasPrefix(v.aux, "Sym") 333 if v.symEffect != "" { 334 if !needEffect { 335 log.Fatalf("symEffect with aux %s not allowed", v.aux) 336 } 337 fmt.Fprintf(w, "symEffect: Sym%s,\n", strings.Replace(v.symEffect, ",", "|Sym", -1)) 338 } else if needEffect { 339 log.Fatalf("symEffect needed for aux %s", v.aux) 340 } 341 if a.name == "generic" { 342 fmt.Fprintln(w, "generic:true,") 343 fmt.Fprintln(w, "},") // close op 344 // generic ops have no reg info or asm 345 continue 346 } 347 if v.asm != "" { 348 fmt.Fprintf(w, "asm: %s.A%s,\n", pkg, v.asm) 349 } 350 if v.scale != 0 { 351 fmt.Fprintf(w, "scale: %d,\n", v.scale) 352 } 353 fmt.Fprintln(w, "reg:regInfo{") 354 355 // Compute input allocation order. We allocate from the 356 // most to the least constrained input. This order guarantees 357 // that we will always be able to find a register. 358 var s []intPair 359 for i, r := range v.reg.inputs { 360 if r != 0 { 361 s = append(s, intPair{countRegs(r), i}) 362 } 363 } 364 if len(s) > 0 { 365 sort.Sort(byKey(s)) 366 fmt.Fprintln(w, "inputs: []inputInfo{") 367 for _, p := range s { 368 r := v.reg.inputs[p.val] 369 fmt.Fprintf(w, "{%d,%d},%s\n", p.val, r, a.regMaskComment(r)) 370 } 371 fmt.Fprintln(w, "},") 372 } 373 374 if v.reg.clobbers > 0 { 375 fmt.Fprintf(w, "clobbers: %d,%s\n", v.reg.clobbers, a.regMaskComment(v.reg.clobbers)) 376 } 377 378 // reg outputs 379 s = s[:0] 380 for i, r := range v.reg.outputs { 381 s = append(s, intPair{countRegs(r), i}) 382 } 383 if len(s) > 0 { 384 sort.Sort(byKey(s)) 385 fmt.Fprintln(w, "outputs: []outputInfo{") 386 for _, p := range s { 387 r := v.reg.outputs[p.val] 388 fmt.Fprintf(w, "{%d,%d},%s\n", p.val, r, a.regMaskComment(r)) 389 } 390 fmt.Fprintln(w, "},") 391 } 392 fmt.Fprintln(w, "},") // close reg info 393 fmt.Fprintln(w, "},") // close op 394 } 395 } 396 fmt.Fprintln(w, "}") 397 398 fmt.Fprintln(w, "func (o Op) Asm() obj.As {return opcodeTable[o].asm}") 399 fmt.Fprintln(w, "func (o Op) Scale() int16 {return int16(opcodeTable[o].scale)}") 400 401 // generate op string method 402 fmt.Fprintln(w, "func (o Op) String() string {return opcodeTable[o].name }") 403 404 fmt.Fprintln(w, "func (o Op) UsesScratch() bool { return opcodeTable[o].usesScratch }") 405 406 fmt.Fprintln(w, "func (o Op) SymEffect() SymEffect { return opcodeTable[o].symEffect }") 407 fmt.Fprintln(w, "func (o Op) IsCall() bool { return opcodeTable[o].call }") 408 fmt.Fprintln(w, "func (o Op) HasSideEffects() bool { return opcodeTable[o].hasSideEffects }") 409 fmt.Fprintln(w, "func (o Op) UnsafePoint() bool { return opcodeTable[o].unsafePoint }") 410 411 // generate registers 412 for _, a := range archs { 413 if a.generic { 414 continue 415 } 416 fmt.Fprintf(w, "var registers%s = [...]Register {\n", a.name) 417 var gcRegN int 418 for i, r := range a.regnames { 419 pkg := a.pkg[len("github.com/gagliardetto/golang-go/cmd/internal/obj/"):] 420 var objname string // name in cmd/internal/obj/$ARCH 421 switch r { 422 case "SB": 423 // SB isn't a real register. cmd/internal/obj expects 0 in this case. 424 objname = "0" 425 case "SP": 426 objname = pkg + ".REGSP" 427 case "g": 428 objname = pkg + ".REGG" 429 default: 430 objname = pkg + ".REG_" + r 431 } 432 // Assign a GC register map index to registers 433 // that may contain pointers. 434 gcRegIdx := -1 435 if a.gpregmask&(1<<uint(i)) != 0 { 436 gcRegIdx = gcRegN 437 gcRegN++ 438 } 439 fmt.Fprintf(w, " {%d, %s, %d, \"%s\"},\n", i, objname, gcRegIdx, r) 440 } 441 if gcRegN > 32 { 442 // Won't fit in a uint32 mask. 443 log.Fatalf("too many GC registers (%d > 32) on %s", gcRegN, a.name) 444 } 445 fmt.Fprintln(w, "}") 446 fmt.Fprintf(w, "var gpRegMask%s = regMask(%d)\n", a.name, a.gpregmask) 447 fmt.Fprintf(w, "var fpRegMask%s = regMask(%d)\n", a.name, a.fpregmask) 448 if a.fp32regmask != 0 { 449 fmt.Fprintf(w, "var fp32RegMask%s = regMask(%d)\n", a.name, a.fp32regmask) 450 } 451 if a.fp64regmask != 0 { 452 fmt.Fprintf(w, "var fp64RegMask%s = regMask(%d)\n", a.name, a.fp64regmask) 453 } 454 fmt.Fprintf(w, "var specialRegMask%s = regMask(%d)\n", a.name, a.specialregmask) 455 fmt.Fprintf(w, "var framepointerReg%s = int8(%d)\n", a.name, a.framepointerreg) 456 fmt.Fprintf(w, "var linkReg%s = int8(%d)\n", a.name, a.linkreg) 457 } 458 459 // gofmt result 460 b := w.Bytes() 461 var err error 462 b, err = format.Source(b) 463 if err != nil { 464 fmt.Printf("%s\n", w.Bytes()) 465 panic(err) 466 } 467 468 if err := ioutil.WriteFile("../opGen.go", b, 0666); err != nil { 469 log.Fatalf("can't write output: %v\n", err) 470 } 471 472 // Check that the arch genfile handles all the arch-specific opcodes. 473 // This is very much a hack, but it is better than nothing. 474 // 475 // Do a single regexp pass to record all ops being handled in a map, and 476 // then compare that with the ops list. This is much faster than one 477 // regexp pass per opcode. 478 for _, a := range archs { 479 if a.genfile == "" { 480 continue 481 } 482 483 pattern := fmt.Sprintf(`\Wssa\.Op%s([a-zA-Z0-9_]+)\W`, a.name) 484 rxOp, err := regexp.Compile(pattern) 485 if err != nil { 486 log.Fatalf("bad opcode regexp %s: %v", pattern, err) 487 } 488 489 src, err := ioutil.ReadFile(a.genfile) 490 if err != nil { 491 log.Fatalf("can't read %s: %v", a.genfile, err) 492 } 493 seen := make(map[string]bool, len(a.ops)) 494 for _, m := range rxOp.FindAllSubmatch(src, -1) { 495 seen[string(m[1])] = true 496 } 497 for _, op := range a.ops { 498 if !seen[op.name] { 499 log.Fatalf("Op%s%s has no code generation in %s", a.name, op.name, a.genfile) 500 } 501 } 502 } 503 } 504 505 // Name returns the name of the architecture for use in Op* and Block* enumerations. 506 func (a arch) Name() string { 507 s := a.name 508 if s == "generic" { 509 s = "" 510 } 511 return s 512 } 513 514 // countRegs returns the number of set bits in the register mask. 515 func countRegs(r regMask) int { 516 n := 0 517 for r != 0 { 518 n += int(r & 1) 519 r >>= 1 520 } 521 return n 522 } 523 524 // for sorting a pair of integers by key 525 type intPair struct { 526 key, val int 527 } 528 type byKey []intPair 529 530 func (a byKey) Len() int { return len(a) } 531 func (a byKey) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 532 func (a byKey) Less(i, j int) bool { return a[i].key < a[j].key } 533 534 type ArchsByName []arch 535 536 func (x ArchsByName) Len() int { return len(x) } 537 func (x ArchsByName) Swap(i, j int) { x[i], x[j] = x[j], x[i] } 538 func (x ArchsByName) Less(i, j int) bool { return x[i].name < x[j].name }