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