github.com/goproxy0/go@v0.0.0-20171111080102-49cc0c489d2c/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 _, _, _, 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 // Resolve the op. 663 var s string 664 s, typ, auxint, aux, args = extract(val) 665 666 // match reports whether x is a good op to select. 667 // If strict is true, rule generation might succeed. 668 // If strict is false, rule generation has failed, 669 // but we're trying to generate a useful error. 670 // Doing strict=true then strict=false allows 671 // precise op matching while retaining good error messages. 672 match := func(x opData, strict bool, archname string) bool { 673 if x.name != s { 674 return false 675 } 676 if x.argLength != -1 && int(x.argLength) != len(args) { 677 if strict { 678 return false 679 } else { 680 log.Printf("%s: op %s (%s) should have %d args, has %d", loc, s, archname, x.argLength, len(args)) 681 } 682 } 683 return true 684 } 685 686 for _, x := range genericOps { 687 if match(x, true, "generic") { 688 op = x 689 break 690 } 691 } 692 if arch.name != "generic" { 693 for _, x := range arch.ops { 694 if match(x, true, arch.name) { 695 if op.name != "" { 696 log.Fatalf("%s: matches for op %s found in both generic and %s", loc, op.name, arch.name) 697 } 698 op = x 699 oparch = arch.name 700 break 701 } 702 } 703 } 704 705 if op.name == "" { 706 // Failed to find the op. 707 // Run through everything again with strict=false 708 // to generate useful diagnosic messages before failing. 709 for _, x := range genericOps { 710 match(x, false, "generic") 711 } 712 for _, x := range arch.ops { 713 match(x, false, arch.name) 714 } 715 log.Fatalf("%s: unknown op %s", loc, s) 716 } 717 718 // Sanity check aux, auxint. 719 if auxint != "" { 720 switch op.aux { 721 case "Bool", "Int8", "Int16", "Int32", "Int64", "Int128", "Float32", "Float64", "SymOff", "SymValAndOff", "SymInt32", "TypSize": 722 default: 723 log.Fatalf("%s: op %s %s can't have auxint", loc, op.name, op.aux) 724 } 725 } 726 if aux != "" { 727 switch op.aux { 728 case "String", "Sym", "SymOff", "SymValAndOff", "SymInt32", "Typ", "TypSize": 729 default: 730 log.Fatalf("%s: op %s %s can't have aux", loc, op.name, op.aux) 731 } 732 } 733 734 return 735 } 736 737 func blockName(name string, arch arch) string { 738 for _, b := range genericBlocks { 739 if b.name == name { 740 return "Block" + name 741 } 742 } 743 return "Block" + arch.name + name 744 } 745 746 // typeName returns the string to use to generate a type. 747 func typeName(typ string) string { 748 if typ[0] == '(' { 749 ts := strings.Split(typ[1:len(typ)-1], ",") 750 if len(ts) != 2 { 751 panic("Tuple expect 2 arguments") 752 } 753 return "types.NewTuple(" + typeName(ts[0]) + ", " + typeName(ts[1]) + ")" 754 } 755 switch typ { 756 case "Flags", "Mem", "Void", "Int128": 757 return "types.Type" + typ 758 default: 759 return "typ." + typ 760 } 761 } 762 763 // unbalanced returns true if there aren't the same number of ( and ) in the string. 764 func unbalanced(s string) bool { 765 var left, right int 766 for _, c := range s { 767 if c == '(' { 768 left++ 769 } 770 if c == ')' { 771 right++ 772 } 773 } 774 return left != right 775 } 776 777 // isVariable reports whether s is a single Go alphanumeric identifier. 778 func isVariable(s string) bool { 779 b, err := regexp.MatchString("^[A-Za-z_][A-Za-z_0-9]*$", s) 780 if err != nil { 781 panic("bad variable regexp") 782 } 783 return b 784 } 785 786 // commute returns all equivalent rules to r after applying all possible 787 // argument swaps to the commutable ops in r. 788 // Potentially exponential, be careful. 789 func commute(r string, arch arch) []string { 790 match, cond, result := Rule{rule: r}.parse() 791 a := commute1(match, varCount(match), arch) 792 for i, m := range a { 793 if cond != "" { 794 m += " && " + cond 795 } 796 m += " -> " + result 797 a[i] = m 798 } 799 if len(a) == 1 && normalizeWhitespace(r) != normalizeWhitespace(a[0]) { 800 fmt.Println(normalizeWhitespace(r)) 801 fmt.Println(normalizeWhitespace(a[0])) 802 panic("commute() is not the identity for noncommuting rule") 803 } 804 if false && len(a) > 1 { 805 fmt.Println(r) 806 for _, x := range a { 807 fmt.Println(" " + x) 808 } 809 } 810 return a 811 } 812 813 func commute1(m string, cnt map[string]int, arch arch) []string { 814 if m[0] == '<' || m[0] == '[' || m[0] == '{' || isVariable(m) { 815 return []string{m} 816 } 817 // Split up input. 818 var prefix string 819 colon := strings.Index(m, ":") 820 if colon >= 0 && isVariable(m[:colon]) { 821 prefix = m[:colon+1] 822 m = m[colon+1:] 823 } 824 if m[0] != '(' || m[len(m)-1] != ')' { 825 panic("non-compound expr in commute1: " + m) 826 } 827 s := split(m[1 : len(m)-1]) 828 op := s[0] 829 830 // Figure out if the op is commutative or not. 831 commutative := false 832 for _, x := range genericOps { 833 if op == x.name { 834 if x.commutative { 835 commutative = true 836 } 837 break 838 } 839 } 840 if arch.name != "generic" { 841 for _, x := range arch.ops { 842 if op == x.name { 843 if x.commutative { 844 commutative = true 845 } 846 break 847 } 848 } 849 } 850 var idx0, idx1 int 851 if commutative { 852 // Find indexes of two args we can swap. 853 for i, arg := range s { 854 if i == 0 || arg[0] == '<' || arg[0] == '[' || arg[0] == '{' { 855 continue 856 } 857 if idx0 == 0 { 858 idx0 = i 859 continue 860 } 861 if idx1 == 0 { 862 idx1 = i 863 break 864 } 865 } 866 if idx1 == 0 { 867 panic("couldn't find first two args of commutative op " + s[0]) 868 } 869 if cnt[s[idx0]] == 1 && cnt[s[idx1]] == 1 || s[idx0] == s[idx1] && cnt[s[idx0]] == 2 { 870 // When we have (Add x y) with no ther uses of x and y in the matching rule, 871 // then we can skip the commutative match (Add y x). 872 commutative = false 873 } 874 } 875 876 // Recursively commute arguments. 877 a := make([][]string, len(s)) 878 for i, arg := range s { 879 a[i] = commute1(arg, cnt, arch) 880 } 881 882 // Choose all possibilities from all args. 883 r := crossProduct(a) 884 885 // If commutative, do that again with its two args reversed. 886 if commutative { 887 a[idx0], a[idx1] = a[idx1], a[idx0] 888 r = append(r, crossProduct(a)...) 889 } 890 891 // Construct result. 892 for i, x := range r { 893 r[i] = prefix + "(" + x + ")" 894 } 895 return r 896 } 897 898 // varCount returns a map which counts the number of occurrences of 899 // Value variables in m. 900 func varCount(m string) map[string]int { 901 cnt := map[string]int{} 902 varCount1(m, cnt) 903 return cnt 904 } 905 func varCount1(m string, cnt map[string]int) { 906 if m[0] == '<' || m[0] == '[' || m[0] == '{' { 907 return 908 } 909 if isVariable(m) { 910 cnt[m]++ 911 return 912 } 913 // Split up input. 914 colon := strings.Index(m, ":") 915 if colon >= 0 && isVariable(m[:colon]) { 916 cnt[m[:colon]]++ 917 m = m[colon+1:] 918 } 919 if m[0] != '(' || m[len(m)-1] != ')' { 920 panic("non-compound expr in commute1: " + m) 921 } 922 s := split(m[1 : len(m)-1]) 923 for _, arg := range s[1:] { 924 varCount1(arg, cnt) 925 } 926 } 927 928 // crossProduct returns all possible values 929 // x[0][i] + " " + x[1][j] + " " + ... + " " + x[len(x)-1][k] 930 // for all valid values of i, j, ..., k. 931 func crossProduct(x [][]string) []string { 932 if len(x) == 1 { 933 return x[0] 934 } 935 var r []string 936 for _, tail := range crossProduct(x[1:]) { 937 for _, first := range x[0] { 938 r = append(r, first+" "+tail) 939 } 940 } 941 return r 942 } 943 944 // normalizeWhitespace replaces 2+ whitespace sequences with a single space. 945 func normalizeWhitespace(x string) string { 946 x = strings.Join(strings.Fields(x), " ") 947 x = strings.Replace(x, "( ", "(", -1) 948 x = strings.Replace(x, " )", ")", -1) 949 return x 950 }