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