github.com/FenixAra/go@v0.0.0-20170127160404-96ea0918e670/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 ::= (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 ../op.go (without the Op prefix) 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 r := Rule{rule: rule, loc: loc} 123 if rawop := strings.Split(rule, " ")[0][1:]; isBlock(rawop, arch) { 124 blockrules[rawop] = append(blockrules[rawop], r) 125 } else { 126 // Do fancier value op matching. 127 match, _, _ := r.parse() 128 op, oparch, _, _, _, _ := parseValue(match, arch, loc) 129 opname := fmt.Sprintf("Op%s%s", oparch, op.name) 130 oprules[opname] = append(oprules[opname], r) 131 } 132 rule = "" 133 ruleLineno = 0 134 } 135 if err := scanner.Err(); err != nil { 136 log.Fatalf("scanner failed: %v\n", err) 137 } 138 if unbalanced(rule) { 139 log.Fatalf("%s.rules:%d: unbalanced rule: %v\n", arch.name, lineno, rule) 140 } 141 142 // Order all the ops. 143 var ops []string 144 for op := range oprules { 145 ops = append(ops, op) 146 } 147 sort.Strings(ops) 148 149 // Start output buffer, write header. 150 w := new(bytes.Buffer) 151 fmt.Fprintf(w, "// autogenerated from gen/%s.rules: do not edit!\n", arch.name) 152 fmt.Fprintln(w, "// generated with: cd gen; go run *.go") 153 fmt.Fprintln(w) 154 fmt.Fprintln(w, "package ssa") 155 fmt.Fprintln(w, "import \"math\"") 156 fmt.Fprintln(w, "var _ = math.MinInt8 // in case not otherwise used") 157 158 // Main rewrite routine is a switch on v.Op. 159 fmt.Fprintf(w, "func rewriteValue%s(v *Value, config *Config) bool {\n", arch.name) 160 fmt.Fprintf(w, "switch v.Op {\n") 161 for _, op := range ops { 162 fmt.Fprintf(w, "case %s:\n", op) 163 fmt.Fprintf(w, "return rewriteValue%s_%s(v, config)\n", arch.name, op) 164 } 165 fmt.Fprintf(w, "}\n") 166 fmt.Fprintf(w, "return false\n") 167 fmt.Fprintf(w, "}\n") 168 169 // Generate a routine per op. Note that we don't make one giant routine 170 // because it is too big for some compilers. 171 for _, op := range ops { 172 fmt.Fprintf(w, "func rewriteValue%s_%s(v *Value, config *Config) bool {\n", arch.name, op) 173 fmt.Fprintln(w, "b := v.Block") 174 fmt.Fprintln(w, "_ = b") 175 var canFail bool 176 for i, rule := range oprules[op] { 177 match, cond, result := rule.parse() 178 fmt.Fprintf(w, "// match: %s\n", match) 179 fmt.Fprintf(w, "// cond: %s\n", cond) 180 fmt.Fprintf(w, "// result: %s\n", result) 181 182 canFail = false 183 fmt.Fprintf(w, "for {\n") 184 if genMatch(w, arch, match, rule.loc) { 185 canFail = true 186 } 187 188 if cond != "" { 189 fmt.Fprintf(w, "if !(%s) {\nbreak\n}\n", cond) 190 canFail = true 191 } 192 if !canFail && i != len(oprules[op])-1 { 193 log.Fatalf("unconditional rule %s is followed by other rules", match) 194 } 195 196 genResult(w, arch, result, rule.loc) 197 if *genLog { 198 fmt.Fprintf(w, "logRule(\"%s\")\n", rule.loc) 199 } 200 fmt.Fprintf(w, "return true\n") 201 202 fmt.Fprintf(w, "}\n") 203 } 204 if canFail { 205 fmt.Fprintf(w, "return false\n") 206 } 207 fmt.Fprintf(w, "}\n") 208 } 209 210 // Generate block rewrite function. There are only a few block types 211 // so we can make this one function with a switch. 212 fmt.Fprintf(w, "func rewriteBlock%s(b *Block, config *Config) bool {\n", arch.name) 213 fmt.Fprintf(w, "switch b.Kind {\n") 214 ops = nil 215 for op := range blockrules { 216 ops = append(ops, op) 217 } 218 sort.Strings(ops) 219 for _, op := range ops { 220 fmt.Fprintf(w, "case %s:\n", blockName(op, arch)) 221 for _, rule := range blockrules[op] { 222 match, cond, result := rule.parse() 223 fmt.Fprintf(w, "// match: %s\n", match) 224 fmt.Fprintf(w, "// cond: %s\n", cond) 225 fmt.Fprintf(w, "// result: %s\n", result) 226 227 fmt.Fprintf(w, "for {\n") 228 229 s := split(match[1 : len(match)-1]) // remove parens, then split 230 231 // check match of control value 232 if s[1] != "nil" { 233 fmt.Fprintf(w, "v := b.Control\n") 234 if strings.Contains(s[1], "(") { 235 genMatch0(w, arch, s[1], "v", map[string]struct{}{}, false, rule.loc) 236 } else { 237 fmt.Fprintf(w, "_ = v\n") // in case we don't use v 238 fmt.Fprintf(w, "%s := b.Control\n", s[1]) 239 } 240 } 241 242 // assign successor names 243 succs := s[2:] 244 for i, a := range succs { 245 if a != "_" { 246 fmt.Fprintf(w, "%s := b.Succs[%d]\n", a, i) 247 } 248 } 249 250 if cond != "" { 251 fmt.Fprintf(w, "if !(%s) {\nbreak\n}\n", cond) 252 } 253 254 // Rule matches. Generate result. 255 t := split(result[1 : len(result)-1]) // remove parens, then split 256 newsuccs := t[2:] 257 258 // Check if newsuccs is the same set as succs. 259 m := map[string]bool{} 260 for _, succ := range succs { 261 if m[succ] { 262 log.Fatalf("can't have a repeat successor name %s in %s", succ, rule) 263 } 264 m[succ] = true 265 } 266 for _, succ := range newsuccs { 267 if !m[succ] { 268 log.Fatalf("unknown successor %s in %s", succ, rule) 269 } 270 delete(m, succ) 271 } 272 if len(m) != 0 { 273 log.Fatalf("unmatched successors %v in %s", m, rule) 274 } 275 276 fmt.Fprintf(w, "b.Kind = %s\n", blockName(t[0], arch)) 277 if t[1] == "nil" { 278 fmt.Fprintf(w, "b.SetControl(nil)\n") 279 } else { 280 fmt.Fprintf(w, "b.SetControl(%s)\n", genResult0(w, arch, t[1], new(int), false, false, rule.loc)) 281 } 282 283 succChanged := false 284 for i := 0; i < len(succs); i++ { 285 if succs[i] != newsuccs[i] { 286 succChanged = true 287 } 288 } 289 if succChanged { 290 if len(succs) != 2 { 291 log.Fatalf("changed successors, len!=2 in %s", rule) 292 } 293 if succs[0] != newsuccs[1] || succs[1] != newsuccs[0] { 294 log.Fatalf("can only handle swapped successors in %s", rule) 295 } 296 fmt.Fprintln(w, "b.swapSuccessors()") 297 } 298 for i := 0; i < len(succs); i++ { 299 fmt.Fprintf(w, "_ = %s\n", newsuccs[i]) 300 } 301 302 if *genLog { 303 fmt.Fprintf(w, "logRule(\"%s\")\n", rule.loc) 304 } 305 fmt.Fprintf(w, "return true\n") 306 307 fmt.Fprintf(w, "}\n") 308 } 309 } 310 fmt.Fprintf(w, "}\n") 311 fmt.Fprintf(w, "return false\n") 312 fmt.Fprintf(w, "}\n") 313 314 // gofmt result 315 b := w.Bytes() 316 src, err := format.Source(b) 317 if err != nil { 318 fmt.Printf("%s\n", b) 319 panic(err) 320 } 321 322 // Write to file 323 err = ioutil.WriteFile("../rewrite"+arch.name+".go", src, 0666) 324 if err != nil { 325 log.Fatalf("can't write output: %v\n", err) 326 } 327 } 328 329 // genMatch returns true if the match can fail. 330 func genMatch(w io.Writer, arch arch, match string, loc string) bool { 331 return genMatch0(w, arch, match, "v", map[string]struct{}{}, true, loc) 332 } 333 334 func genMatch0(w io.Writer, arch arch, match, v string, m map[string]struct{}, top bool, loc string) bool { 335 if match[0] != '(' || match[len(match)-1] != ')' { 336 panic("non-compound expr in genMatch0: " + match) 337 } 338 canFail := false 339 340 op, oparch, typ, auxint, aux, args := parseValue(match, arch, loc) 341 342 // check op 343 if !top { 344 fmt.Fprintf(w, "if %s.Op != Op%s%s {\nbreak\n}\n", v, oparch, op.name) 345 canFail = true 346 } 347 348 if typ != "" { 349 if !isVariable(typ) { 350 // code. We must match the results of this code. 351 fmt.Fprintf(w, "if %s.Type != %s {\nbreak\n}\n", v, typ) 352 canFail = true 353 } else { 354 // variable 355 if _, ok := m[typ]; ok { 356 // must match previous variable 357 fmt.Fprintf(w, "if %s.Type != %s {\nbreak\n}\n", v, typ) 358 canFail = true 359 } else { 360 m[typ] = struct{}{} 361 fmt.Fprintf(w, "%s := %s.Type\n", typ, v) 362 } 363 } 364 } 365 366 if auxint != "" { 367 if !isVariable(auxint) { 368 // code 369 fmt.Fprintf(w, "if %s.AuxInt != %s {\nbreak\n}\n", v, auxint) 370 canFail = true 371 } else { 372 // variable 373 if _, ok := m[auxint]; ok { 374 fmt.Fprintf(w, "if %s.AuxInt != %s {\nbreak\n}\n", v, auxint) 375 canFail = true 376 } else { 377 m[auxint] = struct{}{} 378 fmt.Fprintf(w, "%s := %s.AuxInt\n", auxint, v) 379 } 380 } 381 } 382 383 if aux != "" { 384 385 if !isVariable(aux) { 386 // code 387 fmt.Fprintf(w, "if %s.Aux != %s {\nbreak\n}\n", v, aux) 388 canFail = true 389 } else { 390 // variable 391 if _, ok := m[aux]; ok { 392 fmt.Fprintf(w, "if %s.Aux != %s {\nbreak\n}\n", v, aux) 393 canFail = true 394 } else { 395 m[aux] = struct{}{} 396 fmt.Fprintf(w, "%s := %s.Aux\n", aux, v) 397 } 398 } 399 } 400 401 for i, arg := range args { 402 if arg == "_" { 403 continue 404 } 405 if !strings.Contains(arg, "(") { 406 // leaf variable 407 if _, ok := m[arg]; ok { 408 // variable already has a definition. Check whether 409 // the old definition and the new definition match. 410 // For example, (add x x). Equality is just pointer equality 411 // on Values (so cse is important to do before lowering). 412 fmt.Fprintf(w, "if %s != %s.Args[%d] {\nbreak\n}\n", arg, v, i) 413 canFail = true 414 } else { 415 // remember that this variable references the given value 416 m[arg] = struct{}{} 417 fmt.Fprintf(w, "%s := %s.Args[%d]\n", arg, v, i) 418 } 419 continue 420 } 421 // compound sexpr 422 var argname string 423 colon := strings.Index(arg, ":") 424 openparen := strings.Index(arg, "(") 425 if colon >= 0 && openparen >= 0 && colon < openparen { 426 // rule-specified name 427 argname = arg[:colon] 428 arg = arg[colon+1:] 429 } else { 430 // autogenerated name 431 argname = fmt.Sprintf("%s_%d", v, i) 432 } 433 fmt.Fprintf(w, "%s := %s.Args[%d]\n", argname, v, i) 434 if genMatch0(w, arch, arg, argname, m, false, loc) { 435 canFail = true 436 } 437 } 438 439 if op.argLength == -1 { 440 fmt.Fprintf(w, "if len(%s.Args) != %d {\nbreak\n}\n", v, len(args)) 441 canFail = true 442 } 443 return canFail 444 } 445 446 func genResult(w io.Writer, arch arch, result string, loc string) { 447 move := false 448 if result[0] == '@' { 449 // parse @block directive 450 s := strings.SplitN(result[1:], " ", 2) 451 fmt.Fprintf(w, "b = %s\n", s[0]) 452 result = s[1] 453 move = true 454 } 455 genResult0(w, arch, result, new(int), true, move, loc) 456 } 457 func genResult0(w io.Writer, arch arch, result string, alloc *int, top, move bool, loc string) string { 458 // TODO: when generating a constant result, use f.constVal to avoid 459 // introducing copies just to clean them up again. 460 if result[0] != '(' { 461 // variable 462 if top { 463 // It in not safe in general to move a variable between blocks 464 // (and particularly not a phi node). 465 // Introduce a copy. 466 fmt.Fprintf(w, "v.reset(OpCopy)\n") 467 fmt.Fprintf(w, "v.Type = %s.Type\n", result) 468 fmt.Fprintf(w, "v.AddArg(%s)\n", result) 469 } 470 return result 471 } 472 473 op, oparch, typ, auxint, aux, args := parseValue(result, arch, loc) 474 475 // Find the type of the variable. 476 typeOverride := typ != "" 477 if typ == "" && op.typ != "" { 478 typ = typeName(op.typ) 479 } 480 481 var v string 482 if top && !move { 483 v = "v" 484 fmt.Fprintf(w, "v.reset(Op%s%s)\n", oparch, op.name) 485 if typeOverride { 486 fmt.Fprintf(w, "v.Type = %s\n", typ) 487 } 488 } else { 489 if typ == "" { 490 log.Fatalf("sub-expression %s (op=Op%s%s) must have a type", result, oparch, op.name) 491 } 492 v = fmt.Sprintf("v%d", *alloc) 493 *alloc++ 494 fmt.Fprintf(w, "%s := b.NewValue0(v.Line, Op%s%s, %s)\n", v, oparch, op.name, typ) 495 if move && top { 496 // Rewrite original into a copy 497 fmt.Fprintf(w, "v.reset(OpCopy)\n") 498 fmt.Fprintf(w, "v.AddArg(%s)\n", v) 499 } 500 } 501 502 if auxint != "" { 503 fmt.Fprintf(w, "%s.AuxInt = %s\n", v, auxint) 504 } 505 if aux != "" { 506 fmt.Fprintf(w, "%s.Aux = %s\n", v, aux) 507 } 508 for _, arg := range args { 509 x := genResult0(w, arch, arg, alloc, false, move, loc) 510 fmt.Fprintf(w, "%s.AddArg(%s)\n", v, x) 511 } 512 513 return v 514 } 515 516 func split(s string) []string { 517 var r []string 518 519 outer: 520 for s != "" { 521 d := 0 // depth of ({[< 522 var open, close byte // opening and closing markers ({[< or )}]> 523 nonsp := false // found a non-space char so far 524 for i := 0; i < len(s); i++ { 525 switch { 526 case d == 0 && s[i] == '(': 527 open, close = '(', ')' 528 d++ 529 case d == 0 && s[i] == '<': 530 open, close = '<', '>' 531 d++ 532 case d == 0 && s[i] == '[': 533 open, close = '[', ']' 534 d++ 535 case d == 0 && s[i] == '{': 536 open, close = '{', '}' 537 d++ 538 case d == 0 && (s[i] == ' ' || s[i] == '\t'): 539 if nonsp { 540 r = append(r, strings.TrimSpace(s[:i])) 541 s = s[i:] 542 continue outer 543 } 544 case d > 0 && s[i] == open: 545 d++ 546 case d > 0 && s[i] == close: 547 d-- 548 default: 549 nonsp = true 550 } 551 } 552 if d != 0 { 553 panic("imbalanced expression: " + s) 554 } 555 if nonsp { 556 r = append(r, strings.TrimSpace(s)) 557 } 558 break 559 } 560 return r 561 } 562 563 // isBlock returns true if this op is a block opcode. 564 func isBlock(name string, arch arch) bool { 565 for _, b := range genericBlocks { 566 if b.name == name { 567 return true 568 } 569 } 570 for _, b := range arch.blocks { 571 if b.name == name { 572 return true 573 } 574 } 575 return false 576 } 577 578 // parseValue parses a parenthesized value from a rule. 579 // The value can be from the match or the result side. 580 // It returns the op and unparsed strings for typ, auxint, and aux restrictions and for all args. 581 // oparch is the architecture that op is located in, or "" for generic. 582 func parseValue(val string, arch arch, loc string) (op opData, oparch string, typ string, auxint string, aux string, args []string) { 583 val = val[1 : len(val)-1] // remove () 584 585 // Split val up into regions. 586 // Split by spaces/tabs, except those contained in (), {}, [], or <>. 587 s := split(val) 588 589 // Extract restrictions and args. 590 for _, a := range s[1:] { 591 switch a[0] { 592 case '<': 593 typ = a[1 : len(a)-1] // remove <> 594 case '[': 595 auxint = a[1 : len(a)-1] // remove [] 596 case '{': 597 aux = a[1 : len(a)-1] // remove {} 598 default: 599 args = append(args, a) 600 } 601 } 602 603 // Resolve the op. 604 605 // match reports whether x is a good op to select. 606 // If strict is true, rule generation might succeed. 607 // If strict is false, rule generation has failed, 608 // but we're trying to generate a useful error. 609 // Doing strict=true then strict=false allows 610 // precise op matching while retaining good error messages. 611 match := func(x opData, strict bool, archname string) bool { 612 if x.name != s[0] { 613 return false 614 } 615 if x.argLength != -1 && int(x.argLength) != len(args) { 616 if strict { 617 return false 618 } else { 619 log.Printf("%s: op %s (%s) should have %d args, has %d", loc, s[0], archname, x.argLength, len(args)) 620 } 621 } 622 return true 623 } 624 625 for _, x := range genericOps { 626 if match(x, true, "generic") { 627 op = x 628 break 629 } 630 } 631 if arch.name != "generic" { 632 for _, x := range arch.ops { 633 if match(x, true, arch.name) { 634 if op.name != "" { 635 log.Fatalf("%s: matches for op %s found in both generic and %s", loc, op.name, arch.name) 636 } 637 op = x 638 oparch = arch.name 639 break 640 } 641 } 642 } 643 644 if op.name == "" { 645 // Failed to find the op. 646 // Run through everything again with strict=false 647 // to generate useful diagnosic messages before failing. 648 for _, x := range genericOps { 649 match(x, false, "generic") 650 } 651 for _, x := range arch.ops { 652 match(x, false, arch.name) 653 } 654 log.Fatalf("%s: unknown op %s", loc, s) 655 } 656 657 // Sanity check aux, auxint. 658 if auxint != "" { 659 switch op.aux { 660 case "Bool", "Int8", "Int16", "Int32", "Int64", "Int128", "Float32", "Float64", "SymOff", "SymValAndOff", "SymInt32", "SizeAndAlign": 661 default: 662 log.Fatalf("%s: op %s %s can't have auxint", loc, op.name, op.aux) 663 } 664 } 665 if aux != "" { 666 switch op.aux { 667 case "String", "Sym", "SymOff", "SymValAndOff", "SymInt32": 668 default: 669 log.Fatalf("%s: op %s %s can't have aux", loc, op.name, op.aux) 670 } 671 } 672 673 return 674 } 675 676 func blockName(name string, arch arch) string { 677 for _, b := range genericBlocks { 678 if b.name == name { 679 return "Block" + name 680 } 681 } 682 return "Block" + arch.name + name 683 } 684 685 // typeName returns the string to use to generate a type. 686 func typeName(typ string) string { 687 if typ[0] == '(' { 688 ts := strings.Split(typ[1:len(typ)-1], ",") 689 if len(ts) != 2 { 690 panic("Tuple expect 2 arguments") 691 } 692 return "MakeTuple(" + typeName(ts[0]) + ", " + typeName(ts[1]) + ")" 693 } 694 switch typ { 695 case "Flags", "Mem", "Void", "Int128": 696 return "Type" + typ 697 default: 698 return "config.fe.Type" + typ + "()" 699 } 700 } 701 702 // unbalanced returns true if there aren't the same number of ( and ) in the string. 703 func unbalanced(s string) bool { 704 var left, right int 705 for _, c := range s { 706 if c == '(' { 707 left++ 708 } 709 if c == ')' { 710 right++ 711 } 712 } 713 return left != right 714 } 715 716 // isVariable reports whether s is a single Go alphanumeric identifier. 717 func isVariable(s string) bool { 718 b, err := regexp.MatchString("^[A-Za-z_][A-Za-z_0-9]*$", s) 719 if err != nil { 720 panic("bad variable regexp") 721 } 722 return b 723 }