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