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