Source file src/cmd/compile/internal/ssa/_gen/rulegen.go

     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  // This program generates Go code that applies rewrite rules to a Value.
     6  // The generated code implements a function of type func (v *Value) bool
     7  // which reports whether if did something.
     8  // Ideas stolen from the Swift Java compiler:
     9  // https://bitsavers.org/pdf/dec/tech_reports/WRL-2000-2.pdf
    10  
    11  package main
    12  
    13  import (
    14  	"bufio"
    15  	"bytes"
    16  	"flag"
    17  	"fmt"
    18  	"go/ast"
    19  	"go/format"
    20  	"go/parser"
    21  	"go/printer"
    22  	"go/token"
    23  	"io"
    24  	"log"
    25  	"os"
    26  	"path"
    27  	"regexp"
    28  	"slices"
    29  	"sort"
    30  	"strconv"
    31  	"strings"
    32  
    33  	"golang.org/x/tools/go/ast/astutil"
    34  )
    35  
    36  // rule syntax:
    37  //  sexpr [&& extra conditions] => [@block] sexpr
    38  //
    39  // sexpr are s-expressions (lisp-like parenthesized groupings)
    40  // sexpr ::= [variable:](opcode sexpr*)
    41  //         | variable
    42  //         | <type>
    43  //         | [auxint]
    44  //         | {aux}
    45  //
    46  // aux      ::= variable | {code}
    47  // type     ::= variable | {code}
    48  // variable ::= some token
    49  // opcode   ::= one of the opcodes from the *Ops.go files
    50  
    51  // special rules: trailing ellipsis "..." (in the outermost sexpr?) must match on both sides of a rule.
    52  //                trailing three underscore "___" in the outermost match sexpr indicate the presence of
    53  //                   extra ignored args that need not appear in the replacement
    54  //                if the right-hand side is in {}, then it is code used to generate the result.
    55  
    56  // extra conditions is just a chunk of Go that evaluates to a boolean. It may use
    57  // variables declared in the matching tsexpr. The variable "v" is predefined to be
    58  // the value matched by the entire rule.
    59  
    60  // If multiple rules match, the first one in file order is selected.
    61  
    62  var (
    63  	genLog  = flag.Bool("log", false, "generate code that logs; for debugging only")
    64  	addLine = flag.Bool("line", false, "add line number comment to generated rules; for debugging only")
    65  )
    66  
    67  type Rule struct {
    68  	Rule string
    69  	Loc  string // file name & line number
    70  }
    71  
    72  func (r Rule) String() string {
    73  	return fmt.Sprintf("rule %q at %s", r.Rule, r.Loc)
    74  }
    75  
    76  func normalizeSpaces(s string) string {
    77  	return strings.Join(strings.Fields(strings.TrimSpace(s)), " ")
    78  }
    79  
    80  // parse returns the matching part of the rule, additional conditions, and the result.
    81  func (r Rule) parse() (match, cond, result string) {
    82  	s := strings.Split(r.Rule, "=>")
    83  	match = normalizeSpaces(s[0])
    84  	result = normalizeSpaces(s[1])
    85  	cond = ""
    86  	if i := strings.Index(match, "&&"); i >= 0 {
    87  		cond = normalizeSpaces(match[i+2:])
    88  		match = normalizeSpaces(match[:i])
    89  	}
    90  	return match, cond, result
    91  }
    92  
    93  func genRules(arch arch)          { genRulesSuffix(arch, "") }
    94  func genSplitLoadRules(arch arch) { genRulesSuffix(arch, "splitload") }
    95  func genLateLowerRules(arch arch) { genRulesSuffix(arch, "latelower") }
    96  
    97  func genRulesSuffix(arch arch, suff string) {
    98  	var readers []NamedReader
    99  	// Open input file.
   100  	var text io.Reader
   101  	name := arch.name + suff + ".rules"
   102  	text, err := os.Open(name)
   103  	if err != nil {
   104  		if suff == "" {
   105  			// All architectures must have a plain rules file.
   106  			log.Fatalf("can't read rule file: %v", err)
   107  		}
   108  		// Some architectures have bonus rules files that others don't share. That's fine.
   109  		return
   110  	}
   111  	readers = append(readers, NamedReader{name, text})
   112  
   113  	// Check for file of SIMD rules to add
   114  	if suff == "" {
   115  		simdname := "simd" + arch.name + ".rules"
   116  		simdtext, err := os.Open(simdname)
   117  		if err == nil {
   118  			readers = append(readers, NamedReader{simdname, simdtext})
   119  		}
   120  		// SVE shares the ARM64 backend; add its generated rules too.
   121  		if arch.name == "ARM64" {
   122  			sveName := "simdARM64SVE.rules"
   123  			if sveText, err := os.Open(sveName); err == nil {
   124  				readers = append(readers, NamedReader{sveName, sveText})
   125  			}
   126  		}
   127  	}
   128  
   129  	// oprules contains a list of rules for each block and opcode
   130  	blockrules := map[string][]Rule{}
   131  	oprules := map[string][]Rule{}
   132  
   133  	// read rule file
   134  	scanner := MultiScannerFromReaders(readers)
   135  	rule := ""
   136  	var lineno int
   137  	var ruleLineno int // line number of "=>"
   138  	for scanner.Scan() {
   139  		lineno = scanner.Line()
   140  		line := scanner.Text()
   141  		if i := strings.Index(line, "//"); i >= 0 {
   142  			// Remove comments. Note that this isn't string safe, so
   143  			// it will truncate lines with // inside strings. Oh well.
   144  			line = line[:i]
   145  		}
   146  		rule += " " + line
   147  		rule = strings.TrimSpace(rule)
   148  		if rule == "" {
   149  			continue
   150  		}
   151  		if !strings.Contains(rule, "=>") {
   152  			continue
   153  		}
   154  		if ruleLineno == 0 {
   155  			ruleLineno = lineno
   156  		}
   157  		if strings.HasSuffix(rule, "=>") {
   158  			continue // continue on the next line
   159  		}
   160  		if n := balance(rule); n > 0 {
   161  			continue // open parentheses remain, continue on the next line
   162  		} else if n < 0 {
   163  			break // continuing the line can't help, and it will only make errors worse
   164  		}
   165  
   166  		loc := fmt.Sprintf("%s:%d", scanner.Name(), ruleLineno)
   167  		for _, rule2 := range expandOr(rule) {
   168  			r := Rule{Rule: rule2, Loc: loc}
   169  			if rawop := strings.Split(rule2, " ")[0][1:]; isBlock(rawop, arch) {
   170  				blockrules[rawop] = append(blockrules[rawop], r)
   171  				continue
   172  			}
   173  			// Do fancier value op matching.
   174  			match, _, _ := r.parse()
   175  			op, oparch, _, _, _, _ := parseValue(match, arch, loc)
   176  			opname := fmt.Sprintf("Op%s%s", oparch, op.name)
   177  			oprules[opname] = append(oprules[opname], r)
   178  		}
   179  		rule = ""
   180  		ruleLineno = 0
   181  	}
   182  	if err := scanner.Err(); err != nil {
   183  		log.Fatalf("scanner failed: %v\n", err)
   184  	}
   185  	if balance(rule) != 0 {
   186  		log.Fatalf("%s:%d: unbalanced rule: %v\n", scanner.Name(), lineno, rule)
   187  	}
   188  
   189  	// Order all the ops.
   190  	var ops []string
   191  	for op := range oprules {
   192  		ops = append(ops, op)
   193  	}
   194  	sort.Strings(ops)
   195  
   196  	genFile := &File{Arch: arch, Suffix: suff}
   197  	// Main rewrite routine is a switch on v.Op.
   198  	fn := &Func{Kind: "Value", ArgLen: -1}
   199  
   200  	sw := &Switch{Expr: exprf("v.Op")}
   201  	for _, op := range ops {
   202  		eop, ok := parseEllipsisRules(oprules[op], arch)
   203  		if ok {
   204  			if strings.Contains(oprules[op][0].Rule, "=>") && opByName(arch, op).aux != opByName(arch, eop).aux {
   205  				panic(fmt.Sprintf("can't use ... for ops that have different aux types: %s and %s", op, eop))
   206  			}
   207  			swc := &Case{Expr: exprf("%s%s", splitOpPrefix, op)}
   208  			swc.add(stmtf("v.Op = %s%s", splitOpPrefix, eop))
   209  			swc.add(stmtf("return true"))
   210  			sw.add(swc)
   211  			continue
   212  		}
   213  
   214  		swc := &Case{Expr: exprf("%s%s", splitOpPrefix, op)}
   215  		swc.add(stmtf("return %s(v)", rewriteFuncName("Value", arch.name, suff, "_"+op)))
   216  		sw.add(swc)
   217  	}
   218  	if len(sw.List) > 0 { // skip if empty
   219  		fn.add(sw)
   220  	}
   221  	fn.add(stmtf("return false"))
   222  	genFile.add(fn)
   223  
   224  	// Generate a routine per op. Note that we don't make one giant routine
   225  	// because it is too big for some compilers.
   226  	for _, op := range ops {
   227  		rules := oprules[op]
   228  		_, ok := parseEllipsisRules(oprules[op], arch)
   229  		if ok {
   230  			continue
   231  		}
   232  
   233  		// rr is kept between iterations, so that each rule can check
   234  		// that the previous rule wasn't unconditional.
   235  		var rr *RuleRewrite
   236  		fn := &Func{
   237  			Kind:   "Value",
   238  			Suffix: fmt.Sprintf("_%s", op),
   239  			ArgLen: opByName(arch, op).argLength,
   240  		}
   241  		fn.add(declReserved("b", "v.Block"))
   242  		fn.add(declReserved("config", "b.Func.Config"))
   243  		fn.add(declReserved("fe", "b.Func."+simpleTitle("fe")))
   244  		fn.add(declReserved("typ", "&b.Func.Config.Types"))
   245  		for _, rule := range rules {
   246  			if rr != nil && !rr.CanFail {
   247  				log.Fatalf("unconditional rule %s is followed by other rules", rr.Match)
   248  			}
   249  			rr = &RuleRewrite{Loc: rule.Loc}
   250  			rr.Match, rr.Cond, rr.Result = rule.parse()
   251  			pos, _ := genMatch(rr, arch, rr.Match, fn.ArgLen >= 0)
   252  			if pos == "" {
   253  				pos = "v.Pos"
   254  			}
   255  			if rr.Cond != "" {
   256  				rr.add(breakf("!(%s)", rr.Cond))
   257  			}
   258  			genResult(rr, arch, rr.Result, pos)
   259  			if *genLog {
   260  				rr.add(stmtf("logRule(%q)", rule.Loc))
   261  			}
   262  			fn.add(rr)
   263  		}
   264  		if rr.CanFail {
   265  			fn.add(stmtf("return false"))
   266  		}
   267  		genFile.add(fn)
   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  	fn = &Func{Kind: "Block"}
   273  	fn.add(declReserved("config", "b.Func.Config"))
   274  	fn.add(declReserved("typ", "&b.Func.Config.Types"))
   275  
   276  	sw = &Switch{Expr: exprf("b.Kind")}
   277  	ops = ops[:0]
   278  	for op := range blockrules {
   279  		ops = append(ops, op)
   280  	}
   281  	sort.Strings(ops)
   282  	for _, op := range ops {
   283  		name, data := getBlockInfo(op, arch)
   284  		swc := &Case{Expr: exprf("%s", name)}
   285  		for _, rule := range blockrules[op] {
   286  			swc.add(genBlockRewrite(rule, arch, data))
   287  		}
   288  		sw.add(swc)
   289  	}
   290  	if len(sw.List) > 0 { // skip if empty
   291  		fn.add(sw)
   292  	}
   293  	fn.add(stmtf("return false"))
   294  	genFile.add(fn)
   295  
   296  	// Remove unused imports and variables.
   297  	buf := new(bytes.Buffer)
   298  	fprint(buf, genFile)
   299  	fset := token.NewFileSet()
   300  	file, err := parser.ParseFile(fset, "", buf, parser.ParseComments|parser.SkipObjectResolution)
   301  	if err != nil {
   302  		filename := fmt.Sprintf("%s_broken.go", arch.name)
   303  		if err := os.WriteFile(filename, buf.Bytes(), 0644); err != nil {
   304  			log.Printf("failed to dump broken code to %s: %v", filename, err)
   305  		} else {
   306  			log.Printf("dumped broken code to %s", filename)
   307  		}
   308  		log.Fatalf("failed to parse generated code for arch %s: %v", arch.name, err)
   309  	}
   310  	tfile := fset.File(file.Pos())
   311  
   312  	// First, use unusedInspector to find the unused declarations by their
   313  	// start position.
   314  	u := unusedInspector{unused: make(map[token.Pos]bool)}
   315  	u.node(file)
   316  
   317  	// Then, delete said nodes via astutil.Apply.
   318  	pre := func(c *astutil.Cursor) bool {
   319  		node := c.Node()
   320  		if node == nil {
   321  			return true
   322  		}
   323  		if u.unused[node.Pos()] {
   324  			c.Delete()
   325  			// Unused imports and declarations use exactly
   326  			// one line. Prevent leaving an empty line.
   327  			tfile.MergeLine(tfile.Position(node.Pos()).Line)
   328  			return false
   329  		}
   330  		return true
   331  	}
   332  	post := func(c *astutil.Cursor) bool {
   333  		switch node := c.Node().(type) {
   334  		case *ast.GenDecl:
   335  			if len(node.Specs) == 0 {
   336  				// Don't leave a broken or empty GenDecl behind,
   337  				// such as "import ()".
   338  				c.Delete()
   339  			}
   340  		}
   341  		return true
   342  	}
   343  	file = astutil.Apply(file, pre, post).(*ast.File)
   344  
   345  	// Write the well-formatted source to file
   346  	outName := rewritesDir(arch.name, suff) + "rewrite" + arch.name + suff + ".go"
   347  	mkdirOutFile(outName)
   348  	f, err := os.Create(outFile(outName))
   349  	if err != nil {
   350  		log.Fatalf("can't write output: %v", err)
   351  	}
   352  	defer f.Close()
   353  	// gofmt result; use a buffered writer, as otherwise go/format spends
   354  	// far too much time in syscalls.
   355  	bw := bufio.NewWriter(f)
   356  	if err := format.Node(bw, fset, file); err != nil {
   357  		log.Fatalf("can't format output: %v", err)
   358  	}
   359  	if err := bw.Flush(); err != nil {
   360  		log.Fatalf("can't write output: %v", err)
   361  	}
   362  	if err := f.Close(); err != nil {
   363  		log.Fatalf("can't write output: %v", err)
   364  	}
   365  }
   366  
   367  // unusedInspector can be used to detect unused variables and imports in an
   368  // ast.Node via its node method. The result is available in the "unused" map.
   369  //
   370  // note that unusedInspector is lazy and best-effort; it only supports the node
   371  // types and patterns used by the rulegen program.
   372  type unusedInspector struct {
   373  	// scope is the current scope, which can never be nil when a declaration
   374  	// is encountered. That is, the unusedInspector.node entrypoint should
   375  	// generally be an entire file or block.
   376  	scope *scope
   377  
   378  	// unused is the resulting set of unused declared names, indexed by the
   379  	// starting position of the node that declared the name.
   380  	unused map[token.Pos]bool
   381  
   382  	// defining is the object currently being defined; this is useful so
   383  	// that if "foo := bar" is unused and removed, we can then detect if
   384  	// "bar" becomes unused as well.
   385  	defining *object
   386  }
   387  
   388  // scoped opens a new scope when called, and returns a function which closes
   389  // that same scope. When a scope is closed, unused variables are recorded.
   390  func (u *unusedInspector) scoped() func() {
   391  	outer := u.scope
   392  	u.scope = &scope{outer: outer, objects: map[string]*object{}}
   393  	return func() {
   394  		for anyUnused := true; anyUnused; {
   395  			anyUnused = false
   396  			for _, obj := range u.scope.objects {
   397  				if obj.numUses > 0 {
   398  					continue
   399  				}
   400  				u.unused[obj.pos] = true
   401  				for _, used := range obj.used {
   402  					if used.numUses--; used.numUses == 0 {
   403  						anyUnused = true
   404  					}
   405  				}
   406  				// We've decremented numUses for each of the
   407  				// objects in used. Zero this slice too, to keep
   408  				// everything consistent.
   409  				obj.used = nil
   410  			}
   411  		}
   412  		u.scope = outer
   413  	}
   414  }
   415  
   416  func (u *unusedInspector) exprs(list []ast.Expr) {
   417  	for _, x := range list {
   418  		u.node(x)
   419  	}
   420  }
   421  
   422  func (u *unusedInspector) node(node ast.Node) {
   423  	switch node := node.(type) {
   424  	case *ast.File:
   425  		defer u.scoped()()
   426  		for _, decl := range node.Decls {
   427  			u.node(decl)
   428  		}
   429  	case *ast.GenDecl:
   430  		for _, spec := range node.Specs {
   431  			u.node(spec)
   432  		}
   433  	case *ast.ImportSpec:
   434  		impPath, _ := strconv.Unquote(node.Path.Value)
   435  		name := path.Base(impPath)
   436  		u.scope.objects[name] = &object{
   437  			name: name,
   438  			pos:  node.Pos(),
   439  		}
   440  	case *ast.FuncDecl:
   441  		u.node(node.Type)
   442  		if node.Body != nil {
   443  			u.node(node.Body)
   444  		}
   445  	case *ast.FuncType:
   446  		if node.Params != nil {
   447  			u.node(node.Params)
   448  		}
   449  		if node.Results != nil {
   450  			u.node(node.Results)
   451  		}
   452  	case *ast.FieldList:
   453  		for _, field := range node.List {
   454  			u.node(field)
   455  		}
   456  	case *ast.Field:
   457  		u.node(node.Type)
   458  
   459  	// statements
   460  
   461  	case *ast.BlockStmt:
   462  		defer u.scoped()()
   463  		for _, stmt := range node.List {
   464  			u.node(stmt)
   465  		}
   466  	case *ast.DeclStmt:
   467  		u.node(node.Decl)
   468  	case *ast.IfStmt:
   469  		if node.Init != nil {
   470  			u.node(node.Init)
   471  		}
   472  		u.node(node.Cond)
   473  		u.node(node.Body)
   474  		if node.Else != nil {
   475  			u.node(node.Else)
   476  		}
   477  	case *ast.ForStmt:
   478  		if node.Init != nil {
   479  			u.node(node.Init)
   480  		}
   481  		if node.Cond != nil {
   482  			u.node(node.Cond)
   483  		}
   484  		if node.Post != nil {
   485  			u.node(node.Post)
   486  		}
   487  		u.node(node.Body)
   488  	case *ast.SwitchStmt:
   489  		if node.Init != nil {
   490  			u.node(node.Init)
   491  		}
   492  		if node.Tag != nil {
   493  			u.node(node.Tag)
   494  		}
   495  		u.node(node.Body)
   496  	case *ast.CaseClause:
   497  		u.exprs(node.List)
   498  		defer u.scoped()()
   499  		for _, stmt := range node.Body {
   500  			u.node(stmt)
   501  		}
   502  	case *ast.BranchStmt:
   503  	case *ast.ExprStmt:
   504  		u.node(node.X)
   505  	case *ast.AssignStmt:
   506  		if node.Tok != token.DEFINE {
   507  			u.exprs(node.Rhs)
   508  			u.exprs(node.Lhs)
   509  			break
   510  		}
   511  		lhs := node.Lhs
   512  		if len(lhs) == 2 && lhs[1].(*ast.Ident).Name == "_" {
   513  			lhs = lhs[:1]
   514  		}
   515  		if len(lhs) != 1 {
   516  			panic("no support for := with multiple names")
   517  		}
   518  
   519  		name := lhs[0].(*ast.Ident)
   520  		obj := &object{
   521  			name: name.Name,
   522  			pos:  name.NamePos,
   523  		}
   524  
   525  		old := u.defining
   526  		u.defining = obj
   527  		u.exprs(node.Rhs)
   528  		u.defining = old
   529  
   530  		u.scope.objects[name.Name] = obj
   531  	case *ast.ReturnStmt:
   532  		u.exprs(node.Results)
   533  	case *ast.IncDecStmt:
   534  		u.node(node.X)
   535  
   536  	// expressions
   537  
   538  	case *ast.CallExpr:
   539  		u.node(node.Fun)
   540  		u.exprs(node.Args)
   541  	case *ast.SelectorExpr:
   542  		u.node(node.X)
   543  	case *ast.UnaryExpr:
   544  		u.node(node.X)
   545  	case *ast.BinaryExpr:
   546  		u.node(node.X)
   547  		u.node(node.Y)
   548  	case *ast.StarExpr:
   549  		u.node(node.X)
   550  	case *ast.ParenExpr:
   551  		u.node(node.X)
   552  	case *ast.IndexExpr:
   553  		u.node(node.X)
   554  		u.node(node.Index)
   555  	case *ast.TypeAssertExpr:
   556  		u.node(node.X)
   557  		u.node(node.Type)
   558  	case *ast.Ident:
   559  		if obj := u.scope.Lookup(node.Name); obj != nil {
   560  			obj.numUses++
   561  			if u.defining != nil {
   562  				u.defining.used = append(u.defining.used, obj)
   563  			}
   564  		}
   565  	case *ast.BasicLit:
   566  	case *ast.CompositeLit:
   567  		for _, e := range node.Elts {
   568  			u.node(e)
   569  		}
   570  	case *ast.KeyValueExpr:
   571  		u.node(node.Key)
   572  		u.node(node.Value)
   573  	case *ast.ValueSpec:
   574  		u.exprs(node.Values)
   575  	default:
   576  		panic(fmt.Sprintf("unhandled node: %T", node))
   577  	}
   578  }
   579  
   580  // scope keeps track of a certain scope and its declared names, as well as the
   581  // outer (parent) scope.
   582  type scope struct {
   583  	outer   *scope             // can be nil, if this is the top-level scope
   584  	objects map[string]*object // indexed by each declared name
   585  }
   586  
   587  func (s *scope) Lookup(name string) *object {
   588  	if obj := s.objects[name]; obj != nil {
   589  		return obj
   590  	}
   591  	if s.outer == nil {
   592  		return nil
   593  	}
   594  	return s.outer.Lookup(name)
   595  }
   596  
   597  // object keeps track of a declared name, such as a variable or import.
   598  type object struct {
   599  	name string
   600  	pos  token.Pos // start position of the node declaring the object
   601  
   602  	numUses int       // number of times this object is used
   603  	used    []*object // objects that its declaration makes use of
   604  }
   605  
   606  func fprint(w io.Writer, n Node) {
   607  	switch n := n.(type) {
   608  	case *File:
   609  		file := n
   610  		seenRewrite := make(map[[3]string]string)
   611  		fmt.Fprintf(w, "// Code generated from _gen/%s%s.rules using 'go generate'; DO NOT EDIT.\n", n.Arch.name, n.Suffix)
   612  		fmt.Fprintf(w, "\npackage %s\n", rewritesPkg(n.Arch.name, n.Suffix))
   613  		additionalImports := slices.Clip(n.Arch.imports)
   614  		additionalImports = append(additionalImports, "cmd/compile/internal/ssa/ssaop")
   615  		additionalImports = append(additionalImports, splitCorePath)
   616  
   617  		allImports := append([]string{
   618  			"fmt",
   619  			"internal/buildcfg",
   620  			"math",
   621  			"math/bits",
   622  			"cmd/internal/obj",
   623  			"cmd/compile/internal/base",
   624  			"cmd/compile/internal/types",
   625  			"cmd/compile/internal/ir",
   626  			"cmd/compile/internal/ssa/block",
   627  		}, additionalImports...)
   628  		for _, path := range allImports {
   629  			fmt.Fprintf(w, "import %q\n", path)
   630  		}
   631  		for _, f := range n.List {
   632  			f := f.(*Func)
   633  			fmt.Fprintf(w, "func %s(", rewriteFuncName(f.Kind, n.Arch.name, n.Suffix, f.Suffix))
   634  			fmt.Fprintf(w, "%c *%s%s) bool {\n", strings.ToLower(f.Kind)[0], splitCorePrefix, f.Kind)
   635  			if f.Kind == "Value" && f.ArgLen > 0 {
   636  				for i := f.ArgLen - 1; i >= 0; i-- {
   637  					fmt.Fprintf(w, "v_%d := v.Args[%d]\n", i, i)
   638  				}
   639  			}
   640  			for _, n := range f.List {
   641  				fprint(w, n)
   642  
   643  				if rr, ok := n.(*RuleRewrite); ok {
   644  					k := [3]string{
   645  						normalizeMatch(rr.Match, file.Arch),
   646  						normalizeWhitespace(rr.Cond),
   647  						normalizeWhitespace(rr.Result),
   648  					}
   649  					if prev, ok := seenRewrite[k]; ok {
   650  						log.Fatalf("duplicate rule %s, previously seen at %s\n", rr.Loc, prev)
   651  					}
   652  					seenRewrite[k] = rr.Loc
   653  				}
   654  			}
   655  			fmt.Fprintf(w, "}\n")
   656  		}
   657  	case *Switch:
   658  		fmt.Fprintf(w, "switch ")
   659  		fprint(w, n.Expr)
   660  		fmt.Fprintf(w, " {\n")
   661  		for _, n := range n.List {
   662  			fprint(w, n)
   663  		}
   664  		fmt.Fprintf(w, "}\n")
   665  	case *Case:
   666  		fmt.Fprintf(w, "case ")
   667  		fprint(w, n.Expr)
   668  		fmt.Fprintf(w, ":\n")
   669  		for _, n := range n.List {
   670  			fprint(w, n)
   671  		}
   672  	case *RuleRewrite:
   673  		if *addLine {
   674  			fmt.Fprintf(w, "// %s\n", n.Loc)
   675  		}
   676  		fmt.Fprintf(w, "// match: %s\n", n.Match)
   677  		if n.Cond != "" {
   678  			fmt.Fprintf(w, "// cond: %s\n", n.Cond)
   679  		}
   680  		fmt.Fprintf(w, "// result: %s\n", n.Result)
   681  		fmt.Fprintf(w, "for %s {\n", n.Check)
   682  		nCommutative := 0
   683  		for _, n := range n.List {
   684  			if b, ok := n.(*CondBreak); ok {
   685  				b.InsideCommuteLoop = nCommutative > 0
   686  			}
   687  			fprint(w, n)
   688  			if loop, ok := n.(StartCommuteLoop); ok {
   689  				if nCommutative != loop.Depth {
   690  					panic("mismatch commute loop depth")
   691  				}
   692  				nCommutative++
   693  			}
   694  		}
   695  		fmt.Fprintf(w, "return true\n")
   696  		for i := 0; i < nCommutative; i++ {
   697  			fmt.Fprintln(w, "}")
   698  		}
   699  		if n.CommuteDepth > 0 && n.CanFail {
   700  			fmt.Fprint(w, "break\n")
   701  		}
   702  		fmt.Fprintf(w, "}\n")
   703  	case *Declare:
   704  		fmt.Fprintf(w, "%s := ", n.Name)
   705  		fprint(w, n.Value)
   706  		fmt.Fprintln(w)
   707  	case *CondBreak:
   708  		fmt.Fprintf(w, "if ")
   709  		fprint(w, n.Cond)
   710  		fmt.Fprintf(w, " {\n")
   711  		if n.InsideCommuteLoop {
   712  			fmt.Fprintf(w, "continue")
   713  		} else {
   714  			fmt.Fprintf(w, "break")
   715  		}
   716  		fmt.Fprintf(w, "\n}\n")
   717  	case ast.Node:
   718  		printConfig.Fprint(w, emptyFset, n)
   719  		if _, ok := n.(ast.Stmt); ok {
   720  			fmt.Fprintln(w)
   721  		}
   722  	case StartCommuteLoop:
   723  		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)
   724  	default:
   725  		log.Fatalf("cannot print %T", n)
   726  	}
   727  }
   728  
   729  var printConfig = printer.Config{
   730  	Mode: printer.RawFormat, // we use go/format later, so skip work here
   731  }
   732  
   733  var emptyFset = token.NewFileSet()
   734  
   735  // Node can be a Statement or an ast.Expr.
   736  type Node interface{}
   737  
   738  // Statement can be one of our high-level statement struct types, or an
   739  // ast.Stmt under some limited circumstances.
   740  type Statement interface{}
   741  
   742  // BodyBase is shared by all of our statement pseudo-node types which can
   743  // contain other statements.
   744  type BodyBase struct {
   745  	List    []Statement
   746  	CanFail bool
   747  }
   748  
   749  func (w *BodyBase) add(node Statement) {
   750  	var last Statement
   751  	if len(w.List) > 0 {
   752  		last = w.List[len(w.List)-1]
   753  	}
   754  	if node, ok := node.(*CondBreak); ok {
   755  		w.CanFail = true
   756  		if last, ok := last.(*CondBreak); ok {
   757  			// Add to the previous "if <cond> { break }" via a
   758  			// logical OR, which will save verbosity.
   759  			last.Cond = &ast.BinaryExpr{
   760  				Op: token.LOR,
   761  				X:  last.Cond,
   762  				Y:  node.Cond,
   763  			}
   764  			return
   765  		}
   766  	}
   767  
   768  	w.List = append(w.List, node)
   769  }
   770  
   771  // predeclared contains globally known tokens that should not be redefined.
   772  var predeclared = map[string]bool{
   773  	"nil":   true,
   774  	"false": true,
   775  	"true":  true,
   776  }
   777  
   778  // declared reports if the body contains a Declare with the given name.
   779  func (w *BodyBase) declared(name string) bool {
   780  	if predeclared[name] {
   781  		// Treat predeclared names as having already been declared.
   782  		// This lets us use nil to match an aux field or
   783  		// true and false to match an auxint field.
   784  		return true
   785  	}
   786  	for _, s := range w.List {
   787  		if decl, ok := s.(*Declare); ok && decl.Name == name {
   788  			return true
   789  		}
   790  	}
   791  	return false
   792  }
   793  
   794  // These types define some high-level statement struct types, which can be used
   795  // as a Statement. This allows us to keep some node structs simpler, and have
   796  // higher-level nodes such as an entire rule rewrite.
   797  //
   798  // Note that ast.Expr is always used as-is; we don't declare our own expression
   799  // nodes.
   800  type (
   801  	File struct {
   802  		BodyBase // []*Func
   803  		Arch     arch
   804  		Suffix   string
   805  	}
   806  	Func struct {
   807  		BodyBase
   808  		Kind   string // "Value" or "Block"
   809  		Suffix string
   810  		ArgLen int32 // if kind == "Value", number of args for this op
   811  	}
   812  	Switch struct {
   813  		BodyBase // []*Case
   814  		Expr     ast.Expr
   815  	}
   816  	Case struct {
   817  		BodyBase
   818  		Expr ast.Expr
   819  	}
   820  	RuleRewrite struct {
   821  		BodyBase
   822  		Match, Cond, Result string // top comments
   823  		Check               string // top-level boolean expression
   824  
   825  		Alloc        int    // for unique var names
   826  		Loc          string // file name & line number of the original rule
   827  		CommuteDepth int    // used to track depth of commute loops
   828  	}
   829  	Declare struct {
   830  		Name  string
   831  		Value ast.Expr
   832  	}
   833  	CondBreak struct {
   834  		Cond              ast.Expr
   835  		InsideCommuteLoop bool
   836  	}
   837  	StartCommuteLoop struct {
   838  		Depth int
   839  		V     string
   840  	}
   841  )
   842  
   843  // exprf parses a Go expression generated from fmt.Sprintf, panicking if an
   844  // error occurs.
   845  func exprf(format string, a ...interface{}) ast.Expr {
   846  	src := fmt.Sprintf(format, a...)
   847  	expr, err := parser.ParseExpr(src)
   848  	if err != nil {
   849  		log.Fatalf("expr parse error on %q: %v", src, err)
   850  	}
   851  	return expr
   852  }
   853  
   854  // stmtf parses a Go statement generated from fmt.Sprintf. This function is only
   855  // meant for simple statements that don't have a custom Statement node declared
   856  // in this package, such as ast.ReturnStmt or ast.ExprStmt.
   857  func stmtf(format string, a ...interface{}) Statement {
   858  	src := fmt.Sprintf(format, a...)
   859  	fsrc := "package p\nfunc _() {\n" + src + "\n}\n"
   860  	file, err := parser.ParseFile(token.NewFileSet(), "", fsrc, parser.SkipObjectResolution)
   861  	if err != nil {
   862  		log.Fatalf("stmt parse error on %q: %v", src, err)
   863  	}
   864  	return file.Decls[0].(*ast.FuncDecl).Body.List[0]
   865  }
   866  
   867  var reservedNames = map[string]bool{
   868  	"v":      true, // Values[i], etc
   869  	"b":      true, // v.Block
   870  	"config": true, // b.Func.Config
   871  	"fe":     true, // b.Func.fe
   872  	"typ":    true, // &b.Func.Config.Types
   873  	"op":     true, // op.OpAMD64MOVBQZX
   874  }
   875  
   876  // declf constructs a simple "name := value" declaration,
   877  // using exprf for its value.
   878  //
   879  // name must not be one of reservedNames.
   880  // This helps prevent unintended shadowing and name clashes.
   881  // To declare a reserved name, use declReserved.
   882  func declf(loc, name, format string, a ...interface{}) *Declare {
   883  	if reservedNames[name] {
   884  		log.Fatalf("rule %s uses the reserved name %s", loc, name)
   885  	}
   886  	return &Declare{name, exprf(format, a...)}
   887  }
   888  
   889  // declReserved is like declf, but the name must be one of reservedNames.
   890  // Calls to declReserved should generally be static and top-level.
   891  func declReserved(name, value string) *Declare {
   892  	if !reservedNames[name] {
   893  		panic(fmt.Sprintf("declReserved call does not use a reserved name: %q", name))
   894  	}
   895  	return &Declare{name, exprf("%s", value)}
   896  }
   897  
   898  // breakf constructs a simple "if cond { break }" statement, using exprf for its
   899  // condition.
   900  func breakf(format string, a ...interface{}) *CondBreak {
   901  	return &CondBreak{Cond: exprf(format, a...)}
   902  }
   903  
   904  func genBlockRewrite(rule Rule, arch arch, data blockData) *RuleRewrite {
   905  	rr := &RuleRewrite{Loc: rule.Loc}
   906  	rr.Match, rr.Cond, rr.Result = rule.parse()
   907  	_, _, auxint, aux, s := extract(rr.Match) // remove parens, then split
   908  
   909  	// check match of control values
   910  	if len(s) < data.controls {
   911  		log.Fatalf("incorrect number of arguments in %s, got %v wanted at least %v", rule, len(s), data.controls)
   912  	}
   913  	controls := s[:data.controls]
   914  	pos := make([]string, data.controls)
   915  	for i, arg := range controls {
   916  		cname := fmt.Sprintf("b.Controls[%v]", i)
   917  		if strings.Contains(arg, "(") {
   918  			vname, expr := splitNameExpr(arg)
   919  			if vname == "" {
   920  				vname = fmt.Sprintf("v_%v", i)
   921  			}
   922  			rr.add(declf(rr.Loc, vname, "%s", cname))
   923  			p, op := genMatch0(rr, arch, expr, vname, nil, false) // TODO: pass non-nil cnt?
   924  			if op != "" {
   925  				check := fmt.Sprintf("%s.Op == %s", cname, op)
   926  				if rr.Check == "" {
   927  					rr.Check = check
   928  				} else {
   929  					rr.Check += " && " + check
   930  				}
   931  			}
   932  			if p == "" {
   933  				p = vname + ".Pos"
   934  			}
   935  			pos[i] = p
   936  		} else {
   937  			rr.add(declf(rr.Loc, arg, "%s", cname))
   938  			pos[i] = arg + ".Pos"
   939  		}
   940  	}
   941  	for _, e := range []struct {
   942  		name, field, dclType string
   943  	}{
   944  		{auxint, "AuxInt", data.auxIntType()},
   945  		{aux, "Aux", data.auxType()},
   946  	} {
   947  		if e.name == "" {
   948  			continue
   949  		}
   950  
   951  		if e.dclType == "" {
   952  			log.Fatalf("op %s has no declared type for %s", data.name, e.field)
   953  		}
   954  		conv := convFunc(unTitle(e.field) + "To" + title(e.dclType))
   955  		if !token.IsIdentifier(e.name) || rr.declared(e.name) {
   956  			rr.add(breakf("%s(b.%s) != %s", conv, e.field, e.name))
   957  		} else {
   958  			rr.add(declf(rr.Loc, e.name, "%s(b.%s)", conv, e.field))
   959  		}
   960  	}
   961  	if rr.Cond != "" {
   962  		rr.add(breakf("!(%s)", rr.Cond))
   963  	}
   964  
   965  	// Rule matches. Generate result.
   966  	outop, _, auxint, aux, t := extract(rr.Result) // remove parens, then split
   967  	blockName, outdata := getBlockInfo(outop, arch)
   968  	if len(t) < outdata.controls {
   969  		log.Fatalf("incorrect number of output arguments in %s, got %v wanted at least %v", rule, len(s), outdata.controls)
   970  	}
   971  
   972  	// Check if newsuccs is the same set as succs.
   973  	succs := s[data.controls:]
   974  	newsuccs := t[outdata.controls:]
   975  	m := map[string]bool{}
   976  	for _, succ := range succs {
   977  		if m[succ] {
   978  			log.Fatalf("can't have a repeat successor name %s in %s", succ, rule)
   979  		}
   980  		m[succ] = true
   981  	}
   982  	for _, succ := range newsuccs {
   983  		if !m[succ] {
   984  			log.Fatalf("unknown successor %s in %s", succ, rule)
   985  		}
   986  		delete(m, succ)
   987  	}
   988  	if len(m) != 0 {
   989  		log.Fatalf("unmatched successors %v in %s", m, rule)
   990  	}
   991  
   992  	var genControls [2]string
   993  	for i, control := range t[:outdata.controls] {
   994  		// Select a source position for any new control values.
   995  		// TODO: does it always make sense to use the source position
   996  		// of the original control values or should we be using the
   997  		// block's source position in some cases?
   998  		newpos := "b.Pos" // default to block's source position
   999  		if i < len(pos) && pos[i] != "" {
  1000  			// Use the previous control value's source position.
  1001  			newpos = pos[i]
  1002  		}
  1003  
  1004  		// Generate a new control value (or copy an existing value).
  1005  		genControls[i] = genResult0(rr, arch, control, false, false, newpos, nil)
  1006  	}
  1007  	switch outdata.controls {
  1008  	case 0:
  1009  		rr.add(stmtf("b.Reset(%s)", blockName))
  1010  	case 1:
  1011  		rr.add(stmtf("b.%s(%s, %s)", simpleTitle("resetWithControl"), blockName, genControls[0]))
  1012  	case 2:
  1013  		rr.add(stmtf("b.%s(%s, %s, %s)", simpleTitle("resetWithControl2"), blockName, genControls[0], genControls[1]))
  1014  	default:
  1015  		log.Fatalf("too many controls: %d", outdata.controls)
  1016  	}
  1017  
  1018  	if auxint != "" {
  1019  		// Make sure auxint value has the right type.
  1020  		rr.add(stmtf("b.AuxInt = %s(%s)", convFunc(unTitle(outdata.auxIntType())+"ToAuxInt"), auxint))
  1021  	}
  1022  	if aux != "" {
  1023  		// Make sure aux value has the right type.
  1024  		rr.add(stmtf("b.Aux = %s(%s)", convFunc(unTitle(outdata.auxType())+"ToAux"), aux))
  1025  	}
  1026  
  1027  	succChanged := false
  1028  	for i := 0; i < len(succs); i++ {
  1029  		if succs[i] != newsuccs[i] {
  1030  			succChanged = true
  1031  		}
  1032  	}
  1033  	if succChanged {
  1034  		if len(succs) != 2 {
  1035  			log.Fatalf("changed successors, len!=2 in %s", rule)
  1036  		}
  1037  		if succs[0] != newsuccs[1] || succs[1] != newsuccs[0] {
  1038  			log.Fatalf("can only handle swapped successors in %s", rule)
  1039  		}
  1040  		rr.add(stmtf("b.%s()", simpleTitle("swapSuccessors")))
  1041  	}
  1042  
  1043  	if *genLog {
  1044  		rr.add(stmtf("logRule(%q)", rule.Loc))
  1045  	}
  1046  	return rr
  1047  }
  1048  
  1049  func convFunc(name string) string {
  1050  	return splitCorePrefix + simpleTitle(name)
  1051  }
  1052  
  1053  // genMatch returns the variable whose source position should be used for the
  1054  // result (or "" if no opinion), and a boolean that reports whether the match can fail.
  1055  func genMatch(rr *RuleRewrite, arch arch, match string, pregenTop bool) (pos, checkOp string) {
  1056  	cnt := varCount(rr)
  1057  	return genMatch0(rr, arch, match, "v", cnt, pregenTop)
  1058  }
  1059  
  1060  func genMatch0(rr *RuleRewrite, arch arch, match, v string, cnt map[string]int, pregenTop bool) (pos, checkOp string) {
  1061  	if match[0] != '(' || match[len(match)-1] != ')' {
  1062  		log.Fatalf("%s: non-compound expr in genMatch0: %q", rr.Loc, match)
  1063  	}
  1064  	op, oparch, typ, auxint, aux, args := parseValue(match, arch, rr.Loc)
  1065  
  1066  	checkOp = fmt.Sprintf("%sOp%s%s", splitOpPrefix, oparch, op.name)
  1067  
  1068  	if op.faultOnNilArg0 || op.faultOnNilArg1 {
  1069  		// Prefer the position of an instruction which could fault.
  1070  		pos = v + ".Pos"
  1071  	}
  1072  
  1073  	// If the last argument is ___, it means "don't care about trailing arguments, really"
  1074  	// The likely/intended use is for rewrites that are too tricky to express in the existing pattern language
  1075  	// Do a length check early because long patterns fed short (ultimately not-matching) inputs will
  1076  	// do an indexing error in pattern-matching.
  1077  	if op.argLength == -1 {
  1078  		l := len(args)
  1079  		if l == 0 || args[l-1] != "___" {
  1080  			rr.add(breakf("len(%s.Args) != %d", v, l))
  1081  		} else if l > 1 && args[l-1] == "___" {
  1082  			rr.add(breakf("len(%s.Args) < %d", v, l-1))
  1083  		}
  1084  	}
  1085  
  1086  	for _, e := range []struct {
  1087  		name, field, dclType string
  1088  	}{
  1089  		{typ, "Type", "*types.Type"},
  1090  		{auxint, "AuxInt", op.auxIntType()},
  1091  		{aux, "Aux", op.auxType()},
  1092  	} {
  1093  		if e.name == "" {
  1094  			continue
  1095  		}
  1096  
  1097  		if e.dclType == "" {
  1098  			log.Fatalf("op %s has no declared type for %s", op.name, e.field)
  1099  		}
  1100  		if !token.IsIdentifier(e.name) || rr.declared(e.name) {
  1101  			switch e.field {
  1102  			case "Aux":
  1103  				rr.add(breakf("%s(%s.%s) != %s", convFunc("auxTo"+title(e.dclType)), v, e.field, e.name))
  1104  			case "AuxInt":
  1105  				rr.add(breakf("%s(%s.%s) != %s", convFunc("auxIntTo"+title(e.dclType)), v, e.field, e.name))
  1106  			case "Type":
  1107  				rr.add(breakf("%s.%s != %s", v, e.field, e.name))
  1108  			}
  1109  		} else {
  1110  			switch e.field {
  1111  			case "Aux":
  1112  				rr.add(declf(rr.Loc, e.name, "%s(%s.%s)", convFunc("auxTo"+title(e.dclType)), v, e.field))
  1113  			case "AuxInt":
  1114  				rr.add(declf(rr.Loc, e.name, "%s(%s.%s)", convFunc("auxIntTo"+title(e.dclType)), v, e.field))
  1115  			case "Type":
  1116  				rr.add(declf(rr.Loc, e.name, "%s.%s", v, e.field))
  1117  			}
  1118  		}
  1119  	}
  1120  
  1121  	commutative := op.commutative
  1122  	if commutative {
  1123  		if args[0] == args[1] {
  1124  			// When we have (Add x x), for any x,
  1125  			// even if there are other uses of x besides these two,
  1126  			// and even if x is not a variable,
  1127  			// we can skip the commutative match.
  1128  			commutative = false
  1129  		}
  1130  		if cnt[args[0]] == 1 && cnt[args[1]] == 1 {
  1131  			// When we have (Add x y) with no other uses
  1132  			// of x and y in the matching rule and condition,
  1133  			// then we can skip the commutative match (Add y x).
  1134  			commutative = false
  1135  		}
  1136  	}
  1137  
  1138  	if !pregenTop {
  1139  		// Access last argument first to minimize bounds checks.
  1140  		for n := len(args) - 1; n > 0; n-- {
  1141  			a := args[n]
  1142  			if a == "_" {
  1143  				continue
  1144  			}
  1145  			if !rr.declared(a) && token.IsIdentifier(a) && !(commutative && len(args) == 2) {
  1146  				rr.add(declf(rr.Loc, a, "%s.Args[%d]", v, n))
  1147  				// delete the last argument so it is not reprocessed
  1148  				args = args[:n]
  1149  			} else {
  1150  				rr.add(stmtf("_ = %s.Args[%d]", v, n))
  1151  			}
  1152  			break
  1153  		}
  1154  	}
  1155  	if commutative && !pregenTop {
  1156  		for i := 0; i <= 1; i++ {
  1157  			vname := fmt.Sprintf("%s_%d", v, i)
  1158  			rr.add(declf(rr.Loc, vname, "%s.Args[%d]", v, i))
  1159  		}
  1160  	}
  1161  	if commutative {
  1162  		rr.add(StartCommuteLoop{rr.CommuteDepth, v})
  1163  		rr.CommuteDepth++
  1164  	}
  1165  	for i, arg := range args {
  1166  		if arg == "_" {
  1167  			continue
  1168  		}
  1169  		var rhs string
  1170  		if (commutative && i < 2) || pregenTop {
  1171  			rhs = fmt.Sprintf("%s_%d", v, i)
  1172  		} else {
  1173  			rhs = fmt.Sprintf("%s.Args[%d]", v, i)
  1174  		}
  1175  		if !strings.Contains(arg, "(") {
  1176  			// leaf variable
  1177  			if rr.declared(arg) {
  1178  				// variable already has a definition. Check whether
  1179  				// the old definition and the new definition match.
  1180  				// For example, (add x x).  Equality is just pointer equality
  1181  				// on Values (so cse is important to do before lowering).
  1182  				rr.add(breakf("%s != %s", arg, rhs))
  1183  			} else {
  1184  				if arg != rhs {
  1185  					rr.add(declf(rr.Loc, arg, "%s", rhs))
  1186  				}
  1187  			}
  1188  			continue
  1189  		}
  1190  		// compound sexpr
  1191  		argname, expr := splitNameExpr(arg)
  1192  		if argname == "" {
  1193  			argname = fmt.Sprintf("%s_%d", v, i)
  1194  		}
  1195  		if argname == "b" {
  1196  			log.Fatalf("don't name args 'b', it is ambiguous with blocks")
  1197  		}
  1198  
  1199  		if argname != rhs {
  1200  			rr.add(declf(rr.Loc, argname, "%s", rhs))
  1201  		}
  1202  		bexpr := exprf("%s.Op != addLater", argname)
  1203  		rr.add(&CondBreak{Cond: bexpr})
  1204  		argPos, argCheckOp := genMatch0(rr, arch, expr, argname, cnt, false)
  1205  		bexpr.(*ast.BinaryExpr).Y.(*ast.Ident).Name = argCheckOp
  1206  
  1207  		if argPos != "" {
  1208  			// Keep the argument in preference to the parent, as the
  1209  			// argument is normally earlier in program flow.
  1210  			// Keep the argument in preference to an earlier argument,
  1211  			// as that prefers the memory argument which is also earlier
  1212  			// in the program flow.
  1213  			pos = argPos
  1214  		}
  1215  	}
  1216  
  1217  	return pos, checkOp
  1218  }
  1219  
  1220  func genResult(rr *RuleRewrite, arch arch, result, pos string) {
  1221  	move := result[0] == '@'
  1222  	if move {
  1223  		// parse @block directive
  1224  		s := strings.SplitN(result[1:], " ", 2)
  1225  		rr.add(stmtf("b = %s", s[0]))
  1226  		result = s[1]
  1227  	}
  1228  	if result[0] == '{' {
  1229  		// Arbitrary code used to make the result
  1230  		rr.add(stmtf("v.%s(%s)", simpleTitle("copyOf"), result[1:len(result)-1]))
  1231  		return
  1232  	}
  1233  	cse := make(map[string]string)
  1234  	genResult0(rr, arch, result, true, move, pos, cse)
  1235  }
  1236  
  1237  func genResult0(rr *RuleRewrite, arch arch, result string, top, move bool, pos string, cse map[string]string) string {
  1238  	resname, expr := splitNameExpr(result)
  1239  	result = expr
  1240  	// TODO: when generating a constant result, use f.constVal to avoid
  1241  	// introducing copies just to clean them up again.
  1242  	if result[0] != '(' {
  1243  		// variable
  1244  		if top {
  1245  			// It in not safe in general to move a variable between blocks
  1246  			// (and particularly not a phi node).
  1247  			// Introduce a copy.
  1248  			rr.add(stmtf("v.%s(%s)", simpleTitle("copyOf"), result))
  1249  		}
  1250  		return result
  1251  	}
  1252  
  1253  	w := normalizeWhitespace(result)
  1254  	if prev := cse[w]; prev != "" {
  1255  		return prev
  1256  	}
  1257  
  1258  	op, oparch, typ, auxint, aux, args := parseValue(result, arch, rr.Loc)
  1259  
  1260  	// Find the type of the variable.
  1261  	typeOverride := typ != ""
  1262  	if typ == "" && op.typ != "" {
  1263  		typ = typeName(op.typ)
  1264  	}
  1265  
  1266  	v := "v"
  1267  	if top && !move {
  1268  		rr.add(stmtf("v.%s(%sOp%s%s)", simpleTitle("reset"), splitOpPrefix, oparch, op.name))
  1269  		if typeOverride {
  1270  			rr.add(stmtf("v.Type = %s", typ))
  1271  		}
  1272  	} else {
  1273  		if typ == "" {
  1274  			log.Fatalf("sub-expression %s (op=Op%s%s) at %s must have a type", result, oparch, op.name, rr.Loc)
  1275  		}
  1276  		if resname == "" {
  1277  			v = fmt.Sprintf("v%d", rr.Alloc)
  1278  		} else {
  1279  			v = resname
  1280  		}
  1281  		rr.Alloc++
  1282  		rr.add(declf(rr.Loc, v, "b.NewValue0(%s, %sOp%s%s, %s)", pos, splitOpPrefix, oparch, op.name, typ))
  1283  		if move && top {
  1284  			// Rewrite original into a copy
  1285  			rr.add(stmtf("v.%s(%s)", simpleTitle("copyOf"), v))
  1286  		}
  1287  	}
  1288  
  1289  	if auxint != "" {
  1290  		// Make sure auxint value has the right type.
  1291  		rr.add(stmtf("%s.AuxInt = %s(%s)", v, convFunc(unTitle(op.auxIntType())+"ToAuxInt"), auxint))
  1292  	}
  1293  	if aux != "" {
  1294  		// Make sure aux value has the right type.
  1295  		rr.add(stmtf("%s.Aux = %s(%s)", v, convFunc(unTitle(op.auxType())+"ToAux"), aux))
  1296  	}
  1297  	all := new(strings.Builder)
  1298  	for i, arg := range args {
  1299  		x := genResult0(rr, arch, arg, false, move, pos, cse)
  1300  		if i > 0 {
  1301  			all.WriteString(", ")
  1302  		}
  1303  		all.WriteString(x)
  1304  	}
  1305  	switch len(args) {
  1306  	case 0:
  1307  	case 1:
  1308  		rr.add(stmtf("%s.AddArg(%s)", v, all.String()))
  1309  	case 2, 3, 4, 5, 6:
  1310  		rr.add(stmtf("%s.AddArg%d(%s)", v, len(args), all.String()))
  1311  	default:
  1312  		rr.add(stmtf("%s.AddArgs(%s)", v, all.String()))
  1313  	}
  1314  
  1315  	if cse != nil {
  1316  		cse[w] = v
  1317  	}
  1318  	return v
  1319  }
  1320  
  1321  func split(s string) []string {
  1322  	var r []string
  1323  
  1324  outer:
  1325  	for s != "" {
  1326  		d := 0               // depth of ({[<
  1327  		var open, close byte // opening and closing markers ({[< or )}]>
  1328  		nonsp := false       // found a non-space char so far
  1329  		for i := 0; i < len(s); i++ {
  1330  			switch {
  1331  			case d == 0 && s[i] == '(':
  1332  				open, close = '(', ')'
  1333  				d++
  1334  			case d == 0 && s[i] == '<':
  1335  				open, close = '<', '>'
  1336  				d++
  1337  			case d == 0 && s[i] == '[':
  1338  				open, close = '[', ']'
  1339  				d++
  1340  			case d == 0 && s[i] == '{':
  1341  				open, close = '{', '}'
  1342  				d++
  1343  			case d == 0 && (s[i] == ' ' || s[i] == '\t'):
  1344  				if nonsp {
  1345  					r = append(r, strings.TrimSpace(s[:i]))
  1346  					s = s[i:]
  1347  					continue outer
  1348  				}
  1349  			case d > 0 && s[i] == open:
  1350  				d++
  1351  			case d > 0 && s[i] == close:
  1352  				d--
  1353  			case s[i] == ':':
  1354  				// ignore spaces after colons
  1355  				nonsp = true
  1356  				for i+1 < len(s) && (s[i+1] == ' ' || s[i+1] == '\t') {
  1357  					i++
  1358  				}
  1359  			default:
  1360  				nonsp = true
  1361  			}
  1362  		}
  1363  		if d != 0 {
  1364  			log.Fatalf("imbalanced expression: %q", s)
  1365  		}
  1366  		if nonsp {
  1367  			r = append(r, strings.TrimSpace(s))
  1368  		}
  1369  		break
  1370  	}
  1371  	return r
  1372  }
  1373  
  1374  // isBlock reports whether this op is a block opcode.
  1375  func isBlock(name string, arch arch) bool {
  1376  	for _, b := range genericBlocks {
  1377  		if b.name == name {
  1378  			return true
  1379  		}
  1380  	}
  1381  	for _, b := range arch.blocks {
  1382  		if b.name == name {
  1383  			return true
  1384  		}
  1385  	}
  1386  	return false
  1387  }
  1388  
  1389  func extract(val string) (op, typ, auxint, aux string, args []string) {
  1390  	val = val[1 : len(val)-1] // remove ()
  1391  
  1392  	// Split val up into regions.
  1393  	// Split by spaces/tabs, except those contained in (), {}, [], or <> or after colon.
  1394  	s := split(val)
  1395  
  1396  	// Extract restrictions and args.
  1397  	op = s[0]
  1398  	for _, a := range s[1:] {
  1399  		switch a[0] {
  1400  		case '<':
  1401  			typ = a[1 : len(a)-1] // remove <>
  1402  		case '[':
  1403  			auxint = a[1 : len(a)-1] // remove []
  1404  		case '{':
  1405  			aux = a[1 : len(a)-1] // remove {}
  1406  		default:
  1407  			args = append(args, a)
  1408  		}
  1409  	}
  1410  	return
  1411  }
  1412  
  1413  // parseValue parses a parenthesized value from a rule.
  1414  // The value can be from the match or the result side.
  1415  // It returns the op and unparsed strings for typ, auxint, and aux restrictions and for all args.
  1416  // oparch is the architecture that op is located in, or "" for generic.
  1417  func parseValue(val string, arch arch, loc string) (op opData, oparch, typ, auxint, aux string, args []string) {
  1418  	// Resolve the op.
  1419  	var s string
  1420  	s, typ, auxint, aux, args = extract(val)
  1421  
  1422  	// match reports whether x is a good op to select.
  1423  	// If strict is true, rule generation might succeed.
  1424  	// If strict is false, rule generation has failed,
  1425  	// but we're trying to generate a useful error.
  1426  	// Doing strict=true then strict=false allows
  1427  	// precise op matching while retaining good error messages.
  1428  	match := func(x opData, strict bool, archname string) bool {
  1429  		if x.name != s {
  1430  			return false
  1431  		}
  1432  		if x.argLength != -1 && int(x.argLength) != len(args) && (len(args) != 1 || args[0] != "...") {
  1433  			if strict {
  1434  				return false
  1435  			}
  1436  			log.Printf("%s: op %s (%s) should have %d args, has %d", loc, s, archname, x.argLength, len(args))
  1437  		}
  1438  		return true
  1439  	}
  1440  
  1441  	for _, x := range genericOps {
  1442  		if match(x, true, "generic") {
  1443  			op = x
  1444  			break
  1445  		}
  1446  	}
  1447  	for _, x := range arch.ops {
  1448  		if arch.name != "generic" && match(x, true, arch.name) {
  1449  			if op.name != "" {
  1450  				log.Fatalf("%s: matches for op %s found in both generic and %s", loc, op.name, arch.name)
  1451  			}
  1452  			op = x
  1453  			oparch = arch.name
  1454  			break
  1455  		}
  1456  	}
  1457  
  1458  	if op.name == "" {
  1459  		// Failed to find the op.
  1460  		// Run through everything again with strict=false
  1461  		// to generate useful diagnostic messages before failing.
  1462  		for _, x := range genericOps {
  1463  			match(x, false, "generic")
  1464  		}
  1465  		for _, x := range arch.ops {
  1466  			match(x, false, arch.name)
  1467  		}
  1468  		log.Fatalf("%s: unknown op %s", loc, s)
  1469  	}
  1470  
  1471  	// Sanity check aux, auxint.
  1472  	if auxint != "" && !opHasAuxInt(op) {
  1473  		log.Fatalf("%s: op %s %s can't have auxint", loc, op.name, op.aux)
  1474  	}
  1475  	if aux != "" && !opHasAux(op) {
  1476  		log.Fatalf("%s: op %s %s can't have aux", loc, op.name, op.aux)
  1477  	}
  1478  	return
  1479  }
  1480  
  1481  func opHasAuxInt(op opData) bool {
  1482  	switch op.aux {
  1483  	case "Bool", "Int8", "Int16", "Int32", "Int64", "Int128", "UInt8", "Float32", "Float64",
  1484  		"SymOff", "CallOff", "SymValAndOff", "TypSize", "ARM64BitField", "FlagConstant", "CCop",
  1485  		"PanicBoundsC", "PanicBoundsCC", "ARM64ConditionalParams", "SizeAndAlign":
  1486  		return true
  1487  	}
  1488  	return false
  1489  }
  1490  
  1491  func opHasAux(op opData) bool {
  1492  	switch op.aux {
  1493  	case "String", "Sym", "SymOff", "Call", "CallOff", "SymValAndOff", "Typ", "TypSize",
  1494  		"S390XCCMask", "S390XRotateParams", "PanicBoundsC", "PanicBoundsCC", "SizeAndAlign":
  1495  		return true
  1496  	}
  1497  	return false
  1498  }
  1499  
  1500  // splitNameExpr splits s-expr arg, possibly prefixed by "name:",
  1501  // into name and the unprefixed expression.
  1502  // For example, "x:(Foo)" yields "x", "(Foo)",
  1503  // and "(Foo)" yields "", "(Foo)".
  1504  func splitNameExpr(arg string) (name, expr string) {
  1505  	colon := strings.Index(arg, ":")
  1506  	if colon < 0 {
  1507  		return "", arg
  1508  	}
  1509  	openparen := strings.Index(arg, "(")
  1510  	if openparen < 0 {
  1511  		log.Fatalf("splitNameExpr(%q): colon but no open parens", arg)
  1512  	}
  1513  	if colon > openparen {
  1514  		// colon is inside the parens, such as in "(Foo x:(Bar))".
  1515  		return "", arg
  1516  	}
  1517  	return arg[:colon], strings.TrimSpace(arg[colon+1:])
  1518  }
  1519  
  1520  func getBlockInfo(op string, arch arch) (name string, data blockData) {
  1521  	for _, b := range genericBlocks {
  1522  		if b.name == op {
  1523  			return "block.Block" + op, b
  1524  		}
  1525  	}
  1526  	for _, b := range arch.blocks {
  1527  		if b.name == op {
  1528  			return "block.Block" + arch.name + op, b
  1529  		}
  1530  	}
  1531  	log.Fatalf("could not find block data for %s", op)
  1532  	panic("unreachable")
  1533  }
  1534  
  1535  // typeName returns the string to use to generate a type.
  1536  func typeName(typ string) string {
  1537  	if typ[0] == '(' {
  1538  		ts := strings.Split(typ[1:len(typ)-1], ",")
  1539  		if len(ts) != 2 {
  1540  			log.Fatalf("Tuple expect 2 arguments")
  1541  		}
  1542  		return "types.NewTuple(" + typeName(ts[0]) + ", " + typeName(ts[1]) + ")"
  1543  	}
  1544  	switch typ {
  1545  	case "Flags", "Mem", "Void", "Int128":
  1546  		return "types.Type" + typ
  1547  	default:
  1548  		return "typ." + typ
  1549  	}
  1550  }
  1551  
  1552  // balance returns the number of unclosed '(' characters in s.
  1553  // If a ')' appears without a corresponding '(', balance returns -1.
  1554  func balance(s string) int {
  1555  	balance := 0
  1556  	for _, c := range s {
  1557  		switch c {
  1558  		case '(':
  1559  			balance++
  1560  		case ')':
  1561  			balance--
  1562  			if balance < 0 {
  1563  				// don't allow ")(" to return 0
  1564  				return -1
  1565  			}
  1566  		}
  1567  	}
  1568  	return balance
  1569  }
  1570  
  1571  // findAllOpcode is a function to find the opcode portion of s-expressions.
  1572  var findAllOpcode = regexp.MustCompile(`[(](\w+[|])+\w+[)]`).FindAllStringIndex
  1573  
  1574  // excludeFromExpansion reports whether the substring s[idx[0]:idx[1]] in a rule
  1575  // should be disregarded as a candidate for | expansion.
  1576  // It uses simple syntactic checks to see whether the substring
  1577  // is inside an AuxInt expression or inside the && conditions.
  1578  func excludeFromExpansion(s string, idx []int) bool {
  1579  	left := s[:idx[0]]
  1580  	if strings.LastIndexByte(left, '[') > strings.LastIndexByte(left, ']') {
  1581  		// Inside an AuxInt expression.
  1582  		return true
  1583  	}
  1584  	right := s[idx[1]:]
  1585  	if strings.Contains(left, "&&") && strings.Contains(right, "=>") {
  1586  		// Inside && conditions.
  1587  		return true
  1588  	}
  1589  	return false
  1590  }
  1591  
  1592  // expandOr converts a rule into multiple rules by expanding | ops.
  1593  func expandOr(r string) []string {
  1594  	// Find every occurrence of |-separated things.
  1595  	// They look like MOV(B|W|L|Q|SS|SD)load or MOV(Q|L)loadidx(1|8).
  1596  	// Generate rules selecting one case from each |-form.
  1597  
  1598  	// Count width of |-forms.  They must match.
  1599  	n := 1
  1600  	for _, idx := range findAllOpcode(r, -1) {
  1601  		if excludeFromExpansion(r, idx) {
  1602  			continue
  1603  		}
  1604  		s := r[idx[0]:idx[1]]
  1605  		c := strings.Count(s, "|") + 1
  1606  		if c == 1 {
  1607  			continue
  1608  		}
  1609  		if n > 1 && n != c {
  1610  			log.Fatalf("'|' count doesn't match in %s: both %d and %d\n", r, n, c)
  1611  		}
  1612  		n = c
  1613  	}
  1614  	if n == 1 {
  1615  		// No |-form in this rule.
  1616  		return []string{r}
  1617  	}
  1618  	// Build each new rule.
  1619  	res := make([]string, n)
  1620  	for i := 0; i < n; i++ {
  1621  		buf := new(strings.Builder)
  1622  		x := 0
  1623  		for _, idx := range findAllOpcode(r, -1) {
  1624  			if excludeFromExpansion(r, idx) {
  1625  				continue
  1626  			}
  1627  			buf.WriteString(r[x:idx[0]])              // write bytes we've skipped over so far
  1628  			s := r[idx[0]+1 : idx[1]-1]               // remove leading "(" and trailing ")"
  1629  			buf.WriteString(strings.Split(s, "|")[i]) // write the op component for this rule
  1630  			x = idx[1]                                // note that we've written more bytes
  1631  		}
  1632  		buf.WriteString(r[x:])
  1633  		res[i] = buf.String()
  1634  	}
  1635  	return res
  1636  }
  1637  
  1638  // varCount returns a map which counts the number of occurrences of
  1639  // Value variables in the s-expression rr.Match and the Go expression rr.Cond.
  1640  func varCount(rr *RuleRewrite) map[string]int {
  1641  	cnt := map[string]int{}
  1642  	varCount1(rr.Loc, rr.Match, cnt)
  1643  	if rr.Cond != "" {
  1644  		expr, err := parser.ParseExpr(rr.Cond)
  1645  		if err != nil {
  1646  			log.Fatalf("%s: failed to parse cond %q: %v", rr.Loc, rr.Cond, err)
  1647  		}
  1648  		ast.Inspect(expr, func(n ast.Node) bool {
  1649  			if id, ok := n.(*ast.Ident); ok {
  1650  				cnt[id.Name]++
  1651  			}
  1652  			return true
  1653  		})
  1654  	}
  1655  	return cnt
  1656  }
  1657  
  1658  func varCount1(loc, m string, cnt map[string]int) {
  1659  	if m[0] == '<' || m[0] == '[' || m[0] == '{' {
  1660  		return
  1661  	}
  1662  	if token.IsIdentifier(m) {
  1663  		cnt[m]++
  1664  		return
  1665  	}
  1666  	// Split up input.
  1667  	name, expr := splitNameExpr(m)
  1668  	if name != "" {
  1669  		cnt[name]++
  1670  	}
  1671  	if expr[0] != '(' || expr[len(expr)-1] != ')' {
  1672  		log.Fatalf("%s: non-compound expr in varCount1: %q", loc, expr)
  1673  	}
  1674  	s := split(expr[1 : len(expr)-1])
  1675  	for _, arg := range s[1:] {
  1676  		varCount1(loc, arg, cnt)
  1677  	}
  1678  }
  1679  
  1680  // normalizeWhitespace replaces 2+ whitespace sequences with a single space.
  1681  func normalizeWhitespace(x string) string {
  1682  	x = strings.Join(strings.Fields(x), " ")
  1683  	x = strings.ReplaceAll(x, "( ", "(")
  1684  	x = strings.ReplaceAll(x, " )", ")")
  1685  	x = strings.ReplaceAll(x, "[ ", "[")
  1686  	x = strings.ReplaceAll(x, " ]", "]")
  1687  	x = strings.ReplaceAll(x, ")=>", ") =>")
  1688  	return x
  1689  }
  1690  
  1691  // opIsCommutative reports whether op s is commutative.
  1692  func opIsCommutative(op string, arch arch) bool {
  1693  	for _, x := range genericOps {
  1694  		if op == x.name {
  1695  			if x.commutative {
  1696  				return true
  1697  			}
  1698  			break
  1699  		}
  1700  	}
  1701  	if arch.name != "generic" {
  1702  		for _, x := range arch.ops {
  1703  			if op == x.name {
  1704  				if x.commutative {
  1705  					return true
  1706  				}
  1707  				break
  1708  			}
  1709  		}
  1710  	}
  1711  	return false
  1712  }
  1713  
  1714  func normalizeMatch(m string, arch arch) string {
  1715  	if token.IsIdentifier(m) {
  1716  		return m
  1717  	}
  1718  	op, typ, auxint, aux, args := extract(m)
  1719  	if opIsCommutative(op, arch) {
  1720  		if args[1] < args[0] {
  1721  			args[0], args[1] = args[1], args[0]
  1722  		}
  1723  	}
  1724  	s := new(strings.Builder)
  1725  	fmt.Fprintf(s, "%s <%s> [%s] {%s}", op, typ, auxint, aux)
  1726  	for _, arg := range args {
  1727  		prefix, expr := splitNameExpr(arg)
  1728  		fmt.Fprint(s, " ", prefix, normalizeMatch(expr, arch))
  1729  	}
  1730  	return s.String()
  1731  }
  1732  
  1733  func parseEllipsisRules(rules []Rule, arch arch) (newop string, ok bool) {
  1734  	if len(rules) != 1 {
  1735  		for _, r := range rules {
  1736  			if strings.Contains(r.Rule, "...") {
  1737  				log.Fatalf("%s: found ellipsis in rule, but there are other rules with the same op", r.Loc)
  1738  			}
  1739  		}
  1740  		return "", false
  1741  	}
  1742  	rule := rules[0]
  1743  	match, cond, result := rule.parse()
  1744  	if cond != "" || !isEllipsisValue(match) || !isEllipsisValue(result) {
  1745  		if strings.Contains(rule.Rule, "...") {
  1746  			log.Fatalf("%s: found ellipsis in non-ellipsis rule", rule.Loc)
  1747  		}
  1748  		checkEllipsisRuleCandidate(rule, arch)
  1749  		return "", false
  1750  	}
  1751  	op, oparch, _, _, _, _ := parseValue(result, arch, rule.Loc)
  1752  	return fmt.Sprintf("Op%s%s", oparch, op.name), true
  1753  }
  1754  
  1755  // isEllipsisValue reports whether s is of the form (OpX ...).
  1756  func isEllipsisValue(s string) bool {
  1757  	if len(s) < 2 || s[0] != '(' || s[len(s)-1] != ')' {
  1758  		return false
  1759  	}
  1760  	c := split(s[1 : len(s)-1])
  1761  	if len(c) != 2 || c[1] != "..." {
  1762  		return false
  1763  	}
  1764  	return true
  1765  }
  1766  
  1767  func checkEllipsisRuleCandidate(rule Rule, arch arch) {
  1768  	match, cond, result := rule.parse()
  1769  	if cond != "" {
  1770  		return
  1771  	}
  1772  	op, _, _, auxint, aux, args := parseValue(match, arch, rule.Loc)
  1773  	var auxint2, aux2 string
  1774  	var args2 []string
  1775  	var usingCopy string
  1776  	var eop opData
  1777  	if result[0] != '(' {
  1778  		// Check for (Foo x) => x, which can be converted to (Foo ...) => (Copy ...).
  1779  		args2 = []string{result}
  1780  		usingCopy = " using Copy"
  1781  	} else {
  1782  		eop, _, _, auxint2, aux2, args2 = parseValue(result, arch, rule.Loc)
  1783  	}
  1784  	// Check that all restrictions in match are reproduced exactly in result.
  1785  	if aux != aux2 || auxint != auxint2 || len(args) != len(args2) {
  1786  		return
  1787  	}
  1788  	if strings.Contains(rule.Rule, "=>") && op.aux != eop.aux {
  1789  		return
  1790  	}
  1791  	for i := range args {
  1792  		if args[i] != args2[i] {
  1793  			return
  1794  		}
  1795  	}
  1796  	switch {
  1797  	case opHasAux(op) && aux == "" && aux2 == "":
  1798  		fmt.Printf("%s: rule silently zeros aux, either copy aux or explicitly zero\n", rule.Loc)
  1799  	case opHasAuxInt(op) && auxint == "" && auxint2 == "":
  1800  		fmt.Printf("%s: rule silently zeros auxint, either copy auxint or explicitly zero\n", rule.Loc)
  1801  	default:
  1802  		fmt.Printf("%s: possible ellipsis rule candidate%s: %q\n", rule.Loc, usingCopy, rule.Rule)
  1803  	}
  1804  }
  1805  
  1806  func opByName(arch arch, name string) opData {
  1807  	name = name[2:]
  1808  	for _, x := range genericOps {
  1809  		if name == x.name {
  1810  			return x
  1811  		}
  1812  	}
  1813  	if arch.name != "generic" {
  1814  		name = name[len(arch.name):]
  1815  		for _, x := range arch.ops {
  1816  			if name == x.name {
  1817  				return x
  1818  			}
  1819  		}
  1820  	}
  1821  	log.Fatalf("failed to find op named %s in arch %s", name, arch.name)
  1822  	panic("unreachable")
  1823  }
  1824  
  1825  // auxType returns the Go type that this operation should store in its aux field.
  1826  func (op opData) auxType() string {
  1827  	switch op.aux {
  1828  	case "String":
  1829  		return "string"
  1830  	case "Sym":
  1831  		// Note: a Sym can be an *obj.LSym, a *ir.Name, or nil.
  1832  		return "Sym"
  1833  	case "SymOff":
  1834  		return "Sym"
  1835  	case "Call":
  1836  		return "Call"
  1837  	case "CallOff":
  1838  		return "Call"
  1839  	case "SymValAndOff":
  1840  		return "Sym"
  1841  	case "Typ":
  1842  		return "*types.Type"
  1843  	case "TypSize":
  1844  		return "*types.Type"
  1845  	case "S390XCCMask":
  1846  		return "s390x.CCMask"
  1847  	case "S390XRotateParams":
  1848  		return "s390x.RotateParams"
  1849  	case "PanicBoundsC":
  1850  		return "PanicBoundsC"
  1851  	case "PanicBoundsCC":
  1852  		return "PanicBoundsCC"
  1853  	case "SizeAndAlign":
  1854  		return "int64"
  1855  	default:
  1856  		return "invalid"
  1857  	}
  1858  }
  1859  
  1860  // auxIntType returns the Go type that this operation should store in its auxInt field.
  1861  func (op opData) auxIntType() string {
  1862  	switch op.aux {
  1863  	case "Bool":
  1864  		return "bool"
  1865  	case "Int8":
  1866  		return "int8"
  1867  	case "Int16":
  1868  		return "int16"
  1869  	case "Int32":
  1870  		return "int32"
  1871  	case "Int64":
  1872  		return "int64"
  1873  	case "Int128":
  1874  		return "int128"
  1875  	case "UInt8":
  1876  		return "uint8"
  1877  	case "Float32":
  1878  		return "float32"
  1879  	case "Float64":
  1880  		return "float64"
  1881  	case "CallOff":
  1882  		return "int32"
  1883  	case "SymOff":
  1884  		return "int32"
  1885  	case "SymValAndOff":
  1886  		return "ValAndOff"
  1887  	case "TypSize":
  1888  		return "int64"
  1889  	case "CCop":
  1890  		return "Op"
  1891  	case "FlagConstant":
  1892  		return "flagConstant"
  1893  	case "ARM64BitField":
  1894  		return "arm64BitField"
  1895  	case "ARM64ConditionalParams":
  1896  		return "arm64ConditionalParams"
  1897  	case "PanicBoundsC", "PanicBoundsCC":
  1898  		return "int64"
  1899  	case "SizeAndAlign":
  1900  		return "int64"
  1901  	default:
  1902  		return "invalid"
  1903  	}
  1904  }
  1905  
  1906  // auxType returns the Go type that this block should store in its aux field.
  1907  func (b blockData) auxType() string {
  1908  	switch b.aux {
  1909  	case "Sym":
  1910  		return "Sym"
  1911  	case "S390XCCMask", "S390XCCMaskInt8", "S390XCCMaskUint8":
  1912  		return "s390x.CCMask"
  1913  	case "S390XRotateParams":
  1914  		return "s390x.RotateParams"
  1915  	default:
  1916  		return "invalid"
  1917  	}
  1918  }
  1919  
  1920  // auxIntType returns the Go type that this block should store in its auxInt field.
  1921  func (b blockData) auxIntType() string {
  1922  	switch b.aux {
  1923  	case "S390XCCMaskInt8":
  1924  		return "int8"
  1925  	case "S390XCCMaskUint8":
  1926  		return "uint8"
  1927  	case "Int64":
  1928  		return "int64"
  1929  	default:
  1930  		return "invalid"
  1931  	}
  1932  }
  1933  
  1934  func title(s string) string {
  1935  	if i := strings.Index(s, "."); i >= 0 {
  1936  		switch strings.ToLower(s[:i]) {
  1937  		case "s390x": // keep arch prefix for clarity
  1938  			s = s[:i] + s[i+1:]
  1939  		default:
  1940  			s = s[i+1:]
  1941  		}
  1942  	}
  1943  	return strings.Title(s)
  1944  }
  1945  
  1946  func unTitle(s string) string {
  1947  	if i := strings.Index(s, "."); i >= 0 {
  1948  		switch strings.ToLower(s[:i]) {
  1949  		case "s390x": // keep arch prefix for clarity
  1950  			s = s[:i] + s[i+1:]
  1951  		default:
  1952  			s = s[i+1:]
  1953  		}
  1954  	}
  1955  	return strings.ToLower(s[:1]) + s[1:]
  1956  }
  1957  

View as plain text