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