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

View as plain text