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