github.com/rakyll/go@v0.0.0-20170216000551-64c02460d703/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) *obj.LSym
   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  	default:
   274  		fe.Fatalf(src.NoXPos, "arch %s not implemented", arch)
   275  	}
   276  	c.ctxt = ctxt
   277  	c.optimize = optimize
   278  	c.nacl = obj.GOOS == "nacl"
   279  
   280  	// Don't use Duff's device on Plan 9 AMD64, because floating
   281  	// point operations are not allowed in note handler.
   282  	if obj.GOOS == "plan9" && arch == "amd64" {
   283  		c.noDuffDevice = true
   284  	}
   285  
   286  	if c.nacl {
   287  		c.noDuffDevice = true // Don't use Duff's device on NaCl
   288  
   289  		// runtime call clobber R12 on nacl
   290  		opcodeTable[OpARMUDIVrtcall].reg.clobbers |= 1 << 12 // R12
   291  	}
   292  
   293  	// Assign IDs to preallocated values/blocks.
   294  	for i := range c.values {
   295  		c.values[i].ID = ID(i)
   296  	}
   297  	for i := range c.blocks {
   298  		c.blocks[i].ID = ID(i)
   299  	}
   300  
   301  	c.logfiles = make(map[string]*os.File)
   302  
   303  	// cutoff is compared with product of numblocks and numvalues,
   304  	// if product is smaller than cutoff, use old non-sparse method.
   305  	// cutoff == 0 implies all sparse.
   306  	// cutoff == -1 implies none sparse.
   307  	// Good cutoff values seem to be O(million) depending on constant factor cost of sparse.
   308  	// TODO: get this from a flag, not an environment variable
   309  	c.sparsePhiCutoff = 2500000 // 0 for testing. // 2500000 determined with crude experiments w/ make.bash
   310  	ev := os.Getenv("GO_SSA_PHI_LOC_CUTOFF")
   311  	if ev != "" {
   312  		v, err := strconv.ParseInt(ev, 10, 64)
   313  		if err != nil {
   314  			fe.Fatalf(src.NoXPos, "Environment variable GO_SSA_PHI_LOC_CUTOFF (value '%s') did not parse as a number", ev)
   315  		}
   316  		c.sparsePhiCutoff = uint64(v) // convert -1 to maxint, for never use sparse
   317  	}
   318  
   319  	return c
   320  }
   321  
   322  func (c *Config) Set387(b bool) {
   323  	c.NeedsFpScratch = b
   324  	c.use387 = b
   325  }
   326  
   327  func (c *Config) Frontend() Frontend      { return c.fe }
   328  func (c *Config) SparsePhiCutoff() uint64 { return c.sparsePhiCutoff }
   329  func (c *Config) Ctxt() *obj.Link         { return c.ctxt }
   330  
   331  // NewFunc returns a new, empty function object.
   332  // Caller must call f.Free() before calling NewFunc again.
   333  func (c *Config) NewFunc() *Func {
   334  	// TODO(khr): should this function take name, type, etc. as arguments?
   335  	if c.curFunc != nil {
   336  		c.Fatalf(src.NoXPos, "NewFunc called without previous Free")
   337  	}
   338  	f := &Func{Config: c, NamedValues: map[LocalSlot][]*Value{}}
   339  	c.curFunc = f
   340  	return f
   341  }
   342  
   343  func (c *Config) Logf(msg string, args ...interface{})                 { c.fe.Logf(msg, args...) }
   344  func (c *Config) Log() bool                                            { return c.fe.Log() }
   345  func (c *Config) Fatalf(pos src.XPos, msg string, args ...interface{}) { c.fe.Fatalf(pos, msg, args...) }
   346  func (c *Config) Warnl(pos src.XPos, msg string, args ...interface{})  { c.fe.Warnl(pos, msg, args...) }
   347  func (c *Config) Debug_checknil() bool                                 { return c.fe.Debug_checknil() }
   348  func (c *Config) Debug_wb() bool                                       { return c.fe.Debug_wb() }
   349  
   350  func (c *Config) logDebugHashMatch(evname, name string) {
   351  	file := c.logfiles[evname]
   352  	if file == nil {
   353  		file = os.Stdout
   354  		tmpfile := os.Getenv("GSHS_LOGFILE")
   355  		if tmpfile != "" {
   356  			var ok error
   357  			file, ok = os.Create(tmpfile)
   358  			if ok != nil {
   359  				c.Fatalf(src.NoXPos, "Could not open hash-testing logfile %s", tmpfile)
   360  			}
   361  		}
   362  		c.logfiles[evname] = file
   363  	}
   364  	s := fmt.Sprintf("%s triggered %s\n", evname, name)
   365  	file.WriteString(s)
   366  	file.Sync()
   367  }
   368  
   369  // DebugHashMatch returns true if environment variable evname
   370  // 1) is empty (this is a special more-quickly implemented case of 3)
   371  // 2) is "y" or "Y"
   372  // 3) is a suffix of the sha1 hash of name
   373  // 4) is a suffix of the environment variable
   374  //    fmt.Sprintf("%s%d", evname, n)
   375  //    provided that all such variables are nonempty for 0 <= i <= n
   376  // Otherwise it returns false.
   377  // When true is returned the message
   378  //  "%s triggered %s\n", evname, name
   379  // is printed on the file named in environment variable
   380  //  GSHS_LOGFILE
   381  // or standard out if that is empty or there is an error
   382  // opening the file.
   383  
   384  func (c *Config) DebugHashMatch(evname, name string) bool {
   385  	evhash := os.Getenv(evname)
   386  	if evhash == "" {
   387  		return true // default behavior with no EV is "on"
   388  	}
   389  	if evhash == "y" || evhash == "Y" {
   390  		c.logDebugHashMatch(evname, name)
   391  		return true
   392  	}
   393  	if evhash == "n" || evhash == "N" {
   394  		return false
   395  	}
   396  	// Check the hash of the name against a partial input hash.
   397  	// We use this feature to do a binary search to
   398  	// find a function that is incorrectly compiled.
   399  	hstr := ""
   400  	for _, b := range sha1.Sum([]byte(name)) {
   401  		hstr += fmt.Sprintf("%08b", b)
   402  	}
   403  
   404  	if strings.HasSuffix(hstr, evhash) {
   405  		c.logDebugHashMatch(evname, name)
   406  		return true
   407  	}
   408  
   409  	// Iteratively try additional hashes to allow tests for multi-point
   410  	// failure.
   411  	for i := 0; true; i++ {
   412  		ev := fmt.Sprintf("%s%d", evname, i)
   413  		evv := os.Getenv(ev)
   414  		if evv == "" {
   415  			break
   416  		}
   417  		if strings.HasSuffix(hstr, evv) {
   418  			c.logDebugHashMatch(ev, name)
   419  			return true
   420  		}
   421  	}
   422  	return false
   423  }
   424  
   425  func (c *Config) DebugNameMatch(evname, name string) bool {
   426  	return os.Getenv(evname) == name
   427  }