github.com/ltltlt/go-source-code@v0.0.0-20190830023027-95be009773aa/cmd/compile/internal/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 // +build gen 6 7 // This program generates Go code that applies rewrite rules to a Value. 8 // The generated code implements a function of type func (v *Value) bool 9 // which returns true iff if did something. 10 // Ideas stolen from Swift: http://www.hpl.hp.com/techreports/Compaq-DEC/WRL-2000-2.html 11 12 package main 13 14 import ( 15 "bufio" 16 "bytes" 17 "flag" 18 "fmt" 19 "go/format" 20 "io" 21 "io/ioutil" 22 "log" 23 "os" 24 "regexp" 25 "sort" 26 "strings" 27 ) 28 29 // rule syntax: 30 // sexpr [&& extra conditions] -> [@block] sexpr 31 // 32 // sexpr are s-expressions (lisp-like parenthesized groupings) 33 // sexpr ::= [variable:](opcode sexpr*) 34 // | variable 35 // | <type> 36 // | [auxint] 37 // | {aux} 38 // 39 // aux ::= variable | {code} 40 // type ::= variable | {code} 41 // variable ::= some token 42 // opcode ::= one of the opcodes from the *Ops.go files 43 44 // extra conditions is just a chunk of Go that evaluates to a boolean. It may use 45 // variables declared in the matching sexpr. The variable "v" is predefined to be 46 // the value matched by the entire rule. 47 48 // If multiple rules match, the first one in file order is selected. 49 50 var ( 51 genLog = flag.Bool("log", false, "generate code that logs; for debugging only") 52 ) 53 54 type Rule struct { 55 rule string 56 loc string // file name & line number 57 } 58 59 func (r Rule) String() string { 60 return fmt.Sprintf("rule %q at %s", r.rule, r.loc) 61 } 62 63 // parse returns the matching part of the rule, additional conditions, and the result. 64 func (r Rule) parse() (match, cond, result string) { 65 s := strings.Split(r.rule, "->") 66 if len(s) != 2 { 67 log.Fatalf("no arrow in %s", r) 68 } 69 match = strings.TrimSpace(s[0]) 70 result = strings.TrimSpace(s[1]) 71 cond = "" 72 if i := strings.Index(match, "&&"); i >= 0 { 73 cond = strings.TrimSpace(match[i+2:]) 74 match = strings.TrimSpace(match[:i]) 75 } 76 return match, cond, result 77 } 78 79 func genRules(arch arch) { 80 // Open input file. 81 text, err := os.Open(arch.name + ".rules") 82 if err != nil { 83 log.Fatalf("can't read rule file: %v", err) 84 } 85 86 // oprules contains a list of rules for each block and opcode 87 blockrules := map[string][]Rule{} 88 oprules := map[string][]Rule{} 89 90 // read rule file 91 scanner := bufio.NewScanner(text) 92 rule := "" 93 var lineno int 94 var ruleLineno int // line number of "->" 95 for scanner.Scan() { 96 lineno++ 97 line := scanner.Text() 98 if i := strings.Index(line, "//"); i >= 0 { 99 // Remove comments. Note that this isn't string safe, so 100 // it will truncate lines with // inside strings. Oh well. 101 line = line[:i] 102 } 103 rule += " " + line 104 rule = strings.TrimSpace(rule) 105 if rule == "" { 106 continue 107 } 108 if !strings.Contains(rule, "->") { 109 continue 110 } 111 if ruleLineno == 0 { 112 ruleLineno = lineno 113 } 114 if strings.HasSuffix(rule, "->") { 115 continue 116 } 117 if unbalanced(rule) { 118 continue 119 } 120 121 loc := fmt.Sprintf("%s.rules:%d", arch.name, ruleLineno) 122 for _, crule := range commute(rule, arch) { 123 r := Rule{rule: crule, loc: loc} 124 if rawop := strings.Split(crule, " ")[0][1:]; isBlock(rawop, arch) { 125 blockrules[rawop] = append(blockrules[rawop], r) 126 } else { 127 // Do fancier value op matching. 128 match, _, _ := r.parse() 129 op, oparch, _, _, _, _ := parseValue(match, arch, loc) 130 opname := fmt.Sprintf("Op%s%s", oparch, op.name) 131 oprules[opname] = append(oprules[opname], r) 132 } 133 } 134 rule = "" 135 ruleLineno = 0 136 } 137 if err := scanner.Err(); err != nil { 138 log.Fatalf("scanner failed: %v\n", err) 139 } 140 if unbalanced(rule) { 141 log.Fatalf("%s.rules:%d: unbalanced rule: %v\n", arch.name, lineno, rule) 142 } 143 144 // Order all the ops. 145 var ops []string 146 for op := range oprules { 147 ops = append(ops, op) 148 } 149 sort.Strings(ops) 150 151 // Start output buffer, write header. 152 w := new(bytes.Buffer) 153 fmt.Fprintf(w, "// Code generated from gen/%s.rules; DO NOT EDIT.\n", arch.name) 154 fmt.Fprintln(w, "// generated with: cd gen; go run *.go") 155 fmt.Fprintln(w) 156 fmt.Fprintln(w, "package ssa") 157 fmt.Fprintln(w, "import \"math\"") 158 fmt.Fprintln(w, "import \"cmd/internal/obj\"") 159 fmt.Fprintln(w, "import \"cmd/internal/objabi\"") 160 fmt.Fprintln(w, "import \"cmd/compile/internal/types\"") 161 fmt.Fprintln(w, "var _ = math.MinInt8 // in case not otherwise used") 162 fmt.Fprintln(w, "var _ = obj.ANOP // in case not otherwise used") 163 fmt.Fprintln(w, "var _ = objabi.GOROOT // in case not otherwise used") 164 fmt.Fprintln(w, "var _ = types.TypeMem // in case not otherwise used") 165 fmt.Fprintln(w) 166 167 const chunkSize = 10 168 // Main rewrite routine is a switch on v.Op. 169 fmt.Fprintf(w, "func rewriteValue%s(v *Value) bool {\n", arch.name) 170 fmt.Fprintf(w, "switch v.Op {\n") 171 for _, op := range ops { 172 fmt.Fprintf(w, "case %s:\n", op) 173 fmt.Fprint(w, "return ") 174 for chunk := 0; chunk < len(oprules[op]); chunk += chunkSize { 175 if chunk > 0 { 176 fmt.Fprint(w, " || ") 177 } 178 fmt.Fprintf(w, "rewriteValue%s_%s_%d(v)", arch.name, op, chunk) 179 } 180 fmt.Fprintln(w) 181 } 182 fmt.Fprintf(w, "}\n") 183 fmt.Fprintf(w, "return false\n") 184 fmt.Fprintf(w, "}\n") 185 186 // Generate a routine per op. Note that we don't make one giant routine 187 // because it is too big for some compilers. 188 for _, op := range ops { 189 for chunk := 0; chunk < len(oprules[op]); chunk += chunkSize { 190 buf := new(bytes.Buffer) 191 var canFail bool 192 endchunk := chunk + chunkSize 193 if endchunk > len(oprules[op]) { 194 endchunk = len(oprules[op]) 195 } 196 for i, rule := range oprules[op][chunk:endchunk] { 197 match, cond, result := rule.parse() 198 fmt.Fprintf(buf, "// match: %s\n", match) 199 fmt.Fprintf(buf, "// cond: %s\n", cond) 200 fmt.Fprintf(buf, "// result: %s\n", result) 201 202 canFail = false 203 fmt.Fprintf(buf, "for {\n") 204 if genMatch(buf, arch, match, rule.loc) { 205 canFail = true 206 } 207 208 if cond != "" { 209 fmt.Fprintf(buf, "if !(%s) {\nbreak\n}\n", cond) 210 canFail = true 211 } 212 if !canFail && i+chunk != len(oprules[op])-1 { 213 log.Fatalf("unconditional rule %s is followed by other rules", match) 214 } 215 216 genResult(buf, arch, result, rule.loc) 217 if *genLog { 218 fmt.Fprintf(buf, "logRule(\"%s\")\n", rule.loc) 219 } 220 fmt.Fprintf(buf, "return true\n") 221 222 fmt.Fprintf(buf, "}\n") 223 } 224 if canFail { 225 fmt.Fprintf(buf, "return false\n") 226 } 227 228 body := buf.String() 229 // Do a rough match to predict whether we need b, config, fe, and/or types. 230 // It's not precise--thus the blank assignments--but it's good enough 231 // to avoid generating needless code and doing pointless nil checks. 232 hasb := strings.Contains(body, "b.") 233 hasconfig := strings.Contains(body, "config.") || strings.Contains(body, "config)") 234 hasfe := strings.Contains(body, "fe.") 235 hastyps := strings.Contains(body, "typ.") 236 fmt.Fprintf(w, "func rewriteValue%s_%s_%d(v *Value) bool {\n", arch.name, op, chunk) 237 if hasb || hasconfig || hasfe || hastyps { 238 fmt.Fprintln(w, "b := v.Block") 239 fmt.Fprintln(w, "_ = b") 240 } 241 if hasconfig { 242 fmt.Fprintln(w, "config := b.Func.Config") 243 fmt.Fprintln(w, "_ = config") 244 } 245 if hasfe { 246 fmt.Fprintln(w, "fe := b.Func.fe") 247 fmt.Fprintln(w, "_ = fe") 248 } 249 if hastyps { 250 fmt.Fprintln(w, "typ := &b.Func.Config.Types") 251 fmt.Fprintln(w, "_ = typ") 252 } 253 fmt.Fprint(w, body) 254 fmt.Fprintf(w, "}\n") 255 } 256 } 257 258 // Generate block rewrite function. There are only a few block types 259 // so we can make this one function with a switch. 260 fmt.Fprintf(w, "func rewriteBlock%s(b *Block) bool {\n", arch.name) 261 fmt.Fprintln(w, "config := b.Func.Config") 262 fmt.Fprintln(w, "_ = config") 263 fmt.Fprintln(w, "fe := b.Func.fe") 264 fmt.Fprintln(w, "_ = fe") 265 fmt.Fprintln(w, "typ := &config.Types") 266 fmt.Fprintln(w, "_ = typ") 267 fmt.Fprintf(w, "switch b.Kind {\n") 268 ops = nil 269 for op := range blockrules { 270 ops = append(ops, op) 271 } 272 sort.Strings(ops) 273 for _, op := range ops { 274 fmt.Fprintf(w, "case %s:\n", blockName(op, arch)) 275 for _, rule := range blockrules[op] { 276 match, cond, result := rule.parse() 277 fmt.Fprintf(w, "// match: %s\n", match) 278 fmt.Fprintf(w, "// cond: %s\n", cond) 279 fmt.Fprintf(w, "// result: %s\n", result) 280 281 fmt.Fprintf(w, "for {\n") 282 283 _, _, _, aux, s := extract(match) // remove parens, then split 284 285 // check match of control value 286 if s[0] != "nil" { 287 fmt.Fprintf(w, "v := b.Control\n") 288 if strings.Contains(s[0], "(") { 289 genMatch0(w, arch, s[0], "v", map[string]struct{}{}, false, rule.loc) 290 } else { 291 fmt.Fprintf(w, "_ = v\n") // in case we don't use v 292 fmt.Fprintf(w, "%s := b.Control\n", s[0]) 293 } 294 } 295 if aux != "" { 296 fmt.Fprintf(w, "%s := b.Aux\n", aux) 297 } 298 299 if cond != "" { 300 fmt.Fprintf(w, "if !(%s) {\nbreak\n}\n", cond) 301 } 302 303 // Rule matches. Generate result. 304 outop, _, _, aux, t := extract(result) // remove parens, then split 305 newsuccs := t[1:] 306 307 // Check if newsuccs is the same set as succs. 308 succs := s[1:] 309 m := map[string]bool{} 310 for _, succ := range succs { 311 if m[succ] { 312 log.Fatalf("can't have a repeat successor name %s in %s", succ, rule) 313 } 314 m[succ] = true 315 } 316 for _, succ := range newsuccs { 317 if !m[succ] { 318 log.Fatalf("unknown successor %s in %s", succ, rule) 319 } 320 delete(m, succ) 321 } 322 if len(m) != 0 { 323 log.Fatalf("unmatched successors %v in %s", m, rule) 324 } 325 326 fmt.Fprintf(w, "b.Kind = %s\n", blockName(outop, arch)) 327 if t[0] == "nil" { 328 fmt.Fprintf(w, "b.SetControl(nil)\n") 329 } else { 330 fmt.Fprintf(w, "b.SetControl(%s)\n", genResult0(w, arch, t[0], new(int), false, false, rule.loc)) 331 } 332 if aux != "" { 333 fmt.Fprintf(w, "b.Aux = %s\n", aux) 334 } else { 335 fmt.Fprintln(w, "b.Aux = nil") 336 } 337 338 succChanged := false 339 for i := 0; i < len(succs); i++ { 340 if succs[i] != newsuccs[i] { 341 succChanged = true 342 } 343 } 344 if succChanged { 345 if len(succs) != 2 { 346 log.Fatalf("changed successors, len!=2 in %s", rule) 347 } 348 if succs[0] != newsuccs[1] || succs[1] != newsuccs[0] { 349 log.Fatalf("can only handle swapped successors in %s", rule) 350 } 351 fmt.Fprintln(w, "b.swapSuccessors()") 352 } 353 354 if *genLog { 355 fmt.Fprintf(w, "logRule(\"%s\")\n", rule.loc) 356 } 357 fmt.Fprintf(w, "return true\n") 358 359 fmt.Fprintf(w, "}\n") 360 } 361 } 362 fmt.Fprintf(w, "}\n") 363 fmt.Fprintf(w, "return false\n") 364 fmt.Fprintf(w, "}\n") 365 366 // gofmt result 367 b := w.Bytes() 368 src, err := format.Source(b) 369 if err != nil { 370 fmt.Printf("%s\n", b) 371 panic(err) 372 } 373 374 // Write to file 375 err = ioutil.WriteFile("../rewrite"+arch.name+".go", src, 0666) 376 if err != nil { 377 log.Fatalf("can't write output: %v\n", err) 378 } 379 } 380 381 // genMatch returns true if the match can fail. 382 func genMatch(w io.Writer, arch arch, match string, loc string) bool { 383 return genMatch0(w, arch, match, "v", map[string]struct{}{}, true, loc) 384 } 385 386 func genMatch0(w io.Writer, arch arch, match, v string, m map[string]struct{}, top bool, loc string) bool { 387 if match[0] != '(' || match[len(match)-1] != ')' { 388 panic("non-compound expr in genMatch0: " + match) 389 } 390 canFail := false 391 392 op, oparch, typ, auxint, aux, args := parseValue(match, arch, loc) 393 394 // check op 395 if !top { 396 fmt.Fprintf(w, "if %s.Op != Op%s%s {\nbreak\n}\n", v, oparch, op.name) 397 canFail = true 398 } 399 400 if typ != "" { 401 if !isVariable(typ) { 402 // code. We must match the results of this code. 403 fmt.Fprintf(w, "if %s.Type != %s {\nbreak\n}\n", v, typ) 404 canFail = true 405 } else { 406 // variable 407 if _, ok := m[typ]; ok { 408 // must match previous variable 409 fmt.Fprintf(w, "if %s.Type != %s {\nbreak\n}\n", v, typ) 410 canFail = true 411 } else { 412 m[typ] = struct{}{} 413 fmt.Fprintf(w, "%s := %s.Type\n", typ, v) 414 } 415 } 416 } 417 418 if auxint != "" { 419 if !isVariable(auxint) { 420 // code 421 fmt.Fprintf(w, "if %s.AuxInt != %s {\nbreak\n}\n", v, auxint) 422 canFail = true 423 } else { 424 // variable 425 if _, ok := m[auxint]; ok { 426 fmt.Fprintf(w, "if %s.AuxInt != %s {\nbreak\n}\n", v, auxint) 427 canFail = true 428 } else { 429 m[auxint] = struct{}{} 430 fmt.Fprintf(w, "%s := %s.AuxInt\n", auxint, v) 431 } 432 } 433 } 434 435 if aux != "" { 436 437 if !isVariable(aux) { 438 // code 439 fmt.Fprintf(w, "if %s.Aux != %s {\nbreak\n}\n", v, aux) 440 canFail = true 441 } else { 442 // variable 443 if _, ok := m[aux]; ok { 444 fmt.Fprintf(w, "if %s.Aux != %s {\nbreak\n}\n", v, aux) 445 canFail = true 446 } else { 447 m[aux] = struct{}{} 448 fmt.Fprintf(w, "%s := %s.Aux\n", aux, v) 449 } 450 } 451 } 452 453 if n := len(args); n > 1 { 454 fmt.Fprintf(w, "_ = %s.Args[%d]\n", v, n-1) // combine some bounds checks 455 } 456 for i, arg := range args { 457 if arg == "_" { 458 continue 459 } 460 if !strings.Contains(arg, "(") { 461 // leaf variable 462 if _, ok := m[arg]; ok { 463 // variable already has a definition. Check whether 464 // the old definition and the new definition match. 465 // For example, (add x x). Equality is just pointer equality 466 // on Values (so cse is important to do before lowering). 467 fmt.Fprintf(w, "if %s != %s.Args[%d] {\nbreak\n}\n", arg, v, i) 468 canFail = true 469 } else { 470 // remember that this variable references the given value 471 m[arg] = struct{}{} 472 fmt.Fprintf(w, "%s := %s.Args[%d]\n", arg, v, i) 473 } 474 continue 475 } 476 // compound sexpr 477 var argname string 478 colon := strings.Index(arg, ":") 479 openparen := strings.Index(arg, "(") 480 if colon >= 0 && openparen >= 0 && colon < openparen { 481 // rule-specified name 482 argname = arg[:colon] 483 arg = arg[colon+1:] 484 } else { 485 // autogenerated name 486 argname = fmt.Sprintf("%s_%d", v, i) 487 } 488 fmt.Fprintf(w, "%s := %s.Args[%d]\n", argname, v, i) 489 if genMatch0(w, arch, arg, argname, m, false, loc) { 490 canFail = true 491 } 492 } 493 494 if op.argLength == -1 { 495 fmt.Fprintf(w, "if len(%s.Args) != %d {\nbreak\n}\n", v, len(args)) 496 canFail = true 497 } 498 return canFail 499 } 500 501 func genResult(w io.Writer, arch arch, result string, loc string) { 502 move := false 503 if result[0] == '@' { 504 // parse @block directive 505 s := strings.SplitN(result[1:], " ", 2) 506 fmt.Fprintf(w, "b = %s\n", s[0]) 507 result = s[1] 508 move = true 509 } 510 genResult0(w, arch, result, new(int), true, move, loc) 511 } 512 func genResult0(w io.Writer, arch arch, result string, alloc *int, top, move bool, loc string) string { 513 // TODO: when generating a constant result, use f.constVal to avoid 514 // introducing copies just to clean them up again. 515 if result[0] != '(' { 516 // variable 517 if top { 518 // It in not safe in general to move a variable between blocks 519 // (and particularly not a phi node). 520 // Introduce a copy. 521 fmt.Fprintf(w, "v.reset(OpCopy)\n") 522 fmt.Fprintf(w, "v.Type = %s.Type\n", result) 523 fmt.Fprintf(w, "v.AddArg(%s)\n", result) 524 } 525 return result 526 } 527 528 op, oparch, typ, auxint, aux, args := parseValue(result, arch, loc) 529 530 // Find the type of the variable. 531 typeOverride := typ != "" 532 if typ == "" && op.typ != "" { 533 typ = typeName(op.typ) 534 } 535 536 var v string 537 if top && !move { 538 v = "v" 539 fmt.Fprintf(w, "v.reset(Op%s%s)\n", oparch, op.name) 540 if typeOverride { 541 fmt.Fprintf(w, "v.Type = %s\n", typ) 542 } 543 } else { 544 if typ == "" { 545 log.Fatalf("sub-expression %s (op=Op%s%s) must have a type", result, oparch, op.name) 546 } 547 v = fmt.Sprintf("v%d", *alloc) 548 *alloc++ 549 fmt.Fprintf(w, "%s := b.NewValue0(v.Pos, Op%s%s, %s)\n", v, oparch, op.name, typ) 550 if move && top { 551 // Rewrite original into a copy 552 fmt.Fprintf(w, "v.reset(OpCopy)\n") 553 fmt.Fprintf(w, "v.AddArg(%s)\n", v) 554 } 555 } 556 557 if auxint != "" { 558 fmt.Fprintf(w, "%s.AuxInt = %s\n", v, auxint) 559 } 560 if aux != "" { 561 fmt.Fprintf(w, "%s.Aux = %s\n", v, aux) 562 } 563 for _, arg := range args { 564 x := genResult0(w, arch, arg, alloc, false, move, loc) 565 fmt.Fprintf(w, "%s.AddArg(%s)\n", v, x) 566 } 567 568 return v 569 } 570 571 func split(s string) []string { 572 var r []string 573 574 outer: 575 for s != "" { 576 d := 0 // depth of ({[< 577 var open, close byte // opening and closing markers ({[< or )}]> 578 nonsp := false // found a non-space char so far 579 for i := 0; i < len(s); i++ { 580 switch { 581 case d == 0 && s[i] == '(': 582 open, close = '(', ')' 583 d++ 584 case d == 0 && s[i] == '<': 585 open, close = '<', '>' 586 d++ 587 case d == 0 && s[i] == '[': 588 open, close = '[', ']' 589 d++ 590 case d == 0 && s[i] == '{': 591 open, close = '{', '}' 592 d++ 593 case d == 0 && (s[i] == ' ' || s[i] == '\t'): 594 if nonsp { 595 r = append(r, strings.TrimSpace(s[:i])) 596 s = s[i:] 597 continue outer 598 } 599 case d > 0 && s[i] == open: 600 d++ 601 case d > 0 && s[i] == close: 602 d-- 603 default: 604 nonsp = true 605 } 606 } 607 if d != 0 { 608 panic("imbalanced expression: " + s) 609 } 610 if nonsp { 611 r = append(r, strings.TrimSpace(s)) 612 } 613 break 614 } 615 return r 616 } 617 618 // isBlock returns true if this op is a block opcode. 619 func isBlock(name string, arch arch) bool { 620 for _, b := range genericBlocks { 621 if b.name == name { 622 return true 623 } 624 } 625 for _, b := range arch.blocks { 626 if b.name == name { 627 return true 628 } 629 } 630 return false 631 } 632 633 func extract(val string) (op string, typ string, auxint string, aux string, args []string) { 634 val = val[1 : len(val)-1] // remove () 635 636 // Split val up into regions. 637 // Split by spaces/tabs, except those contained in (), {}, [], or <>. 638 s := split(val) 639 640 // Extract restrictions and args. 641 op = s[0] 642 for _, a := range s[1:] { 643 switch a[0] { 644 case '<': 645 typ = a[1 : len(a)-1] // remove <> 646 case '[': 647 auxint = a[1 : len(a)-1] // remove [] 648 case '{': 649 aux = a[1 : len(a)-1] // remove {} 650 default: 651 args = append(args, a) 652 } 653 } 654 return 655 } 656 657 // parseValue parses a parenthesized value from a rule. 658 // The value can be from the match or the result side. 659 // It returns the op and unparsed strings for typ, auxint, and aux restrictions and for all args. 660 // oparch is the architecture that op is located in, or "" for generic. 661 func parseValue(val string, arch arch, loc string) (op opData, oparch string, typ string, auxint string, aux string, args []string) { 662 663 // Resolve the op. 664 var s string 665 s, typ, auxint, aux, args = extract(val) 666 667 // match reports whether x is a good op to select. 668 // If strict is true, rule generation might succeed. 669 // If strict is false, rule generation has failed, 670 // but we're trying to generate a useful error. 671 // Doing strict=true then strict=false allows 672 // precise op matching while retaining good error messages. 673 match := func(x opData, strict bool, archname string) bool { 674 if x.name != s { 675 return false 676 } 677 if x.argLength != -1 && int(x.argLength) != len(args) { 678 if strict { 679 return false 680 } else { 681 log.Printf("%s: op %s (%s) should have %d args, has %d", loc, s, archname, x.argLength, len(args)) 682 } 683 } 684 return true 685 } 686 687 for _, x := range genericOps { 688 if match(x, true, "generic") { 689 op = x 690 break 691 } 692 } 693 if arch.name != "generic" { 694 for _, x := range arch.ops { 695 if match(x, true, arch.name) { 696 if op.name != "" { 697 log.Fatalf("%s: matches for op %s found in both generic and %s", loc, op.name, arch.name) 698 } 699 op = x 700 oparch = arch.name 701 break 702 } 703 } 704 } 705 706 if op.name == "" { 707 // Failed to find the op. 708 // Run through everything again with strict=false 709 // to generate useful diagnosic messages before failing. 710 for _, x := range genericOps { 711 match(x, false, "generic") 712 } 713 for _, x := range arch.ops { 714 match(x, false, arch.name) 715 } 716 log.Fatalf("%s: unknown op %s", loc, s) 717 } 718 719 // Sanity check aux, auxint. 720 if auxint != "" { 721 switch op.aux { 722 case "Bool", "Int8", "Int16", "Int32", "Int64", "Int128", "Float32", "Float64", "SymOff", "SymValAndOff", "SymInt32", "TypSize": 723 default: 724 log.Fatalf("%s: op %s %s can't have auxint", loc, op.name, op.aux) 725 } 726 } 727 if aux != "" { 728 switch op.aux { 729 case "String", "Sym", "SymOff", "SymValAndOff", "SymInt32", "Typ", "TypSize": 730 default: 731 log.Fatalf("%s: op %s %s can't have aux", loc, op.name, op.aux) 732 } 733 } 734 735 return 736 } 737 738 func blockName(name string, arch arch) string { 739 for _, b := range genericBlocks { 740 if b.name == name { 741 return "Block" + name 742 } 743 } 744 return "Block" + arch.name + name 745 } 746 747 // typeName returns the string to use to generate a type. 748 func typeName(typ string) string { 749 if typ[0] == '(' { 750 ts := strings.Split(typ[1:len(typ)-1], ",") 751 if len(ts) != 2 { 752 panic("Tuple expect 2 arguments") 753 } 754 return "types.NewTuple(" + typeName(ts[0]) + ", " + typeName(ts[1]) + ")" 755 } 756 switch typ { 757 case "Flags", "Mem", "Void", "Int128": 758 return "types.Type" + typ 759 default: 760 return "typ." + typ 761 } 762 } 763 764 // unbalanced returns true if there aren't the same number of ( and ) in the string. 765 func unbalanced(s string) bool { 766 var left, right int 767 for _, c := range s { 768 if c == '(' { 769 left++ 770 } 771 if c == ')' { 772 right++ 773 } 774 } 775 return left != right 776 } 777 778 // isVariable reports whether s is a single Go alphanumeric identifier. 779 func isVariable(s string) bool { 780 b, err := regexp.MatchString("^[A-Za-z_][A-Za-z_0-9]*$", s) 781 if err != nil { 782 panic("bad variable regexp") 783 } 784 return b 785 } 786 787 // commute returns all equivalent rules to r after applying all possible 788 // argument swaps to the commutable ops in r. 789 // Potentially exponential, be careful. 790 func commute(r string, arch arch) []string { 791 match, cond, result := Rule{rule: r}.parse() 792 a := commute1(match, varCount(match), arch) 793 for i, m := range a { 794 if cond != "" { 795 m += " && " + cond 796 } 797 m += " -> " + result 798 a[i] = m 799 } 800 if len(a) == 1 && normalizeWhitespace(r) != normalizeWhitespace(a[0]) { 801 fmt.Println(normalizeWhitespace(r)) 802 fmt.Println(normalizeWhitespace(a[0])) 803 panic("commute() is not the identity for noncommuting rule") 804 } 805 if false && len(a) > 1 { 806 fmt.Println(r) 807 for _, x := range a { 808 fmt.Println(" " + x) 809 } 810 } 811 return a 812 } 813 814 func commute1(m string, cnt map[string]int, arch arch) []string { 815 if m[0] == '<' || m[0] == '[' || m[0] == '{' || isVariable(m) { 816 return []string{m} 817 } 818 // Split up input. 819 var prefix string 820 colon := strings.Index(m, ":") 821 if colon >= 0 && isVariable(m[:colon]) { 822 prefix = m[:colon+1] 823 m = m[colon+1:] 824 } 825 if m[0] != '(' || m[len(m)-1] != ')' { 826 panic("non-compound expr in commute1: " + m) 827 } 828 s := split(m[1 : len(m)-1]) 829 op := s[0] 830 831 // Figure out if the op is commutative or not. 832 commutative := false 833 for _, x := range genericOps { 834 if op == x.name { 835 if x.commutative { 836 commutative = true 837 } 838 break 839 } 840 } 841 if arch.name != "generic" { 842 for _, x := range arch.ops { 843 if op == x.name { 844 if x.commutative { 845 commutative = true 846 } 847 break 848 } 849 } 850 } 851 var idx0, idx1 int 852 if commutative { 853 // Find indexes of two args we can swap. 854 for i, arg := range s { 855 if i == 0 || arg[0] == '<' || arg[0] == '[' || arg[0] == '{' { 856 continue 857 } 858 if idx0 == 0 { 859 idx0 = i 860 continue 861 } 862 if idx1 == 0 { 863 idx1 = i 864 break 865 } 866 } 867 if idx1 == 0 { 868 panic("couldn't find first two args of commutative op " + s[0]) 869 } 870 if cnt[s[idx0]] == 1 && cnt[s[idx1]] == 1 || s[idx0] == s[idx1] && cnt[s[idx0]] == 2 { 871 // When we have (Add x y) with no ther uses of x and y in the matching rule, 872 // then we can skip the commutative match (Add y x). 873 commutative = false 874 } 875 } 876 877 // Recursively commute arguments. 878 a := make([][]string, len(s)) 879 for i, arg := range s { 880 a[i] = commute1(arg, cnt, arch) 881 } 882 883 // Choose all possibilities from all args. 884 r := crossProduct(a) 885 886 // If commutative, do that again with its two args reversed. 887 if commutative { 888 a[idx0], a[idx1] = a[idx1], a[idx0] 889 r = append(r, crossProduct(a)...) 890 } 891 892 // Construct result. 893 for i, x := range r { 894 r[i] = prefix + "(" + x + ")" 895 } 896 return r 897 } 898 899 // varCount returns a map which counts the number of occurrences of 900 // Value variables in m. 901 func varCount(m string) map[string]int { 902 cnt := map[string]int{} 903 varCount1(m, cnt) 904 return cnt 905 } 906 func varCount1(m string, cnt map[string]int) { 907 if m[0] == '<' || m[0] == '[' || m[0] == '{' { 908 return 909 } 910 if isVariable(m) { 911 cnt[m]++ 912 return 913 } 914 // Split up input. 915 colon := strings.Index(m, ":") 916 if colon >= 0 && isVariable(m[:colon]) { 917 cnt[m[:colon]]++ 918 m = m[colon+1:] 919 } 920 if m[0] != '(' || m[len(m)-1] != ')' { 921 panic("non-compound expr in commute1: " + m) 922 } 923 s := split(m[1 : len(m)-1]) 924 for _, arg := range s[1:] { 925 varCount1(arg, cnt) 926 } 927 } 928 929 // crossProduct returns all possible values 930 // x[0][i] + " " + x[1][j] + " " + ... + " " + x[len(x)-1][k] 931 // for all valid values of i, j, ..., k. 932 func crossProduct(x [][]string) []string { 933 if len(x) == 1 { 934 return x[0] 935 } 936 var r []string 937 for _, tail := range crossProduct(x[1:]) { 938 for _, first := range x[0] { 939 r = append(r, first+" "+tail) 940 } 941 } 942 return r 943 } 944 945 // normalizeWhitespace replaces 2+ whitespace sequences with a single space. 946 func normalizeWhitespace(x string) string { 947 x = strings.Join(strings.Fields(x), " ") 948 x = strings.Replace(x, "( ", "(", -1) 949 x = strings.Replace(x, " )", ")", -1) 950 return x 951 }