github.com/gocuntian/go@v0.0.0-20160610041250-fee02d270bf8/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  	fe              Frontend                   // callbacks into compiler frontend
    24  	HTML            *HTMLWriter                // html writer, for debugging
    25  	ctxt            *obj.Link                  // Generic arch information
    26  	optimize        bool                       // Do optimization
    27  	noDuffDevice    bool                       // Don't use Duff's device
    28  	sparsePhiCutoff uint64                     // Sparse phi location algorithm used above this #blocks*#variables score
    29  	curFunc         *Func
    30  
    31  	// TODO: more stuff. Compiler flags of interest, ...
    32  
    33  	// Given an environment variable used for debug hash match,
    34  	// what file (if any) receives the yes/no logging?
    35  	logfiles map[string]*os.File
    36  
    37  	// Storage for low-numbered values and blocks.
    38  	values [2000]Value
    39  	blocks [200]Block
    40  
    41  	// Reusable stackAllocState.
    42  	// See stackalloc.go's {new,put}StackAllocState.
    43  	stackAllocState *stackAllocState
    44  
    45  	domblockstore []ID         // scratch space for computing dominators
    46  	scrSparse     []*sparseSet // scratch sparse sets to be re-used.
    47  }
    48  
    49  type TypeSource interface {
    50  	TypeBool() Type
    51  	TypeInt8() Type
    52  	TypeInt16() Type
    53  	TypeInt32() Type
    54  	TypeInt64() Type
    55  	TypeUInt8() Type
    56  	TypeUInt16() Type
    57  	TypeUInt32() Type
    58  	TypeUInt64() Type
    59  	TypeInt() Type
    60  	TypeFloat32() Type
    61  	TypeFloat64() Type
    62  	TypeUintptr() Type
    63  	TypeString() Type
    64  	TypeBytePtr() Type // TODO: use unsafe.Pointer instead?
    65  
    66  	CanSSA(t Type) bool
    67  }
    68  
    69  type Logger interface {
    70  	// Logf logs a message from the compiler.
    71  	Logf(string, ...interface{})
    72  
    73  	// Log returns true if logging is not a no-op
    74  	// some logging calls account for more than a few heap allocations.
    75  	Log() bool
    76  
    77  	// Fatal reports a compiler error and exits.
    78  	Fatalf(line int32, msg string, args ...interface{})
    79  
    80  	// Unimplemented reports that the function cannot be compiled.
    81  	// It will be removed once SSA work is complete.
    82  	Unimplementedf(line int32, msg string, args ...interface{})
    83  
    84  	// Warnl writes compiler messages in the form expected by "errorcheck" tests
    85  	Warnl(line int32, fmt_ string, args ...interface{})
    86  
    87  	// Fowards the Debug_checknil flag from gc
    88  	Debug_checknil() bool
    89  }
    90  
    91  type Frontend interface {
    92  	TypeSource
    93  	Logger
    94  
    95  	// StringData returns a symbol pointing to the given string's contents.
    96  	StringData(string) interface{} // returns *gc.Sym
    97  
    98  	// Auto returns a Node for an auto variable of the given type.
    99  	// The SSA compiler uses this function to allocate space for spills.
   100  	Auto(Type) GCNode
   101  
   102  	// Given the name for a compound type, returns the name we should use
   103  	// for the parts of that compound type.
   104  	SplitString(LocalSlot) (LocalSlot, LocalSlot)
   105  	SplitInterface(LocalSlot) (LocalSlot, LocalSlot)
   106  	SplitSlice(LocalSlot) (LocalSlot, LocalSlot, LocalSlot)
   107  	SplitComplex(LocalSlot) (LocalSlot, LocalSlot)
   108  	SplitStruct(LocalSlot, int) LocalSlot
   109  
   110  	// Line returns a string describing the given line number.
   111  	Line(int32) string
   112  }
   113  
   114  // interface used to hold *gc.Node. We'd use *gc.Node directly but
   115  // that would lead to an import cycle.
   116  type GCNode interface {
   117  	Typ() Type
   118  	String() string
   119  }
   120  
   121  // NewConfig returns a new configuration object for the given architecture.
   122  func NewConfig(arch string, fe Frontend, ctxt *obj.Link, optimize bool) *Config {
   123  	c := &Config{arch: arch, fe: fe}
   124  	switch arch {
   125  	case "amd64":
   126  		c.IntSize = 8
   127  		c.PtrSize = 8
   128  		c.lowerBlock = rewriteBlockAMD64
   129  		c.lowerValue = rewriteValueAMD64
   130  		c.registers = registersAMD64[:]
   131  	case "386":
   132  		c.IntSize = 4
   133  		c.PtrSize = 4
   134  		c.lowerBlock = rewriteBlockAMD64
   135  		c.lowerValue = rewriteValueAMD64 // TODO(khr): full 32-bit support
   136  	case "arm":
   137  		c.IntSize = 4
   138  		c.PtrSize = 4
   139  		c.lowerBlock = rewriteBlockARM
   140  		c.lowerValue = rewriteValueARM
   141  		c.registers = registersARM[:]
   142  	default:
   143  		fe.Unimplementedf(0, "arch %s not implemented", arch)
   144  	}
   145  	c.ctxt = ctxt
   146  	c.optimize = optimize
   147  
   148  	// Don't use Duff's device on Plan 9, because floating
   149  	// point operations are not allowed in note handler.
   150  	if obj.Getgoos() == "plan9" {
   151  		c.noDuffDevice = true
   152  	}
   153  
   154  	// Assign IDs to preallocated values/blocks.
   155  	for i := range c.values {
   156  		c.values[i].ID = ID(i)
   157  	}
   158  	for i := range c.blocks {
   159  		c.blocks[i].ID = ID(i)
   160  	}
   161  
   162  	c.logfiles = make(map[string]*os.File)
   163  
   164  	// cutoff is compared with product of numblocks and numvalues,
   165  	// if product is smaller than cutoff, use old non-sparse method.
   166  	// cutoff == 0 implies all sparse.
   167  	// cutoff == -1 implies none sparse.
   168  	// Good cutoff values seem to be O(million) depending on constant factor cost of sparse.
   169  	// TODO: get this from a flag, not an environment variable
   170  	c.sparsePhiCutoff = 2500000 // 0 for testing. // 2500000 determined with crude experiments w/ make.bash
   171  	ev := os.Getenv("GO_SSA_PHI_LOC_CUTOFF")
   172  	if ev != "" {
   173  		v, err := strconv.ParseInt(ev, 10, 64)
   174  		if err != nil {
   175  			fe.Fatalf(0, "Environment variable GO_SSA_PHI_LOC_CUTOFF (value '%s') did not parse as a number", ev)
   176  		}
   177  		c.sparsePhiCutoff = uint64(v) // convert -1 to maxint, for never use sparse
   178  	}
   179  
   180  	return c
   181  }
   182  
   183  func (c *Config) Frontend() Frontend      { return c.fe }
   184  func (c *Config) SparsePhiCutoff() uint64 { return c.sparsePhiCutoff }
   185  
   186  // NewFunc returns a new, empty function object.
   187  // Caller must call f.Free() before calling NewFunc again.
   188  func (c *Config) NewFunc() *Func {
   189  	// TODO(khr): should this function take name, type, etc. as arguments?
   190  	if c.curFunc != nil {
   191  		c.Fatalf(0, "NewFunc called without previous Free")
   192  	}
   193  	f := &Func{Config: c, NamedValues: map[LocalSlot][]*Value{}}
   194  	c.curFunc = f
   195  	return f
   196  }
   197  
   198  func (c *Config) Logf(msg string, args ...interface{})               { c.fe.Logf(msg, args...) }
   199  func (c *Config) Log() bool                                          { return c.fe.Log() }
   200  func (c *Config) Fatalf(line int32, msg string, args ...interface{}) { c.fe.Fatalf(line, msg, args...) }
   201  func (c *Config) Unimplementedf(line int32, msg string, args ...interface{}) {
   202  	c.fe.Unimplementedf(line, msg, args...)
   203  }
   204  func (c *Config) Warnl(line int32, msg string, args ...interface{}) { c.fe.Warnl(line, msg, args...) }
   205  func (c *Config) Debug_checknil() bool                              { return c.fe.Debug_checknil() }
   206  
   207  func (c *Config) logDebugHashMatch(evname, name string) {
   208  	file := c.logfiles[evname]
   209  	if file == nil {
   210  		file = os.Stdout
   211  		tmpfile := os.Getenv("GSHS_LOGFILE")
   212  		if tmpfile != "" {
   213  			var ok error
   214  			file, ok = os.Create(tmpfile)
   215  			if ok != nil {
   216  				c.Fatalf(0, "Could not open hash-testing logfile %s", tmpfile)
   217  			}
   218  		}
   219  		c.logfiles[evname] = file
   220  	}
   221  	s := fmt.Sprintf("%s triggered %s\n", evname, name)
   222  	file.WriteString(s)
   223  	file.Sync()
   224  }
   225  
   226  // DebugHashMatch returns true if environment variable evname
   227  // 1) is empty (this is a special more-quickly implemented case of 3)
   228  // 2) is "y" or "Y"
   229  // 3) is a suffix of the sha1 hash of name
   230  // 4) is a suffix of the environment variable
   231  //    fmt.Sprintf("%s%d", evname, n)
   232  //    provided that all such variables are nonempty for 0 <= i <= n
   233  // Otherwise it returns false.
   234  // When true is returned the message
   235  //  "%s triggered %s\n", evname, name
   236  // is printed on the file named in environment variable
   237  //  GSHS_LOGFILE
   238  // or standard out if that is empty or there is an error
   239  // opening the file.
   240  
   241  func (c *Config) DebugHashMatch(evname, name string) bool {
   242  	evhash := os.Getenv(evname)
   243  	if evhash == "" {
   244  		return true // default behavior with no EV is "on"
   245  	}
   246  	if evhash == "y" || evhash == "Y" {
   247  		c.logDebugHashMatch(evname, name)
   248  		return true
   249  	}
   250  	if evhash == "n" || evhash == "N" {
   251  		return false
   252  	}
   253  	// Check the hash of the name against a partial input hash.
   254  	// We use this feature to do a binary search to
   255  	// find a function that is incorrectly compiled.
   256  	hstr := ""
   257  	for _, b := range sha1.Sum([]byte(name)) {
   258  		hstr += fmt.Sprintf("%08b", b)
   259  	}
   260  
   261  	if strings.HasSuffix(hstr, evhash) {
   262  		c.logDebugHashMatch(evname, name)
   263  		return true
   264  	}
   265  
   266  	// Iteratively try additional hashes to allow tests for multi-point
   267  	// failure.
   268  	for i := 0; true; i++ {
   269  		ev := fmt.Sprintf("%s%d", evname, i)
   270  		evv := os.Getenv(ev)
   271  		if evv == "" {
   272  			break
   273  		}
   274  		if strings.HasSuffix(hstr, evv) {
   275  			c.logDebugHashMatch(ev, name)
   276  			return true
   277  		}
   278  	}
   279  	return false
   280  }
   281  
   282  func (c *Config) DebugNameMatch(evname, name string) bool {
   283  	return os.Getenv(evname) == name
   284  }