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