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