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