github.com/riscv/riscv-go@v0.0.0-20200123204226-124ebd6fcc8e/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) interface{} // returns *gc.Sym 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 case "riscv": 274 c.IntSize = 8 275 c.PtrSize = 8 276 c.RegSize = 8 277 c.lowerBlock = rewriteBlockRISCV 278 c.lowerValue = rewriteValueRISCV 279 c.registers = registersRISCV[:] 280 c.gpRegMask = gpRegMaskRISCV 281 c.fpRegMask = fpRegMaskRISCV 282 c.FPReg = framepointerRegRISCV 283 c.hasGReg = true 284 default: 285 fe.Fatalf(src.NoXPos, "arch %s not implemented", arch) 286 } 287 c.ctxt = ctxt 288 c.optimize = optimize 289 c.nacl = obj.GOOS == "nacl" 290 291 // Don't use Duff's device on Plan 9 AMD64, because floating 292 // point operations are not allowed in note handler. 293 if obj.GOOS == "plan9" && arch == "amd64" { 294 c.noDuffDevice = true 295 } 296 297 if c.nacl { 298 c.noDuffDevice = true // Don't use Duff's device on NaCl 299 300 // runtime call clobber R12 on nacl 301 opcodeTable[OpARMUDIVrtcall].reg.clobbers |= 1 << 12 // R12 302 } 303 304 // Assign IDs to preallocated values/blocks. 305 for i := range c.values { 306 c.values[i].ID = ID(i) 307 } 308 for i := range c.blocks { 309 c.blocks[i].ID = ID(i) 310 } 311 312 c.logfiles = make(map[string]*os.File) 313 314 // cutoff is compared with product of numblocks and numvalues, 315 // if product is smaller than cutoff, use old non-sparse method. 316 // cutoff == 0 implies all sparse. 317 // cutoff == -1 implies none sparse. 318 // Good cutoff values seem to be O(million) depending on constant factor cost of sparse. 319 // TODO: get this from a flag, not an environment variable 320 c.sparsePhiCutoff = 2500000 // 0 for testing. // 2500000 determined with crude experiments w/ make.bash 321 ev := os.Getenv("GO_SSA_PHI_LOC_CUTOFF") 322 if ev != "" { 323 v, err := strconv.ParseInt(ev, 10, 64) 324 if err != nil { 325 fe.Fatalf(src.NoXPos, "Environment variable GO_SSA_PHI_LOC_CUTOFF (value '%s') did not parse as a number", ev) 326 } 327 c.sparsePhiCutoff = uint64(v) // convert -1 to maxint, for never use sparse 328 } 329 330 return c 331 } 332 333 func (c *Config) Set387(b bool) { 334 c.NeedsFpScratch = b 335 c.use387 = b 336 } 337 338 func (c *Config) Frontend() Frontend { return c.fe } 339 func (c *Config) SparsePhiCutoff() uint64 { return c.sparsePhiCutoff } 340 func (c *Config) Ctxt() *obj.Link { return c.ctxt } 341 342 // NewFunc returns a new, empty function object. 343 // Caller must call f.Free() before calling NewFunc again. 344 func (c *Config) NewFunc() *Func { 345 // TODO(khr): should this function take name, type, etc. as arguments? 346 if c.curFunc != nil { 347 c.Fatalf(src.NoXPos, "NewFunc called without previous Free") 348 } 349 f := &Func{Config: c, NamedValues: map[LocalSlot][]*Value{}} 350 c.curFunc = f 351 return f 352 } 353 354 func (c *Config) Logf(msg string, args ...interface{}) { c.fe.Logf(msg, args...) } 355 func (c *Config) Log() bool { return c.fe.Log() } 356 func (c *Config) Fatalf(pos src.XPos, msg string, args ...interface{}) { c.fe.Fatalf(pos, msg, args...) } 357 func (c *Config) Warnl(pos src.XPos, msg string, args ...interface{}) { c.fe.Warnl(pos, msg, args...) } 358 func (c *Config) Debug_checknil() bool { return c.fe.Debug_checknil() } 359 func (c *Config) Debug_wb() bool { return c.fe.Debug_wb() } 360 361 func (c *Config) logDebugHashMatch(evname, name string) { 362 file := c.logfiles[evname] 363 if file == nil { 364 file = os.Stdout 365 tmpfile := os.Getenv("GSHS_LOGFILE") 366 if tmpfile != "" { 367 var ok error 368 file, ok = os.Create(tmpfile) 369 if ok != nil { 370 c.Fatalf(src.NoXPos, "Could not open hash-testing logfile %s", tmpfile) 371 } 372 } 373 c.logfiles[evname] = file 374 } 375 s := fmt.Sprintf("%s triggered %s\n", evname, name) 376 file.WriteString(s) 377 file.Sync() 378 } 379 380 // DebugHashMatch returns true if environment variable evname 381 // 1) is empty (this is a special more-quickly implemented case of 3) 382 // 2) is "y" or "Y" 383 // 3) is a suffix of the sha1 hash of name 384 // 4) is a suffix of the environment variable 385 // fmt.Sprintf("%s%d", evname, n) 386 // provided that all such variables are nonempty for 0 <= i <= n 387 // Otherwise it returns false. 388 // When true is returned the message 389 // "%s triggered %s\n", evname, name 390 // is printed on the file named in environment variable 391 // GSHS_LOGFILE 392 // or standard out if that is empty or there is an error 393 // opening the file. 394 395 func (c *Config) DebugHashMatch(evname, name string) bool { 396 evhash := os.Getenv(evname) 397 if evhash == "" { 398 return true // default behavior with no EV is "on" 399 } 400 if evhash == "y" || evhash == "Y" { 401 c.logDebugHashMatch(evname, name) 402 return true 403 } 404 if evhash == "n" || evhash == "N" { 405 return false 406 } 407 // Check the hash of the name against a partial input hash. 408 // We use this feature to do a binary search to 409 // find a function that is incorrectly compiled. 410 hstr := "" 411 for _, b := range sha1.Sum([]byte(name)) { 412 hstr += fmt.Sprintf("%08b", b) 413 } 414 415 if strings.HasSuffix(hstr, evhash) { 416 c.logDebugHashMatch(evname, name) 417 return true 418 } 419 420 // Iteratively try additional hashes to allow tests for multi-point 421 // failure. 422 for i := 0; true; i++ { 423 ev := fmt.Sprintf("%s%d", evname, i) 424 evv := os.Getenv(ev) 425 if evv == "" { 426 break 427 } 428 if strings.HasSuffix(hstr, evv) { 429 c.logDebugHashMatch(ev, name) 430 return true 431 } 432 } 433 return false 434 } 435 436 func (c *Config) DebugNameMatch(evname, name string) bool { 437 return os.Getenv(evname) == name 438 }