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 }