github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/compile/ssa/_gen/rulegen.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 // This program generates Go code that applies rewrite rules to a Value. 6 // The generated code implements a function of type func (v *Value) bool 7 // which reports whether if did something. 8 // Ideas stolen from Swift: http://www.hpl.hp.com/techreports/Compaq-DEC/WRL-2000-2.html 9 10 package main 11 12 import ( 13 "bufio" 14 "bytes" 15 "flag" 16 "fmt" 17 "go/ast" 18 "go/format" 19 "go/parser" 20 "go/printer" 21 "go/token" 22 "io" 23 "log" 24 "os" 25 "path" 26 "regexp" 27 "sort" 28 "strconv" 29 "strings" 30 31 "golang.org/x/tools/go/ast/astutil" 32 ) 33 34 // rule syntax: 35 // sexpr [&& extra conditions] => [@block] sexpr 36 // 37 // sexpr are s-expressions (lisp-like parenthesized groupings) 38 // sexpr ::= [variable:](opcode sexpr*) 39 // | variable 40 // | <type> 41 // | [auxint] 42 // | {aux} 43 // 44 // aux ::= variable | {code} 45 // type ::= variable | {code} 46 // variable ::= some token 47 // opcode ::= one of the opcodes from the *Ops.go files 48 49 // special rules: trailing ellipsis "..." (in the outermost sexpr?) must match on both sides of a rule. 50 // trailing three underscore "___" in the outermost match sexpr indicate the presence of 51 // extra ignored args that need not appear in the replacement 52 53 // extra conditions is just a chunk of Go that evaluates to a boolean. It may use 54 // variables declared in the matching tsexpr. The variable "v" is predefined to be 55 // the value matched by the entire rule. 56 57 // If multiple rules match, the first one in file order is selected. 58 59 var ( 60 genLog = flag.Bool("log", false, "generate code that logs; for debugging only") 61 addLine = flag.Bool("line", false, "add line number comment to generated rules; for debugging only") 62 ) 63 64 type Rule struct { 65 Rule string 66 Loc string // file name & line number 67 } 68 69 func (r Rule) String() string { 70 return fmt.Sprintf("rule %q at %s", r.Rule, r.Loc) 71 } 72 73 func normalizeSpaces(s string) string { 74 return strings.Join(strings.Fields(strings.TrimSpace(s)), " ") 75 } 76 77 // parse returns the matching part of the rule, additional conditions, and the result. 78 func (r Rule) parse() (match, cond, result string) { 79 s := strings.Split(r.Rule, "=>") 80 match = normalizeSpaces(s[0]) 81 result = normalizeSpaces(s[1]) 82 cond = "" 83 if i := strings.Index(match, "&&"); i >= 0 { 84 cond = normalizeSpaces(match[i+2:]) 85 match = normalizeSpaces(match[:i]) 86 } 87 return match, cond, result 88 } 89 90 func genRules(arch arch) { genRulesSuffix(arch, "") } 91 func genSplitLoadRules(arch arch) { genRulesSuffix(arch, "splitload") } 92 func genLateLowerRules(arch arch) { genRulesSuffix(arch, "latelower") } 93 94 func genRulesSuffix(arch arch, suff string) { 95 // Open input file. 96 text, err := os.Open(arch.name + suff + ".rules") 97 if err != nil { 98 if suff == "" { 99 // All architectures must have a plain rules file. 100 log.Fatalf("can't read rule file: %v", err) 101 } 102 // Some architectures have bonus rules files that others don't share. That's fine. 103 return 104 } 105 106 // oprules contains a list of rules for each block and opcode 107 blockrules := map[string][]Rule{} 108 oprules := map[string][]Rule{} 109 110 // read rule file 111 scanner := bufio.NewScanner(text) 112 rule := "" 113 var lineno int 114 var ruleLineno int // line number of "=>" 115 for scanner.Scan() { 116 lineno++ 117 line := scanner.Text() 118 if i := strings.Index(line, "//"); i >= 0 { 119 // Remove comments. Note that this isn't string safe, so 120 // it will truncate lines with // inside strings. Oh well. 121 line = line[:i] 122 } 123 rule += " " + line 124 rule = strings.TrimSpace(rule) 125 if rule == "" { 126 continue 127 } 128 if !strings.Contains(rule, "=>") { 129 continue 130 } 131 if ruleLineno == 0 { 132 ruleLineno = lineno 133 } 134 if strings.HasSuffix(rule, "=>") { 135 continue // continue on the next line 136 } 137 if n := balance(rule); n > 0 { 138 continue // open parentheses remain, continue on the next line 139 } else if n < 0 { 140 break // continuing the line can't help, and it will only make errors worse 141 } 142 143 loc := fmt.Sprintf("%s%s.rules:%d", arch.name, suff, ruleLineno) 144 for _, rule2 := range expandOr(rule) { 145 r := Rule{Rule: rule2, Loc: loc} 146 if rawop := strings.Split(rule2, " ")[0][1:]; isBlock(rawop, arch) { 147 blockrules[rawop] = append(blockrules[rawop], r) 148 continue 149 } 150 // Do fancier value op matching. 151 match, _, _ := r.parse() 152 op, oparch, _, _, _, _ := parseValue(match, arch, loc) 153 opname := fmt.Sprintf("Op%s%s", oparch, op.name) 154 oprules[opname] = append(oprules[opname], r) 155 } 156 rule = "" 157 ruleLineno = 0 158 } 159 if err := scanner.Err(); err != nil { 160 log.Fatalf("scanner failed: %v\n", err) 161 } 162 if balance(rule) != 0 { 163 log.Fatalf("%s.rules:%d: unbalanced rule: %v\n", arch.name, lineno, rule) 164 } 165 166 // Order all the ops. 167 var ops []string 168 for op := range oprules { 169 ops = append(ops, op) 170 } 171 sort.Strings(ops) 172 173 genFile := &File{Arch: arch, Suffix: suff} 174 // Main rewrite routine is a switch on v.Op. 175 fn := &Func{Kind: "Value", ArgLen: -1} 176 177 sw := &Switch{Expr: exprf("v.Op")} 178 for _, op := range ops { 179 eop, ok := parseEllipsisRules(oprules[op], arch) 180 if ok { 181 if strings.Contains(oprules[op][0].Rule, "=>") && opByName(arch, op).aux != opByName(arch, eop).aux { 182 panic(fmt.Sprintf("can't use ... for ops that have different aux types: %s and %s", op, eop)) 183 } 184 swc := &Case{Expr: exprf("%s", op)} 185 swc.add(stmtf("v.Op = %s", eop)) 186 swc.add(stmtf("return true")) 187 sw.add(swc) 188 continue 189 } 190 191 swc := &Case{Expr: exprf("%s", op)} 192 swc.add(stmtf("return rewriteValue%s%s_%s(v)", arch.name, suff, op)) 193 sw.add(swc) 194 } 195 if len(sw.List) > 0 { // skip if empty 196 fn.add(sw) 197 } 198 fn.add(stmtf("return false")) 199 genFile.add(fn) 200 201 // Generate a routine per op. Note that we don't make one giant routine 202 // because it is too big for some compilers. 203 for _, op := range ops { 204 rules := oprules[op] 205 _, ok := parseEllipsisRules(oprules[op], arch) 206 if ok { 207 continue 208 } 209 210 // rr is kept between iterations, so that each rule can check 211 // that the previous rule wasn't unconditional. 212 var rr *RuleRewrite 213 fn := &Func{ 214 Kind: "Value", 215 Suffix: fmt.Sprintf("_%s", op), 216 ArgLen: opByName(arch, op).argLength, 217 } 218 fn.add(declReserved("b", "v.Block")) 219 fn.add(declReserved("config", "b.Func.Config")) 220 fn.add(declReserved("fe", "b.Func.fe")) 221 fn.add(declReserved("typ", "&b.Func.Config.Types")) 222 for _, rule := range rules { 223 if rr != nil && !rr.CanFail { 224 log.Fatalf("unconditional rule %s is followed by other rules", rr.Match) 225 } 226 rr = &RuleRewrite{Loc: rule.Loc} 227 rr.Match, rr.Cond, rr.Result = rule.parse() 228 pos, _ := genMatch(rr, arch, rr.Match, fn.ArgLen >= 0) 229 if pos == "" { 230 pos = "v.Pos" 231 } 232 if rr.Cond != "" { 233 rr.add(breakf("!(%s)", rr.Cond)) 234 } 235 genResult(rr, arch, rr.Result, pos) 236 if *genLog { 237 rr.add(stmtf("logRule(%q)", rule.Loc)) 238 } 239 fn.add(rr) 240 } 241 if rr.CanFail { 242 fn.add(stmtf("return false")) 243 } 244 genFile.add(fn) 245 } 246 247 // Generate block rewrite function. There are only a few block types 248 // so we can make this one function with a switch. 249 fn = &Func{Kind: "Block"} 250 fn.add(declReserved("config", "b.Func.Config")) 251 fn.add(declReserved("typ", "&b.Func.Config.Types")) 252 253 sw = &Switch{Expr: exprf("b.Kind")} 254 ops = ops[:0] 255 for op := range blockrules { 256 ops = append(ops, op) 257 } 258 sort.Strings(ops) 259 for _, op := range ops { 260 name, data := getBlockInfo(op, arch) 261 swc := &Case{Expr: exprf("%s", name)} 262 for _, rule := range blockrules[op] { 263 swc.add(genBlockRewrite(rule, arch, data)) 264 } 265 sw.add(swc) 266 } 267 if len(sw.List) > 0 { // skip if empty 268 fn.add(sw) 269 } 270 fn.add(stmtf("return false")) 271 genFile.add(fn) 272 273 // Remove unused imports and variables. 274 buf := new(bytes.Buffer) 275 fprint(buf, genFile) 276 fset := token.NewFileSet() 277 file, err := parser.ParseFile(fset, "", buf, parser.ParseComments) 278 if err != nil { 279 filename := fmt.Sprintf("%s_broken.go", arch.name) 280 if err := os.WriteFile(filename, buf.Bytes(), 0644); err != nil { 281 log.Printf("failed to dump broken code to %s: %v", filename, err) 282 } else { 283 log.Printf("dumped broken code to %s", filename) 284 } 285 log.Fatalf("failed to parse generated code for arch %s: %v", arch.name, err) 286 } 287 tfile := fset.File(file.Pos()) 288 289 // First, use unusedInspector to find the unused declarations by their 290 // start position. 291 u := unusedInspector{unused: make(map[token.Pos]bool)} 292 u.node(file) 293 294 // Then, delete said nodes via astutil.Apply. 295 pre := func(c *astutil.Cursor) bool { 296 node := c.Node() 297 if node == nil { 298 return true 299 } 300 if u.unused[node.Pos()] { 301 c.Delete() 302 // Unused imports and declarations use exactly 303 // one line. Prevent leaving an empty line. 304 tfile.MergeLine(tfile.Position(node.Pos()).Line) 305 return false 306 } 307 return true 308 } 309 post := func(c *astutil.Cursor) bool { 310 switch node := c.Node().(type) { 311 case *ast.GenDecl: 312 if len(node.Specs) == 0 { 313 // Don't leave a broken or empty GenDecl behind, 314 // such as "import ()". 315 c.Delete() 316 } 317 } 318 return true 319 } 320 file = astutil.Apply(file, pre, post).(*ast.File) 321 322 // Write the well-formatted source to file 323 f, err := os.Create("../rewrite" + arch.name + suff + ".go") 324 if err != nil { 325 log.Fatalf("can't write output: %v", err) 326 } 327 defer f.Close() 328 // gofmt result; use a buffered writer, as otherwise go/format spends 329 // far too much time in syscalls. 330 bw := bufio.NewWriter(f) 331 if err := format.Node(bw, fset, file); err != nil { 332 log.Fatalf("can't format output: %v", err) 333 } 334 if err := bw.Flush(); err != nil { 335 log.Fatalf("can't write output: %v", err) 336 } 337 if err := f.Close(); err != nil { 338 log.Fatalf("can't write output: %v", err) 339 } 340 } 341 342 // unusedInspector can be used to detect unused variables and imports in an 343 // ast.Node via its node method. The result is available in the "unused" map. 344 // 345 // note that unusedInspector is lazy and best-effort; it only supports the node 346 // types and patterns used by the rulegen program. 347 type unusedInspector struct { 348 // scope is the current scope, which can never be nil when a declaration 349 // is encountered. That is, the unusedInspector.node entrypoint should 350 // generally be an entire file or block. 351 scope *scope 352 353 // unused is the resulting set of unused declared names, indexed by the 354 // starting position of the node that declared the name. 355 unused map[token.Pos]bool 356 357 // defining is the object currently being defined; this is useful so 358 // that if "foo := bar" is unused and removed, we can then detect if 359 // "bar" becomes unused as well. 360 defining *object 361 } 362 363 // scoped opens a new scope when called, and returns a function which closes 364 // that same scope. When a scope is closed, unused variables are recorded. 365 func (u *unusedInspector) scoped() func() { 366 outer := u.scope 367 u.scope = &scope{outer: outer, objects: map[string]*object{}} 368 return func() { 369 for anyUnused := true; anyUnused; { 370 anyUnused = false 371 for _, obj := range u.scope.objects { 372 if obj.numUses > 0 { 373 continue 374 } 375 u.unused[obj.pos] = true 376 for _, used := range obj.used { 377 if used.numUses--; used.numUses == 0 { 378 anyUnused = true 379 } 380 } 381 // We've decremented numUses for each of the 382 // objects in used. Zero this slice too, to keep 383 // everything consistent. 384 obj.used = nil 385 } 386 } 387 u.scope = outer 388 } 389 } 390 391 func (u *unusedInspector) exprs(list []ast.Expr) { 392 for _, x := range list { 393 u.node(x) 394 } 395 } 396 397 func (u *unusedInspector) node(node ast.Node) { 398 switch node := node.(type) { 399 case *ast.File: 400 defer u.scoped()() 401 for _, decl := range node.Decls { 402 u.node(decl) 403 } 404 case *ast.GenDecl: 405 for _, spec := range node.Specs { 406 u.node(spec) 407 } 408 case *ast.ImportSpec: 409 impPath, _ := strconv.Unquote(node.Path.Value) 410 name := path.Base(impPath) 411 u.scope.objects[name] = &object{ 412 name: name, 413 pos: node.Pos(), 414 } 415 case *ast.FuncDecl: 416 u.node(node.Type) 417 if node.Body != nil { 418 u.node(node.Body) 419 } 420 case *ast.FuncType: 421 if node.Params != nil { 422 u.node(node.Params) 423 } 424 if node.Results != nil { 425 u.node(node.Results) 426 } 427 case *ast.FieldList: 428 for _, field := range node.List { 429 u.node(field) 430 } 431 case *ast.Field: 432 u.node(node.Type) 433 434 // statements 435 436 case *ast.BlockStmt: 437 defer u.scoped()() 438 for _, stmt := range node.List { 439 u.node(stmt) 440 } 441 case *ast.DeclStmt: 442 u.node(node.Decl) 443 case *ast.IfStmt: 444 if node.Init != nil { 445 u.node(node.Init) 446 } 447 u.node(node.Cond) 448 u.node(node.Body) 449 if node.Else != nil { 450 u.node(node.Else) 451 } 452 case *ast.ForStmt: 453 if node.Init != nil { 454 u.node(node.Init) 455 } 456 if node.Cond != nil { 457 u.node(node.Cond) 458 } 459 if node.Post != nil { 460 u.node(node.Post) 461 } 462 u.node(node.Body) 463 case *ast.SwitchStmt: 464 if node.Init != nil { 465 u.node(node.Init) 466 } 467 if node.Tag != nil { 468 u.node(node.Tag) 469 } 470 u.node(node.Body) 471 case *ast.CaseClause: 472 u.exprs(node.List) 473 defer u.scoped()() 474 for _, stmt := range node.Body { 475 u.node(stmt) 476 } 477 case *ast.BranchStmt: 478 case *ast.ExprStmt: 479 u.node(node.X) 480 case *ast.AssignStmt: 481 if node.Tok != token.DEFINE { 482 u.exprs(node.Rhs) 483 u.exprs(node.Lhs) 484 break 485 } 486 lhs := node.Lhs 487 if len(lhs) == 2 && lhs[1].(*ast.Ident).Name == "_" { 488 lhs = lhs[:1] 489 } 490 if len(lhs) != 1 { 491 panic("no support for := with multiple names") 492 } 493 494 name := lhs[0].(*ast.Ident) 495 obj := &object{ 496 name: name.Name, 497 pos: name.NamePos, 498 } 499 500 old := u.defining 501 u.defining = obj 502 u.exprs(node.Rhs) 503 u.defining = old 504 505 u.scope.objects[name.Name] = obj 506 case *ast.ReturnStmt: 507 u.exprs(node.Results) 508 case *ast.IncDecStmt: 509 u.node(node.X) 510 511 // expressions 512 513 case *ast.CallExpr: 514 u.node(node.Fun) 515 u.exprs(node.Args) 516 case *ast.SelectorExpr: 517 u.node(node.X) 518 case *ast.UnaryExpr: 519 u.node(node.X) 520 case *ast.BinaryExpr: 521 u.node(node.X) 522 u.node(node.Y) 523 case *ast.StarExpr: 524 u.node(node.X) 525 case *ast.ParenExpr: 526 u.node(node.X) 527 case *ast.IndexExpr: 528 u.node(node.X) 529 u.node(node.Index) 530 case *ast.TypeAssertExpr: 531 u.node(node.X) 532 u.node(node.Type) 533 case *ast.Ident: 534 if obj := u.scope.Lookup(node.Name); obj != nil { 535 obj.numUses++ 536 if u.defining != nil { 537 u.defining.used = append(u.defining.used, obj) 538 } 539 } 540 case *ast.BasicLit: 541 case *ast.ValueSpec: 542 u.exprs(node.Values) 543 default: 544 panic(fmt.Sprintf("unhandled node: %T", node)) 545 } 546 } 547 548 // scope keeps track of a certain scope and its declared names, as well as the 549 // outer (parent) scope. 550 type scope struct { 551 outer *scope // can be nil, if this is the top-level scope 552 objects map[string]*object // indexed by each declared name 553 } 554 555 func (s *scope) Lookup(name string) *object { 556 if obj := s.objects[name]; obj != nil { 557 return obj 558 } 559 if s.outer == nil { 560 return nil 561 } 562 return s.outer.Lookup(name) 563 } 564 565 // object keeps track of a declared name, such as a variable or import. 566 type object struct { 567 name string 568 pos token.Pos // start position of the node declaring the object 569 570 numUses int // number of times this object is used 571 used []*object // objects that its declaration makes use of 572 } 573 574 func fprint(w io.Writer, n Node) { 575 switch n := n.(type) { 576 case *File: 577 file := n 578 seenRewrite := make(map[[3]string]string) 579 fmt.Fprintf(w, "// Code generated from _gen/%s%s.rules using 'go generate'; DO NOT EDIT.\n", n.Arch.name, n.Suffix) 580 fmt.Fprintf(w, "\npackage ssa\n") 581 for _, path := range append([]string{ 582 "fmt", 583 "github.com/go-asm/go/buildcfg", 584 "math", 585 "github.com/go-asm/go/cmd/obj", 586 "github.com/go-asm/go/cmd/compile/base", 587 "github.com/go-asm/go/cmd/compile/types", 588 "github.com/go-asm/go/cmd/compile/ir", 589 }, n.Arch.imports...) { 590 fmt.Fprintf(w, "import %q\n", path) 591 } 592 for _, f := range n.List { 593 f := f.(*Func) 594 fmt.Fprintf(w, "func rewrite%s%s%s%s(", f.Kind, n.Arch.name, n.Suffix, f.Suffix) 595 fmt.Fprintf(w, "%c *%s) bool {\n", strings.ToLower(f.Kind)[0], f.Kind) 596 if f.Kind == "Value" && f.ArgLen > 0 { 597 for i := f.ArgLen - 1; i >= 0; i-- { 598 fmt.Fprintf(w, "v_%d := v.Args[%d]\n", i, i) 599 } 600 } 601 for _, n := range f.List { 602 fprint(w, n) 603 604 if rr, ok := n.(*RuleRewrite); ok { 605 k := [3]string{ 606 normalizeMatch(rr.Match, file.Arch), 607 normalizeWhitespace(rr.Cond), 608 normalizeWhitespace(rr.Result), 609 } 610 if prev, ok := seenRewrite[k]; ok { 611 log.Fatalf("duplicate rule %s, previously seen at %s\n", rr.Loc, prev) 612 } 613 seenRewrite[k] = rr.Loc 614 } 615 } 616 fmt.Fprintf(w, "}\n") 617 } 618 case *Switch: 619 fmt.Fprintf(w, "switch ") 620 fprint(w, n.Expr) 621 fmt.Fprintf(w, " {\n") 622 for _, n := range n.List { 623 fprint(w, n) 624 } 625 fmt.Fprintf(w, "}\n") 626 case *Case: 627 fmt.Fprintf(w, "case ") 628 fprint(w, n.Expr) 629 fmt.Fprintf(w, ":\n") 630 for _, n := range n.List { 631 fprint(w, n) 632 } 633 case *RuleRewrite: 634 if *addLine { 635 fmt.Fprintf(w, "// %s\n", n.Loc) 636 } 637 fmt.Fprintf(w, "// match: %s\n", n.Match) 638 if n.Cond != "" { 639 fmt.Fprintf(w, "// cond: %s\n", n.Cond) 640 } 641 fmt.Fprintf(w, "// result: %s\n", n.Result) 642 fmt.Fprintf(w, "for %s {\n", n.Check) 643 nCommutative := 0 644 for _, n := range n.List { 645 if b, ok := n.(*CondBreak); ok { 646 b.InsideCommuteLoop = nCommutative > 0 647 } 648 fprint(w, n) 649 if loop, ok := n.(StartCommuteLoop); ok { 650 if nCommutative != loop.Depth { 651 panic("mismatch commute loop depth") 652 } 653 nCommutative++ 654 } 655 } 656 fmt.Fprintf(w, "return true\n") 657 for i := 0; i < nCommutative; i++ { 658 fmt.Fprintln(w, "}") 659 } 660 if n.CommuteDepth > 0 && n.CanFail { 661 fmt.Fprint(w, "break\n") 662 } 663 fmt.Fprintf(w, "}\n") 664 case *Declare: 665 fmt.Fprintf(w, "%s := ", n.Name) 666 fprint(w, n.Value) 667 fmt.Fprintln(w) 668 case *CondBreak: 669 fmt.Fprintf(w, "if ") 670 fprint(w, n.Cond) 671 fmt.Fprintf(w, " {\n") 672 if n.InsideCommuteLoop { 673 fmt.Fprintf(w, "continue") 674 } else { 675 fmt.Fprintf(w, "break") 676 } 677 fmt.Fprintf(w, "\n}\n") 678 case ast.Node: 679 printConfig.Fprint(w, emptyFset, n) 680 if _, ok := n.(ast.Stmt); ok { 681 fmt.Fprintln(w) 682 } 683 case StartCommuteLoop: 684 fmt.Fprintf(w, "for _i%[1]d := 0; _i%[1]d <= 1; _i%[1]d, %[2]s_0, %[2]s_1 = _i%[1]d + 1, %[2]s_1, %[2]s_0 {\n", n.Depth, n.V) 685 default: 686 log.Fatalf("cannot print %T", n) 687 } 688 } 689 690 var printConfig = printer.Config{ 691 Mode: printer.RawFormat, // we use go/format later, so skip work here 692 } 693 694 var emptyFset = token.NewFileSet() 695 696 // Node can be a Statement or an ast.Expr. 697 type Node interface{} 698 699 // Statement can be one of our high-level statement struct types, or an 700 // ast.Stmt under some limited circumstances. 701 type Statement interface{} 702 703 // BodyBase is shared by all of our statement pseudo-node types which can 704 // contain other statements. 705 type BodyBase struct { 706 List []Statement 707 CanFail bool 708 } 709 710 func (w *BodyBase) add(node Statement) { 711 var last Statement 712 if len(w.List) > 0 { 713 last = w.List[len(w.List)-1] 714 } 715 if node, ok := node.(*CondBreak); ok { 716 w.CanFail = true 717 if last, ok := last.(*CondBreak); ok { 718 // Add to the previous "if <cond> { break }" via a 719 // logical OR, which will save verbosity. 720 last.Cond = &ast.BinaryExpr{ 721 Op: token.LOR, 722 X: last.Cond, 723 Y: node.Cond, 724 } 725 return 726 } 727 } 728 729 w.List = append(w.List, node) 730 } 731 732 // predeclared contains globally known tokens that should not be redefined. 733 var predeclared = map[string]bool{ 734 "nil": true, 735 "false": true, 736 "true": true, 737 } 738 739 // declared reports if the body contains a Declare with the given name. 740 func (w *BodyBase) declared(name string) bool { 741 if predeclared[name] { 742 // Treat predeclared names as having already been declared. 743 // This lets us use nil to match an aux field or 744 // true and false to match an auxint field. 745 return true 746 } 747 for _, s := range w.List { 748 if decl, ok := s.(*Declare); ok && decl.Name == name { 749 return true 750 } 751 } 752 return false 753 } 754 755 // These types define some high-level statement struct types, which can be used 756 // as a Statement. This allows us to keep some node structs simpler, and have 757 // higher-level nodes such as an entire rule rewrite. 758 // 759 // Note that ast.Expr is always used as-is; we don't declare our own expression 760 // nodes. 761 type ( 762 File struct { 763 BodyBase // []*Func 764 Arch arch 765 Suffix string 766 } 767 Func struct { 768 BodyBase 769 Kind string // "Value" or "Block" 770 Suffix string 771 ArgLen int32 // if kind == "Value", number of args for this op 772 } 773 Switch struct { 774 BodyBase // []*Case 775 Expr ast.Expr 776 } 777 Case struct { 778 BodyBase 779 Expr ast.Expr 780 } 781 RuleRewrite struct { 782 BodyBase 783 Match, Cond, Result string // top comments 784 Check string // top-level boolean expression 785 786 Alloc int // for unique var names 787 Loc string // file name & line number of the original rule 788 CommuteDepth int // used to track depth of commute loops 789 } 790 Declare struct { 791 Name string 792 Value ast.Expr 793 } 794 CondBreak struct { 795 Cond ast.Expr 796 InsideCommuteLoop bool 797 } 798 StartCommuteLoop struct { 799 Depth int 800 V string 801 } 802 ) 803 804 // exprf parses a Go expression generated from fmt.Sprintf, panicking if an 805 // error occurs. 806 func exprf(format string, a ...interface{}) ast.Expr { 807 src := fmt.Sprintf(format, a...) 808 expr, err := parser.ParseExpr(src) 809 if err != nil { 810 log.Fatalf("expr parse error on %q: %v", src, err) 811 } 812 return expr 813 } 814 815 // stmtf parses a Go statement generated from fmt.Sprintf. This function is only 816 // meant for simple statements that don't have a custom Statement node declared 817 // in this package, such as ast.ReturnStmt or ast.ExprStmt. 818 func stmtf(format string, a ...interface{}) Statement { 819 src := fmt.Sprintf(format, a...) 820 fsrc := "package p\nfunc _() {\n" + src + "\n}\n" 821 file, err := parser.ParseFile(token.NewFileSet(), "", fsrc, 0) 822 if err != nil { 823 log.Fatalf("stmt parse error on %q: %v", src, err) 824 } 825 return file.Decls[0].(*ast.FuncDecl).Body.List[0] 826 } 827 828 var reservedNames = map[string]bool{ 829 "v": true, // Values[i], etc 830 "b": true, // v.Block 831 "config": true, // b.Func.Config 832 "fe": true, // b.Func.fe 833 "typ": true, // &b.Func.Config.Types 834 } 835 836 // declf constructs a simple "name := value" declaration, 837 // using exprf for its value. 838 // 839 // name must not be one of reservedNames. 840 // This helps prevent unintended shadowing and name clashes. 841 // To declare a reserved name, use declReserved. 842 func declf(loc, name, format string, a ...interface{}) *Declare { 843 if reservedNames[name] { 844 log.Fatalf("rule %s uses the reserved name %s", loc, name) 845 } 846 return &Declare{name, exprf(format, a...)} 847 } 848 849 // declReserved is like declf, but the name must be one of reservedNames. 850 // Calls to declReserved should generally be static and top-level. 851 func declReserved(name, value string) *Declare { 852 if !reservedNames[name] { 853 panic(fmt.Sprintf("declReserved call does not use a reserved name: %q", name)) 854 } 855 return &Declare{name, exprf(value)} 856 } 857 858 // breakf constructs a simple "if cond { break }" statement, using exprf for its 859 // condition. 860 func breakf(format string, a ...interface{}) *CondBreak { 861 return &CondBreak{Cond: exprf(format, a...)} 862 } 863 864 func genBlockRewrite(rule Rule, arch arch, data blockData) *RuleRewrite { 865 rr := &RuleRewrite{Loc: rule.Loc} 866 rr.Match, rr.Cond, rr.Result = rule.parse() 867 _, _, auxint, aux, s := extract(rr.Match) // remove parens, then split 868 869 // check match of control values 870 if len(s) < data.controls { 871 log.Fatalf("incorrect number of arguments in %s, got %v wanted at least %v", rule, len(s), data.controls) 872 } 873 controls := s[:data.controls] 874 pos := make([]string, data.controls) 875 for i, arg := range controls { 876 cname := fmt.Sprintf("b.Controls[%v]", i) 877 if strings.Contains(arg, "(") { 878 vname, expr := splitNameExpr(arg) 879 if vname == "" { 880 vname = fmt.Sprintf("v_%v", i) 881 } 882 rr.add(declf(rr.Loc, vname, cname)) 883 p, op := genMatch0(rr, arch, expr, vname, nil, false) // TODO: pass non-nil cnt? 884 if op != "" { 885 check := fmt.Sprintf("%s.Op == %s", cname, op) 886 if rr.Check == "" { 887 rr.Check = check 888 } else { 889 rr.Check += " && " + check 890 } 891 } 892 if p == "" { 893 p = vname + ".Pos" 894 } 895 pos[i] = p 896 } else { 897 rr.add(declf(rr.Loc, arg, cname)) 898 pos[i] = arg + ".Pos" 899 } 900 } 901 for _, e := range []struct { 902 name, field, dclType string 903 }{ 904 {auxint, "AuxInt", data.auxIntType()}, 905 {aux, "Aux", data.auxType()}, 906 } { 907 if e.name == "" { 908 continue 909 } 910 911 if e.dclType == "" { 912 log.Fatalf("op %s has no declared type for %s", data.name, e.field) 913 } 914 if !token.IsIdentifier(e.name) || rr.declared(e.name) { 915 rr.add(breakf("%sTo%s(b.%s) != %s", unTitle(e.field), title(e.dclType), e.field, e.name)) 916 } else { 917 rr.add(declf(rr.Loc, e.name, "%sTo%s(b.%s)", unTitle(e.field), title(e.dclType), e.field)) 918 } 919 } 920 if rr.Cond != "" { 921 rr.add(breakf("!(%s)", rr.Cond)) 922 } 923 924 // Rule matches. Generate result. 925 outop, _, auxint, aux, t := extract(rr.Result) // remove parens, then split 926 blockName, outdata := getBlockInfo(outop, arch) 927 if len(t) < outdata.controls { 928 log.Fatalf("incorrect number of output arguments in %s, got %v wanted at least %v", rule, len(s), outdata.controls) 929 } 930 931 // Check if newsuccs is the same set as succs. 932 succs := s[data.controls:] 933 newsuccs := t[outdata.controls:] 934 m := map[string]bool{} 935 for _, succ := range succs { 936 if m[succ] { 937 log.Fatalf("can't have a repeat successor name %s in %s", succ, rule) 938 } 939 m[succ] = true 940 } 941 for _, succ := range newsuccs { 942 if !m[succ] { 943 log.Fatalf("unknown successor %s in %s", succ, rule) 944 } 945 delete(m, succ) 946 } 947 if len(m) != 0 { 948 log.Fatalf("unmatched successors %v in %s", m, rule) 949 } 950 951 var genControls [2]string 952 for i, control := range t[:outdata.controls] { 953 // Select a source position for any new control values. 954 // TODO: does it always make sense to use the source position 955 // of the original control values or should we be using the 956 // block's source position in some cases? 957 newpos := "b.Pos" // default to block's source position 958 if i < len(pos) && pos[i] != "" { 959 // Use the previous control value's source position. 960 newpos = pos[i] 961 } 962 963 // Generate a new control value (or copy an existing value). 964 genControls[i] = genResult0(rr, arch, control, false, false, newpos, nil) 965 } 966 switch outdata.controls { 967 case 0: 968 rr.add(stmtf("b.Reset(%s)", blockName)) 969 case 1: 970 rr.add(stmtf("b.resetWithControl(%s, %s)", blockName, genControls[0])) 971 case 2: 972 rr.add(stmtf("b.resetWithControl2(%s, %s, %s)", blockName, genControls[0], genControls[1])) 973 default: 974 log.Fatalf("too many controls: %d", outdata.controls) 975 } 976 977 if auxint != "" { 978 // Make sure auxint value has the right type. 979 rr.add(stmtf("b.AuxInt = %sToAuxInt(%s)", unTitle(outdata.auxIntType()), auxint)) 980 } 981 if aux != "" { 982 // Make sure aux value has the right type. 983 rr.add(stmtf("b.Aux = %sToAux(%s)", unTitle(outdata.auxType()), aux)) 984 } 985 986 succChanged := false 987 for i := 0; i < len(succs); i++ { 988 if succs[i] != newsuccs[i] { 989 succChanged = true 990 } 991 } 992 if succChanged { 993 if len(succs) != 2 { 994 log.Fatalf("changed successors, len!=2 in %s", rule) 995 } 996 if succs[0] != newsuccs[1] || succs[1] != newsuccs[0] { 997 log.Fatalf("can only handle swapped successors in %s", rule) 998 } 999 rr.add(stmtf("b.swapSuccessors()")) 1000 } 1001 1002 if *genLog { 1003 rr.add(stmtf("logRule(%q)", rule.Loc)) 1004 } 1005 return rr 1006 } 1007 1008 // genMatch returns the variable whose source position should be used for the 1009 // result (or "" if no opinion), and a boolean that reports whether the match can fail. 1010 func genMatch(rr *RuleRewrite, arch arch, match string, pregenTop bool) (pos, checkOp string) { 1011 cnt := varCount(rr) 1012 return genMatch0(rr, arch, match, "v", cnt, pregenTop) 1013 } 1014 1015 func genMatch0(rr *RuleRewrite, arch arch, match, v string, cnt map[string]int, pregenTop bool) (pos, checkOp string) { 1016 if match[0] != '(' || match[len(match)-1] != ')' { 1017 log.Fatalf("%s: non-compound expr in genMatch0: %q", rr.Loc, match) 1018 } 1019 op, oparch, typ, auxint, aux, args := parseValue(match, arch, rr.Loc) 1020 1021 checkOp = fmt.Sprintf("Op%s%s", oparch, op.name) 1022 1023 if op.faultOnNilArg0 || op.faultOnNilArg1 { 1024 // Prefer the position of an instruction which could fault. 1025 pos = v + ".Pos" 1026 } 1027 1028 // If the last argument is ___, it means "don't care about trailing arguments, really" 1029 // The likely/intended use is for rewrites that are too tricky to express in the existing pattern language 1030 // Do a length check early because long patterns fed short (ultimately not-matching) inputs will 1031 // do an indexing error in pattern-matching. 1032 if op.argLength == -1 { 1033 l := len(args) 1034 if l == 0 || args[l-1] != "___" { 1035 rr.add(breakf("len(%s.Args) != %d", v, l)) 1036 } else if l > 1 && args[l-1] == "___" { 1037 rr.add(breakf("len(%s.Args) < %d", v, l-1)) 1038 } 1039 } 1040 1041 for _, e := range []struct { 1042 name, field, dclType string 1043 }{ 1044 {typ, "Type", "*types.Type"}, 1045 {auxint, "AuxInt", op.auxIntType()}, 1046 {aux, "Aux", op.auxType()}, 1047 } { 1048 if e.name == "" { 1049 continue 1050 } 1051 1052 if e.dclType == "" { 1053 log.Fatalf("op %s has no declared type for %s", op.name, e.field) 1054 } 1055 if !token.IsIdentifier(e.name) || rr.declared(e.name) { 1056 switch e.field { 1057 case "Aux": 1058 rr.add(breakf("auxTo%s(%s.%s) != %s", title(e.dclType), v, e.field, e.name)) 1059 case "AuxInt": 1060 rr.add(breakf("auxIntTo%s(%s.%s) != %s", title(e.dclType), v, e.field, e.name)) 1061 case "Type": 1062 rr.add(breakf("%s.%s != %s", v, e.field, e.name)) 1063 } 1064 } else { 1065 switch e.field { 1066 case "Aux": 1067 rr.add(declf(rr.Loc, e.name, "auxTo%s(%s.%s)", title(e.dclType), v, e.field)) 1068 case "AuxInt": 1069 rr.add(declf(rr.Loc, e.name, "auxIntTo%s(%s.%s)", title(e.dclType), v, e.field)) 1070 case "Type": 1071 rr.add(declf(rr.Loc, e.name, "%s.%s", v, e.field)) 1072 } 1073 } 1074 } 1075 1076 commutative := op.commutative 1077 if commutative { 1078 if args[0] == args[1] { 1079 // When we have (Add x x), for any x, 1080 // even if there are other uses of x besides these two, 1081 // and even if x is not a variable, 1082 // we can skip the commutative match. 1083 commutative = false 1084 } 1085 if cnt[args[0]] == 1 && cnt[args[1]] == 1 { 1086 // When we have (Add x y) with no other uses 1087 // of x and y in the matching rule and condition, 1088 // then we can skip the commutative match (Add y x). 1089 commutative = false 1090 } 1091 } 1092 1093 if !pregenTop { 1094 // Access last argument first to minimize bounds checks. 1095 for n := len(args) - 1; n > 0; n-- { 1096 a := args[n] 1097 if a == "_" { 1098 continue 1099 } 1100 if !rr.declared(a) && token.IsIdentifier(a) && !(commutative && len(args) == 2) { 1101 rr.add(declf(rr.Loc, a, "%s.Args[%d]", v, n)) 1102 // delete the last argument so it is not reprocessed 1103 args = args[:n] 1104 } else { 1105 rr.add(stmtf("_ = %s.Args[%d]", v, n)) 1106 } 1107 break 1108 } 1109 } 1110 if commutative && !pregenTop { 1111 for i := 0; i <= 1; i++ { 1112 vname := fmt.Sprintf("%s_%d", v, i) 1113 rr.add(declf(rr.Loc, vname, "%s.Args[%d]", v, i)) 1114 } 1115 } 1116 if commutative { 1117 rr.add(StartCommuteLoop{rr.CommuteDepth, v}) 1118 rr.CommuteDepth++ 1119 } 1120 for i, arg := range args { 1121 if arg == "_" { 1122 continue 1123 } 1124 var rhs string 1125 if (commutative && i < 2) || pregenTop { 1126 rhs = fmt.Sprintf("%s_%d", v, i) 1127 } else { 1128 rhs = fmt.Sprintf("%s.Args[%d]", v, i) 1129 } 1130 if !strings.Contains(arg, "(") { 1131 // leaf variable 1132 if rr.declared(arg) { 1133 // variable already has a definition. Check whether 1134 // the old definition and the new definition match. 1135 // For example, (add x x). Equality is just pointer equality 1136 // on Values (so cse is important to do before lowering). 1137 rr.add(breakf("%s != %s", arg, rhs)) 1138 } else { 1139 if arg != rhs { 1140 rr.add(declf(rr.Loc, arg, "%s", rhs)) 1141 } 1142 } 1143 continue 1144 } 1145 // compound sexpr 1146 argname, expr := splitNameExpr(arg) 1147 if argname == "" { 1148 argname = fmt.Sprintf("%s_%d", v, i) 1149 } 1150 if argname == "b" { 1151 log.Fatalf("don't name args 'b', it is ambiguous with blocks") 1152 } 1153 1154 if argname != rhs { 1155 rr.add(declf(rr.Loc, argname, "%s", rhs)) 1156 } 1157 bexpr := exprf("%s.Op != addLater", argname) 1158 rr.add(&CondBreak{Cond: bexpr}) 1159 argPos, argCheckOp := genMatch0(rr, arch, expr, argname, cnt, false) 1160 bexpr.(*ast.BinaryExpr).Y.(*ast.Ident).Name = argCheckOp 1161 1162 if argPos != "" { 1163 // Keep the argument in preference to the parent, as the 1164 // argument is normally earlier in program flow. 1165 // Keep the argument in preference to an earlier argument, 1166 // as that prefers the memory argument which is also earlier 1167 // in the program flow. 1168 pos = argPos 1169 } 1170 } 1171 1172 return pos, checkOp 1173 } 1174 1175 func genResult(rr *RuleRewrite, arch arch, result, pos string) { 1176 move := result[0] == '@' 1177 if move { 1178 // parse @block directive 1179 s := strings.SplitN(result[1:], " ", 2) 1180 rr.add(stmtf("b = %s", s[0])) 1181 result = s[1] 1182 } 1183 cse := make(map[string]string) 1184 genResult0(rr, arch, result, true, move, pos, cse) 1185 } 1186 1187 func genResult0(rr *RuleRewrite, arch arch, result string, top, move bool, pos string, cse map[string]string) string { 1188 resname, expr := splitNameExpr(result) 1189 result = expr 1190 // TODO: when generating a constant result, use f.constVal to avoid 1191 // introducing copies just to clean them up again. 1192 if result[0] != '(' { 1193 // variable 1194 if top { 1195 // It in not safe in general to move a variable between blocks 1196 // (and particularly not a phi node). 1197 // Introduce a copy. 1198 rr.add(stmtf("v.copyOf(%s)", result)) 1199 } 1200 return result 1201 } 1202 1203 w := normalizeWhitespace(result) 1204 if prev := cse[w]; prev != "" { 1205 return prev 1206 } 1207 1208 op, oparch, typ, auxint, aux, args := parseValue(result, arch, rr.Loc) 1209 1210 // Find the type of the variable. 1211 typeOverride := typ != "" 1212 if typ == "" && op.typ != "" { 1213 typ = typeName(op.typ) 1214 } 1215 1216 v := "v" 1217 if top && !move { 1218 rr.add(stmtf("v.reset(Op%s%s)", oparch, op.name)) 1219 if typeOverride { 1220 rr.add(stmtf("v.Type = %s", typ)) 1221 } 1222 } else { 1223 if typ == "" { 1224 log.Fatalf("sub-expression %s (op=Op%s%s) at %s must have a type", result, oparch, op.name, rr.Loc) 1225 } 1226 if resname == "" { 1227 v = fmt.Sprintf("v%d", rr.Alloc) 1228 } else { 1229 v = resname 1230 } 1231 rr.Alloc++ 1232 rr.add(declf(rr.Loc, v, "b.NewValue0(%s, Op%s%s, %s)", pos, oparch, op.name, typ)) 1233 if move && top { 1234 // Rewrite original into a copy 1235 rr.add(stmtf("v.copyOf(%s)", v)) 1236 } 1237 } 1238 1239 if auxint != "" { 1240 // Make sure auxint value has the right type. 1241 rr.add(stmtf("%s.AuxInt = %sToAuxInt(%s)", v, unTitle(op.auxIntType()), auxint)) 1242 } 1243 if aux != "" { 1244 // Make sure aux value has the right type. 1245 rr.add(stmtf("%s.Aux = %sToAux(%s)", v, unTitle(op.auxType()), aux)) 1246 } 1247 all := new(strings.Builder) 1248 for i, arg := range args { 1249 x := genResult0(rr, arch, arg, false, move, pos, cse) 1250 if i > 0 { 1251 all.WriteString(", ") 1252 } 1253 all.WriteString(x) 1254 } 1255 switch len(args) { 1256 case 0: 1257 case 1: 1258 rr.add(stmtf("%s.AddArg(%s)", v, all.String())) 1259 default: 1260 rr.add(stmtf("%s.AddArg%d(%s)", v, len(args), all.String())) 1261 } 1262 1263 if cse != nil { 1264 cse[w] = v 1265 } 1266 return v 1267 } 1268 1269 func split(s string) []string { 1270 var r []string 1271 1272 outer: 1273 for s != "" { 1274 d := 0 // depth of ({[< 1275 var open, close byte // opening and closing markers ({[< or )}]> 1276 nonsp := false // found a non-space char so far 1277 for i := 0; i < len(s); i++ { 1278 switch { 1279 case d == 0 && s[i] == '(': 1280 open, close = '(', ')' 1281 d++ 1282 case d == 0 && s[i] == '<': 1283 open, close = '<', '>' 1284 d++ 1285 case d == 0 && s[i] == '[': 1286 open, close = '[', ']' 1287 d++ 1288 case d == 0 && s[i] == '{': 1289 open, close = '{', '}' 1290 d++ 1291 case d == 0 && (s[i] == ' ' || s[i] == '\t'): 1292 if nonsp { 1293 r = append(r, strings.TrimSpace(s[:i])) 1294 s = s[i:] 1295 continue outer 1296 } 1297 case d > 0 && s[i] == open: 1298 d++ 1299 case d > 0 && s[i] == close: 1300 d-- 1301 default: 1302 nonsp = true 1303 } 1304 } 1305 if d != 0 { 1306 log.Fatalf("imbalanced expression: %q", s) 1307 } 1308 if nonsp { 1309 r = append(r, strings.TrimSpace(s)) 1310 } 1311 break 1312 } 1313 return r 1314 } 1315 1316 // isBlock reports whether this op is a block opcode. 1317 func isBlock(name string, arch arch) bool { 1318 for _, b := range genericBlocks { 1319 if b.name == name { 1320 return true 1321 } 1322 } 1323 for _, b := range arch.blocks { 1324 if b.name == name { 1325 return true 1326 } 1327 } 1328 return false 1329 } 1330 1331 func extract(val string) (op, typ, auxint, aux string, args []string) { 1332 val = val[1 : len(val)-1] // remove () 1333 1334 // Split val up into regions. 1335 // Split by spaces/tabs, except those contained in (), {}, [], or <>. 1336 s := split(val) 1337 1338 // Extract restrictions and args. 1339 op = s[0] 1340 for _, a := range s[1:] { 1341 switch a[0] { 1342 case '<': 1343 typ = a[1 : len(a)-1] // remove <> 1344 case '[': 1345 auxint = a[1 : len(a)-1] // remove [] 1346 case '{': 1347 aux = a[1 : len(a)-1] // remove {} 1348 default: 1349 args = append(args, a) 1350 } 1351 } 1352 return 1353 } 1354 1355 // parseValue parses a parenthesized value from a rule. 1356 // The value can be from the match or the result side. 1357 // It returns the op and unparsed strings for typ, auxint, and aux restrictions and for all args. 1358 // oparch is the architecture that op is located in, or "" for generic. 1359 func parseValue(val string, arch arch, loc string) (op opData, oparch, typ, auxint, aux string, args []string) { 1360 // Resolve the op. 1361 var s string 1362 s, typ, auxint, aux, args = extract(val) 1363 1364 // match reports whether x is a good op to select. 1365 // If strict is true, rule generation might succeed. 1366 // If strict is false, rule generation has failed, 1367 // but we're trying to generate a useful error. 1368 // Doing strict=true then strict=false allows 1369 // precise op matching while retaining good error messages. 1370 match := func(x opData, strict bool, archname string) bool { 1371 if x.name != s { 1372 return false 1373 } 1374 if x.argLength != -1 && int(x.argLength) != len(args) && (len(args) != 1 || args[0] != "...") { 1375 if strict { 1376 return false 1377 } 1378 log.Printf("%s: op %s (%s) should have %d args, has %d", loc, s, archname, x.argLength, len(args)) 1379 } 1380 return true 1381 } 1382 1383 for _, x := range genericOps { 1384 if match(x, true, "generic") { 1385 op = x 1386 break 1387 } 1388 } 1389 for _, x := range arch.ops { 1390 if arch.name != "generic" && match(x, true, arch.name) { 1391 if op.name != "" { 1392 log.Fatalf("%s: matches for op %s found in both generic and %s", loc, op.name, arch.name) 1393 } 1394 op = x 1395 oparch = arch.name 1396 break 1397 } 1398 } 1399 1400 if op.name == "" { 1401 // Failed to find the op. 1402 // Run through everything again with strict=false 1403 // to generate useful diagnostic messages before failing. 1404 for _, x := range genericOps { 1405 match(x, false, "generic") 1406 } 1407 for _, x := range arch.ops { 1408 match(x, false, arch.name) 1409 } 1410 log.Fatalf("%s: unknown op %s", loc, s) 1411 } 1412 1413 // Sanity check aux, auxint. 1414 if auxint != "" && !opHasAuxInt(op) { 1415 log.Fatalf("%s: op %s %s can't have auxint", loc, op.name, op.aux) 1416 } 1417 if aux != "" && !opHasAux(op) { 1418 log.Fatalf("%s: op %s %s can't have aux", loc, op.name, op.aux) 1419 } 1420 return 1421 } 1422 1423 func opHasAuxInt(op opData) bool { 1424 switch op.aux { 1425 case "Bool", "Int8", "Int16", "Int32", "Int64", "Int128", "UInt8", "Float32", "Float64", 1426 "SymOff", "CallOff", "SymValAndOff", "TypSize", "ARM64BitField", "FlagConstant", "CCop": 1427 return true 1428 } 1429 return false 1430 } 1431 1432 func opHasAux(op opData) bool { 1433 switch op.aux { 1434 case "String", "Sym", "SymOff", "Call", "CallOff", "SymValAndOff", "Typ", "TypSize", 1435 "S390XCCMask", "S390XRotateParams": 1436 return true 1437 } 1438 return false 1439 } 1440 1441 // splitNameExpr splits s-expr arg, possibly prefixed by "name:", 1442 // into name and the unprefixed expression. 1443 // For example, "x:(Foo)" yields "x", "(Foo)", 1444 // and "(Foo)" yields "", "(Foo)". 1445 func splitNameExpr(arg string) (name, expr string) { 1446 colon := strings.Index(arg, ":") 1447 if colon < 0 { 1448 return "", arg 1449 } 1450 openparen := strings.Index(arg, "(") 1451 if openparen < 0 { 1452 log.Fatalf("splitNameExpr(%q): colon but no open parens", arg) 1453 } 1454 if colon > openparen { 1455 // colon is inside the parens, such as in "(Foo x:(Bar))". 1456 return "", arg 1457 } 1458 return arg[:colon], arg[colon+1:] 1459 } 1460 1461 func getBlockInfo(op string, arch arch) (name string, data blockData) { 1462 for _, b := range genericBlocks { 1463 if b.name == op { 1464 return "Block" + op, b 1465 } 1466 } 1467 for _, b := range arch.blocks { 1468 if b.name == op { 1469 return "Block" + arch.name + op, b 1470 } 1471 } 1472 log.Fatalf("could not find block data for %s", op) 1473 panic("unreachable") 1474 } 1475 1476 // typeName returns the string to use to generate a type. 1477 func typeName(typ string) string { 1478 if typ[0] == '(' { 1479 ts := strings.Split(typ[1:len(typ)-1], ",") 1480 if len(ts) != 2 { 1481 log.Fatalf("Tuple expect 2 arguments") 1482 } 1483 return "types.NewTuple(" + typeName(ts[0]) + ", " + typeName(ts[1]) + ")" 1484 } 1485 switch typ { 1486 case "Flags", "Mem", "Void", "Int128": 1487 return "types.Type" + typ 1488 default: 1489 return "typ." + typ 1490 } 1491 } 1492 1493 // balance returns the number of unclosed '(' characters in s. 1494 // If a ')' appears without a corresponding '(', balance returns -1. 1495 func balance(s string) int { 1496 balance := 0 1497 for _, c := range s { 1498 switch c { 1499 case '(': 1500 balance++ 1501 case ')': 1502 balance-- 1503 if balance < 0 { 1504 // don't allow ")(" to return 0 1505 return -1 1506 } 1507 } 1508 } 1509 return balance 1510 } 1511 1512 // findAllOpcode is a function to find the opcode portion of s-expressions. 1513 var findAllOpcode = regexp.MustCompile(`[(](\w+[|])+\w+[)]`).FindAllStringIndex 1514 1515 // excludeFromExpansion reports whether the substring s[idx[0]:idx[1]] in a rule 1516 // should be disregarded as a candidate for | expansion. 1517 // It uses simple syntactic checks to see whether the substring 1518 // is inside an AuxInt expression or inside the && conditions. 1519 func excludeFromExpansion(s string, idx []int) bool { 1520 left := s[:idx[0]] 1521 if strings.LastIndexByte(left, '[') > strings.LastIndexByte(left, ']') { 1522 // Inside an AuxInt expression. 1523 return true 1524 } 1525 right := s[idx[1]:] 1526 if strings.Contains(left, "&&") && strings.Contains(right, "=>") { 1527 // Inside && conditions. 1528 return true 1529 } 1530 return false 1531 } 1532 1533 // expandOr converts a rule into multiple rules by expanding | ops. 1534 func expandOr(r string) []string { 1535 // Find every occurrence of |-separated things. 1536 // They look like MOV(B|W|L|Q|SS|SD)load or MOV(Q|L)loadidx(1|8). 1537 // Generate rules selecting one case from each |-form. 1538 1539 // Count width of |-forms. They must match. 1540 n := 1 1541 for _, idx := range findAllOpcode(r, -1) { 1542 if excludeFromExpansion(r, idx) { 1543 continue 1544 } 1545 s := r[idx[0]:idx[1]] 1546 c := strings.Count(s, "|") + 1 1547 if c == 1 { 1548 continue 1549 } 1550 if n > 1 && n != c { 1551 log.Fatalf("'|' count doesn't match in %s: both %d and %d\n", r, n, c) 1552 } 1553 n = c 1554 } 1555 if n == 1 { 1556 // No |-form in this rule. 1557 return []string{r} 1558 } 1559 // Build each new rule. 1560 res := make([]string, n) 1561 for i := 0; i < n; i++ { 1562 buf := new(strings.Builder) 1563 x := 0 1564 for _, idx := range findAllOpcode(r, -1) { 1565 if excludeFromExpansion(r, idx) { 1566 continue 1567 } 1568 buf.WriteString(r[x:idx[0]]) // write bytes we've skipped over so far 1569 s := r[idx[0]+1 : idx[1]-1] // remove leading "(" and trailing ")" 1570 buf.WriteString(strings.Split(s, "|")[i]) // write the op component for this rule 1571 x = idx[1] // note that we've written more bytes 1572 } 1573 buf.WriteString(r[x:]) 1574 res[i] = buf.String() 1575 } 1576 return res 1577 } 1578 1579 // varCount returns a map which counts the number of occurrences of 1580 // Value variables in the s-expression rr.Match and the Go expression rr.Cond. 1581 func varCount(rr *RuleRewrite) map[string]int { 1582 cnt := map[string]int{} 1583 varCount1(rr.Loc, rr.Match, cnt) 1584 if rr.Cond != "" { 1585 expr, err := parser.ParseExpr(rr.Cond) 1586 if err != nil { 1587 log.Fatalf("%s: failed to parse cond %q: %v", rr.Loc, rr.Cond, err) 1588 } 1589 ast.Inspect(expr, func(n ast.Node) bool { 1590 if id, ok := n.(*ast.Ident); ok { 1591 cnt[id.Name]++ 1592 } 1593 return true 1594 }) 1595 } 1596 return cnt 1597 } 1598 1599 func varCount1(loc, m string, cnt map[string]int) { 1600 if m[0] == '<' || m[0] == '[' || m[0] == '{' { 1601 return 1602 } 1603 if token.IsIdentifier(m) { 1604 cnt[m]++ 1605 return 1606 } 1607 // Split up input. 1608 name, expr := splitNameExpr(m) 1609 if name != "" { 1610 cnt[name]++ 1611 } 1612 if expr[0] != '(' || expr[len(expr)-1] != ')' { 1613 log.Fatalf("%s: non-compound expr in varCount1: %q", loc, expr) 1614 } 1615 s := split(expr[1 : len(expr)-1]) 1616 for _, arg := range s[1:] { 1617 varCount1(loc, arg, cnt) 1618 } 1619 } 1620 1621 // normalizeWhitespace replaces 2+ whitespace sequences with a single space. 1622 func normalizeWhitespace(x string) string { 1623 x = strings.Join(strings.Fields(x), " ") 1624 x = strings.Replace(x, "( ", "(", -1) 1625 x = strings.Replace(x, " )", ")", -1) 1626 x = strings.Replace(x, "[ ", "[", -1) 1627 x = strings.Replace(x, " ]", "]", -1) 1628 x = strings.Replace(x, ")=>", ") =>", -1) 1629 return x 1630 } 1631 1632 // opIsCommutative reports whether op s is commutative. 1633 func opIsCommutative(op string, arch arch) bool { 1634 for _, x := range genericOps { 1635 if op == x.name { 1636 if x.commutative { 1637 return true 1638 } 1639 break 1640 } 1641 } 1642 if arch.name != "generic" { 1643 for _, x := range arch.ops { 1644 if op == x.name { 1645 if x.commutative { 1646 return true 1647 } 1648 break 1649 } 1650 } 1651 } 1652 return false 1653 } 1654 1655 func normalizeMatch(m string, arch arch) string { 1656 if token.IsIdentifier(m) { 1657 return m 1658 } 1659 op, typ, auxint, aux, args := extract(m) 1660 if opIsCommutative(op, arch) { 1661 if args[1] < args[0] { 1662 args[0], args[1] = args[1], args[0] 1663 } 1664 } 1665 s := new(strings.Builder) 1666 fmt.Fprintf(s, "%s <%s> [%s] {%s}", op, typ, auxint, aux) 1667 for _, arg := range args { 1668 prefix, expr := splitNameExpr(arg) 1669 fmt.Fprint(s, " ", prefix, normalizeMatch(expr, arch)) 1670 } 1671 return s.String() 1672 } 1673 1674 func parseEllipsisRules(rules []Rule, arch arch) (newop string, ok bool) { 1675 if len(rules) != 1 { 1676 for _, r := range rules { 1677 if strings.Contains(r.Rule, "...") { 1678 log.Fatalf("%s: found ellipsis in rule, but there are other rules with the same op", r.Loc) 1679 } 1680 } 1681 return "", false 1682 } 1683 rule := rules[0] 1684 match, cond, result := rule.parse() 1685 if cond != "" || !isEllipsisValue(match) || !isEllipsisValue(result) { 1686 if strings.Contains(rule.Rule, "...") { 1687 log.Fatalf("%s: found ellipsis in non-ellipsis rule", rule.Loc) 1688 } 1689 checkEllipsisRuleCandidate(rule, arch) 1690 return "", false 1691 } 1692 op, oparch, _, _, _, _ := parseValue(result, arch, rule.Loc) 1693 return fmt.Sprintf("Op%s%s", oparch, op.name), true 1694 } 1695 1696 // isEllipsisValue reports whether s is of the form (OpX ...). 1697 func isEllipsisValue(s string) bool { 1698 if len(s) < 2 || s[0] != '(' || s[len(s)-1] != ')' { 1699 return false 1700 } 1701 c := split(s[1 : len(s)-1]) 1702 if len(c) != 2 || c[1] != "..." { 1703 return false 1704 } 1705 return true 1706 } 1707 1708 func checkEllipsisRuleCandidate(rule Rule, arch arch) { 1709 match, cond, result := rule.parse() 1710 if cond != "" { 1711 return 1712 } 1713 op, _, _, auxint, aux, args := parseValue(match, arch, rule.Loc) 1714 var auxint2, aux2 string 1715 var args2 []string 1716 var usingCopy string 1717 var eop opData 1718 if result[0] != '(' { 1719 // Check for (Foo x) => x, which can be converted to (Foo ...) => (Copy ...). 1720 args2 = []string{result} 1721 usingCopy = " using Copy" 1722 } else { 1723 eop, _, _, auxint2, aux2, args2 = parseValue(result, arch, rule.Loc) 1724 } 1725 // Check that all restrictions in match are reproduced exactly in result. 1726 if aux != aux2 || auxint != auxint2 || len(args) != len(args2) { 1727 return 1728 } 1729 if strings.Contains(rule.Rule, "=>") && op.aux != eop.aux { 1730 return 1731 } 1732 for i := range args { 1733 if args[i] != args2[i] { 1734 return 1735 } 1736 } 1737 switch { 1738 case opHasAux(op) && aux == "" && aux2 == "": 1739 fmt.Printf("%s: rule silently zeros aux, either copy aux or explicitly zero\n", rule.Loc) 1740 case opHasAuxInt(op) && auxint == "" && auxint2 == "": 1741 fmt.Printf("%s: rule silently zeros auxint, either copy auxint or explicitly zero\n", rule.Loc) 1742 default: 1743 fmt.Printf("%s: possible ellipsis rule candidate%s: %q\n", rule.Loc, usingCopy, rule.Rule) 1744 } 1745 } 1746 1747 func opByName(arch arch, name string) opData { 1748 name = name[2:] 1749 for _, x := range genericOps { 1750 if name == x.name { 1751 return x 1752 } 1753 } 1754 if arch.name != "generic" { 1755 name = name[len(arch.name):] 1756 for _, x := range arch.ops { 1757 if name == x.name { 1758 return x 1759 } 1760 } 1761 } 1762 log.Fatalf("failed to find op named %s in arch %s", name, arch.name) 1763 panic("unreachable") 1764 } 1765 1766 // auxType returns the Go type that this operation should store in its aux field. 1767 func (op opData) auxType() string { 1768 switch op.aux { 1769 case "String": 1770 return "string" 1771 case "Sym": 1772 // Note: a Sym can be an *obj.LSym, a *gc.Node, or nil. 1773 return "Sym" 1774 case "SymOff": 1775 return "Sym" 1776 case "Call": 1777 return "Call" 1778 case "CallOff": 1779 return "Call" 1780 case "SymValAndOff": 1781 return "Sym" 1782 case "Typ": 1783 return "*types.Type" 1784 case "TypSize": 1785 return "*types.Type" 1786 case "S390XCCMask": 1787 return "s390x.CCMask" 1788 case "S390XRotateParams": 1789 return "s390x.RotateParams" 1790 default: 1791 return "invalid" 1792 } 1793 } 1794 1795 // auxIntType returns the Go type that this operation should store in its auxInt field. 1796 func (op opData) auxIntType() string { 1797 switch op.aux { 1798 case "Bool": 1799 return "bool" 1800 case "Int8": 1801 return "int8" 1802 case "Int16": 1803 return "int16" 1804 case "Int32": 1805 return "int32" 1806 case "Int64": 1807 return "int64" 1808 case "Int128": 1809 return "int128" 1810 case "UInt8": 1811 return "uint8" 1812 case "Float32": 1813 return "float32" 1814 case "Float64": 1815 return "float64" 1816 case "CallOff": 1817 return "int32" 1818 case "SymOff": 1819 return "int32" 1820 case "SymValAndOff": 1821 return "ValAndOff" 1822 case "TypSize": 1823 return "int64" 1824 case "CCop": 1825 return "Op" 1826 case "FlagConstant": 1827 return "flagConstant" 1828 case "ARM64BitField": 1829 return "arm64BitField" 1830 default: 1831 return "invalid" 1832 } 1833 } 1834 1835 // auxType returns the Go type that this block should store in its aux field. 1836 func (b blockData) auxType() string { 1837 switch b.aux { 1838 case "Sym": 1839 return "Sym" 1840 case "S390XCCMask", "S390XCCMaskInt8", "S390XCCMaskUint8": 1841 return "s390x.CCMask" 1842 case "S390XRotateParams": 1843 return "s390x.RotateParams" 1844 default: 1845 return "invalid" 1846 } 1847 } 1848 1849 // auxIntType returns the Go type that this block should store in its auxInt field. 1850 func (b blockData) auxIntType() string { 1851 switch b.aux { 1852 case "S390XCCMaskInt8": 1853 return "int8" 1854 case "S390XCCMaskUint8": 1855 return "uint8" 1856 case "Int64": 1857 return "int64" 1858 default: 1859 return "invalid" 1860 } 1861 } 1862 1863 func title(s string) string { 1864 if i := strings.Index(s, "."); i >= 0 { 1865 switch strings.ToLower(s[:i]) { 1866 case "s390x": // keep arch prefix for clarity 1867 s = s[:i] + s[i+1:] 1868 default: 1869 s = s[i+1:] 1870 } 1871 } 1872 return strings.Title(s) 1873 } 1874 1875 func unTitle(s string) string { 1876 if i := strings.Index(s, "."); i >= 0 { 1877 switch strings.ToLower(s[:i]) { 1878 case "s390x": // keep arch prefix for clarity 1879 s = s[:i] + s[i+1:] 1880 default: 1881 s = s[i+1:] 1882 } 1883 } 1884 return strings.ToLower(s[:1]) + s[1:] 1885 }