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