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

View as plain text