github.com/rakyll/go@v0.0.0-20170216000551-64c02460d703/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.PosTable.Pos(p.Pos).String() 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 p.From3.Type == TYPE_CONST && p.As == ATEXT { 147 // Special case - omit $. 148 fmt.Fprintf(&buf, "%s%d", sep, p.From3.Offset) 149 } else if quadOpAmd64 { 150 fmt.Fprintf(&buf, "%s%v", sep, Rconv(int(p.From3.Reg))) 151 } else { 152 fmt.Fprintf(&buf, "%s%v", sep, Dconv(p, p.From3)) 153 } 154 sep = ", " 155 } 156 if p.To.Type != TYPE_NONE { 157 fmt.Fprintf(&buf, "%s%v", sep, Dconv(p, &p.To)) 158 } 159 if p.RegTo2 != REG_NONE && !quadOpAmd64 { 160 fmt.Fprintf(&buf, "%s%v", sep, Rconv(int(p.RegTo2))) 161 } 162 return buf.String() 163 } 164 165 func (ctxt *Link) NewProg() *Prog { 166 var p *Prog 167 if i := ctxt.allocIdx; i < len(ctxt.progs) { 168 p = &ctxt.progs[i] 169 ctxt.allocIdx = i + 1 170 } else { 171 p = new(Prog) // should be the only call to this; all others should use ctxt.NewProg 172 } 173 p.Ctxt = ctxt 174 return p 175 } 176 func (ctxt *Link) freeProgs() { 177 s := ctxt.progs[:ctxt.allocIdx] 178 for i := range s { 179 s[i] = Prog{} 180 } 181 ctxt.allocIdx = 0 182 } 183 184 func Getcallerpc(interface{}) uintptr { 185 return 1 186 } 187 188 func (ctxt *Link) Dconv(a *Addr) string { 189 return Dconv(nil, a) 190 } 191 192 func Dconv(p *Prog, a *Addr) string { 193 var str string 194 195 switch a.Type { 196 default: 197 str = fmt.Sprintf("type=%d", a.Type) 198 199 case TYPE_NONE: 200 str = "" 201 if a.Name != NAME_NONE || a.Reg != 0 || a.Sym != nil { 202 str = fmt.Sprintf("%v(%v)(NONE)", Mconv(a), Rconv(int(a.Reg))) 203 } 204 205 case TYPE_REG: 206 // TODO(rsc): This special case is for x86 instructions like 207 // PINSRQ CX,$1,X6 208 // where the $1 is included in the p->to Addr. 209 // Move into a new field. 210 if a.Offset != 0 { 211 str = fmt.Sprintf("$%d,%v", a.Offset, Rconv(int(a.Reg))) 212 break 213 } 214 215 str = Rconv(int(a.Reg)) 216 if a.Name != NAME_NONE || a.Sym != nil { 217 str = fmt.Sprintf("%v(%v)(REG)", Mconv(a), Rconv(int(a.Reg))) 218 } 219 220 case TYPE_BRANCH: 221 if a.Sym != nil { 222 str = fmt.Sprintf("%s(SB)", a.Sym.Name) 223 } else if p != nil && p.Pcond != nil { 224 str = fmt.Sprint(p.Pcond.Pc) 225 } else if a.Val != nil { 226 str = fmt.Sprint(a.Val.(*Prog).Pc) 227 } else { 228 str = fmt.Sprintf("%d(PC)", a.Offset) 229 } 230 231 case TYPE_INDIR: 232 str = fmt.Sprintf("*%s", Mconv(a)) 233 234 case TYPE_MEM: 235 str = Mconv(a) 236 if a.Index != REG_NONE { 237 str += fmt.Sprintf("(%v*%d)", Rconv(int(a.Index)), int(a.Scale)) 238 } 239 240 case TYPE_CONST: 241 if a.Reg != 0 { 242 str = fmt.Sprintf("$%v(%v)", Mconv(a), Rconv(int(a.Reg))) 243 } else { 244 str = fmt.Sprintf("$%v", Mconv(a)) 245 } 246 247 case TYPE_TEXTSIZE: 248 if a.Val.(int32) == ArgsSizeUnknown { 249 str = fmt.Sprintf("$%d", a.Offset) 250 } else { 251 str = fmt.Sprintf("$%d-%d", a.Offset, a.Val.(int32)) 252 } 253 254 case TYPE_FCONST: 255 str = fmt.Sprintf("%.17g", a.Val.(float64)) 256 // Make sure 1 prints as 1.0 257 if !strings.ContainsAny(str, ".e") { 258 str += ".0" 259 } 260 str = fmt.Sprintf("$(%s)", str) 261 262 case TYPE_SCONST: 263 str = fmt.Sprintf("$%q", a.Val.(string)) 264 265 case TYPE_ADDR: 266 str = fmt.Sprintf("$%s", Mconv(a)) 267 268 case TYPE_SHIFT: 269 v := int(a.Offset) 270 ops := "<<>>->@>" 271 switch GOARCH { 272 case "arm": 273 op := ops[((v>>5)&3)<<1:] 274 if v&(1<<4) != 0 { 275 str = fmt.Sprintf("R%d%c%cR%d", v&15, op[0], op[1], (v>>8)&15) 276 } else { 277 str = fmt.Sprintf("R%d%c%c%d", v&15, op[0], op[1], (v>>7)&31) 278 } 279 if a.Reg != 0 { 280 str += fmt.Sprintf("(%v)", Rconv(int(a.Reg))) 281 } 282 case "arm64": 283 op := ops[((v>>22)&3)<<1:] 284 str = fmt.Sprintf("R%d%c%c%d", (v>>16)&31, op[0], op[1], (v>>10)&63) 285 default: 286 panic("TYPE_SHIFT is not supported on " + GOARCH) 287 } 288 289 case TYPE_REGREG: 290 str = fmt.Sprintf("(%v, %v)", Rconv(int(a.Reg)), Rconv(int(a.Offset))) 291 292 case TYPE_REGREG2: 293 str = fmt.Sprintf("%v, %v", Rconv(int(a.Reg)), Rconv(int(a.Offset))) 294 295 case TYPE_REGLIST: 296 str = regListConv(int(a.Offset)) 297 } 298 299 return str 300 } 301 302 func Mconv(a *Addr) string { 303 var str string 304 305 switch a.Name { 306 default: 307 str = fmt.Sprintf("name=%d", a.Name) 308 309 case NAME_NONE: 310 switch { 311 case a.Reg == REG_NONE: 312 str = fmt.Sprint(a.Offset) 313 case a.Offset == 0: 314 str = fmt.Sprintf("(%v)", Rconv(int(a.Reg))) 315 case a.Offset != 0: 316 str = fmt.Sprintf("%d(%v)", a.Offset, Rconv(int(a.Reg))) 317 } 318 319 case NAME_EXTERN: 320 if a.Sym != nil { 321 str = fmt.Sprintf("%s%s(SB)", a.Sym.Name, offConv(a.Offset)) 322 } else { 323 str = fmt.Sprintf("%s(SB)", offConv(a.Offset)) 324 } 325 326 case NAME_GOTREF: 327 if a.Sym != nil { 328 str = fmt.Sprintf("%s%s@GOT(SB)", a.Sym.Name, offConv(a.Offset)) 329 } else { 330 str = fmt.Sprintf("%s@GOT(SB)", offConv(a.Offset)) 331 } 332 333 case NAME_STATIC: 334 if a.Sym != nil { 335 str = fmt.Sprintf("%s<>%s(SB)", a.Sym.Name, offConv(a.Offset)) 336 } else { 337 str = fmt.Sprintf("<>%s(SB)", offConv(a.Offset)) 338 } 339 340 case NAME_AUTO: 341 if a.Sym != nil { 342 str = fmt.Sprintf("%s%s(SP)", a.Sym.Name, offConv(a.Offset)) 343 } else { 344 str = fmt.Sprintf("%s(SP)", offConv(a.Offset)) 345 } 346 347 case NAME_PARAM: 348 if a.Sym != nil { 349 str = fmt.Sprintf("%s%s(FP)", a.Sym.Name, offConv(a.Offset)) 350 } else { 351 str = fmt.Sprintf("%s(FP)", offConv(a.Offset)) 352 } 353 } 354 return str 355 } 356 357 func offConv(off int64) string { 358 if off == 0 { 359 return "" 360 } 361 return fmt.Sprintf("%+d", off) 362 } 363 364 type regSet struct { 365 lo int 366 hi int 367 Rconv func(int) string 368 } 369 370 // Few enough architectures that a linear scan is fastest. 371 // Not even worth sorting. 372 var regSpace []regSet 373 374 /* 375 Each architecture defines a register space as a unique 376 integer range. 377 Here is the list of architectures and the base of their register spaces. 378 */ 379 380 const ( 381 // Because of masking operations in the encodings, each register 382 // space should start at 0 modulo some power of 2. 383 RBase386 = 1 * 1024 384 RBaseAMD64 = 2 * 1024 385 RBaseARM = 3 * 1024 386 RBasePPC64 = 4 * 1024 // range [4k, 8k) 387 RBaseARM64 = 8 * 1024 // range [8k, 13k) 388 RBaseMIPS = 13 * 1024 // range [13k, 14k) 389 RBaseS390X = 14 * 1024 // range [14k, 15k) 390 ) 391 392 // RegisterRegister binds a pretty-printer (Rconv) for register 393 // numbers to a given register number range. Lo is inclusive, 394 // hi exclusive (valid registers are lo through hi-1). 395 func RegisterRegister(lo, hi int, Rconv func(int) string) { 396 regSpace = append(regSpace, regSet{lo, hi, Rconv}) 397 } 398 399 func Rconv(reg int) string { 400 if reg == REG_NONE { 401 return "NONE" 402 } 403 for i := range regSpace { 404 rs := ®Space[i] 405 if rs.lo <= reg && reg < rs.hi { 406 return rs.Rconv(reg) 407 } 408 } 409 return fmt.Sprintf("R???%d", reg) 410 } 411 412 func regListConv(list int) string { 413 str := "" 414 415 for i := 0; i < 16; i++ { // TODO: 16 is ARM-specific. 416 if list&(1<<uint(i)) != 0 { 417 if str == "" { 418 str += "[" 419 } else { 420 str += "," 421 } 422 // This is ARM-specific; R10 is g. 423 if i == 10 { 424 str += "g" 425 } else { 426 str += fmt.Sprintf("R%d", i) 427 } 428 } 429 } 430 431 str += "]" 432 return str 433 } 434 435 type opSet struct { 436 lo As 437 names []string 438 } 439 440 // Not even worth sorting 441 var aSpace []opSet 442 443 // RegisterOpcode binds a list of instruction names 444 // to a given instruction number range. 445 func RegisterOpcode(lo As, Anames []string) { 446 if len(Anames) > AllowedOpCodes { 447 panic(fmt.Sprintf("too many instructions, have %d max %d", len(Anames), AllowedOpCodes)) 448 } 449 aSpace = append(aSpace, opSet{lo, Anames}) 450 } 451 452 func (a As) String() string { 453 if 0 <= a && int(a) < len(Anames) { 454 return Anames[a] 455 } 456 for i := range aSpace { 457 as := &aSpace[i] 458 if as.lo <= a && int(a-as.lo) < len(as.names) { 459 return as.names[a-as.lo] 460 } 461 } 462 return fmt.Sprintf("A???%d", a) 463 } 464 465 var Anames = []string{ 466 "XXX", 467 "CALL", 468 "DUFFCOPY", 469 "DUFFZERO", 470 "END", 471 "FUNCDATA", 472 "JMP", 473 "NOP", 474 "PCDATA", 475 "RET", 476 "TEXT", 477 "UNDEF", 478 "USEFIELD", 479 "VARDEF", 480 "VARKILL", 481 "VARLIVE", 482 } 483 484 func Bool2int(b bool) int { 485 // The compiler currently only optimizes this form. 486 // See issue 6011. 487 var i int 488 if b { 489 i = 1 490 } else { 491 i = 0 492 } 493 return i 494 }