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