github.com/megatontech/mynoteforgo@v0.0.0-20200507084910-5d0c6ea6e890/源码/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  }