github.com/tidwall/go@v0.0.0-20170415222209-6694a6888b7d/src/cmd/internal/obj/util.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 obj 6 7 import ( 8 "bytes" 9 "fmt" 10 "log" 11 "os" 12 "strings" 13 "time" 14 ) 15 16 const REG_NONE = 0 17 18 var start time.Time 19 20 func Cputime() float64 { 21 if start.IsZero() { 22 start = time.Now() 23 } 24 return time.Since(start).Seconds() 25 } 26 27 func envOr(key, value string) string { 28 if x := os.Getenv(key); x != "" { 29 return x 30 } 31 return value 32 } 33 34 var ( 35 GOROOT = envOr("GOROOT", defaultGOROOT) 36 GOARCH = envOr("GOARCH", defaultGOARCH) 37 GOOS = envOr("GOOS", defaultGOOS) 38 GO386 = envOr("GO386", defaultGO386) 39 GOARM = goarm() 40 Version = version 41 ) 42 43 func goarm() int { 44 switch v := envOr("GOARM", defaultGOARM); v { 45 case "5": 46 return 5 47 case "6": 48 return 6 49 case "7": 50 return 7 51 } 52 // Fail here, rather than validate at multiple call sites. 53 log.Fatalf("Invalid GOARM value. Must be 5, 6, or 7.") 54 panic("unreachable") 55 } 56 57 func Getgoextlinkenabled() string { 58 return envOr("GO_EXTLINK_ENABLED", defaultGO_EXTLINK_ENABLED) 59 } 60 61 func (p *Prog) Line() string { 62 return p.Ctxt.OutermostPos(p.Pos).Format(false) 63 } 64 65 var armCondCode = []string{ 66 ".EQ", 67 ".NE", 68 ".CS", 69 ".CC", 70 ".MI", 71 ".PL", 72 ".VS", 73 ".VC", 74 ".HI", 75 ".LS", 76 ".GE", 77 ".LT", 78 ".GT", 79 ".LE", 80 "", 81 ".NV", 82 } 83 84 /* ARM scond byte */ 85 const ( 86 C_SCOND = (1 << 4) - 1 87 C_SBIT = 1 << 4 88 C_PBIT = 1 << 5 89 C_WBIT = 1 << 6 90 C_FBIT = 1 << 7 91 C_UBIT = 1 << 7 92 C_SCOND_XOR = 14 93 ) 94 95 // CConv formats ARM condition codes. 96 func CConv(s uint8) string { 97 if s == 0 { 98 return "" 99 } 100 sc := armCondCode[(s&C_SCOND)^C_SCOND_XOR] 101 if s&C_SBIT != 0 { 102 sc += ".S" 103 } 104 if s&C_PBIT != 0 { 105 sc += ".P" 106 } 107 if s&C_WBIT != 0 { 108 sc += ".W" 109 } 110 if s&C_UBIT != 0 { /* ambiguous with FBIT */ 111 sc += ".U" 112 } 113 return sc 114 } 115 116 func (p *Prog) String() string { 117 if p == nil { 118 return "<nil Prog>" 119 } 120 121 if p.Ctxt == nil { 122 return "<Prog without ctxt>" 123 } 124 125 sc := CConv(p.Scond) 126 127 var buf bytes.Buffer 128 129 fmt.Fprintf(&buf, "%.5d (%v)\t%v%s", p.Pc, p.Line(), p.As, sc) 130 sep := "\t" 131 quadOpAmd64 := p.RegTo2 == -1 132 if quadOpAmd64 { 133 fmt.Fprintf(&buf, "%s$%d", sep, p.From3.Offset) 134 sep = ", " 135 } 136 if p.From.Type != TYPE_NONE { 137 fmt.Fprintf(&buf, "%s%v", sep, Dconv(p, &p.From)) 138 sep = ", " 139 } 140 if p.Reg != REG_NONE { 141 // Should not happen but might as well show it if it does. 142 fmt.Fprintf(&buf, "%s%v", sep, Rconv(int(p.Reg))) 143 sep = ", " 144 } 145 if p.From3Type() != TYPE_NONE { 146 if quadOpAmd64 { 147 fmt.Fprintf(&buf, "%s%v", sep, Rconv(int(p.From3.Reg))) 148 } else { 149 fmt.Fprintf(&buf, "%s%v", sep, Dconv(p, p.From3)) 150 } 151 sep = ", " 152 } 153 if p.As == ATEXT { 154 // If there are attributes, print them. Otherwise, skip the comma. 155 // In short, print one of these two: 156 // TEXT foo(SB), DUPOK|NOSPLIT, $0 157 // TEXT foo(SB), $0 158 s := p.From.Sym.Attribute.TextAttrString() 159 if s != "" { 160 fmt.Fprintf(&buf, "%s%s", sep, s) 161 sep = ", " 162 } 163 } 164 if p.To.Type != TYPE_NONE { 165 fmt.Fprintf(&buf, "%s%v", sep, Dconv(p, &p.To)) 166 } 167 if p.RegTo2 != REG_NONE && !quadOpAmd64 { 168 fmt.Fprintf(&buf, "%s%v", sep, Rconv(int(p.RegTo2))) 169 } 170 return buf.String() 171 } 172 173 func (ctxt *Link) NewProg() *Prog { 174 p := new(Prog) 175 p.Ctxt = ctxt 176 return p 177 } 178 179 func (ctxt *Link) CanReuseProgs() bool { 180 return !ctxt.Debugasm 181 } 182 183 func (ctxt *Link) Dconv(a *Addr) string { 184 return Dconv(nil, a) 185 } 186 187 func Dconv(p *Prog, a *Addr) string { 188 var str string 189 190 switch a.Type { 191 default: 192 str = fmt.Sprintf("type=%d", a.Type) 193 194 case TYPE_NONE: 195 str = "" 196 if a.Name != NAME_NONE || a.Reg != 0 || a.Sym != nil { 197 str = fmt.Sprintf("%v(%v)(NONE)", Mconv(a), Rconv(int(a.Reg))) 198 } 199 200 case TYPE_REG: 201 // TODO(rsc): This special case is for x86 instructions like 202 // PINSRQ CX,$1,X6 203 // where the $1 is included in the p->to Addr. 204 // Move into a new field. 205 if a.Offset != 0 { 206 str = fmt.Sprintf("$%d,%v", a.Offset, Rconv(int(a.Reg))) 207 break 208 } 209 210 str = Rconv(int(a.Reg)) 211 if a.Name != NAME_NONE || a.Sym != nil { 212 str = fmt.Sprintf("%v(%v)(REG)", Mconv(a), Rconv(int(a.Reg))) 213 } 214 215 case TYPE_BRANCH: 216 if a.Sym != nil { 217 str = fmt.Sprintf("%s(SB)", a.Sym.Name) 218 } else if p != nil && p.Pcond != nil { 219 str = fmt.Sprint(p.Pcond.Pc) 220 } else if a.Val != nil { 221 str = fmt.Sprint(a.Val.(*Prog).Pc) 222 } else { 223 str = fmt.Sprintf("%d(PC)", a.Offset) 224 } 225 226 case TYPE_INDIR: 227 str = fmt.Sprintf("*%s", Mconv(a)) 228 229 case TYPE_MEM: 230 str = Mconv(a) 231 if a.Index != REG_NONE { 232 str += fmt.Sprintf("(%v*%d)", Rconv(int(a.Index)), int(a.Scale)) 233 } 234 235 case TYPE_CONST: 236 if a.Reg != 0 { 237 str = fmt.Sprintf("$%v(%v)", Mconv(a), Rconv(int(a.Reg))) 238 } else { 239 str = fmt.Sprintf("$%v", Mconv(a)) 240 } 241 242 case TYPE_TEXTSIZE: 243 if a.Val.(int32) == ArgsSizeUnknown { 244 str = fmt.Sprintf("$%d", a.Offset) 245 } else { 246 str = fmt.Sprintf("$%d-%d", a.Offset, a.Val.(int32)) 247 } 248 249 case TYPE_FCONST: 250 str = fmt.Sprintf("%.17g", a.Val.(float64)) 251 // Make sure 1 prints as 1.0 252 if !strings.ContainsAny(str, ".e") { 253 str += ".0" 254 } 255 str = fmt.Sprintf("$(%s)", str) 256 257 case TYPE_SCONST: 258 str = fmt.Sprintf("$%q", a.Val.(string)) 259 260 case TYPE_ADDR: 261 str = fmt.Sprintf("$%s", Mconv(a)) 262 263 case TYPE_SHIFT: 264 v := int(a.Offset) 265 ops := "<<>>->@>" 266 switch GOARCH { 267 case "arm": 268 op := ops[((v>>5)&3)<<1:] 269 if v&(1<<4) != 0 { 270 str = fmt.Sprintf("R%d%c%cR%d", v&15, op[0], op[1], (v>>8)&15) 271 } else { 272 str = fmt.Sprintf("R%d%c%c%d", v&15, op[0], op[1], (v>>7)&31) 273 } 274 if a.Reg != 0 { 275 str += fmt.Sprintf("(%v)", Rconv(int(a.Reg))) 276 } 277 case "arm64": 278 op := ops[((v>>22)&3)<<1:] 279 str = fmt.Sprintf("R%d%c%c%d", (v>>16)&31, op[0], op[1], (v>>10)&63) 280 default: 281 panic("TYPE_SHIFT is not supported on " + GOARCH) 282 } 283 284 case TYPE_REGREG: 285 str = fmt.Sprintf("(%v, %v)", Rconv(int(a.Reg)), Rconv(int(a.Offset))) 286 287 case TYPE_REGREG2: 288 str = fmt.Sprintf("%v, %v", Rconv(int(a.Reg)), Rconv(int(a.Offset))) 289 290 case TYPE_REGLIST: 291 str = regListConv(int(a.Offset)) 292 } 293 294 return str 295 } 296 297 func Mconv(a *Addr) string { 298 var str string 299 300 switch a.Name { 301 default: 302 str = fmt.Sprintf("name=%d", a.Name) 303 304 case NAME_NONE: 305 switch { 306 case a.Reg == REG_NONE: 307 str = fmt.Sprint(a.Offset) 308 case a.Offset == 0: 309 str = fmt.Sprintf("(%v)", Rconv(int(a.Reg))) 310 case a.Offset != 0: 311 str = fmt.Sprintf("%d(%v)", a.Offset, Rconv(int(a.Reg))) 312 } 313 314 // Note: a.Reg == REG_NONE encodes the default base register for the NAME_ type. 315 case NAME_EXTERN: 316 reg := "SB" 317 if a.Reg != REG_NONE { 318 reg = Rconv(int(a.Reg)) 319 } 320 if a.Sym != nil { 321 str = fmt.Sprintf("%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg) 322 } else { 323 str = fmt.Sprintf("%s(%s)", offConv(a.Offset), reg) 324 } 325 326 case NAME_GOTREF: 327 reg := "SB" 328 if a.Reg != REG_NONE { 329 reg = Rconv(int(a.Reg)) 330 } 331 if a.Sym != nil { 332 str = fmt.Sprintf("%s%s@GOT(%s)", a.Sym.Name, offConv(a.Offset), reg) 333 } else { 334 str = fmt.Sprintf("%s@GOT(%s)", offConv(a.Offset), reg) 335 } 336 337 case NAME_STATIC: 338 reg := "SB" 339 if a.Reg != REG_NONE { 340 reg = Rconv(int(a.Reg)) 341 } 342 if a.Sym != nil { 343 str = fmt.Sprintf("%s<>%s(%s)", a.Sym.Name, offConv(a.Offset), reg) 344 } else { 345 str = fmt.Sprintf("<>%s(%s)", offConv(a.Offset), reg) 346 } 347 348 case NAME_AUTO: 349 reg := "SP" 350 if a.Reg != REG_NONE { 351 reg = Rconv(int(a.Reg)) 352 } 353 if a.Sym != nil { 354 str = fmt.Sprintf("%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg) 355 } else { 356 str = fmt.Sprintf("%s(%s)", offConv(a.Offset), reg) 357 } 358 359 case NAME_PARAM: 360 reg := "FP" 361 if a.Reg != REG_NONE { 362 reg = Rconv(int(a.Reg)) 363 } 364 if a.Sym != nil { 365 str = fmt.Sprintf("%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg) 366 } else { 367 str = fmt.Sprintf("%s(%s)", offConv(a.Offset), reg) 368 } 369 } 370 return str 371 } 372 373 func offConv(off int64) string { 374 if off == 0 { 375 return "" 376 } 377 return fmt.Sprintf("%+d", off) 378 } 379 380 type regSet struct { 381 lo int 382 hi int 383 Rconv func(int) string 384 } 385 386 // Few enough architectures that a linear scan is fastest. 387 // Not even worth sorting. 388 var regSpace []regSet 389 390 /* 391 Each architecture defines a register space as a unique 392 integer range. 393 Here is the list of architectures and the base of their register spaces. 394 */ 395 396 const ( 397 // Because of masking operations in the encodings, each register 398 // space should start at 0 modulo some power of 2. 399 RBase386 = 1 * 1024 400 RBaseAMD64 = 2 * 1024 401 RBaseARM = 3 * 1024 402 RBasePPC64 = 4 * 1024 // range [4k, 8k) 403 RBaseARM64 = 8 * 1024 // range [8k, 13k) 404 RBaseMIPS = 13 * 1024 // range [13k, 14k) 405 RBaseS390X = 14 * 1024 // range [14k, 15k) 406 ) 407 408 // RegisterRegister binds a pretty-printer (Rconv) for register 409 // numbers to a given register number range. Lo is inclusive, 410 // hi exclusive (valid registers are lo through hi-1). 411 func RegisterRegister(lo, hi int, Rconv func(int) string) { 412 regSpace = append(regSpace, regSet{lo, hi, Rconv}) 413 } 414 415 func Rconv(reg int) string { 416 if reg == REG_NONE { 417 return "NONE" 418 } 419 for i := range regSpace { 420 rs := ®Space[i] 421 if rs.lo <= reg && reg < rs.hi { 422 return rs.Rconv(reg) 423 } 424 } 425 return fmt.Sprintf("R???%d", reg) 426 } 427 428 func regListConv(list int) string { 429 str := "" 430 431 for i := 0; i < 16; i++ { // TODO: 16 is ARM-specific. 432 if list&(1<<uint(i)) != 0 { 433 if str == "" { 434 str += "[" 435 } else { 436 str += "," 437 } 438 // This is ARM-specific; R10 is g. 439 if i == 10 { 440 str += "g" 441 } else { 442 str += fmt.Sprintf("R%d", i) 443 } 444 } 445 } 446 447 str += "]" 448 return str 449 } 450 451 type opSet struct { 452 lo As 453 names []string 454 } 455 456 // Not even worth sorting 457 var aSpace []opSet 458 459 // RegisterOpcode binds a list of instruction names 460 // to a given instruction number range. 461 func RegisterOpcode(lo As, Anames []string) { 462 if len(Anames) > AllowedOpCodes { 463 panic(fmt.Sprintf("too many instructions, have %d max %d", len(Anames), AllowedOpCodes)) 464 } 465 aSpace = append(aSpace, opSet{lo, Anames}) 466 } 467 468 func (a As) String() string { 469 if 0 <= a && int(a) < len(Anames) { 470 return Anames[a] 471 } 472 for i := range aSpace { 473 as := &aSpace[i] 474 if as.lo <= a && int(a-as.lo) < len(as.names) { 475 return as.names[a-as.lo] 476 } 477 } 478 return fmt.Sprintf("A???%d", a) 479 } 480 481 var Anames = []string{ 482 "XXX", 483 "CALL", 484 "DUFFCOPY", 485 "DUFFZERO", 486 "END", 487 "FUNCDATA", 488 "JMP", 489 "NOP", 490 "PCDATA", 491 "RET", 492 "TEXT", 493 "UNDEF", 494 } 495 496 func Bool2int(b bool) int { 497 // The compiler currently only optimizes this form. 498 // See issue 6011. 499 var i int 500 if b { 501 i = 1 502 } else { 503 i = 0 504 } 505 return i 506 }