github.com/riscv/riscv-go@v0.0.0-20200123204226-124ebd6fcc8e/src/cmd/compile/internal/ssa/config.go (about)

     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  package ssa
     6  
     7  import (
     8  	"cmd/internal/obj"
     9  	"cmd/internal/src"
    10  	"crypto/sha1"
    11  	"fmt"
    12  	"os"
    13  	"strconv"
    14  	"strings"
    15  )
    16  
    17  type Config struct {
    18  	arch            string                     // "amd64", etc.
    19  	IntSize         int64                      // 4 or 8
    20  	PtrSize         int64                      // 4 or 8
    21  	RegSize         int64                      // 4 or 8
    22  	lowerBlock      func(*Block, *Config) bool // lowering function
    23  	lowerValue      func(*Value, *Config) bool // lowering function
    24  	registers       []Register                 // machine registers
    25  	gpRegMask       regMask                    // general purpose integer register mask
    26  	fpRegMask       regMask                    // floating point register mask
    27  	specialRegMask  regMask                    // special register mask
    28  	FPReg           int8                       // register number of frame pointer, -1 if not used
    29  	LinkReg         int8                       // register number of link register if it is a general purpose register, -1 if not used
    30  	hasGReg         bool                       // has hardware g register
    31  	fe              Frontend                   // callbacks into compiler frontend
    32  	HTML            *HTMLWriter                // html writer, for debugging
    33  	ctxt            *obj.Link                  // Generic arch information
    34  	optimize        bool                       // Do optimization
    35  	noDuffDevice    bool                       // Don't use Duff's device
    36  	nacl            bool                       // GOOS=nacl
    37  	use387          bool                       // GO386=387
    38  	OldArch         bool                       // True for older versions of architecture, e.g. true for PPC64BE, false for PPC64LE
    39  	NeedsFpScratch  bool                       // No direct move between GP and FP register sets
    40  	BigEndian       bool                       //
    41  	DebugTest       bool                       // default true unless $GOSSAHASH != ""; as a debugging aid, make new code conditional on this and use GOSSAHASH to binary search for failing cases
    42  	sparsePhiCutoff uint64                     // Sparse phi location algorithm used above this #blocks*#variables score
    43  	curFunc         *Func
    44  
    45  	// TODO: more stuff. Compiler flags of interest, ...
    46  
    47  	// Given an environment variable used for debug hash match,
    48  	// what file (if any) receives the yes/no logging?
    49  	logfiles map[string]*os.File
    50  
    51  	// Storage for low-numbered values and blocks.
    52  	values [2000]Value
    53  	blocks [200]Block
    54  	locs   [2000]Location
    55  
    56  	// Reusable stackAllocState.
    57  	// See stackalloc.go's {new,put}StackAllocState.
    58  	stackAllocState *stackAllocState
    59  
    60  	domblockstore []ID         // scratch space for computing dominators
    61  	scrSparse     []*sparseSet // scratch sparse sets to be re-used.
    62  }
    63  
    64  type TypeSource interface {
    65  	TypeBool() Type
    66  	TypeInt8() Type
    67  	TypeInt16() Type
    68  	TypeInt32() Type
    69  	TypeInt64() Type
    70  	TypeUInt8() Type
    71  	TypeUInt16() Type
    72  	TypeUInt32() Type
    73  	TypeUInt64() Type
    74  	TypeInt() Type
    75  	TypeFloat32() Type
    76  	TypeFloat64() Type
    77  	TypeUintptr() Type
    78  	TypeString() Type
    79  	TypeBytePtr() Type // TODO: use unsafe.Pointer instead?
    80  
    81  	CanSSA(t Type) bool
    82  }
    83  
    84  type Logger interface {
    85  	// Logf logs a message from the compiler.
    86  	Logf(string, ...interface{})
    87  
    88  	// Log returns true if logging is not a no-op
    89  	// some logging calls account for more than a few heap allocations.
    90  	Log() bool
    91  
    92  	// Fatal reports a compiler error and exits.
    93  	Fatalf(pos src.XPos, msg string, args ...interface{})
    94  
    95  	// Warnl writes compiler messages in the form expected by "errorcheck" tests
    96  	Warnl(pos src.XPos, fmt_ string, args ...interface{})
    97  
    98  	// Forwards the Debug flags from gc
    99  	Debug_checknil() bool
   100  	Debug_wb() bool
   101  }
   102  
   103  type Frontend interface {
   104  	TypeSource
   105  	Logger
   106  
   107  	// StringData returns a symbol pointing to the given string's contents.
   108  	StringData(string) interface{} // returns *gc.Sym
   109  
   110  	// Auto returns a Node for an auto variable of the given type.
   111  	// The SSA compiler uses this function to allocate space for spills.
   112  	Auto(Type) GCNode
   113  
   114  	// Given the name for a compound type, returns the name we should use
   115  	// for the parts of that compound type.
   116  	SplitString(LocalSlot) (LocalSlot, LocalSlot)
   117  	SplitInterface(LocalSlot) (LocalSlot, LocalSlot)
   118  	SplitSlice(LocalSlot) (LocalSlot, LocalSlot, LocalSlot)
   119  	SplitComplex(LocalSlot) (LocalSlot, LocalSlot)
   120  	SplitStruct(LocalSlot, int) LocalSlot
   121  	SplitArray(LocalSlot) LocalSlot              // array must be length 1
   122  	SplitInt64(LocalSlot) (LocalSlot, LocalSlot) // returns (hi, lo)
   123  
   124  	// Line returns a string describing the given position.
   125  	Line(src.XPos) string
   126  
   127  	// AllocFrame assigns frame offsets to all live auto variables.
   128  	AllocFrame(f *Func)
   129  
   130  	// Syslook returns a symbol of the runtime function/variable with the
   131  	// given name.
   132  	Syslook(string) interface{} // returns *gc.Sym
   133  }
   134  
   135  // interface used to hold *gc.Node. We'd use *gc.Node directly but
   136  // that would lead to an import cycle.
   137  type GCNode interface {
   138  	Typ() Type
   139  	String() string
   140  }
   141  
   142  // NewConfig returns a new configuration object for the given architecture.
   143  func NewConfig(arch string, fe Frontend, ctxt *obj.Link, optimize bool) *Config {
   144  	c := &Config{arch: arch, fe: fe}
   145  	switch arch {
   146  	case "amd64":
   147  		c.IntSize = 8
   148  		c.PtrSize = 8
   149  		c.RegSize = 8
   150  		c.lowerBlock = rewriteBlockAMD64
   151  		c.lowerValue = rewriteValueAMD64
   152  		c.registers = registersAMD64[:]
   153  		c.gpRegMask = gpRegMaskAMD64
   154  		c.fpRegMask = fpRegMaskAMD64
   155  		c.FPReg = framepointerRegAMD64
   156  		c.LinkReg = linkRegAMD64
   157  		c.hasGReg = false
   158  	case "amd64p32":
   159  		c.IntSize = 4
   160  		c.PtrSize = 4
   161  		c.RegSize = 8
   162  		c.lowerBlock = rewriteBlockAMD64
   163  		c.lowerValue = rewriteValueAMD64
   164  		c.registers = registersAMD64[:]
   165  		c.gpRegMask = gpRegMaskAMD64
   166  		c.fpRegMask = fpRegMaskAMD64
   167  		c.FPReg = framepointerRegAMD64
   168  		c.LinkReg = linkRegAMD64
   169  		c.hasGReg = false
   170  		c.noDuffDevice = true
   171  	case "386":
   172  		c.IntSize = 4
   173  		c.PtrSize = 4
   174  		c.RegSize = 4
   175  		c.lowerBlock = rewriteBlock386
   176  		c.lowerValue = rewriteValue386
   177  		c.registers = registers386[:]
   178  		c.gpRegMask = gpRegMask386
   179  		c.fpRegMask = fpRegMask386
   180  		c.FPReg = framepointerReg386
   181  		c.LinkReg = linkReg386
   182  		c.hasGReg = false
   183  	case "arm":
   184  		c.IntSize = 4
   185  		c.PtrSize = 4
   186  		c.RegSize = 4
   187  		c.lowerBlock = rewriteBlockARM
   188  		c.lowerValue = rewriteValueARM
   189  		c.registers = registersARM[:]
   190  		c.gpRegMask = gpRegMaskARM
   191  		c.fpRegMask = fpRegMaskARM
   192  		c.FPReg = framepointerRegARM
   193  		c.LinkReg = linkRegARM
   194  		c.hasGReg = true
   195  	case "arm64":
   196  		c.IntSize = 8
   197  		c.PtrSize = 8
   198  		c.RegSize = 8
   199  		c.lowerBlock = rewriteBlockARM64
   200  		c.lowerValue = rewriteValueARM64
   201  		c.registers = registersARM64[:]
   202  		c.gpRegMask = gpRegMaskARM64
   203  		c.fpRegMask = fpRegMaskARM64
   204  		c.FPReg = framepointerRegARM64
   205  		c.LinkReg = linkRegARM64
   206  		c.hasGReg = true
   207  		c.noDuffDevice = obj.GOOS == "darwin" // darwin linker cannot handle BR26 reloc with non-zero addend
   208  	case "ppc64":
   209  		c.OldArch = true
   210  		c.BigEndian = true
   211  		fallthrough
   212  	case "ppc64le":
   213  		c.IntSize = 8
   214  		c.PtrSize = 8
   215  		c.RegSize = 8
   216  		c.lowerBlock = rewriteBlockPPC64
   217  		c.lowerValue = rewriteValuePPC64
   218  		c.registers = registersPPC64[:]
   219  		c.gpRegMask = gpRegMaskPPC64
   220  		c.fpRegMask = fpRegMaskPPC64
   221  		c.FPReg = framepointerRegPPC64
   222  		c.LinkReg = linkRegPPC64
   223  		c.noDuffDevice = true // TODO: Resolve PPC64 DuffDevice (has zero, but not copy)
   224  		c.NeedsFpScratch = true
   225  		c.hasGReg = true
   226  	case "mips64":
   227  		c.BigEndian = true
   228  		fallthrough
   229  	case "mips64le":
   230  		c.IntSize = 8
   231  		c.PtrSize = 8
   232  		c.RegSize = 8
   233  		c.lowerBlock = rewriteBlockMIPS64
   234  		c.lowerValue = rewriteValueMIPS64
   235  		c.registers = registersMIPS64[:]
   236  		c.gpRegMask = gpRegMaskMIPS64
   237  		c.fpRegMask = fpRegMaskMIPS64
   238  		c.specialRegMask = specialRegMaskMIPS64
   239  		c.FPReg = framepointerRegMIPS64
   240  		c.LinkReg = linkRegMIPS64
   241  		c.hasGReg = true
   242  	case "s390x":
   243  		c.IntSize = 8
   244  		c.PtrSize = 8
   245  		c.RegSize = 8
   246  		c.lowerBlock = rewriteBlockS390X
   247  		c.lowerValue = rewriteValueS390X
   248  		c.registers = registersS390X[:]
   249  		c.gpRegMask = gpRegMaskS390X
   250  		c.fpRegMask = fpRegMaskS390X
   251  		c.FPReg = framepointerRegS390X
   252  		c.LinkReg = linkRegS390X
   253  		c.hasGReg = true
   254  		c.noDuffDevice = true
   255  		c.BigEndian = true
   256  	case "mips":
   257  		c.BigEndian = true
   258  		fallthrough
   259  	case "mipsle":
   260  		c.IntSize = 4
   261  		c.PtrSize = 4
   262  		c.RegSize = 4
   263  		c.lowerBlock = rewriteBlockMIPS
   264  		c.lowerValue = rewriteValueMIPS
   265  		c.registers = registersMIPS[:]
   266  		c.gpRegMask = gpRegMaskMIPS
   267  		c.fpRegMask = fpRegMaskMIPS
   268  		c.specialRegMask = specialRegMaskMIPS
   269  		c.FPReg = framepointerRegMIPS
   270  		c.LinkReg = linkRegMIPS
   271  		c.hasGReg = true
   272  		c.noDuffDevice = true
   273  	case "riscv":
   274  		c.IntSize = 8
   275  		c.PtrSize = 8
   276  		c.RegSize = 8
   277  		c.lowerBlock = rewriteBlockRISCV
   278  		c.lowerValue = rewriteValueRISCV
   279  		c.registers = registersRISCV[:]
   280  		c.gpRegMask = gpRegMaskRISCV
   281  		c.fpRegMask = fpRegMaskRISCV
   282  		c.FPReg = framepointerRegRISCV
   283  		c.hasGReg = true
   284  	default:
   285  		fe.Fatalf(src.NoXPos, "arch %s not implemented", arch)
   286  	}
   287  	c.ctxt = ctxt
   288  	c.optimize = optimize
   289  	c.nacl = obj.GOOS == "nacl"
   290  
   291  	// Don't use Duff's device on Plan 9 AMD64, because floating
   292  	// point operations are not allowed in note handler.
   293  	if obj.GOOS == "plan9" && arch == "amd64" {
   294  		c.noDuffDevice = true
   295  	}
   296  
   297  	if c.nacl {
   298  		c.noDuffDevice = true // Don't use Duff's device on NaCl
   299  
   300  		// runtime call clobber R12 on nacl
   301  		opcodeTable[OpARMUDIVrtcall].reg.clobbers |= 1 << 12 // R12
   302  	}
   303  
   304  	// Assign IDs to preallocated values/blocks.
   305  	for i := range c.values {
   306  		c.values[i].ID = ID(i)
   307  	}
   308  	for i := range c.blocks {
   309  		c.blocks[i].ID = ID(i)
   310  	}
   311  
   312  	c.logfiles = make(map[string]*os.File)
   313  
   314  	// cutoff is compared with product of numblocks and numvalues,
   315  	// if product is smaller than cutoff, use old non-sparse method.
   316  	// cutoff == 0 implies all sparse.
   317  	// cutoff == -1 implies none sparse.
   318  	// Good cutoff values seem to be O(million) depending on constant factor cost of sparse.
   319  	// TODO: get this from a flag, not an environment variable
   320  	c.sparsePhiCutoff = 2500000 // 0 for testing. // 2500000 determined with crude experiments w/ make.bash
   321  	ev := os.Getenv("GO_SSA_PHI_LOC_CUTOFF")
   322  	if ev != "" {
   323  		v, err := strconv.ParseInt(ev, 10, 64)
   324  		if err != nil {
   325  			fe.Fatalf(src.NoXPos, "Environment variable GO_SSA_PHI_LOC_CUTOFF (value '%s') did not parse as a number", ev)
   326  		}
   327  		c.sparsePhiCutoff = uint64(v) // convert -1 to maxint, for never use sparse
   328  	}
   329  
   330  	return c
   331  }
   332  
   333  func (c *Config) Set387(b bool) {
   334  	c.NeedsFpScratch = b
   335  	c.use387 = b
   336  }
   337  
   338  func (c *Config) Frontend() Frontend      { return c.fe }
   339  func (c *Config) SparsePhiCutoff() uint64 { return c.sparsePhiCutoff }
   340  func (c *Config) Ctxt() *obj.Link         { return c.ctxt }
   341  
   342  // NewFunc returns a new, empty function object.
   343  // Caller must call f.Free() before calling NewFunc again.
   344  func (c *Config) NewFunc() *Func {
   345  	// TODO(khr): should this function take name, type, etc. as arguments?
   346  	if c.curFunc != nil {
   347  		c.Fatalf(src.NoXPos, "NewFunc called without previous Free")
   348  	}
   349  	f := &Func{Config: c, NamedValues: map[LocalSlot][]*Value{}}
   350  	c.curFunc = f
   351  	return f
   352  }
   353  
   354  func (c *Config) Logf(msg string, args ...interface{})                 { c.fe.Logf(msg, args...) }
   355  func (c *Config) Log() bool                                            { return c.fe.Log() }
   356  func (c *Config) Fatalf(pos src.XPos, msg string, args ...interface{}) { c.fe.Fatalf(pos, msg, args...) }
   357  func (c *Config) Warnl(pos src.XPos, msg string, args ...interface{})  { c.fe.Warnl(pos, msg, args...) }
   358  func (c *Config) Debug_checknil() bool                                 { return c.fe.Debug_checknil() }
   359  func (c *Config) Debug_wb() bool                                       { return c.fe.Debug_wb() }
   360  
   361  func (c *Config) logDebugHashMatch(evname, name string) {
   362  	file := c.logfiles[evname]
   363  	if file == nil {
   364  		file = os.Stdout
   365  		tmpfile := os.Getenv("GSHS_LOGFILE")
   366  		if tmpfile != "" {
   367  			var ok error
   368  			file, ok = os.Create(tmpfile)
   369  			if ok != nil {
   370  				c.Fatalf(src.NoXPos, "Could not open hash-testing logfile %s", tmpfile)
   371  			}
   372  		}
   373  		c.logfiles[evname] = file
   374  	}
   375  	s := fmt.Sprintf("%s triggered %s\n", evname, name)
   376  	file.WriteString(s)
   377  	file.Sync()
   378  }
   379  
   380  // DebugHashMatch returns true if environment variable evname
   381  // 1) is empty (this is a special more-quickly implemented case of 3)
   382  // 2) is "y" or "Y"
   383  // 3) is a suffix of the sha1 hash of name
   384  // 4) is a suffix of the environment variable
   385  //    fmt.Sprintf("%s%d", evname, n)
   386  //    provided that all such variables are nonempty for 0 <= i <= n
   387  // Otherwise it returns false.
   388  // When true is returned the message
   389  //  "%s triggered %s\n", evname, name
   390  // is printed on the file named in environment variable
   391  //  GSHS_LOGFILE
   392  // or standard out if that is empty or there is an error
   393  // opening the file.
   394  
   395  func (c *Config) DebugHashMatch(evname, name string) bool {
   396  	evhash := os.Getenv(evname)
   397  	if evhash == "" {
   398  		return true // default behavior with no EV is "on"
   399  	}
   400  	if evhash == "y" || evhash == "Y" {
   401  		c.logDebugHashMatch(evname, name)
   402  		return true
   403  	}
   404  	if evhash == "n" || evhash == "N" {
   405  		return false
   406  	}
   407  	// Check the hash of the name against a partial input hash.
   408  	// We use this feature to do a binary search to
   409  	// find a function that is incorrectly compiled.
   410  	hstr := ""
   411  	for _, b := range sha1.Sum([]byte(name)) {
   412  		hstr += fmt.Sprintf("%08b", b)
   413  	}
   414  
   415  	if strings.HasSuffix(hstr, evhash) {
   416  		c.logDebugHashMatch(evname, name)
   417  		return true
   418  	}
   419  
   420  	// Iteratively try additional hashes to allow tests for multi-point
   421  	// failure.
   422  	for i := 0; true; i++ {
   423  		ev := fmt.Sprintf("%s%d", evname, i)
   424  		evv := os.Getenv(ev)
   425  		if evv == "" {
   426  			break
   427  		}
   428  		if strings.HasSuffix(hstr, evv) {
   429  			c.logDebugHashMatch(ev, name)
   430  			return true
   431  		}
   432  	}
   433  	return false
   434  }
   435  
   436  func (c *Config) DebugNameMatch(evname, name string) bool {
   437  	return os.Getenv(evname) == name
   438  }