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