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.
8 // This program generates Go code that applies rewrite rules to a Value.
9 // The generated code implements a function of type func (v *Value) bool
10 // which reports whether if did something.
11 // Ideas stolen from Swift: http://www.hpl.hp.com/techreports/Compaq-DEC/WRL-2000-2.html
35 "golang.org/x/tools/go/ast/astutil"
39 // sexpr [&& extra conditions] => [@block] sexpr
41 // sexpr are s-expressions (lisp-like parenthesized groupings)
42 // sexpr ::= [variable:](opcode sexpr*)
48 // aux ::= variable | {code}
49 // type ::= variable | {code}
50 // variable ::= some token
51 // opcode ::= one of the opcodes from the *Ops.go files
53 // special rules: trailing ellipsis "..." (in the outermost sexpr?) must match on both sides of a rule.
54 // trailing three underscore "___" in the outermost match sexpr indicate the presence of
55 // extra ignored args that need not appear in the replacement
57 // extra conditions is just a chunk of Go that evaluates to a boolean. It may use
58 // variables declared in the matching tsexpr. The variable "v" is predefined to be
59 // the value matched by the entire rule.
61 // If multiple rules match, the first one in file order is selected.
64 genLog = flag.Bool("log", false, "generate code that logs; for debugging only")
65 addLine = flag.Bool("line", false, "add line number comment to generated rules; for debugging only")
70 Loc string // file name & line number
73 func (r Rule) String() string {
74 return fmt.Sprintf("rule %q at %s", r.Rule, r.Loc)
77 func normalizeSpaces(s string) string {
78 return strings.Join(strings.Fields(strings.TrimSpace(s)), " ")
81 // parse returns the matching part of the rule, additional conditions, and the result.
82 func (r Rule) parse() (match, cond, result string) {
83 s := strings.Split(r.Rule, "=>")
84 match = normalizeSpaces(s[0])
85 result = normalizeSpaces(s[1])
87 if i := strings.Index(match, "&&"); i >= 0 {
88 cond = normalizeSpaces(match[i+2:])
89 match = normalizeSpaces(match[:i])
91 return match, cond, result
94 func genRules(arch arch) { genRulesSuffix(arch, "") }
95 func genSplitLoadRules(arch arch) { genRulesSuffix(arch, "splitload") }
97 func genRulesSuffix(arch arch, suff string) {
99 text, err := os.Open(arch.name + suff + ".rules")
102 // All architectures must have a plain rules file.
103 log.Fatalf("can't read rule file: %v", err)
105 // Some architectures have bonus rules files that others don't share. That's fine.
109 // oprules contains a list of rules for each block and opcode
110 blockrules := map[string][]Rule{}
111 oprules := map[string][]Rule{}
114 scanner := bufio.NewScanner(text)
117 var ruleLineno int // line number of "=>"
120 line := scanner.Text()
121 if i := strings.Index(line, "//"); i >= 0 {
122 // Remove comments. Note that this isn't string safe, so
123 // it will truncate lines with // inside strings. Oh well.
127 rule = strings.TrimSpace(rule)
131 if !strings.Contains(rule, "=>") {
137 if strings.HasSuffix(rule, "=>") {
138 continue // continue on the next line
140 if n := balance(rule); n > 0 {
141 continue // open parentheses remain, continue on the next line
143 break // continuing the line can't help, and it will only make errors worse
146 loc := fmt.Sprintf("%s%s.rules:%d", arch.name, suff, ruleLineno)
147 for _, rule2 := range expandOr(rule) {
148 r := Rule{Rule: rule2, Loc: loc}
149 if rawop := strings.Split(rule2, " ")[0][1:]; isBlock(rawop, arch) {
150 blockrules[rawop] = append(blockrules[rawop], r)
153 // Do fancier value op matching.
154 match, _, _ := r.parse()
155 op, oparch, _, _, _, _ := parseValue(match, arch, loc)
156 opname := fmt.Sprintf("Op%s%s", oparch, op.name)
157 oprules[opname] = append(oprules[opname], r)
162 if err := scanner.Err(); err != nil {
163 log.Fatalf("scanner failed: %v\n", err)
165 if balance(rule) != 0 {
166 log.Fatalf("%s.rules:%d: unbalanced rule: %v\n", arch.name, lineno, rule)
169 // Order all the ops.
171 for op := range oprules {
172 ops = append(ops, op)
176 genFile := &File{Arch: arch, Suffix: suff}
177 // Main rewrite routine is a switch on v.Op.
178 fn := &Func{Kind: "Value", ArgLen: -1}
180 sw := &Switch{Expr: exprf("v.Op")}
181 for _, op := range ops {
182 eop, ok := parseEllipsisRules(oprules[op], arch)
184 if strings.Contains(oprules[op][0].Rule, "=>") && opByName(arch, op).aux != opByName(arch, eop).aux {
185 panic(fmt.Sprintf("can't use ... for ops that have different aux types: %s and %s", op, eop))
187 swc := &Case{Expr: exprf("%s", op)}
188 swc.add(stmtf("v.Op = %s", eop))
189 swc.add(stmtf("return true"))
194 swc := &Case{Expr: exprf("%s", op)}
195 swc.add(stmtf("return rewriteValue%s%s_%s(v)", arch.name, suff, op))
198 if len(sw.List) > 0 { // skip if empty
201 fn.add(stmtf("return false"))
204 // Generate a routine per op. Note that we don't make one giant routine
205 // because it is too big for some compilers.
206 for _, op := range ops {
208 _, ok := parseEllipsisRules(oprules[op], arch)
213 // rr is kept between iterations, so that each rule can check
214 // that the previous rule wasn't unconditional.
218 Suffix: fmt.Sprintf("_%s", op),
219 ArgLen: opByName(arch, op).argLength,
221 fn.add(declReserved("b", "v.Block"))
222 fn.add(declReserved("config", "b.Func.Config"))
223 fn.add(declReserved("fe", "b.Func.fe"))
224 fn.add(declReserved("typ", "&b.Func.Config.Types"))
225 for _, rule := range rules {
226 if rr != nil && !rr.CanFail {
227 log.Fatalf("unconditional rule %s is followed by other rules", rr.Match)
229 rr = &RuleRewrite{Loc: rule.Loc}
230 rr.Match, rr.Cond, rr.Result = rule.parse()
231 pos, _ := genMatch(rr, arch, rr.Match, fn.ArgLen >= 0)
236 rr.add(breakf("!(%s)", rr.Cond))
238 genResult(rr, arch, rr.Result, pos)
240 rr.add(stmtf("logRule(%q)", rule.Loc))
245 fn.add(stmtf("return false"))
250 // Generate block rewrite function. There are only a few block types
251 // so we can make this one function with a switch.
252 fn = &Func{Kind: "Block"}
253 fn.add(declReserved("config", "b.Func.Config"))
254 fn.add(declReserved("typ", "&b.Func.Config.Types"))
256 sw = &Switch{Expr: exprf("b.Kind")}
258 for op := range blockrules {
259 ops = append(ops, op)
262 for _, op := range ops {
263 name, data := getBlockInfo(op, arch)
264 swc := &Case{Expr: exprf("%s", name)}
265 for _, rule := range blockrules[op] {
266 swc.add(genBlockRewrite(rule, arch, data))
270 if len(sw.List) > 0 { // skip if empty
273 fn.add(stmtf("return false"))
276 // Remove unused imports and variables.
277 buf := new(bytes.Buffer)
279 fset := token.NewFileSet()
280 file, err := parser.ParseFile(fset, "", buf, parser.ParseComments)
282 filename := fmt.Sprintf("%s_broken.go", arch.name)
283 if err := ioutil.WriteFile(filename, buf.Bytes(), 0644); err != nil {
284 log.Printf("failed to dump broken code to %s: %v", filename, err)
286 log.Printf("dumped broken code to %s", filename)
288 log.Fatalf("failed to parse generated code for arch %s: %v", arch.name, err)
290 tfile := fset.File(file.Pos())
292 // First, use unusedInspector to find the unused declarations by their
294 u := unusedInspector{unused: make(map[token.Pos]bool)}
297 // Then, delete said nodes via astutil.Apply.
298 pre := func(c *astutil.Cursor) bool {
303 if u.unused[node.Pos()] {
305 // Unused imports and declarations use exactly
306 // one line. Prevent leaving an empty line.
307 tfile.MergeLine(tfile.Position(node.Pos()).Line)
312 post := func(c *astutil.Cursor) bool {
313 switch node := c.Node().(type) {
315 if len(node.Specs) == 0 {
316 // Don't leave a broken or empty GenDecl behind,
317 // such as "import ()".
323 file = astutil.Apply(file, pre, post).(*ast.File)
325 // Write the well-formatted source to file
326 f, err := os.Create("../rewrite" + arch.name + suff + ".go")
328 log.Fatalf("can't write output: %v", err)
331 // gofmt result; use a buffered writer, as otherwise go/format spends
332 // far too much time in syscalls.
333 bw := bufio.NewWriter(f)
334 if err := format.Node(bw, fset, file); err != nil {
335 log.Fatalf("can't format output: %v", err)
337 if err := bw.Flush(); err != nil {
338 log.Fatalf("can't write output: %v", err)
340 if err := f.Close(); err != nil {
341 log.Fatalf("can't write output: %v", err)
345 // unusedInspector can be used to detect unused variables and imports in an
346 // ast.Node via its node method. The result is available in the "unused" map.
348 // note that unusedInspector is lazy and best-effort; it only supports the node
349 // types and patterns used by the rulegen program.
350 type unusedInspector struct {
351 // scope is the current scope, which can never be nil when a declaration
352 // is encountered. That is, the unusedInspector.node entrypoint should
353 // generally be an entire file or block.
356 // unused is the resulting set of unused declared names, indexed by the
357 // starting position of the node that declared the name.
358 unused map[token.Pos]bool
360 // defining is the object currently being defined; this is useful so
361 // that if "foo := bar" is unused and removed, we can then detect if
362 // "bar" becomes unused as well.
366 // scoped opens a new scope when called, and returns a function which closes
367 // that same scope. When a scope is closed, unused variables are recorded.
368 func (u *unusedInspector) scoped() func() {
370 u.scope = &scope{outer: outer, objects: map[string]*object{}}
372 for anyUnused := true; anyUnused; {
374 for _, obj := range u.scope.objects {
378 u.unused[obj.pos] = true
379 for _, used := range obj.used {
380 if used.numUses--; used.numUses == 0 {
384 // We've decremented numUses for each of the
385 // objects in used. Zero this slice too, to keep
386 // everything consistent.
394 func (u *unusedInspector) exprs(list []ast.Expr) {
395 for _, x := range list {
400 func (u *unusedInspector) node(node ast.Node) {
401 switch node := node.(type) {
404 for _, decl := range node.Decls {
408 for _, spec := range node.Specs {
411 case *ast.ImportSpec:
412 impPath, _ := strconv.Unquote(node.Path.Value)
413 name := path.Base(impPath)
414 u.scope.objects[name] = &object{
420 if node.Body != nil {
424 if node.Params != nil {
427 if node.Results != nil {
431 for _, field := range node.List {
441 for _, stmt := range node.List {
447 if node.Init != nil {
452 if node.Else != nil {
456 if node.Init != nil {
459 if node.Cond != nil {
462 if node.Post != nil {
466 case *ast.SwitchStmt:
467 if node.Init != nil {
474 case *ast.CaseClause:
477 for _, stmt := range node.Body {
480 case *ast.BranchStmt:
483 case *ast.AssignStmt:
484 if node.Tok != token.DEFINE {
490 if len(lhs) == 2 && lhs[1].(*ast.Ident).Name == "_" {
494 panic("no support for := with multiple names")
497 name := lhs[0].(*ast.Ident)
508 u.scope.objects[name.Name] = obj
509 case *ast.ReturnStmt:
510 u.exprs(node.Results)
511 case *ast.IncDecStmt:
519 case *ast.SelectorExpr:
523 case *ast.BinaryExpr:
533 case *ast.TypeAssertExpr:
537 if obj := u.scope.Lookup(node.Name); obj != nil {
539 if u.defining != nil {
540 u.defining.used = append(u.defining.used, obj)
547 panic(fmt.Sprintf("unhandled node: %T", node))
551 // scope keeps track of a certain scope and its declared names, as well as the
552 // outer (parent) scope.
554 outer *scope // can be nil, if this is the top-level scope
555 objects map[string]*object // indexed by each declared name
558 func (s *scope) Lookup(name string) *object {
559 if obj := s.objects[name]; obj != nil {
565 return s.outer.Lookup(name)
568 // object keeps track of a declared name, such as a variable or import.
571 pos token.Pos // start position of the node declaring the object
573 numUses int // number of times this object is used
574 used []*object // objects that its declaration makes use of
577 func fprint(w io.Writer, n Node) {
578 switch n := n.(type) {
581 seenRewrite := make(map[[3]string]string)
582 fmt.Fprintf(w, "// Code generated from gen/%s%s.rules; DO NOT EDIT.\n", n.Arch.name, n.Suffix)
583 fmt.Fprintf(w, "// generated with: cd gen; go run *.go\n")
584 fmt.Fprintf(w, "\npackage ssa\n")
585 for _, path := range append([]string{
590 "cmd/compile/internal/base",
591 "cmd/compile/internal/types",
592 }, n.Arch.imports...) {
593 fmt.Fprintf(w, "import %q\n", path)
595 for _, f := range n.List {
597 fmt.Fprintf(w, "func rewrite%s%s%s%s(", f.Kind, n.Arch.name, n.Suffix, f.Suffix)
598 fmt.Fprintf(w, "%c *%s) bool {\n", strings.ToLower(f.Kind)[0], f.Kind)
599 if f.Kind == "Value" && f.ArgLen > 0 {
600 for i := f.ArgLen - 1; i >= 0; i-- {
601 fmt.Fprintf(w, "v_%d := v.Args[%d]\n", i, i)
604 for _, n := range f.List {
607 if rr, ok := n.(*RuleRewrite); ok {
609 normalizeMatch(rr.Match, file.Arch),
610 normalizeWhitespace(rr.Cond),
611 normalizeWhitespace(rr.Result),
613 if prev, ok := seenRewrite[k]; ok {
614 log.Fatalf("duplicate rule %s, previously seen at %s\n", rr.Loc, prev)
616 seenRewrite[k] = rr.Loc
619 fmt.Fprintf(w, "}\n")
622 fmt.Fprintf(w, "switch ")
624 fmt.Fprintf(w, " {\n")
625 for _, n := range n.List {
628 fmt.Fprintf(w, "}\n")
630 fmt.Fprintf(w, "case ")
632 fmt.Fprintf(w, ":\n")
633 for _, n := range n.List {
638 fmt.Fprintf(w, "// %s\n", n.Loc)
640 fmt.Fprintf(w, "// match: %s\n", n.Match)
642 fmt.Fprintf(w, "// cond: %s\n", n.Cond)
644 fmt.Fprintf(w, "// result: %s\n", n.Result)
645 fmt.Fprintf(w, "for %s {\n", n.Check)
647 for _, n := range n.List {
648 if b, ok := n.(*CondBreak); ok {
649 b.InsideCommuteLoop = nCommutative > 0
652 if loop, ok := n.(StartCommuteLoop); ok {
653 if nCommutative != loop.Depth {
654 panic("mismatch commute loop depth")
659 fmt.Fprintf(w, "return true\n")
660 for i := 0; i < nCommutative; i++ {
663 if n.CommuteDepth > 0 && n.CanFail {
664 fmt.Fprint(w, "break\n")
666 fmt.Fprintf(w, "}\n")
668 fmt.Fprintf(w, "%s := ", n.Name)
672 fmt.Fprintf(w, "if ")
674 fmt.Fprintf(w, " {\n")
675 if n.InsideCommuteLoop {
676 fmt.Fprintf(w, "continue")
678 fmt.Fprintf(w, "break")
680 fmt.Fprintf(w, "\n}\n")
682 printConfig.Fprint(w, emptyFset, n)
683 if _, ok := n.(ast.Stmt); ok {
686 case StartCommuteLoop:
687 fmt.Fprintf(w, "for _i%[1]d := 0; _i%[1]d <= 1; _i%[1]d, %[2]s_0, %[2]s_1 = _i%[1]d + 1, %[2]s_1, %[2]s_0 {\n", n.Depth, n.V)
689 log.Fatalf("cannot print %T", n)
693 var printConfig = printer.Config{
694 Mode: printer.RawFormat, // we use go/format later, so skip work here
697 var emptyFset = token.NewFileSet()
699 // Node can be a Statement or an ast.Expr.
700 type Node interface{}
702 // Statement can be one of our high-level statement struct types, or an
703 // ast.Stmt under some limited circumstances.
704 type Statement interface{}
706 // BodyBase is shared by all of our statement pseudo-node types which can
707 // contain other statements.
708 type BodyBase struct {
713 func (w *BodyBase) add(node Statement) {
716 last = w.List[len(w.List)-1]
718 if node, ok := node.(*CondBreak); ok {
720 if last, ok := last.(*CondBreak); ok {
721 // Add to the previous "if <cond> { break }" via a
722 // logical OR, which will save verbosity.
723 last.Cond = &ast.BinaryExpr{
732 w.List = append(w.List, node)
735 // predeclared contains globally known tokens that should not be redefined.
736 var predeclared = map[string]bool{
742 // declared reports if the body contains a Declare with the given name.
743 func (w *BodyBase) declared(name string) bool {
744 if predeclared[name] {
745 // Treat predeclared names as having already been declared.
746 // This lets us use nil to match an aux field or
747 // true and false to match an auxint field.
750 for _, s := range w.List {
751 if decl, ok := s.(*Declare); ok && decl.Name == name {
758 // These types define some high-level statement struct types, which can be used
759 // as a Statement. This allows us to keep some node structs simpler, and have
760 // higher-level nodes such as an entire rule rewrite.
762 // Note that ast.Expr is always used as-is; we don't declare our own expression
772 Kind string // "Value" or "Block"
774 ArgLen int32 // if kind == "Value", number of args for this op
786 Match, Cond, Result string // top comments
787 Check string // top-level boolean expression
789 Alloc int // for unique var names
790 Loc string // file name & line number of the original rule
791 CommuteDepth int // used to track depth of commute loops
799 InsideCommuteLoop bool
801 StartCommuteLoop struct {
807 // exprf parses a Go expression generated from fmt.Sprintf, panicking if an
809 func exprf(format string, a ...interface{}) ast.Expr {
810 src := fmt.Sprintf(format, a...)
811 expr, err := parser.ParseExpr(src)
813 log.Fatalf("expr parse error on %q: %v", src, err)
818 // stmtf parses a Go statement generated from fmt.Sprintf. This function is only
819 // meant for simple statements that don't have a custom Statement node declared
820 // in this package, such as ast.ReturnStmt or ast.ExprStmt.
821 func stmtf(format string, a ...interface{}) Statement {
822 src := fmt.Sprintf(format, a...)
823 fsrc := "package p\nfunc _() {\n" + src + "\n}\n"
824 file, err := parser.ParseFile(token.NewFileSet(), "", fsrc, 0)
826 log.Fatalf("stmt parse error on %q: %v", src, err)
828 return file.Decls[0].(*ast.FuncDecl).Body.List[0]
831 var reservedNames = map[string]bool{
832 "v": true, // Values[i], etc
833 "b": true, // v.Block
834 "config": true, // b.Func.Config
835 "fe": true, // b.Func.fe
836 "typ": true, // &b.Func.Config.Types
839 // declf constructs a simple "name := value" declaration,
840 // using exprf for its value.
842 // name must not be one of reservedNames.
843 // This helps prevent unintended shadowing and name clashes.
844 // To declare a reserved name, use declReserved.
845 func declf(loc, name, format string, a ...interface{}) *Declare {
846 if reservedNames[name] {
847 log.Fatalf("rule %s uses the reserved name %s", loc, name)
849 return &Declare{name, exprf(format, a...)}
852 // declReserved is like declf, but the name must be one of reservedNames.
853 // Calls to declReserved should generally be static and top-level.
854 func declReserved(name, value string) *Declare {
855 if !reservedNames[name] {
856 panic(fmt.Sprintf("declReserved call does not use a reserved name: %q", name))
858 return &Declare{name, exprf(value)}
861 // breakf constructs a simple "if cond { break }" statement, using exprf for its
863 func breakf(format string, a ...interface{}) *CondBreak {
864 return &CondBreak{Cond: exprf(format, a...)}
867 func genBlockRewrite(rule Rule, arch arch, data blockData) *RuleRewrite {
868 rr := &RuleRewrite{Loc: rule.Loc}
869 rr.Match, rr.Cond, rr.Result = rule.parse()
870 _, _, auxint, aux, s := extract(rr.Match) // remove parens, then split
872 // check match of control values
873 if len(s) < data.controls {
874 log.Fatalf("incorrect number of arguments in %s, got %v wanted at least %v", rule, len(s), data.controls)
876 controls := s[:data.controls]
877 pos := make([]string, data.controls)
878 for i, arg := range controls {
879 cname := fmt.Sprintf("b.Controls[%v]", i)
880 if strings.Contains(arg, "(") {
881 vname, expr := splitNameExpr(arg)
883 vname = fmt.Sprintf("v_%v", i)
885 rr.add(declf(rr.Loc, vname, cname))
886 p, op := genMatch0(rr, arch, expr, vname, nil, false) // TODO: pass non-nil cnt?
888 check := fmt.Sprintf("%s.Op == %s", cname, op)
892 rr.Check += " && " + check
900 rr.add(declf(rr.Loc, arg, cname))
901 pos[i] = arg + ".Pos"
904 for _, e := range []struct {
905 name, field, dclType string
907 {auxint, "AuxInt", data.auxIntType()},
908 {aux, "Aux", data.auxType()},
915 log.Fatalf("op %s has no declared type for %s", data.name, e.field)
917 if !token.IsIdentifier(e.name) || rr.declared(e.name) {
918 rr.add(breakf("%sTo%s(b.%s) != %s", unTitle(e.field), title(e.dclType), e.field, e.name))
920 rr.add(declf(rr.Loc, e.name, "%sTo%s(b.%s)", unTitle(e.field), title(e.dclType), e.field))
924 rr.add(breakf("!(%s)", rr.Cond))
927 // Rule matches. Generate result.
928 outop, _, auxint, aux, t := extract(rr.Result) // remove parens, then split
929 blockName, outdata := getBlockInfo(outop, arch)
930 if len(t) < outdata.controls {
931 log.Fatalf("incorrect number of output arguments in %s, got %v wanted at least %v", rule, len(s), outdata.controls)
934 // Check if newsuccs is the same set as succs.
935 succs := s[data.controls:]
936 newsuccs := t[outdata.controls:]
937 m := map[string]bool{}
938 for _, succ := range succs {
940 log.Fatalf("can't have a repeat successor name %s in %s", succ, rule)
944 for _, succ := range newsuccs {
946 log.Fatalf("unknown successor %s in %s", succ, rule)
951 log.Fatalf("unmatched successors %v in %s", m, rule)
954 var genControls [2]string
955 for i, control := range t[:outdata.controls] {
956 // Select a source position for any new control values.
957 // TODO: does it always make sense to use the source position
958 // of the original control values or should we be using the
959 // block's source position in some cases?
960 newpos := "b.Pos" // default to block's source position
961 if i < len(pos) && pos[i] != "" {
962 // Use the previous control value's source position.
966 // Generate a new control value (or copy an existing value).
967 genControls[i] = genResult0(rr, arch, control, false, false, newpos, nil)
969 switch outdata.controls {
971 rr.add(stmtf("b.Reset(%s)", blockName))
973 rr.add(stmtf("b.resetWithControl(%s, %s)", blockName, genControls[0]))
975 rr.add(stmtf("b.resetWithControl2(%s, %s, %s)", blockName, genControls[0], genControls[1]))
977 log.Fatalf("too many controls: %d", outdata.controls)
981 // Make sure auxint value has the right type.
982 rr.add(stmtf("b.AuxInt = %sToAuxInt(%s)", unTitle(outdata.auxIntType()), auxint))
985 // Make sure aux value has the right type.
986 rr.add(stmtf("b.Aux = %sToAux(%s)", unTitle(outdata.auxType()), aux))
990 for i := 0; i < len(succs); i++ {
991 if succs[i] != newsuccs[i] {
997 log.Fatalf("changed successors, len!=2 in %s", rule)
999 if succs[0] != newsuccs[1] || succs[1] != newsuccs[0] {
1000 log.Fatalf("can only handle swapped successors in %s", rule)
1002 rr.add(stmtf("b.swapSuccessors()"))
1006 rr.add(stmtf("logRule(%q)", rule.Loc))
1011 // genMatch returns the variable whose source position should be used for the
1012 // result (or "" if no opinion), and a boolean that reports whether the match can fail.
1013 func genMatch(rr *RuleRewrite, arch arch, match string, pregenTop bool) (pos, checkOp string) {
1015 return genMatch0(rr, arch, match, "v", cnt, pregenTop)
1018 func genMatch0(rr *RuleRewrite, arch arch, match, v string, cnt map[string]int, pregenTop bool) (pos, checkOp string) {
1019 if match[0] != '(' || match[len(match)-1] != ')' {
1020 log.Fatalf("%s: non-compound expr in genMatch0: %q", rr.Loc, match)
1022 op, oparch, typ, auxint, aux, args := parseValue(match, arch, rr.Loc)
1024 checkOp = fmt.Sprintf("Op%s%s", oparch, op.name)
1026 if op.faultOnNilArg0 || op.faultOnNilArg1 {
1027 // Prefer the position of an instruction which could fault.
1031 // If the last argument is ___, it means "don't care about trailing arguments, really"
1032 // The likely/intended use is for rewrites that are too tricky to express in the existing pattern language
1033 // Do a length check early because long patterns fed short (ultimately not-matching) inputs will
1034 // do an indexing error in pattern-matching.
1035 if op.argLength == -1 {
1037 if l == 0 || args[l-1] != "___" {
1038 rr.add(breakf("len(%s.Args) != %d", v, l))
1039 } else if l > 1 && args[l-1] == "___" {
1040 rr.add(breakf("len(%s.Args) < %d", v, l-1))
1044 for _, e := range []struct {
1045 name, field, dclType string
1047 {typ, "Type", "*types.Type"},
1048 {auxint, "AuxInt", op.auxIntType()},
1049 {aux, "Aux", op.auxType()},
1055 if e.dclType == "" {
1056 log.Fatalf("op %s has no declared type for %s", op.name, e.field)
1058 if !token.IsIdentifier(e.name) || rr.declared(e.name) {
1061 rr.add(breakf("auxTo%s(%s.%s) != %s", title(e.dclType), v, e.field, e.name))
1063 rr.add(breakf("auxIntTo%s(%s.%s) != %s", title(e.dclType), v, e.field, e.name))
1065 rr.add(breakf("%s.%s != %s", v, e.field, e.name))
1070 rr.add(declf(rr.Loc, e.name, "auxTo%s(%s.%s)", title(e.dclType), v, e.field))
1072 rr.add(declf(rr.Loc, e.name, "auxIntTo%s(%s.%s)", title(e.dclType), v, e.field))
1074 rr.add(declf(rr.Loc, e.name, "%s.%s", v, e.field))
1079 commutative := op.commutative
1081 if args[0] == args[1] {
1082 // When we have (Add x x), for any x,
1083 // even if there are other uses of x besides these two,
1084 // and even if x is not a variable,
1085 // we can skip the commutative match.
1088 if cnt[args[0]] == 1 && cnt[args[1]] == 1 {
1089 // When we have (Add x y) with no other uses
1090 // of x and y in the matching rule and condition,
1091 // then we can skip the commutative match (Add y x).
1097 // Access last argument first to minimize bounds checks.
1098 for n := len(args) - 1; n > 0; n-- {
1103 if !rr.declared(a) && token.IsIdentifier(a) && !(commutative && len(args) == 2) {
1104 rr.add(declf(rr.Loc, a, "%s.Args[%d]", v, n))
1105 // delete the last argument so it is not reprocessed
1108 rr.add(stmtf("_ = %s.Args[%d]", v, n))
1113 if commutative && !pregenTop {
1114 for i := 0; i <= 1; i++ {
1115 vname := fmt.Sprintf("%s_%d", v, i)
1116 rr.add(declf(rr.Loc, vname, "%s.Args[%d]", v, i))
1120 rr.add(StartCommuteLoop{rr.CommuteDepth, v})
1123 for i, arg := range args {
1128 if (commutative && i < 2) || pregenTop {
1129 rhs = fmt.Sprintf("%s_%d", v, i)
1131 rhs = fmt.Sprintf("%s.Args[%d]", v, i)
1133 if !strings.Contains(arg, "(") {
1135 if rr.declared(arg) {
1136 // variable already has a definition. Check whether
1137 // the old definition and the new definition match.
1138 // For example, (add x x). Equality is just pointer equality
1139 // on Values (so cse is important to do before lowering).
1140 rr.add(breakf("%s != %s", arg, rhs))
1143 rr.add(declf(rr.Loc, arg, "%s", rhs))
1149 argname, expr := splitNameExpr(arg)
1151 argname = fmt.Sprintf("%s_%d", v, i)
1154 log.Fatalf("don't name args 'b', it is ambiguous with blocks")
1158 rr.add(declf(rr.Loc, argname, "%s", rhs))
1160 bexpr := exprf("%s.Op != addLater", argname)
1161 rr.add(&CondBreak{Cond: bexpr})
1162 argPos, argCheckOp := genMatch0(rr, arch, expr, argname, cnt, false)
1163 bexpr.(*ast.BinaryExpr).Y.(*ast.Ident).Name = argCheckOp
1166 // Keep the argument in preference to the parent, as the
1167 // argument is normally earlier in program flow.
1168 // Keep the argument in preference to an earlier argument,
1169 // as that prefers the memory argument which is also earlier
1170 // in the program flow.
1178 func genResult(rr *RuleRewrite, arch arch, result, pos string) {
1179 move := result[0] == '@'
1181 // parse @block directive
1182 s := strings.SplitN(result[1:], " ", 2)
1183 rr.add(stmtf("b = %s", s[0]))
1186 cse := make(map[string]string)
1187 genResult0(rr, arch, result, true, move, pos, cse)
1190 func genResult0(rr *RuleRewrite, arch arch, result string, top, move bool, pos string, cse map[string]string) string {
1191 resname, expr := splitNameExpr(result)
1193 // TODO: when generating a constant result, use f.constVal to avoid
1194 // introducing copies just to clean them up again.
1195 if result[0] != '(' {
1198 // It in not safe in general to move a variable between blocks
1199 // (and particularly not a phi node).
1200 // Introduce a copy.
1201 rr.add(stmtf("v.copyOf(%s)", result))
1206 w := normalizeWhitespace(result)
1207 if prev := cse[w]; prev != "" {
1211 op, oparch, typ, auxint, aux, args := parseValue(result, arch, rr.Loc)
1213 // Find the type of the variable.
1214 typeOverride := typ != ""
1215 if typ == "" && op.typ != "" {
1216 typ = typeName(op.typ)
1221 rr.add(stmtf("v.reset(Op%s%s)", oparch, op.name))
1223 rr.add(stmtf("v.Type = %s", typ))
1227 log.Fatalf("sub-expression %s (op=Op%s%s) at %s must have a type", result, oparch, op.name, rr.Loc)
1230 v = fmt.Sprintf("v%d", rr.Alloc)
1235 rr.add(declf(rr.Loc, v, "b.NewValue0(%s, Op%s%s, %s)", pos, oparch, op.name, typ))
1237 // Rewrite original into a copy
1238 rr.add(stmtf("v.copyOf(%s)", v))
1243 // Make sure auxint value has the right type.
1244 rr.add(stmtf("%s.AuxInt = %sToAuxInt(%s)", v, unTitle(op.auxIntType()), auxint))
1247 // Make sure aux value has the right type.
1248 rr.add(stmtf("%s.Aux = %sToAux(%s)", v, unTitle(op.auxType()), aux))
1250 all := new(strings.Builder)
1251 for i, arg := range args {
1252 x := genResult0(rr, arch, arg, false, move, pos, cse)
1254 all.WriteString(", ")
1261 rr.add(stmtf("%s.AddArg(%s)", v, all.String()))
1263 rr.add(stmtf("%s.AddArg%d(%s)", v, len(args), all.String()))
1272 func split(s string) []string {
1277 d := 0 // depth of ({[<
1278 var open, close byte // opening and closing markers ({[< or )}]>
1279 nonsp := false // found a non-space char so far
1280 for i := 0; i < len(s); i++ {
1282 case d == 0 && s[i] == '(':
1283 open, close = '(', ')'
1285 case d == 0 && s[i] == '<':
1286 open, close = '<', '>'
1288 case d == 0 && s[i] == '[':
1289 open, close = '[', ']'
1291 case d == 0 && s[i] == '{':
1292 open, close = '{', '}'
1294 case d == 0 && (s[i] == ' ' || s[i] == '\t'):
1296 r = append(r, strings.TrimSpace(s[:i]))
1300 case d > 0 && s[i] == open:
1302 case d > 0 && s[i] == close:
1309 log.Fatalf("imbalanced expression: %q", s)
1312 r = append(r, strings.TrimSpace(s))
1319 // isBlock reports whether this op is a block opcode.
1320 func isBlock(name string, arch arch) bool {
1321 for _, b := range genericBlocks {
1326 for _, b := range arch.blocks {
1334 func extract(val string) (op, typ, auxint, aux string, args []string) {
1335 val = val[1 : len(val)-1] // remove ()
1337 // Split val up into regions.
1338 // Split by spaces/tabs, except those contained in (), {}, [], or <>.
1341 // Extract restrictions and args.
1343 for _, a := range s[1:] {
1346 typ = a[1 : len(a)-1] // remove <>
1348 auxint = a[1 : len(a)-1] // remove []
1350 aux = a[1 : len(a)-1] // remove {}
1352 args = append(args, a)
1358 // parseValue parses a parenthesized value from a rule.
1359 // The value can be from the match or the result side.
1360 // It returns the op and unparsed strings for typ, auxint, and aux restrictions and for all args.
1361 // oparch is the architecture that op is located in, or "" for generic.
1362 func parseValue(val string, arch arch, loc string) (op opData, oparch, typ, auxint, aux string, args []string) {
1365 s, typ, auxint, aux, args = extract(val)
1367 // match reports whether x is a good op to select.
1368 // If strict is true, rule generation might succeed.
1369 // If strict is false, rule generation has failed,
1370 // but we're trying to generate a useful error.
1371 // Doing strict=true then strict=false allows
1372 // precise op matching while retaining good error messages.
1373 match := func(x opData, strict bool, archname string) bool {
1377 if x.argLength != -1 && int(x.argLength) != len(args) && (len(args) != 1 || args[0] != "...") {
1381 log.Printf("%s: op %s (%s) should have %d args, has %d", loc, s, archname, x.argLength, len(args))
1386 for _, x := range genericOps {
1387 if match(x, true, "generic") {
1392 for _, x := range arch.ops {
1393 if arch.name != "generic" && match(x, true, arch.name) {
1395 log.Fatalf("%s: matches for op %s found in both generic and %s", loc, op.name, arch.name)
1404 // Failed to find the op.
1405 // Run through everything again with strict=false
1406 // to generate useful diagnosic messages before failing.
1407 for _, x := range genericOps {
1408 match(x, false, "generic")
1410 for _, x := range arch.ops {
1411 match(x, false, arch.name)
1413 log.Fatalf("%s: unknown op %s", loc, s)
1416 // Sanity check aux, auxint.
1417 if auxint != "" && !opHasAuxInt(op) {
1418 log.Fatalf("%s: op %s %s can't have auxint", loc, op.name, op.aux)
1420 if aux != "" && !opHasAux(op) {
1421 log.Fatalf("%s: op %s %s can't have aux", loc, op.name, op.aux)
1426 func opHasAuxInt(op opData) bool {
1428 case "Bool", "Int8", "Int16", "Int32", "Int64", "Int128", "UInt8", "Float32", "Float64",
1429 "SymOff", "CallOff", "SymValAndOff", "TypSize", "ARM64BitField", "FlagConstant", "CCop":
1435 func opHasAux(op opData) bool {
1437 case "String", "Sym", "SymOff", "Call", "CallOff", "SymValAndOff", "Typ", "TypSize",
1438 "S390XCCMask", "S390XRotateParams":
1444 // splitNameExpr splits s-expr arg, possibly prefixed by "name:",
1445 // into name and the unprefixed expression.
1446 // For example, "x:(Foo)" yields "x", "(Foo)",
1447 // and "(Foo)" yields "", "(Foo)".
1448 func splitNameExpr(arg string) (name, expr string) {
1449 colon := strings.Index(arg, ":")
1453 openparen := strings.Index(arg, "(")
1455 log.Fatalf("splitNameExpr(%q): colon but no open parens", arg)
1457 if colon > openparen {
1458 // colon is inside the parens, such as in "(Foo x:(Bar))".
1461 return arg[:colon], arg[colon+1:]
1464 func getBlockInfo(op string, arch arch) (name string, data blockData) {
1465 for _, b := range genericBlocks {
1467 return "Block" + op, b
1470 for _, b := range arch.blocks {
1472 return "Block" + arch.name + op, b
1475 log.Fatalf("could not find block data for %s", op)
1476 panic("unreachable")
1479 // typeName returns the string to use to generate a type.
1480 func typeName(typ string) string {
1482 ts := strings.Split(typ[1:len(typ)-1], ",")
1484 log.Fatalf("Tuple expect 2 arguments")
1486 return "types.NewTuple(" + typeName(ts[0]) + ", " + typeName(ts[1]) + ")"
1489 case "Flags", "Mem", "Void", "Int128":
1490 return "types.Type" + typ
1496 // balance returns the number of unclosed '(' characters in s.
1497 // If a ')' appears without a corresponding '(', balance returns -1.
1498 func balance(s string) int {
1500 for _, c := range s {
1507 // don't allow ")(" to return 0
1515 // findAllOpcode is a function to find the opcode portion of s-expressions.
1516 var findAllOpcode = regexp.MustCompile(`[(](\w+[|])+\w+[)]`).FindAllStringIndex
1518 // excludeFromExpansion reports whether the substring s[idx[0]:idx[1]] in a rule
1519 // should be disregarded as a candidate for | expansion.
1520 // It uses simple syntactic checks to see whether the substring
1521 // is inside an AuxInt expression or inside the && conditions.
1522 func excludeFromExpansion(s string, idx []int) bool {
1524 if strings.LastIndexByte(left, '[') > strings.LastIndexByte(left, ']') {
1525 // Inside an AuxInt expression.
1529 if strings.Contains(left, "&&") && strings.Contains(right, "=>") {
1530 // Inside && conditions.
1536 // expandOr converts a rule into multiple rules by expanding | ops.
1537 func expandOr(r string) []string {
1538 // Find every occurrence of |-separated things.
1539 // They look like MOV(B|W|L|Q|SS|SD)load or MOV(Q|L)loadidx(1|8).
1540 // Generate rules selecting one case from each |-form.
1542 // Count width of |-forms. They must match.
1544 for _, idx := range findAllOpcode(r, -1) {
1545 if excludeFromExpansion(r, idx) {
1548 s := r[idx[0]:idx[1]]
1549 c := strings.Count(s, "|") + 1
1553 if n > 1 && n != c {
1554 log.Fatalf("'|' count doesn't match in %s: both %d and %d\n", r, n, c)
1559 // No |-form in this rule.
1562 // Build each new rule.
1563 res := make([]string, n)
1564 for i := 0; i < n; i++ {
1565 buf := new(strings.Builder)
1567 for _, idx := range findAllOpcode(r, -1) {
1568 if excludeFromExpansion(r, idx) {
1571 buf.WriteString(r[x:idx[0]]) // write bytes we've skipped over so far
1572 s := r[idx[0]+1 : idx[1]-1] // remove leading "(" and trailing ")"
1573 buf.WriteString(strings.Split(s, "|")[i]) // write the op component for this rule
1574 x = idx[1] // note that we've written more bytes
1576 buf.WriteString(r[x:])
1577 res[i] = buf.String()
1582 // varCount returns a map which counts the number of occurrences of
1583 // Value variables in the s-expression rr.Match and the Go expression rr.Cond.
1584 func varCount(rr *RuleRewrite) map[string]int {
1585 cnt := map[string]int{}
1586 varCount1(rr.Loc, rr.Match, cnt)
1588 expr, err := parser.ParseExpr(rr.Cond)
1590 log.Fatalf("%s: failed to parse cond %q: %v", rr.Loc, rr.Cond, err)
1592 ast.Inspect(expr, func(n ast.Node) bool {
1593 if id, ok := n.(*ast.Ident); ok {
1602 func varCount1(loc, m string, cnt map[string]int) {
1603 if m[0] == '<' || m[0] == '[' || m[0] == '{' {
1606 if token.IsIdentifier(m) {
1611 name, expr := splitNameExpr(m)
1615 if expr[0] != '(' || expr[len(expr)-1] != ')' {
1616 log.Fatalf("%s: non-compound expr in varCount1: %q", loc, expr)
1618 s := split(expr[1 : len(expr)-1])
1619 for _, arg := range s[1:] {
1620 varCount1(loc, arg, cnt)
1624 // normalizeWhitespace replaces 2+ whitespace sequences with a single space.
1625 func normalizeWhitespace(x string) string {
1626 x = strings.Join(strings.Fields(x), " ")
1627 x = strings.Replace(x, "( ", "(", -1)
1628 x = strings.Replace(x, " )", ")", -1)
1629 x = strings.Replace(x, "[ ", "[", -1)
1630 x = strings.Replace(x, " ]", "]", -1)
1631 x = strings.Replace(x, ")=>", ") =>", -1)
1635 // opIsCommutative reports whether op s is commutative.
1636 func opIsCommutative(op string, arch arch) bool {
1637 for _, x := range genericOps {
1645 if arch.name != "generic" {
1646 for _, x := range arch.ops {
1658 func normalizeMatch(m string, arch arch) string {
1659 if token.IsIdentifier(m) {
1662 op, typ, auxint, aux, args := extract(m)
1663 if opIsCommutative(op, arch) {
1664 if args[1] < args[0] {
1665 args[0], args[1] = args[1], args[0]
1668 s := new(strings.Builder)
1669 fmt.Fprintf(s, "%s <%s> [%s] {%s}", op, typ, auxint, aux)
1670 for _, arg := range args {
1671 prefix, expr := splitNameExpr(arg)
1672 fmt.Fprint(s, " ", prefix, normalizeMatch(expr, arch))
1677 func parseEllipsisRules(rules []Rule, arch arch) (newop string, ok bool) {
1678 if len(rules) != 1 {
1679 for _, r := range rules {
1680 if strings.Contains(r.Rule, "...") {
1681 log.Fatalf("%s: found ellipsis in rule, but there are other rules with the same op", r.Loc)
1687 match, cond, result := rule.parse()
1688 if cond != "" || !isEllipsisValue(match) || !isEllipsisValue(result) {
1689 if strings.Contains(rule.Rule, "...") {
1690 log.Fatalf("%s: found ellipsis in non-ellipsis rule", rule.Loc)
1692 checkEllipsisRuleCandidate(rule, arch)
1695 op, oparch, _, _, _, _ := parseValue(result, arch, rule.Loc)
1696 return fmt.Sprintf("Op%s%s", oparch, op.name), true
1699 // isEllipsisValue reports whether s is of the form (OpX ...).
1700 func isEllipsisValue(s string) bool {
1701 if len(s) < 2 || s[0] != '(' || s[len(s)-1] != ')' {
1704 c := split(s[1 : len(s)-1])
1705 if len(c) != 2 || c[1] != "..." {
1711 func checkEllipsisRuleCandidate(rule Rule, arch arch) {
1712 match, cond, result := rule.parse()
1716 op, _, _, auxint, aux, args := parseValue(match, arch, rule.Loc)
1717 var auxint2, aux2 string
1719 var usingCopy string
1721 if result[0] != '(' {
1722 // Check for (Foo x) => x, which can be converted to (Foo ...) => (Copy ...).
1723 args2 = []string{result}
1724 usingCopy = " using Copy"
1726 eop, _, _, auxint2, aux2, args2 = parseValue(result, arch, rule.Loc)
1728 // Check that all restrictions in match are reproduced exactly in result.
1729 if aux != aux2 || auxint != auxint2 || len(args) != len(args2) {
1732 if strings.Contains(rule.Rule, "=>") && op.aux != eop.aux {
1735 for i := range args {
1736 if args[i] != args2[i] {
1741 case opHasAux(op) && aux == "" && aux2 == "":
1742 fmt.Printf("%s: rule silently zeros aux, either copy aux or explicitly zero\n", rule.Loc)
1743 case opHasAuxInt(op) && auxint == "" && auxint2 == "":
1744 fmt.Printf("%s: rule silently zeros auxint, either copy auxint or explicitly zero\n", rule.Loc)
1746 fmt.Printf("%s: possible ellipsis rule candidate%s: %q\n", rule.Loc, usingCopy, rule.Rule)
1750 func opByName(arch arch, name string) opData {
1752 for _, x := range genericOps {
1757 if arch.name != "generic" {
1758 name = name[len(arch.name):]
1759 for _, x := range arch.ops {
1765 log.Fatalf("failed to find op named %s in arch %s", name, arch.name)
1766 panic("unreachable")
1769 // auxType returns the Go type that this operation should store in its aux field.
1770 func (op opData) auxType() string {
1775 // Note: a Sym can be an *obj.LSym, a *gc.Node, or nil.
1783 case "SymValAndOff":
1786 return "*types.Type"
1788 return "*types.Type"
1790 return "s390x.CCMask"
1791 case "S390XRotateParams":
1792 return "s390x.RotateParams"
1798 // auxIntType returns the Go type that this operation should store in its auxInt field.
1799 func (op opData) auxIntType() string {
1823 case "SymValAndOff":
1829 case "FlagConstant":
1830 return "flagConstant"
1831 case "ARM64BitField":
1832 return "arm64BitField"
1838 // auxType returns the Go type that this block should store in its aux field.
1839 func (b blockData) auxType() string {
1841 case "S390XCCMask", "S390XCCMaskInt8", "S390XCCMaskUint8":
1842 return "s390x.CCMask"
1843 case "S390XRotateParams":
1844 return "s390x.RotateParams"
1850 // auxIntType returns the Go type that this block should store in its auxInt field.
1851 func (b blockData) auxIntType() string {
1853 case "S390XCCMaskInt8":
1855 case "S390XCCMaskUint8":
1864 func title(s string) string {
1865 if i := strings.Index(s, "."); i >= 0 {
1866 switch strings.ToLower(s[:i]) {
1867 case "s390x": // keep arch prefix for clarity
1873 return strings.Title(s)
1876 func unTitle(s string) string {
1877 if i := strings.Index(s, "."); i >= 0 {
1878 switch strings.ToLower(s[:i]) {
1879 case "s390x": // keep arch prefix for clarity
1885 return strings.ToLower(s[:1]) + s[1:]