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 }