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

View as plain text