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  //go:build ignore
     6  // +build ignore
     7  
     8  // The gen command generates Go code (in the parent directory) for all
     9  // the architecture-specific opcodes, blocks, and rewrites.
    10  package main
    11  
    12  import (
    13  	"bytes"
    14  	"flag"
    15  	"fmt"
    16  	"go/format"
    17  	"io/ioutil"
    18  	"log"
    19  	"os"
    20  	"path"
    21  	"regexp"
    22  	"runtime"
    23  	"runtime/pprof"
    24  	"runtime/trace"
    25  	"sort"
    26  	"strings"
    27  	"sync"
    28  )
    29  
    30  // TODO: capitalize these types, so that we can more easily tell variable names
    31  // apart from type names, and avoid awkward func parameters like "arch arch".
    32  
    33  type arch struct {
    34  	name               string
    35  	pkg                string // obj package to import for this arch.
    36  	genfile            string // source file containing opcode code generation.
    37  	ops                []opData
    38  	blocks             []blockData
    39  	regnames           []string
    40  	ParamIntRegNames   string
    41  	ParamFloatRegNames string
    42  	gpregmask          regMask
    43  	fpregmask          regMask
    44  	fp32regmask        regMask
    45  	fp64regmask        regMask
    46  	specialregmask     regMask
    47  	framepointerreg    int8
    48  	linkreg            int8
    49  	generic            bool
    50  	imports            []string
    51  }
    52  
    53  type opData struct {
    54  	name              string
    55  	reg               regInfo
    56  	asm               string
    57  	typ               string // default result type
    58  	aux               string
    59  	rematerializeable bool
    60  	argLength         int32  // number of arguments, if -1, then this operation has a variable number of arguments
    61  	commutative       bool   // this operation is commutative on its first 2 arguments (e.g. addition)
    62  	resultInArg0      bool   // (first, if a tuple) output of v and v.Args[0] must be allocated to the same register
    63  	resultNotInArgs   bool   // outputs must not be allocated to the same registers as inputs
    64  	clobberFlags      bool   // this op clobbers flags 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  	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 bytes.Buffer
    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  	sort.Sort(ArchsByName(archs))
   148  
   149  	// The generate tasks are run concurrently, since they are CPU-intensive
   150  	// that can easily make use of many cores on a machine.
   151  	//
   152  	// Note that there is no limit on the concurrency at the moment. On a
   153  	// four-core laptop at the time of writing, peak RSS usually reaches
   154  	// ~200MiB, which seems doable by practically any machine nowadays. If
   155  	// that stops being the case, we can cap this func to a fixed number of
   156  	// architectures being generated at once.
   157  
   158  	tasks := []func(){
   159  		genOp,
   160  	}
   161  	for _, a := range archs {
   162  		a := a // the funcs are ran concurrently at a later time
   163  		tasks = append(tasks, func() {
   164  			genRules(a)
   165  			genSplitLoadRules(a)
   166  		})
   167  	}
   168  	var wg sync.WaitGroup
   169  	for _, task := range tasks {
   170  		task := task
   171  		wg.Add(1)
   172  		go func() {
   173  			task()
   174  			wg.Done()
   175  		}()
   176  	}
   177  	wg.Wait()
   178  
   179  	if *memprofile != "" {
   180  		f, err := os.Create(*memprofile)
   181  		if err != nil {
   182  			log.Fatal("could not create memory profile: ", err)
   183  		}
   184  		defer f.Close()
   185  		runtime.GC() // get up-to-date statistics
   186  		if err := pprof.WriteHeapProfile(f); err != nil {
   187  			log.Fatal("could not write memory profile: ", err)
   188  		}
   189  	}
   190  }
   191  
   192  func genOp() {
   193  	w := new(bytes.Buffer)
   194  	fmt.Fprintf(w, "// Code generated from gen/*Ops.go; DO NOT EDIT.\n")
   195  	fmt.Fprintln(w)
   196  	fmt.Fprintln(w, "package ssa")
   197  
   198  	fmt.Fprintln(w, "import (")
   199  	fmt.Fprintln(w, "\"cmd/internal/obj\"")
   200  	for _, a := range archs {
   201  		if a.pkg != "" {
   202  			fmt.Fprintf(w, "%q\n", a.pkg)
   203  		}
   204  	}
   205  	fmt.Fprintln(w, ")")
   206  
   207  	// generate Block* declarations
   208  	fmt.Fprintln(w, "const (")
   209  	fmt.Fprintln(w, "BlockInvalid BlockKind = iota")
   210  	for _, a := range archs {
   211  		fmt.Fprintln(w)
   212  		for _, d := range a.blocks {
   213  			fmt.Fprintf(w, "Block%s%s\n", a.Name(), d.name)
   214  		}
   215  	}
   216  	fmt.Fprintln(w, ")")
   217  
   218  	// generate block kind string method
   219  	fmt.Fprintln(w, "var blockString = [...]string{")
   220  	fmt.Fprintln(w, "BlockInvalid:\"BlockInvalid\",")
   221  	for _, a := range archs {
   222  		fmt.Fprintln(w)
   223  		for _, b := range a.blocks {
   224  			fmt.Fprintf(w, "Block%s%s:\"%s\",\n", a.Name(), b.name, b.name)
   225  		}
   226  	}
   227  	fmt.Fprintln(w, "}")
   228  	fmt.Fprintln(w, "func (k BlockKind) String() string {return blockString[k]}")
   229  
   230  	// generate block kind auxint method
   231  	fmt.Fprintln(w, "func (k BlockKind) AuxIntType() string {")
   232  	fmt.Fprintln(w, "switch k {")
   233  	for _, a := range archs {
   234  		for _, b := range a.blocks {
   235  			if b.auxIntType() == "invalid" {
   236  				continue
   237  			}
   238  			fmt.Fprintf(w, "case Block%s%s: return \"%s\"\n", a.Name(), b.name, b.auxIntType())
   239  		}
   240  	}
   241  	fmt.Fprintln(w, "}")
   242  	fmt.Fprintln(w, "return \"\"")
   243  	fmt.Fprintln(w, "}")
   244  
   245  	// generate Op* declarations
   246  	fmt.Fprintln(w, "const (")
   247  	fmt.Fprintln(w, "OpInvalid Op = iota") // make sure OpInvalid is 0.
   248  	for _, a := range archs {
   249  		fmt.Fprintln(w)
   250  		for _, v := range a.ops {
   251  			if v.name == "Invalid" {
   252  				continue
   253  			}
   254  			fmt.Fprintf(w, "Op%s%s\n", a.Name(), v.name)
   255  		}
   256  	}
   257  	fmt.Fprintln(w, ")")
   258  
   259  	// generate OpInfo table
   260  	fmt.Fprintln(w, "var opcodeTable = [...]opInfo{")
   261  	fmt.Fprintln(w, " { name: \"OpInvalid\" },")
   262  	for _, a := range archs {
   263  		fmt.Fprintln(w)
   264  
   265  		pkg := path.Base(a.pkg)
   266  		for _, v := range a.ops {
   267  			if v.name == "Invalid" {
   268  				continue
   269  			}
   270  			fmt.Fprintln(w, "{")
   271  			fmt.Fprintf(w, "name:\"%s\",\n", v.name)
   272  
   273  			// flags
   274  			if v.aux != "" {
   275  				fmt.Fprintf(w, "auxType: aux%s,\n", v.aux)
   276  			}
   277  			fmt.Fprintf(w, "argLen: %d,\n", v.argLength)
   278  
   279  			if v.rematerializeable {
   280  				if v.reg.clobbers != 0 {
   281  					log.Fatalf("%s is rematerializeable and clobbers registers", v.name)
   282  				}
   283  				if v.clobberFlags {
   284  					log.Fatalf("%s is rematerializeable and clobbers flags", v.name)
   285  				}
   286  				fmt.Fprintln(w, "rematerializeable: true,")
   287  			}
   288  			if v.commutative {
   289  				fmt.Fprintln(w, "commutative: true,")
   290  			}
   291  			if v.resultInArg0 {
   292  				fmt.Fprintln(w, "resultInArg0: true,")
   293  				// OpConvert's register mask is selected dynamically,
   294  				// so don't try to check it in the static table.
   295  				if v.name != "Convert" && v.reg.inputs[0] != v.reg.outputs[0] {
   296  					log.Fatalf("%s: input[0] and output[0] must use the same registers for %s", a.name, v.name)
   297  				}
   298  				if v.name != "Convert" && v.commutative && v.reg.inputs[1] != v.reg.outputs[0] {
   299  					log.Fatalf("%s: input[1] and output[0] must use the same registers for %s", a.name, v.name)
   300  				}
   301  			}
   302  			if v.resultNotInArgs {
   303  				fmt.Fprintln(w, "resultNotInArgs: true,")
   304  			}
   305  			if v.clobberFlags {
   306  				fmt.Fprintln(w, "clobberFlags: true,")
   307  			}
   308  			if v.call {
   309  				fmt.Fprintln(w, "call: true,")
   310  			}
   311  			if v.tailCall {
   312  				fmt.Fprintln(w, "tailCall: true,")
   313  			}
   314  			if v.nilCheck {
   315  				fmt.Fprintln(w, "nilCheck: true,")
   316  			}
   317  			if v.faultOnNilArg0 {
   318  				fmt.Fprintln(w, "faultOnNilArg0: true,")
   319  				if v.aux != "Sym" && v.aux != "SymOff" && v.aux != "SymValAndOff" && v.aux != "Int64" && v.aux != "Int32" && v.aux != "" {
   320  					log.Fatalf("faultOnNilArg0 with aux %s not allowed", v.aux)
   321  				}
   322  			}
   323  			if v.faultOnNilArg1 {
   324  				fmt.Fprintln(w, "faultOnNilArg1: true,")
   325  				if v.aux != "Sym" && v.aux != "SymOff" && v.aux != "SymValAndOff" && v.aux != "Int64" && v.aux != "Int32" && v.aux != "" {
   326  					log.Fatalf("faultOnNilArg1 with aux %s not allowed", v.aux)
   327  				}
   328  			}
   329  			if v.hasSideEffects {
   330  				fmt.Fprintln(w, "hasSideEffects: true,")
   331  			}
   332  			if v.zeroWidth {
   333  				fmt.Fprintln(w, "zeroWidth: true,")
   334  			}
   335  			if v.unsafePoint {
   336  				fmt.Fprintln(w, "unsafePoint: true,")
   337  			}
   338  			needEffect := strings.HasPrefix(v.aux, "Sym")
   339  			if v.symEffect != "" {
   340  				if !needEffect {
   341  					log.Fatalf("symEffect with aux %s not allowed", v.aux)
   342  				}
   343  				fmt.Fprintf(w, "symEffect: Sym%s,\n", strings.Replace(v.symEffect, ",", "|Sym", -1))
   344  			} else if needEffect {
   345  				log.Fatalf("symEffect needed for aux %s", v.aux)
   346  			}
   347  			if a.name == "generic" {
   348  				fmt.Fprintln(w, "generic:true,")
   349  				fmt.Fprintln(w, "},") // close op
   350  				// generic ops have no reg info or asm
   351  				continue
   352  			}
   353  			if v.asm != "" {
   354  				fmt.Fprintf(w, "asm: %s.A%s,\n", pkg, v.asm)
   355  			}
   356  			if v.scale != 0 {
   357  				fmt.Fprintf(w, "scale: %d,\n", v.scale)
   358  			}
   359  			fmt.Fprintln(w, "reg:regInfo{")
   360  
   361  			// Compute input allocation order. We allocate from the
   362  			// most to the least constrained input. This order guarantees
   363  			// that we will always be able to find a register.
   364  			var s []intPair
   365  			for i, r := range v.reg.inputs {
   366  				if r != 0 {
   367  					s = append(s, intPair{countRegs(r), i})
   368  				}
   369  			}
   370  			if len(s) > 0 {
   371  				sort.Sort(byKey(s))
   372  				fmt.Fprintln(w, "inputs: []inputInfo{")
   373  				for _, p := range s {
   374  					r := v.reg.inputs[p.val]
   375  					fmt.Fprintf(w, "{%d,%d},%s\n", p.val, r, a.regMaskComment(r))
   376  				}
   377  				fmt.Fprintln(w, "},")
   378  			}
   379  
   380  			if v.reg.clobbers > 0 {
   381  				fmt.Fprintf(w, "clobbers: %d,%s\n", v.reg.clobbers, a.regMaskComment(v.reg.clobbers))
   382  			}
   383  
   384  			// reg outputs
   385  			s = s[:0]
   386  			for i, r := range v.reg.outputs {
   387  				s = append(s, intPair{countRegs(r), i})
   388  			}
   389  			if len(s) > 0 {
   390  				sort.Sort(byKey(s))
   391  				fmt.Fprintln(w, "outputs: []outputInfo{")
   392  				for _, p := range s {
   393  					r := v.reg.outputs[p.val]
   394  					fmt.Fprintf(w, "{%d,%d},%s\n", p.val, r, a.regMaskComment(r))
   395  				}
   396  				fmt.Fprintln(w, "},")
   397  			}
   398  			fmt.Fprintln(w, "},") // close reg info
   399  			fmt.Fprintln(w, "},") // close op
   400  		}
   401  	}
   402  	fmt.Fprintln(w, "}")
   403  
   404  	fmt.Fprintln(w, "func (o Op) Asm() obj.As {return opcodeTable[o].asm}")
   405  	fmt.Fprintln(w, "func (o Op) Scale() int16 {return int16(opcodeTable[o].scale)}")
   406  
   407  	// generate op string method
   408  	fmt.Fprintln(w, "func (o Op) String() string {return opcodeTable[o].name }")
   409  
   410  	fmt.Fprintln(w, "func (o Op) SymEffect() SymEffect { return opcodeTable[o].symEffect }")
   411  	fmt.Fprintln(w, "func (o Op) IsCall() bool { return opcodeTable[o].call }")
   412  	fmt.Fprintln(w, "func (o Op) IsTailCall() bool { return opcodeTable[o].tailCall }")
   413  	fmt.Fprintln(w, "func (o Op) HasSideEffects() bool { return opcodeTable[o].hasSideEffects }")
   414  	fmt.Fprintln(w, "func (o Op) UnsafePoint() bool { return opcodeTable[o].unsafePoint }")
   415  	fmt.Fprintln(w, "func (o Op) ResultInArg0() bool { return opcodeTable[o].resultInArg0 }")
   416  
   417  	// generate registers
   418  	for _, a := range archs {
   419  		if a.generic {
   420  			continue
   421  		}
   422  		fmt.Fprintf(w, "var registers%s = [...]Register {\n", a.name)
   423  		var gcRegN int
   424  		num := map[string]int8{}
   425  		for i, r := range a.regnames {
   426  			num[r] = int8(i)
   427  			pkg := a.pkg[len("cmd/internal/obj/"):]
   428  			var objname string // name in cmd/internal/obj/$ARCH
   429  			switch r {
   430  			case "SB":
   431  				// SB isn't a real register.  cmd/internal/obj expects 0 in this case.
   432  				objname = "0"
   433  			case "SP":
   434  				objname = pkg + ".REGSP"
   435  			case "g":
   436  				objname = pkg + ".REGG"
   437  			default:
   438  				objname = pkg + ".REG_" + r
   439  			}
   440  			// Assign a GC register map index to registers
   441  			// that may contain pointers.
   442  			gcRegIdx := -1
   443  			if a.gpregmask&(1<<uint(i)) != 0 {
   444  				gcRegIdx = gcRegN
   445  				gcRegN++
   446  			}
   447  			fmt.Fprintf(w, "  {%d, %s, %d, \"%s\"},\n", i, objname, gcRegIdx, r)
   448  		}
   449  		parameterRegisterList := func(paramNamesString string) []int8 {
   450  			paramNamesString = strings.TrimSpace(paramNamesString)
   451  			if paramNamesString == "" {
   452  				return nil
   453  			}
   454  			paramNames := strings.Split(paramNamesString, " ")
   455  			var paramRegs []int8
   456  			for _, regName := range paramNames {
   457  				if regName == "" {
   458  					// forgive extra spaces
   459  					continue
   460  				}
   461  				if regNum, ok := num[regName]; ok {
   462  					paramRegs = append(paramRegs, regNum)
   463  					delete(num, regName)
   464  				} else {
   465  					log.Fatalf("parameter register %s for architecture %s not a register name (or repeated in parameter list)", regName, a.name)
   466  				}
   467  			}
   468  			return paramRegs
   469  		}
   470  
   471  		paramIntRegs := parameterRegisterList(a.ParamIntRegNames)
   472  		paramFloatRegs := parameterRegisterList(a.ParamFloatRegNames)
   473  
   474  		if gcRegN > 32 {
   475  			// Won't fit in a uint32 mask.
   476  			log.Fatalf("too many GC registers (%d > 32) on %s", gcRegN, a.name)
   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 := ioutil.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 := ioutil.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  	n := 0
   552  	for r != 0 {
   553  		n += int(r & 1)
   554  		r >>= 1
   555  	}
   556  	return n
   557  }
   558  
   559  // for sorting a pair of integers by key
   560  type intPair struct {
   561  	key, val int
   562  }
   563  type byKey []intPair
   564  
   565  func (a byKey) Len() int           { return len(a) }
   566  func (a byKey) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
   567  func (a byKey) Less(i, j int) bool { return a[i].key < a[j].key }
   568  
   569  type ArchsByName []arch
   570  
   571  func (x ArchsByName) Len() int           { return len(x) }
   572  func (x ArchsByName) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }
   573  func (x ArchsByName) Less(i, j int) bool { return x[i].name < x[j].name }
   574  

View as plain text