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