Source file src/cmd/compile/internal/ssa/_gen/main.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  // The gen command generates Go code (in the parent directory) for all
     6  // the architecture-specific opcodes, blocks, and rewrites.
     7  package main
     8  
     9  import (
    10  	"bytes"
    11  	"flag"
    12  	"fmt"
    13  	"go/format"
    14  	"log"
    15  	"math/bits"
    16  	"os"
    17  	"path"
    18  	"regexp"
    19  	"runtime"
    20  	"runtime/pprof"
    21  	"runtime/trace"
    22  	"slices"
    23  	"sort"
    24  	"strings"
    25  	"sync"
    26  )
    27  
    28  // TODO: capitalize these types, so that we can more easily tell variable names
    29  // apart from type names, and avoid awkward func parameters like "arch arch".
    30  
    31  type arch struct {
    32  	name               string
    33  	pkg                string // obj package to import for this arch.
    34  	genfile            string // source file containing opcode code generation.
    35  	genSIMDfile        string // source file containing opcode code generation for SIMD.
    36  	ops                []opData
    37  	blocks             []blockData
    38  	regnames           []string
    39  	ParamIntRegNames   string
    40  	ParamFloatRegNames string
    41  	gpregmask          regMask
    42  	fpregmask          regMask
    43  	fp32regmask        regMask
    44  	fp64regmask        regMask
    45  	specialregmask     regMask
    46  	framepointerreg    int8
    47  	linkreg            int8
    48  	generic            bool
    49  	imports            []string
    50  }
    51  
    52  type opData struct {
    53  	name              string
    54  	reg               regInfo
    55  	asm               string
    56  	typ               string // default result type
    57  	aux               string
    58  	rematerializeable bool
    59  	argLength         int32  // number of arguments, if -1, then this operation has a variable number of arguments
    60  	commutative       bool   // this operation is commutative on its first 2 arguments (e.g. addition)
    61  	resultInArg0      bool   // (first, if a tuple) output of v and v.Args[0] must be allocated to the same register
    62  	resultNotInArgs   bool   // outputs must not be allocated to the same registers as inputs
    63  	clobberFlags      bool   // this op clobbers flags register
    64  	needIntTemp       bool   // need a temporary free integer register
    65  	call              bool   // is a function call
    66  	tailCall          bool   // is a tail call
    67  	nilCheck          bool   // this op is a nil check on arg0
    68  	faultOnNilArg0    bool   // this op will fault if arg0 is nil (and aux encodes a small offset)
    69  	faultOnNilArg1    bool   // this op will fault if arg1 is nil (and aux encodes a small offset)
    70  	hasSideEffects    bool   // for "reasons", not to be eliminated.  E.g., atomic store, #19182.
    71  	zeroWidth         bool   // op never translates into any machine code. example: copy, which may sometimes translate to machine code, is not zero-width.
    72  	unsafePoint       bool   // this op is an unsafe point, i.e. not safe for async preemption
    73  	fixedReg          bool   // this op will be assigned a fixed register
    74  	addrSinkArg0      bool   // the address in arg0 does not propagate to the result
    75  	addrSinkArg1      bool   // the address in arg1 does not propagate to the result
    76  	symEffect         string // effect this op has on symbol in aux
    77  	scale             uint8  // amd64/386 indexed load scale
    78  }
    79  
    80  type blockData struct {
    81  	name     string // the suffix for this block ("EQ", "LT", etc.)
    82  	controls int    // the number of control values this type of block requires
    83  	aux      string // the type of the Aux/AuxInt value, if any
    84  }
    85  
    86  type regInfo struct {
    87  	// inputs[i] encodes the set of registers allowed for the i'th input.
    88  	// Inputs that don't use registers (flags, memory, etc.) should be 0.
    89  	inputs []regMask
    90  	// clobbers encodes the set of registers that are overwritten by
    91  	// the instruction (other than the output registers).
    92  	clobbers regMask
    93  	// Instruction clobbers the register containing input 0.
    94  	clobbersArg0 bool
    95  	// Instruction clobbers the register containing input 1.
    96  	clobbersArg1 bool
    97  	// outputs[i] encodes the set of registers allowed for the i'th output.
    98  	outputs []regMask
    99  }
   100  
   101  type regMask uint64
   102  
   103  func (a arch) regMaskComment(r regMask) string {
   104  	var buf strings.Builder
   105  	for i := uint64(0); r != 0; i++ {
   106  		if r&1 != 0 {
   107  			if buf.Len() == 0 {
   108  				buf.WriteString(" //")
   109  			}
   110  			buf.WriteString(" ")
   111  			buf.WriteString(a.regnames[i])
   112  		}
   113  		r >>= 1
   114  	}
   115  	return buf.String()
   116  }
   117  
   118  var archs []arch
   119  
   120  var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to `file`")
   121  var memprofile = flag.String("memprofile", "", "write memory profile to `file`")
   122  var tracefile = flag.String("trace", "", "write trace to `file`")
   123  var outDir = flag.String("outdir", "..", "directory in which to write generated files")
   124  
   125  func main() {
   126  	flag.Parse()
   127  	if *cpuprofile != "" {
   128  		f, err := os.Create(*cpuprofile)
   129  		if err != nil {
   130  			log.Fatal("could not create CPU profile: ", err)
   131  		}
   132  		defer f.Close()
   133  		if err := pprof.StartCPUProfile(f); err != nil {
   134  			log.Fatal("could not start CPU profile: ", err)
   135  		}
   136  		defer pprof.StopCPUProfile()
   137  	}
   138  	if *tracefile != "" {
   139  		f, err := os.Create(*tracefile)
   140  		if err != nil {
   141  			log.Fatalf("failed to create trace output file: %v", err)
   142  		}
   143  		defer func() {
   144  			if err := f.Close(); err != nil {
   145  				log.Fatalf("failed to close trace file: %v", err)
   146  			}
   147  		}()
   148  
   149  		if err := trace.Start(f); err != nil {
   150  			log.Fatalf("failed to start trace: %v", err)
   151  		}
   152  		defer trace.Stop()
   153  	}
   154  
   155  	if *outDir != ".." {
   156  		err := os.MkdirAll(*outDir, 0755)
   157  		if err != nil {
   158  			log.Fatalf("failed to create output directory: %v", err)
   159  		}
   160  	}
   161  
   162  	slices.SortFunc(archs, func(a, b arch) int {
   163  		return strings.Compare(a.name, b.name)
   164  	})
   165  
   166  	// The generate tasks are run concurrently, since they are CPU-intensive
   167  	// that can easily make use of many cores on a machine.
   168  	//
   169  	// Note that there is no limit on the concurrency at the moment. On a
   170  	// four-core laptop at the time of writing, peak RSS usually reaches
   171  	// ~200MiB, which seems doable by practically any machine nowadays. If
   172  	// that stops being the case, we can cap this func to a fixed number of
   173  	// architectures being generated at once.
   174  
   175  	tasks := []func(){
   176  		genOp,
   177  		genAllocators,
   178  	}
   179  	for _, a := range archs {
   180  		a := a // the funcs are ran concurrently at a later time
   181  		tasks = append(tasks, func() {
   182  			genRules(a)
   183  			genSplitLoadRules(a)
   184  			genLateLowerRules(a)
   185  		})
   186  	}
   187  	var wg sync.WaitGroup
   188  	for _, task := range tasks {
   189  		task := task
   190  		wg.Add(1)
   191  		go func() {
   192  			task()
   193  			wg.Done()
   194  		}()
   195  	}
   196  	wg.Wait()
   197  
   198  	if *memprofile != "" {
   199  		f, err := os.Create(*memprofile)
   200  		if err != nil {
   201  			log.Fatal("could not create memory profile: ", err)
   202  		}
   203  		defer f.Close()
   204  		runtime.GC() // get up-to-date statistics
   205  		if err := pprof.WriteHeapProfile(f); err != nil {
   206  			log.Fatal("could not write memory profile: ", err)
   207  		}
   208  	}
   209  }
   210  
   211  func outFile(file string) string {
   212  	return *outDir + "/" + file
   213  }
   214  
   215  func genOp() {
   216  	w := new(bytes.Buffer)
   217  	fmt.Fprintf(w, "// Code generated from _gen/*Ops.go using 'go generate'; DO NOT EDIT.\n")
   218  	fmt.Fprintln(w)
   219  	fmt.Fprintln(w, "package ssa")
   220  
   221  	fmt.Fprintln(w, "import (")
   222  	fmt.Fprintln(w, "\"cmd/internal/obj\"")
   223  	for _, a := range archs {
   224  		if a.pkg != "" {
   225  			fmt.Fprintf(w, "%q\n", a.pkg)
   226  		}
   227  	}
   228  	fmt.Fprintln(w, ")")
   229  
   230  	// generate Block* declarations
   231  	fmt.Fprintln(w, "const (")
   232  	fmt.Fprintln(w, "BlockInvalid BlockKind = iota")
   233  	for _, a := range archs {
   234  		fmt.Fprintln(w)
   235  		for _, d := range a.blocks {
   236  			fmt.Fprintf(w, "Block%s%s\n", a.Name(), d.name)
   237  		}
   238  	}
   239  	fmt.Fprintln(w, ")")
   240  
   241  	// generate block kind string method
   242  	fmt.Fprintln(w, "var blockString = [...]string{")
   243  	fmt.Fprintln(w, "BlockInvalid:\"BlockInvalid\",")
   244  	for _, a := range archs {
   245  		fmt.Fprintln(w)
   246  		for _, b := range a.blocks {
   247  			fmt.Fprintf(w, "Block%s%s:\"%s\",\n", a.Name(), b.name, b.name)
   248  		}
   249  	}
   250  	fmt.Fprintln(w, "}")
   251  	fmt.Fprintln(w, "func (k BlockKind) String() string {return blockString[k]}")
   252  
   253  	// generate block kind auxint method
   254  	fmt.Fprintln(w, "func (k BlockKind) AuxIntType() string {")
   255  	fmt.Fprintln(w, "switch k {")
   256  	for _, a := range archs {
   257  		for _, b := range a.blocks {
   258  			if b.auxIntType() == "invalid" {
   259  				continue
   260  			}
   261  			fmt.Fprintf(w, "case Block%s%s: return \"%s\"\n", a.Name(), b.name, b.auxIntType())
   262  		}
   263  	}
   264  	fmt.Fprintln(w, "}")
   265  	fmt.Fprintln(w, "return \"\"")
   266  	fmt.Fprintln(w, "}")
   267  
   268  	// generate Op* declarations
   269  	fmt.Fprintln(w, "const (")
   270  	fmt.Fprintln(w, "OpInvalid Op = iota") // make sure OpInvalid is 0.
   271  	for _, a := range archs {
   272  		fmt.Fprintln(w)
   273  		for _, v := range a.ops {
   274  			if v.name == "Invalid" {
   275  				continue
   276  			}
   277  			fmt.Fprintf(w, "Op%s%s\n", a.Name(), v.name)
   278  		}
   279  	}
   280  	fmt.Fprintln(w, ")")
   281  
   282  	// generate OpInfo table
   283  	fmt.Fprintln(w, "var opcodeTable = [...]opInfo{")
   284  	fmt.Fprintln(w, " { name: \"OpInvalid\" },")
   285  	for _, a := range archs {
   286  		fmt.Fprintln(w)
   287  
   288  		pkg := path.Base(a.pkg)
   289  		for _, v := range a.ops {
   290  			if v.name == "Invalid" {
   291  				continue
   292  			}
   293  			fmt.Fprintln(w, "{")
   294  			fmt.Fprintf(w, "name:\"%s\",\n", v.name)
   295  
   296  			// flags
   297  			if v.aux != "" {
   298  				fmt.Fprintf(w, "auxType: aux%s,\n", v.aux)
   299  			}
   300  			fmt.Fprintf(w, "argLen: %d,\n", v.argLength)
   301  
   302  			if v.rematerializeable {
   303  				if v.reg.clobbers != 0 || v.reg.clobbersArg0 || v.reg.clobbersArg1 {
   304  					log.Fatalf("%s is rematerializeable and clobbers registers", v.name)
   305  				}
   306  				if v.clobberFlags {
   307  					log.Fatalf("%s is rematerializeable and clobbers flags", v.name)
   308  				}
   309  				fmt.Fprintln(w, "rematerializeable: true,")
   310  			}
   311  			if v.commutative {
   312  				fmt.Fprintln(w, "commutative: true,")
   313  			}
   314  			if v.resultInArg0 {
   315  				fmt.Fprintln(w, "resultInArg0: true,")
   316  				// OpConvert's register mask is selected dynamically,
   317  				// so don't try to check it in the static table.
   318  				if v.name != "Convert" && v.reg.inputs[0] != v.reg.outputs[0] {
   319  					log.Fatalf("%s: input[0] and output[0] must use the same registers for %s", a.name, v.name)
   320  				}
   321  				if v.name != "Convert" && v.commutative && v.reg.inputs[1] != v.reg.outputs[0] {
   322  					log.Fatalf("%s: input[1] and output[0] must use the same registers for %s", a.name, v.name)
   323  				}
   324  			}
   325  			if v.resultNotInArgs {
   326  				fmt.Fprintln(w, "resultNotInArgs: true,")
   327  			}
   328  			if v.clobberFlags {
   329  				fmt.Fprintln(w, "clobberFlags: true,")
   330  			}
   331  			if v.needIntTemp {
   332  				fmt.Fprintln(w, "needIntTemp: true,")
   333  			}
   334  			if v.call {
   335  				fmt.Fprintln(w, "call: true,")
   336  			}
   337  			if v.tailCall {
   338  				fmt.Fprintln(w, "tailCall: true,")
   339  			}
   340  			if v.nilCheck {
   341  				fmt.Fprintln(w, "nilCheck: true,")
   342  			}
   343  			if v.faultOnNilArg0 {
   344  				fmt.Fprintln(w, "faultOnNilArg0: true,")
   345  				if v.aux != "Sym" && v.aux != "SymOff" && v.aux != "SymValAndOff" && v.aux != "Int64" && v.aux != "Int32" && v.aux != "" {
   346  					log.Fatalf("faultOnNilArg0 with aux %s not allowed", v.aux)
   347  				}
   348  			}
   349  			if v.faultOnNilArg1 {
   350  				fmt.Fprintln(w, "faultOnNilArg1: true,")
   351  				if v.aux != "Sym" && v.aux != "SymOff" && v.aux != "SymValAndOff" && v.aux != "Int64" && v.aux != "Int32" && v.aux != "" {
   352  					log.Fatalf("faultOnNilArg1 with aux %s not allowed", v.aux)
   353  				}
   354  			}
   355  			if v.hasSideEffects {
   356  				fmt.Fprintln(w, "hasSideEffects: true,")
   357  			}
   358  			if v.zeroWidth {
   359  				fmt.Fprintln(w, "zeroWidth: true,")
   360  			}
   361  			if v.fixedReg {
   362  				fmt.Fprintln(w, "fixedReg: true,")
   363  			}
   364  			if v.addrSinkArg0 {
   365  				fmt.Fprintln(w, "addrSinkArg0: true,")
   366  			}
   367  			if v.addrSinkArg1 {
   368  				fmt.Fprintln(w, "addrSinkArg1: true,")
   369  			}
   370  			if v.unsafePoint {
   371  				fmt.Fprintln(w, "unsafePoint: true,")
   372  			}
   373  			needEffect := strings.HasPrefix(v.aux, "Sym")
   374  			if v.symEffect != "" {
   375  				if !needEffect {
   376  					log.Fatalf("symEffect with aux %s not allowed", v.aux)
   377  				}
   378  				fmt.Fprintf(w, "symEffect: Sym%s,\n", strings.ReplaceAll(v.symEffect, ",", "|Sym"))
   379  			} else if needEffect {
   380  				log.Fatalf("symEffect needed for aux %s", v.aux)
   381  			}
   382  			if a.name == "generic" {
   383  				fmt.Fprintln(w, "generic:true,")
   384  				fmt.Fprintln(w, "},") // close op
   385  				// generic ops have no reg info or asm
   386  				continue
   387  			}
   388  			if v.asm != "" {
   389  				fmt.Fprintf(w, "asm: %s.A%s,\n", pkg, v.asm)
   390  			}
   391  			if v.scale != 0 {
   392  				fmt.Fprintf(w, "scale: %d,\n", v.scale)
   393  			}
   394  			fmt.Fprintln(w, "reg:regInfo{")
   395  
   396  			// Compute input allocation order. We allocate from the
   397  			// most to the least constrained input. This order guarantees
   398  			// that we will always be able to find a register.
   399  			var s []intPair
   400  			for i, r := range v.reg.inputs {
   401  				if r != 0 {
   402  					s = append(s, intPair{countRegs(r), i})
   403  				}
   404  			}
   405  			if len(s) > 0 {
   406  				sort.Sort(byKey(s))
   407  				fmt.Fprintln(w, "inputs: []inputInfo{")
   408  				for _, p := range s {
   409  					r := v.reg.inputs[p.val]
   410  					fmt.Fprintf(w, "{%d,%d},%s\n", p.val, r, a.regMaskComment(r))
   411  				}
   412  				fmt.Fprintln(w, "},")
   413  			}
   414  
   415  			if v.reg.clobbers > 0 {
   416  				fmt.Fprintf(w, "clobbers: %d,%s\n", v.reg.clobbers, a.regMaskComment(v.reg.clobbers))
   417  			}
   418  			if v.reg.clobbersArg0 {
   419  				fmt.Fprintf(w, "clobbersArg0: true,\n")
   420  			}
   421  			if v.reg.clobbersArg1 {
   422  				fmt.Fprintf(w, "clobbersArg1: true,\n")
   423  			}
   424  
   425  			// reg outputs
   426  			s = s[:0]
   427  			for i, r := range v.reg.outputs {
   428  				s = append(s, intPair{countRegs(r), i})
   429  			}
   430  			if len(s) > 0 {
   431  				sort.Sort(byKey(s))
   432  				fmt.Fprintln(w, "outputs: []outputInfo{")
   433  				for _, p := range s {
   434  					r := v.reg.outputs[p.val]
   435  					fmt.Fprintf(w, "{%d,%d},%s\n", p.val, r, a.regMaskComment(r))
   436  				}
   437  				fmt.Fprintln(w, "},")
   438  			}
   439  			fmt.Fprintln(w, "},") // close reg info
   440  			fmt.Fprintln(w, "},") // close op
   441  		}
   442  	}
   443  	fmt.Fprintln(w, "}")
   444  
   445  	fmt.Fprintln(w, "func (o Op) Asm() obj.As {return opcodeTable[o].asm}")
   446  	fmt.Fprintln(w, "func (o Op) Scale() int16 {return int16(opcodeTable[o].scale)}")
   447  
   448  	// generate op string method
   449  	fmt.Fprintln(w, "func (o Op) String() string {return opcodeTable[o].name }")
   450  
   451  	fmt.Fprintln(w, "func (o Op) SymEffect() SymEffect { return opcodeTable[o].symEffect }")
   452  	fmt.Fprintln(w, "func (o Op) IsCall() bool { return opcodeTable[o].call }")
   453  	fmt.Fprintln(w, "func (o Op) IsTailCall() bool { return opcodeTable[o].tailCall }")
   454  	fmt.Fprintln(w, "func (o Op) HasSideEffects() bool { return opcodeTable[o].hasSideEffects }")
   455  	fmt.Fprintln(w, "func (o Op) UnsafePoint() bool { return opcodeTable[o].unsafePoint }")
   456  	fmt.Fprintln(w, "func (o Op) ResultInArg0() bool { return opcodeTable[o].resultInArg0 }")
   457  
   458  	// generate registers
   459  	for _, a := range archs {
   460  		if a.generic {
   461  			continue
   462  		}
   463  		fmt.Fprintf(w, "var registers%s = [...]Register {\n", a.name)
   464  		num := map[string]int8{}
   465  		for i, r := range a.regnames {
   466  			num[r] = int8(i)
   467  			pkg := a.pkg[len("cmd/internal/obj/"):]
   468  			var objname string // name in cmd/internal/obj/$ARCH
   469  			switch r {
   470  			case "SB":
   471  				// SB isn't a real register.  cmd/internal/obj expects 0 in this case.
   472  				objname = "0"
   473  			case "SP":
   474  				objname = pkg + ".REGSP"
   475  			case "g":
   476  				objname = pkg + ".REGG"
   477  			case "ZERO":
   478  				objname = pkg + ".REGZERO"
   479  			default:
   480  				objname = pkg + ".REG_" + r
   481  			}
   482  			fmt.Fprintf(w, "  {%d, %s, \"%s\"},\n", i, objname, r)
   483  		}
   484  		parameterRegisterList := func(paramNamesString string) []int8 {
   485  			paramNamesString = strings.TrimSpace(paramNamesString)
   486  			if paramNamesString == "" {
   487  				return nil
   488  			}
   489  			paramNames := strings.Split(paramNamesString, " ")
   490  			var paramRegs []int8
   491  			for _, regName := range paramNames {
   492  				if regName == "" {
   493  					// forgive extra spaces
   494  					continue
   495  				}
   496  				if regNum, ok := num[regName]; ok {
   497  					paramRegs = append(paramRegs, regNum)
   498  					delete(num, regName)
   499  				} else {
   500  					log.Fatalf("parameter register %s for architecture %s not a register name (or repeated in parameter list)", regName, a.name)
   501  				}
   502  			}
   503  			return paramRegs
   504  		}
   505  
   506  		paramIntRegs := parameterRegisterList(a.ParamIntRegNames)
   507  		paramFloatRegs := parameterRegisterList(a.ParamFloatRegNames)
   508  
   509  		fmt.Fprintln(w, "}")
   510  		fmt.Fprintf(w, "var paramIntReg%s = %#v\n", a.name, paramIntRegs)
   511  		fmt.Fprintf(w, "var paramFloatReg%s = %#v\n", a.name, paramFloatRegs)
   512  		fmt.Fprintf(w, "var gpRegMask%s = regMask(%d)\n", a.name, a.gpregmask)
   513  		fmt.Fprintf(w, "var fpRegMask%s = regMask(%d)\n", a.name, a.fpregmask)
   514  		if a.fp32regmask != 0 {
   515  			fmt.Fprintf(w, "var fp32RegMask%s = regMask(%d)\n", a.name, a.fp32regmask)
   516  		}
   517  		if a.fp64regmask != 0 {
   518  			fmt.Fprintf(w, "var fp64RegMask%s = regMask(%d)\n", a.name, a.fp64regmask)
   519  		}
   520  		fmt.Fprintf(w, "var specialRegMask%s = regMask(%d)\n", a.name, a.specialregmask)
   521  		fmt.Fprintf(w, "var framepointerReg%s = int8(%d)\n", a.name, a.framepointerreg)
   522  		fmt.Fprintf(w, "var linkReg%s = int8(%d)\n", a.name, a.linkreg)
   523  	}
   524  
   525  	// gofmt result
   526  	b := w.Bytes()
   527  	var err error
   528  	b, err = format.Source(b)
   529  	if err != nil {
   530  		fmt.Printf("%s\n", w.Bytes())
   531  		panic(err)
   532  	}
   533  
   534  	if err := os.WriteFile(outFile("opGen.go"), b, 0666); err != nil {
   535  		log.Fatalf("can't write output: %v\n", err)
   536  	}
   537  
   538  	// Check that the arch genfile handles all the arch-specific opcodes.
   539  	// This is very much a hack, but it is better than nothing.
   540  	//
   541  	// Do a single regexp pass to record all ops being handled in a map, and
   542  	// then compare that with the ops list. This is much faster than one
   543  	// regexp pass per opcode.
   544  	for _, a := range archs {
   545  		if a.genfile == "" {
   546  			continue
   547  		}
   548  
   549  		pattern := fmt.Sprintf(`\Wssa\.Op%s([a-zA-Z0-9_]+)\W`, a.name)
   550  		rxOp, err := regexp.Compile(pattern)
   551  		if err != nil {
   552  			log.Fatalf("bad opcode regexp %s: %v", pattern, err)
   553  		}
   554  
   555  		src, err := os.ReadFile(a.genfile)
   556  		if err != nil {
   557  			log.Fatalf("can't read %s: %v", a.genfile, err)
   558  		}
   559  		// Append the file of simd operations, too
   560  		if a.genSIMDfile != "" {
   561  			simdSrc, err := os.ReadFile(a.genSIMDfile)
   562  			if err != nil {
   563  				log.Fatalf("can't read %s: %v", a.genSIMDfile, err)
   564  			}
   565  			src = append(src, simdSrc...)
   566  		}
   567  
   568  		seen := make(map[string]bool, len(a.ops))
   569  		for _, m := range rxOp.FindAllSubmatch(src, -1) {
   570  			seen[string(m[1])] = true
   571  		}
   572  		for _, op := range a.ops {
   573  			if !seen[op.name] {
   574  				log.Fatalf("Op%s%s has no code generation in %s", a.name, op.name, a.genfile)
   575  			}
   576  		}
   577  	}
   578  }
   579  
   580  // Name returns the name of the architecture for use in Op* and Block* enumerations.
   581  func (a arch) Name() string {
   582  	s := a.name
   583  	if s == "generic" {
   584  		s = ""
   585  	}
   586  	return s
   587  }
   588  
   589  // countRegs returns the number of set bits in the register mask.
   590  func countRegs(r regMask) int {
   591  	return bits.OnesCount64(uint64(r))
   592  }
   593  
   594  // for sorting a pair of integers by key
   595  type intPair struct {
   596  	key, val int
   597  }
   598  type byKey []intPair
   599  
   600  func (a byKey) Len() int           { return len(a) }
   601  func (a byKey) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
   602  func (a byKey) Less(i, j int) bool { return a[i].key < a[j].key }
   603  

View as plain text