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