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//go:build ignore 6// +build ignore 7 8// The gen command generates Go code (in the parent directory) for all 9// the architecture-specific opcodes, blocks, and rewrites. 10package main 11 12import ( 13 "bytes" 14 "flag" 15 "fmt" 16 "go/format" 17 "io/ioutil" 18 "log" 19 "os" 20 "path" 21 "regexp" 22 "runtime" 23 "runtime/pprof" 24 "runtime/trace" 25 "sort" 26 "strings" 27 "sync" 28) 29 30// TODO: capitalize these types, so that we can more easily tell variable names 31// apart from type names, and avoid awkward func parameters like "arch arch". 32 33type arch struct { 34 name string 35 pkg string // obj package to import for this arch. 36 genfile string // source file containing opcode code generation. 37 ops []opData 38 blocks []blockData 39 regnames []string 40 ParamIntRegNames string 41 ParamFloatRegNames string 42 gpregmask regMask 43 fpregmask regMask 44 fp32regmask regMask 45 fp64regmask regMask 46 specialregmask regMask 47 framepointerreg int8 48 linkreg int8 49 generic bool 50 imports []string 51} 52 53type opData struct { 54 name string 55 reg regInfo 56 asm string 57 typ string // default result type 58 aux string 59 rematerializeable bool 60 argLength int32 // number of arguments, if -1, then this operation has a variable number of arguments 61 commutative bool // this operation is commutative on its first 2 arguments (e.g. addition) 62 resultInArg0 bool // (first, if a tuple) output of v and v.Args[0] must be allocated to the same register 63 resultNotInArgs bool // outputs must not be allocated to the same registers as inputs 64 clobberFlags bool // this op clobbers flags register 65 call bool // is a function call 66 tailCall bool // is a tail call 67 nilCheck bool // this op is a nil check on arg0 68 faultOnNilArg0 bool // this op will fault if arg0 is nil (and aux encodes a small offset) 69 faultOnNilArg1 bool // this op will fault if arg1 is nil (and aux encodes a small offset) 70 hasSideEffects bool // for "reasons", not to be eliminated. E.g., atomic store, #19182. 71 zeroWidth bool // op never translates into any machine code. example: copy, which may sometimes translate to machine code, is not zero-width. 72 unsafePoint bool // this op is an unsafe point, i.e. not safe for async preemption 73 symEffect string // effect this op has on symbol in aux 74 scale uint8 // amd64/386 indexed load scale 75} 76 77type blockData struct { 78 name string // the suffix for this block ("EQ", "LT", etc.) 79 controls int // the number of control values this type of block requires 80 aux string // the type of the Aux/AuxInt value, if any 81} 82 83type regInfo struct { 84 // inputs[i] encodes the set of registers allowed for the i'th input. 85 // Inputs that don't use registers (flags, memory, etc.) should be 0. 86 inputs []regMask 87 // clobbers encodes the set of registers that are overwritten by 88 // the instruction (other than the output registers). 89 clobbers regMask 90 // outputs[i] encodes the set of registers allowed for the i'th output. 91 outputs []regMask 92} 93 94type regMask uint64 95 96func (a arch) regMaskComment(r regMask) string { 97 var buf bytes.Buffer 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 111var archs []arch 112 113var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to `file`") 114var memprofile = flag.String("memprofile", "", "write memory profile to `file`") 115var tracefile = flag.String("trace", "", "write trace to `file`") 116 117func 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 sort.Sort(ArchsByName(archs)) 148 149 // The generate tasks are run concurrently, since they are CPU-intensive 150 // that can easily make use of many cores on a machine. 151 // 152 // Note that there is no limit on the concurrency at the moment. On a 153 // four-core laptop at the time of writing, peak RSS usually reaches 154 // ~200MiB, which seems doable by practically any machine nowadays. If 155 // that stops being the case, we can cap this func to a fixed number of 156 // architectures being generated at once. 157 158 tasks := []func(){ 159 genOp, 160 } 161 for _, a := range archs { 162 a := a // the funcs are ran concurrently at a later time 163 tasks = append(tasks, func() { 164 genRules(a) 165 genSplitLoadRules(a) 166 }) 167 } 168 var wg sync.WaitGroup 169 for _, task := range tasks { 170 task := task 171 wg.Add(1) 172 go func() { 173 task() 174 wg.Done() 175 }() 176 } 177 wg.Wait() 178 179 if *memprofile != "" { 180 f, err := os.Create(*memprofile) 181 if err != nil { 182 log.Fatal("could not create memory profile: ", err) 183 } 184 defer f.Close() 185 runtime.GC() // get up-to-date statistics 186 if err := pprof.WriteHeapProfile(f); err != nil { 187 log.Fatal("could not write memory profile: ", err) 188 } 189 } 190} 191 192func genOp() { 193 w := new(bytes.Buffer) 194 fmt.Fprintf(w, "// Code generated from gen/*Ops.go; DO NOT EDIT.\n") 195 fmt.Fprintln(w) 196 fmt.Fprintln(w, "package ssa") 197 198 fmt.Fprintln(w, "import (") 199 fmt.Fprintln(w, "\"cmd/internal/obj\"") 200 for _, a := range archs { 201 if a.pkg != "" { 202 fmt.Fprintf(w, "%q\n", a.pkg) 203 } 204 } 205 fmt.Fprintln(w, ")") 206 207 // generate Block* declarations 208 fmt.Fprintln(w, "const (") 209 fmt.Fprintln(w, "BlockInvalid BlockKind = iota") 210 for _, a := range archs { 211 fmt.Fprintln(w) 212 for _, d := range a.blocks { 213 fmt.Fprintf(w, "Block%s%s\n", a.Name(), d.name) 214 } 215 } 216 fmt.Fprintln(w, ")") 217 218 // generate block kind string method 219 fmt.Fprintln(w, "var blockString = [...]string{") 220 fmt.Fprintln(w, "BlockInvalid:\"BlockInvalid\",") 221 for _, a := range archs { 222 fmt.Fprintln(w) 223 for _, b := range a.blocks { 224 fmt.Fprintf(w, "Block%s%s:\"%s\",\n", a.Name(), b.name, b.name) 225 } 226 } 227 fmt.Fprintln(w, "}") 228 fmt.Fprintln(w, "func (k BlockKind) String() string {return blockString[k]}") 229 230 // generate block kind auxint method 231 fmt.Fprintln(w, "func (k BlockKind) AuxIntType() string {") 232 fmt.Fprintln(w, "switch k {") 233 for _, a := range archs { 234 for _, b := range a.blocks { 235 if b.auxIntType() == "invalid" { 236 continue 237 } 238 fmt.Fprintf(w, "case Block%s%s: return \"%s\"\n", a.Name(), b.name, b.auxIntType()) 239 } 240 } 241 fmt.Fprintln(w, "}") 242 fmt.Fprintln(w, "return \"\"") 243 fmt.Fprintln(w, "}") 244 245 // generate Op* declarations 246 fmt.Fprintln(w, "const (") 247 fmt.Fprintln(w, "OpInvalid Op = iota") // make sure OpInvalid is 0. 248 for _, a := range archs { 249 fmt.Fprintln(w) 250 for _, v := range a.ops { 251 if v.name == "Invalid" { 252 continue 253 } 254 fmt.Fprintf(w, "Op%s%s\n", a.Name(), v.name) 255 } 256 } 257 fmt.Fprintln(w, ")") 258 259 // generate OpInfo table 260 fmt.Fprintln(w, "var opcodeTable = [...]opInfo{") 261 fmt.Fprintln(w, " { name: \"OpInvalid\" },") 262 for _, a := range archs { 263 fmt.Fprintln(w) 264 265 pkg := path.Base(a.pkg) 266 for _, v := range a.ops { 267 if v.name == "Invalid" { 268 continue 269 } 270 fmt.Fprintln(w, "{") 271 fmt.Fprintf(w, "name:\"%s\",\n", v.name) 272 273 // flags 274 if v.aux != "" { 275 fmt.Fprintf(w, "auxType: aux%s,\n", v.aux) 276 } 277 fmt.Fprintf(w, "argLen: %d,\n", v.argLength) 278 279 if v.rematerializeable { 280 if v.reg.clobbers != 0 { 281 log.Fatalf("%s is rematerializeable and clobbers registers", v.name) 282 } 283 if v.clobberFlags { 284 log.Fatalf("%s is rematerializeable and clobbers flags", v.name) 285 } 286 fmt.Fprintln(w, "rematerializeable: true,") 287 } 288 if v.commutative { 289 fmt.Fprintln(w, "commutative: true,") 290 } 291 if v.resultInArg0 { 292 fmt.Fprintln(w, "resultInArg0: true,") 293 // OpConvert's register mask is selected dynamically, 294 // so don't try to check it in the static table. 295 if v.name != "Convert" && v.reg.inputs[0] != v.reg.outputs[0] { 296 log.Fatalf("%s: input[0] and output[0] must use the same registers for %s", a.name, v.name) 297 } 298 if v.name != "Convert" && v.commutative && v.reg.inputs[1] != v.reg.outputs[0] { 299 log.Fatalf("%s: input[1] and output[0] must use the same registers for %s", a.name, v.name) 300 } 301 } 302 if v.resultNotInArgs { 303 fmt.Fprintln(w, "resultNotInArgs: true,") 304 } 305 if v.clobberFlags { 306 fmt.Fprintln(w, "clobberFlags: true,") 307 } 308 if v.call { 309 fmt.Fprintln(w, "call: true,") 310 } 311 if v.tailCall { 312 fmt.Fprintln(w, "tailCall: true,") 313 } 314 if v.nilCheck { 315 fmt.Fprintln(w, "nilCheck: true,") 316 } 317 if v.faultOnNilArg0 { 318 fmt.Fprintln(w, "faultOnNilArg0: true,") 319 if v.aux != "Sym" && v.aux != "SymOff" && v.aux != "SymValAndOff" && v.aux != "Int64" && v.aux != "Int32" && v.aux != "" { 320 log.Fatalf("faultOnNilArg0 with aux %s not allowed", v.aux) 321 } 322 } 323 if v.faultOnNilArg1 { 324 fmt.Fprintln(w, "faultOnNilArg1: true,") 325 if v.aux != "Sym" && v.aux != "SymOff" && v.aux != "SymValAndOff" && v.aux != "Int64" && v.aux != "Int32" && v.aux != "" { 326 log.Fatalf("faultOnNilArg1 with aux %s not allowed", v.aux) 327 } 328 } 329 if v.hasSideEffects { 330 fmt.Fprintln(w, "hasSideEffects: true,") 331 } 332 if v.zeroWidth { 333 fmt.Fprintln(w, "zeroWidth: true,") 334 } 335 if v.unsafePoint { 336 fmt.Fprintln(w, "unsafePoint: true,") 337 } 338 needEffect := strings.HasPrefix(v.aux, "Sym") 339 if v.symEffect != "" { 340 if !needEffect { 341 log.Fatalf("symEffect with aux %s not allowed", v.aux) 342 } 343 fmt.Fprintf(w, "symEffect: Sym%s,\n", strings.Replace(v.symEffect, ",", "|Sym", -1)) 344 } else if needEffect { 345 log.Fatalf("symEffect needed for aux %s", v.aux) 346 } 347 if a.name == "generic" { 348 fmt.Fprintln(w, "generic:true,") 349 fmt.Fprintln(w, "},") // close op 350 // generic ops have no reg info or asm 351 continue 352 } 353 if v.asm != "" { 354 fmt.Fprintf(w, "asm: %s.A%s,\n", pkg, v.asm) 355 } 356 if v.scale != 0 { 357 fmt.Fprintf(w, "scale: %d,\n", v.scale) 358 } 359 fmt.Fprintln(w, "reg:regInfo{") 360 361 // Compute input allocation order. We allocate from the 362 // most to the least constrained input. This order guarantees 363 // that we will always be able to find a register. 364 var s []intPair 365 for i, r := range v.reg.inputs { 366 if r != 0 { 367 s = append(s, intPair{countRegs(r), i}) 368 } 369 } 370 if len(s) > 0 { 371 sort.Sort(byKey(s)) 372 fmt.Fprintln(w, "inputs: []inputInfo{") 373 for _, p := range s { 374 r := v.reg.inputs[p.val] 375 fmt.Fprintf(w, "{%d,%d},%s\n", p.val, r, a.regMaskComment(r)) 376 } 377 fmt.Fprintln(w, "},") 378 } 379 380 if v.reg.clobbers > 0 { 381 fmt.Fprintf(w, "clobbers: %d,%s\n", v.reg.clobbers, a.regMaskComment(v.reg.clobbers)) 382 } 383 384 // reg outputs 385 s = s[:0] 386 for i, r := range v.reg.outputs { 387 s = append(s, intPair{countRegs(r), i}) 388 } 389 if len(s) > 0 { 390 sort.Sort(byKey(s)) 391 fmt.Fprintln(w, "outputs: []outputInfo{") 392 for _, p := range s { 393 r := v.reg.outputs[p.val] 394 fmt.Fprintf(w, "{%d,%d},%s\n", p.val, r, a.regMaskComment(r)) 395 } 396 fmt.Fprintln(w, "},") 397 } 398 fmt.Fprintln(w, "},") // close reg info 399 fmt.Fprintln(w, "},") // close op 400 } 401 } 402 fmt.Fprintln(w, "}") 403 404 fmt.Fprintln(w, "func (o Op) Asm() obj.As {return opcodeTable[o].asm}") 405 fmt.Fprintln(w, "func (o Op) Scale() int16 {return int16(opcodeTable[o].scale)}") 406 407 // generate op string method 408 fmt.Fprintln(w, "func (o Op) String() string {return opcodeTable[o].name }") 409 410 fmt.Fprintln(w, "func (o Op) SymEffect() SymEffect { return opcodeTable[o].symEffect }") 411 fmt.Fprintln(w, "func (o Op) IsCall() bool { return opcodeTable[o].call }") 412 fmt.Fprintln(w, "func (o Op) IsTailCall() bool { return opcodeTable[o].tailCall }") 413 fmt.Fprintln(w, "func (o Op) HasSideEffects() bool { return opcodeTable[o].hasSideEffects }") 414 fmt.Fprintln(w, "func (o Op) UnsafePoint() bool { return opcodeTable[o].unsafePoint }") 415 fmt.Fprintln(w, "func (o Op) ResultInArg0() bool { return opcodeTable[o].resultInArg0 }") 416 417 // generate registers 418 for _, a := range archs { 419 if a.generic { 420 continue 421 } 422 fmt.Fprintf(w, "var registers%s = [...]Register {\n", a.name) 423 var gcRegN int 424 num := map[string]int8{} 425 for i, r := range a.regnames { 426 num[r] = int8(i) 427 pkg := a.pkg[len("cmd/internal/obj/"):] 428 var objname string // name in cmd/internal/obj/$ARCH 429 switch r { 430 case "SB": 431 // SB isn't a real register. cmd/internal/obj expects 0 in this case. 432 objname = "0" 433 case "SP": 434 objname = pkg + ".REGSP" 435 case "g": 436 objname = pkg + ".REGG" 437 default: 438 objname = pkg + ".REG_" + r 439 } 440 // Assign a GC register map index to registers 441 // that may contain pointers. 442 gcRegIdx := -1 443 if a.gpregmask&(1<<uint(i)) != 0 { 444 gcRegIdx = gcRegN 445 gcRegN++ 446 } 447 fmt.Fprintf(w, " {%d, %s, %d, \"%s\"},\n", i, objname, gcRegIdx, r) 448 } 449 parameterRegisterList := func(paramNamesString string) []int8 { 450 paramNamesString = strings.TrimSpace(paramNamesString) 451 if paramNamesString == "" { 452 return nil 453 } 454 paramNames := strings.Split(paramNamesString, " ") 455 var paramRegs []int8 456 for _, regName := range paramNames { 457 if regName == "" { 458 // forgive extra spaces 459 continue 460 } 461 if regNum, ok := num[regName]; ok { 462 paramRegs = append(paramRegs, regNum) 463 delete(num, regName) 464 } else { 465 log.Fatalf("parameter register %s for architecture %s not a register name (or repeated in parameter list)", regName, a.name) 466 } 467 } 468 return paramRegs 469 } 470 471 paramIntRegs := parameterRegisterList(a.ParamIntRegNames) 472 paramFloatRegs := parameterRegisterList(a.ParamFloatRegNames) 473 474 if gcRegN > 32 { 475 // Won't fit in a uint32 mask. 476 log.Fatalf("too many GC registers (%d > 32) on %s", gcRegN, a.name) 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 // gofmt result 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 := ioutil.WriteFile("../opGen.go", b, 0666); err != nil { 504 log.Fatalf("can't write output: %v\n", err) 505 } 506 507 // Check that the arch genfile handles all the arch-specific opcodes. 508 // This is very much a hack, but it is better than nothing. 509 // 510 // Do a single regexp pass to record all ops being handled in a map, and 511 // then compare that with the ops list. This is much faster than one 512 // regexp pass per opcode. 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 := ioutil.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// Name returns the name of the architecture for use in Op* and Block* enumerations. 541func (a arch) Name() string { 542 s := a.name 543 if s == "generic" { 544 s = "" 545 } 546 return s 547} 548 549// countRegs returns the number of set bits in the register mask. 550func countRegs(r regMask) int { 551 n := 0 552 for r != 0 { 553 n += int(r & 1) 554 r >>= 1 555 } 556 return n 557} 558 559// for sorting a pair of integers by key 560type intPair struct { 561 key, val int 562} 563type byKey []intPair 564 565func (a byKey) Len() int { return len(a) } 566func (a byKey) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 567func (a byKey) Less(i, j int) bool { return a[i].key < a[j].key } 568 569type ArchsByName []arch 570 571func (x ArchsByName) Len() int { return len(x) } 572func (x ArchsByName) Swap(i, j int) { x[i], x[j] = x[j], x[i] } 573func (x ArchsByName) Less(i, j int) bool { return x[i].name < x[j].name } 574