github.com/euank/go@v0.0.0-20160829210321-495514729181/src/cmd/compile/internal/ssa/gen/rulegen.go (about)

     1  // Copyright 2015 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // +build gen
     6  
     7  // This program generates Go code that applies rewrite rules to a Value.
     8  // The generated code implements a function of type func (v *Value) bool
     9  // which returns true iff if did something.
    10  // Ideas stolen from Swift: http://www.hpl.hp.com/techreports/Compaq-DEC/WRL-2000-2.html
    11  
    12  package main
    13  
    14  import (
    15  	"bufio"
    16  	"bytes"
    17  	"flag"
    18  	"fmt"
    19  	"go/format"
    20  	"io"
    21  	"io/ioutil"
    22  	"log"
    23  	"os"
    24  	"regexp"
    25  	"sort"
    26  	"strings"
    27  )
    28  
    29  // rule syntax:
    30  //  sexpr [&& extra conditions] -> [@block] sexpr
    31  //
    32  // sexpr are s-expressions (lisp-like parenthesized groupings)
    33  // sexpr ::= (opcode sexpr*)
    34  //         | variable
    35  //         | <type>
    36  //         | [auxint]
    37  //         | {aux}
    38  //
    39  // aux      ::= variable | {code}
    40  // type     ::= variable | {code}
    41  // variable ::= some token
    42  // opcode   ::= one of the opcodes from ../op.go (without the Op prefix)
    43  
    44  // extra conditions is just a chunk of Go that evaluates to a boolean. It may use
    45  // variables declared in the matching sexpr. The variable "v" is predefined to be
    46  // the value matched by the entire rule.
    47  
    48  // If multiple rules match, the first one in file order is selected.
    49  
    50  var (
    51  	genLog = flag.Bool("log", false, "generate code that logs; for debugging only")
    52  )
    53  
    54  type Rule struct {
    55  	rule string
    56  	loc  string // file name & line number
    57  }
    58  
    59  func (r Rule) String() string {
    60  	return fmt.Sprintf("rule %q at %s", r.rule, r.loc)
    61  }
    62  
    63  // parse returns the matching part of the rule, additional conditions, and the result.
    64  func (r Rule) parse() (match, cond, result string) {
    65  	s := strings.Split(r.rule, "->")
    66  	if len(s) != 2 {
    67  		log.Fatalf("no arrow in %s", r)
    68  	}
    69  	match = strings.TrimSpace(s[0])
    70  	result = strings.TrimSpace(s[1])
    71  	cond = ""
    72  	if i := strings.Index(match, "&&"); i >= 0 {
    73  		cond = strings.TrimSpace(match[i+2:])
    74  		match = strings.TrimSpace(match[:i])
    75  	}
    76  	return match, cond, result
    77  }
    78  
    79  func genRules(arch arch) {
    80  	// Open input file.
    81  	text, err := os.Open(arch.name + ".rules")
    82  	if err != nil {
    83  		log.Fatalf("can't read rule file: %v", err)
    84  	}
    85  
    86  	// oprules contains a list of rules for each block and opcode
    87  	blockrules := map[string][]Rule{}
    88  	oprules := map[string][]Rule{}
    89  
    90  	// read rule file
    91  	scanner := bufio.NewScanner(text)
    92  	rule := ""
    93  	var lineno int
    94  	var ruleLineno int // line number of "->"
    95  	for scanner.Scan() {
    96  		lineno++
    97  		line := scanner.Text()
    98  		if i := strings.Index(line, "//"); i >= 0 {
    99  			// Remove comments. Note that this isn't string safe, so
   100  			// it will truncate lines with // inside strings. Oh well.
   101  			line = line[:i]
   102  		}
   103  		rule += " " + line
   104  		rule = strings.TrimSpace(rule)
   105  		if rule == "" {
   106  			continue
   107  		}
   108  		if !strings.Contains(rule, "->") {
   109  			continue
   110  		}
   111  		if ruleLineno == 0 {
   112  			ruleLineno = lineno
   113  		}
   114  		if strings.HasSuffix(rule, "->") {
   115  			continue
   116  		}
   117  		if unbalanced(rule) {
   118  			continue
   119  		}
   120  
   121  		loc := fmt.Sprintf("%s.rules:%d", arch.name, ruleLineno)
   122  		r := Rule{rule: rule, loc: loc}
   123  		if rawop := strings.Split(rule, " ")[0][1:]; isBlock(rawop, arch) {
   124  			blockrules[rawop] = append(blockrules[rawop], r)
   125  		} else {
   126  			// Do fancier value op matching.
   127  			match, _, _ := r.parse()
   128  			op, oparch, _, _, _, _ := parseValue(match, arch, loc)
   129  			opname := fmt.Sprintf("Op%s%s", oparch, op.name)
   130  			oprules[opname] = append(oprules[opname], r)
   131  		}
   132  		rule = ""
   133  		ruleLineno = 0
   134  	}
   135  	if err := scanner.Err(); err != nil {
   136  		log.Fatalf("scanner failed: %v\n", err)
   137  	}
   138  	if unbalanced(rule) {
   139  		log.Fatalf("%s.rules:%d: unbalanced rule: %v\n", arch.name, lineno, rule)
   140  	}
   141  
   142  	// Order all the ops.
   143  	var ops []string
   144  	for op := range oprules {
   145  		ops = append(ops, op)
   146  	}
   147  	sort.Strings(ops)
   148  
   149  	// Start output buffer, write header.
   150  	w := new(bytes.Buffer)
   151  	fmt.Fprintf(w, "// autogenerated from gen/%s.rules: do not edit!\n", arch.name)
   152  	fmt.Fprintln(w, "// generated with: cd gen; go run *.go")
   153  	fmt.Fprintln(w)
   154  	fmt.Fprintln(w, "package ssa")
   155  	fmt.Fprintln(w, "import \"math\"")
   156  	fmt.Fprintln(w, "var _ = math.MinInt8 // in case not otherwise used")
   157  
   158  	// Main rewrite routine is a switch on v.Op.
   159  	fmt.Fprintf(w, "func rewriteValue%s(v *Value, config *Config) bool {\n", arch.name)
   160  	fmt.Fprintf(w, "switch v.Op {\n")
   161  	for _, op := range ops {
   162  		fmt.Fprintf(w, "case %s:\n", op)
   163  		fmt.Fprintf(w, "return rewriteValue%s_%s(v, config)\n", arch.name, op)
   164  	}
   165  	fmt.Fprintf(w, "}\n")
   166  	fmt.Fprintf(w, "return false\n")
   167  	fmt.Fprintf(w, "}\n")
   168  
   169  	// Generate a routine per op. Note that we don't make one giant routine
   170  	// because it is too big for some compilers.
   171  	for _, op := range ops {
   172  		fmt.Fprintf(w, "func rewriteValue%s_%s(v *Value, config *Config) bool {\n", arch.name, op)
   173  		fmt.Fprintln(w, "b := v.Block")
   174  		fmt.Fprintln(w, "_ = b")
   175  		var canFail bool
   176  		for i, rule := range oprules[op] {
   177  			match, cond, result := rule.parse()
   178  			fmt.Fprintf(w, "// match: %s\n", match)
   179  			fmt.Fprintf(w, "// cond: %s\n", cond)
   180  			fmt.Fprintf(w, "// result: %s\n", result)
   181  
   182  			canFail = false
   183  			fmt.Fprintf(w, "for {\n")
   184  			if genMatch(w, arch, match, rule.loc) {
   185  				canFail = true
   186  			}
   187  
   188  			if cond != "" {
   189  				fmt.Fprintf(w, "if !(%s) {\nbreak\n}\n", cond)
   190  				canFail = true
   191  			}
   192  			if !canFail && i != len(oprules[op])-1 {
   193  				log.Fatalf("unconditional rule %s is followed by other rules", match)
   194  			}
   195  
   196  			genResult(w, arch, result, rule.loc)
   197  			if *genLog {
   198  				fmt.Fprintf(w, "logRule(\"%s\")\n", rule.loc)
   199  			}
   200  			fmt.Fprintf(w, "return true\n")
   201  
   202  			fmt.Fprintf(w, "}\n")
   203  		}
   204  		if canFail {
   205  			fmt.Fprintf(w, "return false\n")
   206  		}
   207  		fmt.Fprintf(w, "}\n")
   208  	}
   209  
   210  	// Generate block rewrite function. There are only a few block types
   211  	// so we can make this one function with a switch.
   212  	fmt.Fprintf(w, "func rewriteBlock%s(b *Block) bool {\n", arch.name)
   213  	fmt.Fprintf(w, "switch b.Kind {\n")
   214  	ops = nil
   215  	for op := range blockrules {
   216  		ops = append(ops, op)
   217  	}
   218  	sort.Strings(ops)
   219  	for _, op := range ops {
   220  		fmt.Fprintf(w, "case %s:\n", blockName(op, arch))
   221  		for _, rule := range blockrules[op] {
   222  			match, cond, result := rule.parse()
   223  			fmt.Fprintf(w, "// match: %s\n", match)
   224  			fmt.Fprintf(w, "// cond: %s\n", cond)
   225  			fmt.Fprintf(w, "// result: %s\n", result)
   226  
   227  			fmt.Fprintf(w, "for {\n")
   228  
   229  			s := split(match[1 : len(match)-1]) // remove parens, then split
   230  
   231  			// check match of control value
   232  			if s[1] != "nil" {
   233  				fmt.Fprintf(w, "v := b.Control\n")
   234  				if strings.Contains(s[1], "(") {
   235  					genMatch0(w, arch, s[1], "v", map[string]struct{}{}, false, rule.loc)
   236  				} else {
   237  					fmt.Fprintf(w, "_ = v\n") // in case we don't use v
   238  					fmt.Fprintf(w, "%s := b.Control\n", s[1])
   239  				}
   240  			}
   241  
   242  			// assign successor names
   243  			succs := s[2:]
   244  			for i, a := range succs {
   245  				if a != "_" {
   246  					fmt.Fprintf(w, "%s := b.Succs[%d]\n", a, i)
   247  				}
   248  			}
   249  
   250  			if cond != "" {
   251  				fmt.Fprintf(w, "if !(%s) {\nbreak\n}\n", cond)
   252  			}
   253  
   254  			// Rule matches. Generate result.
   255  			t := split(result[1 : len(result)-1]) // remove parens, then split
   256  			newsuccs := t[2:]
   257  
   258  			// Check if newsuccs is the same set as succs.
   259  			m := map[string]bool{}
   260  			for _, succ := range succs {
   261  				if m[succ] {
   262  					log.Fatalf("can't have a repeat successor name %s in %s", succ, rule)
   263  				}
   264  				m[succ] = true
   265  			}
   266  			for _, succ := range newsuccs {
   267  				if !m[succ] {
   268  					log.Fatalf("unknown successor %s in %s", succ, rule)
   269  				}
   270  				delete(m, succ)
   271  			}
   272  			if len(m) != 0 {
   273  				log.Fatalf("unmatched successors %v in %s", m, rule)
   274  			}
   275  
   276  			fmt.Fprintf(w, "b.Kind = %s\n", blockName(t[0], arch))
   277  			if t[1] == "nil" {
   278  				fmt.Fprintf(w, "b.SetControl(nil)\n")
   279  			} else {
   280  				fmt.Fprintf(w, "b.SetControl(%s)\n", genResult0(w, arch, t[1], new(int), false, false, rule.loc))
   281  			}
   282  
   283  			succChanged := false
   284  			for i := 0; i < len(succs); i++ {
   285  				if succs[i] != newsuccs[i] {
   286  					succChanged = true
   287  				}
   288  			}
   289  			if succChanged {
   290  				if len(succs) != 2 {
   291  					log.Fatalf("changed successors, len!=2 in %s", rule)
   292  				}
   293  				if succs[0] != newsuccs[1] || succs[1] != newsuccs[0] {
   294  					log.Fatalf("can only handle swapped successors in %s", rule)
   295  				}
   296  				fmt.Fprintln(w, "b.swapSuccessors()")
   297  			}
   298  			for i := 0; i < len(succs); i++ {
   299  				fmt.Fprintf(w, "_ = %s\n", newsuccs[i])
   300  			}
   301  
   302  			if *genLog {
   303  				fmt.Fprintf(w, "logRule(\"%s\")\n", rule.loc)
   304  			}
   305  			fmt.Fprintf(w, "return true\n")
   306  
   307  			fmt.Fprintf(w, "}\n")
   308  		}
   309  	}
   310  	fmt.Fprintf(w, "}\n")
   311  	fmt.Fprintf(w, "return false\n")
   312  	fmt.Fprintf(w, "}\n")
   313  
   314  	// gofmt result
   315  	b := w.Bytes()
   316  	src, err := format.Source(b)
   317  	if err != nil {
   318  		fmt.Printf("%s\n", b)
   319  		panic(err)
   320  	}
   321  
   322  	// Write to file
   323  	err = ioutil.WriteFile("../rewrite"+arch.name+".go", src, 0666)
   324  	if err != nil {
   325  		log.Fatalf("can't write output: %v\n", err)
   326  	}
   327  }
   328  
   329  // genMatch returns true if the match can fail.
   330  func genMatch(w io.Writer, arch arch, match string, loc string) bool {
   331  	return genMatch0(w, arch, match, "v", map[string]struct{}{}, true, loc)
   332  }
   333  
   334  func genMatch0(w io.Writer, arch arch, match, v string, m map[string]struct{}, top bool, loc string) bool {
   335  	if match[0] != '(' || match[len(match)-1] != ')' {
   336  		panic("non-compound expr in genMatch0: " + match)
   337  	}
   338  	canFail := false
   339  
   340  	op, oparch, typ, auxint, aux, args := parseValue(match, arch, loc)
   341  
   342  	// check op
   343  	if !top {
   344  		fmt.Fprintf(w, "if %s.Op != Op%s%s {\nbreak\n}\n", v, oparch, op.name)
   345  		canFail = true
   346  	}
   347  
   348  	if typ != "" {
   349  		if !isVariable(typ) {
   350  			// code. We must match the results of this code.
   351  			fmt.Fprintf(w, "if %s.Type != %s {\nbreak\n}\n", v, typ)
   352  			canFail = true
   353  		} else {
   354  			// variable
   355  			if _, ok := m[typ]; ok {
   356  				// must match previous variable
   357  				fmt.Fprintf(w, "if %s.Type != %s {\nbreak\n}\n", v, typ)
   358  				canFail = true
   359  			} else {
   360  				m[typ] = struct{}{}
   361  				fmt.Fprintf(w, "%s := %s.Type\n", typ, v)
   362  			}
   363  		}
   364  	}
   365  
   366  	if auxint != "" {
   367  		if !isVariable(auxint) {
   368  			// code
   369  			fmt.Fprintf(w, "if %s.AuxInt != %s {\nbreak\n}\n", v, auxint)
   370  			canFail = true
   371  		} else {
   372  			// variable
   373  			if _, ok := m[auxint]; ok {
   374  				fmt.Fprintf(w, "if %s.AuxInt != %s {\nbreak\n}\n", v, auxint)
   375  				canFail = true
   376  			} else {
   377  				m[auxint] = struct{}{}
   378  				fmt.Fprintf(w, "%s := %s.AuxInt\n", auxint, v)
   379  			}
   380  		}
   381  	}
   382  
   383  	if aux != "" {
   384  
   385  		if !isVariable(aux) {
   386  			// code
   387  			fmt.Fprintf(w, "if %s.Aux != %s {\nbreak\n}\n", v, aux)
   388  			canFail = true
   389  		} else {
   390  			// variable
   391  			if _, ok := m[aux]; ok {
   392  				fmt.Fprintf(w, "if %s.Aux != %s {\nbreak\n}\n", v, aux)
   393  				canFail = true
   394  			} else {
   395  				m[aux] = struct{}{}
   396  				fmt.Fprintf(w, "%s := %s.Aux\n", aux, v)
   397  			}
   398  		}
   399  	}
   400  
   401  	for i, arg := range args {
   402  		if arg == "_" {
   403  			continue
   404  		}
   405  		if !strings.Contains(arg, "(") {
   406  			// leaf variable
   407  			if _, ok := m[arg]; ok {
   408  				// variable already has a definition. Check whether
   409  				// the old definition and the new definition match.
   410  				// For example, (add x x).  Equality is just pointer equality
   411  				// on Values (so cse is important to do before lowering).
   412  				fmt.Fprintf(w, "if %s != %s.Args[%d] {\nbreak\n}\n", arg, v, i)
   413  				canFail = true
   414  			} else {
   415  				// remember that this variable references the given value
   416  				m[arg] = struct{}{}
   417  				fmt.Fprintf(w, "%s := %s.Args[%d]\n", arg, v, i)
   418  			}
   419  			continue
   420  		}
   421  		// compound sexpr
   422  		var argname string
   423  		colon := strings.Index(arg, ":")
   424  		openparen := strings.Index(arg, "(")
   425  		if colon >= 0 && openparen >= 0 && colon < openparen {
   426  			// rule-specified name
   427  			argname = arg[:colon]
   428  			arg = arg[colon+1:]
   429  		} else {
   430  			// autogenerated name
   431  			argname = fmt.Sprintf("%s_%d", v, i)
   432  		}
   433  		fmt.Fprintf(w, "%s := %s.Args[%d]\n", argname, v, i)
   434  		if genMatch0(w, arch, arg, argname, m, false, loc) {
   435  			canFail = true
   436  		}
   437  	}
   438  
   439  	if op.argLength == -1 {
   440  		fmt.Fprintf(w, "if len(%s.Args) != %d {\nbreak\n}\n", v, len(args))
   441  		canFail = true
   442  	}
   443  	return canFail
   444  }
   445  
   446  func genResult(w io.Writer, arch arch, result string, loc string) {
   447  	move := false
   448  	if result[0] == '@' {
   449  		// parse @block directive
   450  		s := strings.SplitN(result[1:], " ", 2)
   451  		fmt.Fprintf(w, "b = %s\n", s[0])
   452  		result = s[1]
   453  		move = true
   454  	}
   455  	genResult0(w, arch, result, new(int), true, move, loc)
   456  }
   457  func genResult0(w io.Writer, arch arch, result string, alloc *int, top, move bool, loc string) string {
   458  	// TODO: when generating a constant result, use f.constVal to avoid
   459  	// introducing copies just to clean them up again.
   460  	if result[0] != '(' {
   461  		// variable
   462  		if top {
   463  			// It in not safe in general to move a variable between blocks
   464  			// (and particularly not a phi node).
   465  			// Introduce a copy.
   466  			fmt.Fprintf(w, "v.reset(OpCopy)\n")
   467  			fmt.Fprintf(w, "v.Type = %s.Type\n", result)
   468  			fmt.Fprintf(w, "v.AddArg(%s)\n", result)
   469  		}
   470  		return result
   471  	}
   472  
   473  	op, oparch, typ, auxint, aux, args := parseValue(result, arch, loc)
   474  
   475  	// Find the type of the variable.
   476  	typeOverride := typ != ""
   477  	if typ == "" && op.typ != "" {
   478  		typ = typeName(op.typ)
   479  	}
   480  
   481  	var v string
   482  	if top && !move {
   483  		v = "v"
   484  		fmt.Fprintf(w, "v.reset(Op%s%s)\n", oparch, op.name)
   485  		if typeOverride {
   486  			fmt.Fprintf(w, "v.Type = %s\n", typ)
   487  		}
   488  	} else {
   489  		if typ == "" {
   490  			log.Fatalf("sub-expression %s (op=Op%s%s) must have a type", result, oparch, op.name)
   491  		}
   492  		v = fmt.Sprintf("v%d", *alloc)
   493  		*alloc++
   494  		fmt.Fprintf(w, "%s := b.NewValue0(v.Line, Op%s%s, %s)\n", v, oparch, op.name, typ)
   495  		if move && top {
   496  			// Rewrite original into a copy
   497  			fmt.Fprintf(w, "v.reset(OpCopy)\n")
   498  			fmt.Fprintf(w, "v.AddArg(%s)\n", v)
   499  		}
   500  	}
   501  
   502  	if auxint != "" {
   503  		fmt.Fprintf(w, "%s.AuxInt = %s\n", v, auxint)
   504  	}
   505  	if aux != "" {
   506  		fmt.Fprintf(w, "%s.Aux = %s\n", v, aux)
   507  	}
   508  	for _, arg := range args {
   509  		x := genResult0(w, arch, arg, alloc, false, move, loc)
   510  		fmt.Fprintf(w, "%s.AddArg(%s)\n", v, x)
   511  	}
   512  
   513  	return v
   514  }
   515  
   516  func split(s string) []string {
   517  	var r []string
   518  
   519  outer:
   520  	for s != "" {
   521  		d := 0               // depth of ({[<
   522  		var open, close byte // opening and closing markers ({[< or )}]>
   523  		nonsp := false       // found a non-space char so far
   524  		for i := 0; i < len(s); i++ {
   525  			switch {
   526  			case d == 0 && s[i] == '(':
   527  				open, close = '(', ')'
   528  				d++
   529  			case d == 0 && s[i] == '<':
   530  				open, close = '<', '>'
   531  				d++
   532  			case d == 0 && s[i] == '[':
   533  				open, close = '[', ']'
   534  				d++
   535  			case d == 0 && s[i] == '{':
   536  				open, close = '{', '}'
   537  				d++
   538  			case d == 0 && (s[i] == ' ' || s[i] == '\t'):
   539  				if nonsp {
   540  					r = append(r, strings.TrimSpace(s[:i]))
   541  					s = s[i:]
   542  					continue outer
   543  				}
   544  			case d > 0 && s[i] == open:
   545  				d++
   546  			case d > 0 && s[i] == close:
   547  				d--
   548  			default:
   549  				nonsp = true
   550  			}
   551  		}
   552  		if d != 0 {
   553  			panic("imbalanced expression: " + s)
   554  		}
   555  		if nonsp {
   556  			r = append(r, strings.TrimSpace(s))
   557  		}
   558  		break
   559  	}
   560  	return r
   561  }
   562  
   563  // isBlock returns true if this op is a block opcode.
   564  func isBlock(name string, arch arch) bool {
   565  	for _, b := range genericBlocks {
   566  		if b.name == name {
   567  			return true
   568  		}
   569  	}
   570  	for _, b := range arch.blocks {
   571  		if b.name == name {
   572  			return true
   573  		}
   574  	}
   575  	return false
   576  }
   577  
   578  // parseValue parses a parenthesized value from a rule.
   579  // The value can be from the match or the result side.
   580  // It returns the op and unparsed strings for typ, auxint, and aux restrictions and for all args.
   581  // oparch is the architecture that op is located in, or "" for generic.
   582  func parseValue(val string, arch arch, loc string) (op opData, oparch string, typ string, auxint string, aux string, args []string) {
   583  	val = val[1 : len(val)-1] // remove ()
   584  
   585  	// Split val up into regions.
   586  	// Split by spaces/tabs, except those contained in (), {}, [], or <>.
   587  	s := split(val)
   588  
   589  	// Extract restrictions and args.
   590  	for _, a := range s[1:] {
   591  		switch a[0] {
   592  		case '<':
   593  			typ = a[1 : len(a)-1] // remove <>
   594  		case '[':
   595  			auxint = a[1 : len(a)-1] // remove []
   596  		case '{':
   597  			aux = a[1 : len(a)-1] // remove {}
   598  		default:
   599  			args = append(args, a)
   600  		}
   601  	}
   602  
   603  	// Resolve the op.
   604  
   605  	// match reports whether x is a good op to select.
   606  	// If strict is true, rule generation might succeed.
   607  	// If strict is false, rule generation has failed,
   608  	// but we're trying to generate a useful error.
   609  	// Doing strict=true then strict=false allows
   610  	// precise op matching while retaining good error messages.
   611  	match := func(x opData, strict bool, archname string) bool {
   612  		if x.name != s[0] {
   613  			return false
   614  		}
   615  		if x.argLength != -1 && int(x.argLength) != len(args) {
   616  			if strict {
   617  				return false
   618  			} else {
   619  				log.Printf("%s: op %s (%s) should have %d args, has %d", loc, s[0], archname, op.argLength, len(args))
   620  			}
   621  		}
   622  		return true
   623  	}
   624  
   625  	for _, x := range genericOps {
   626  		if match(x, true, "generic") {
   627  			op = x
   628  			break
   629  		}
   630  	}
   631  	if arch.name != "generic" {
   632  		for _, x := range arch.ops {
   633  			if match(x, true, arch.name) {
   634  				if op.name != "" {
   635  					log.Fatalf("%s: matches for op %s found in both generic and %s", loc, op.name, arch.name)
   636  				}
   637  				op = x
   638  				oparch = arch.name
   639  				break
   640  			}
   641  		}
   642  	}
   643  
   644  	if op.name == "" {
   645  		// Failed to find the op.
   646  		// Run through everything again with strict=false
   647  		// to generate useful diagnosic messages before failing.
   648  		for _, x := range genericOps {
   649  			match(x, false, "generic")
   650  		}
   651  		for _, x := range arch.ops {
   652  			match(x, false, arch.name)
   653  		}
   654  		log.Fatalf("%s: unknown op %s", loc, s)
   655  	}
   656  
   657  	// Sanity check aux, auxint.
   658  	if auxint != "" {
   659  		switch op.aux {
   660  		case "Bool", "Int8", "Int16", "Int32", "Int64", "Int128", "Float32", "Float64", "SymOff", "SymValAndOff", "SymInt32":
   661  		default:
   662  			log.Fatalf("%s: op %s %s can't have auxint", loc, op.name, op.aux)
   663  		}
   664  	}
   665  	if aux != "" {
   666  		switch op.aux {
   667  		case "String", "Sym", "SymOff", "SymValAndOff", "SymInt32":
   668  		default:
   669  			log.Fatalf("%s: op %s %s can't have aux", loc, op.name, op.aux)
   670  		}
   671  	}
   672  
   673  	return
   674  }
   675  
   676  func blockName(name string, arch arch) string {
   677  	for _, b := range genericBlocks {
   678  		if b.name == name {
   679  			return "Block" + name
   680  		}
   681  	}
   682  	return "Block" + arch.name + name
   683  }
   684  
   685  // typeName returns the string to use to generate a type.
   686  func typeName(typ string) string {
   687  	if typ[0] == '(' {
   688  		ts := strings.Split(typ[1:len(typ)-1], ",")
   689  		if len(ts) != 2 {
   690  			panic("Tuple expect 2 arguments")
   691  		}
   692  		return "MakeTuple(" + typeName(ts[0]) + ", " + typeName(ts[1]) + ")"
   693  	}
   694  	switch typ {
   695  	case "Flags", "Mem", "Void", "Int128":
   696  		return "Type" + typ
   697  	default:
   698  		return "config.fe.Type" + typ + "()"
   699  	}
   700  }
   701  
   702  // unbalanced returns true if there aren't the same number of ( and ) in the string.
   703  func unbalanced(s string) bool {
   704  	var left, right int
   705  	for _, c := range s {
   706  		if c == '(' {
   707  			left++
   708  		}
   709  		if c == ')' {
   710  			right++
   711  		}
   712  	}
   713  	return left != right
   714  }
   715  
   716  // isVariable reports whether s is a single Go alphanumeric identifier.
   717  func isVariable(s string) bool {
   718  	b, err := regexp.MatchString("^[A-Za-z_][A-Za-z_0-9]*$", s)
   719  	if err != nil {
   720  		panic("bad variable regexp")
   721  	}
   722  	return b
   723  }