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