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