github.com/hikaru7719/go@v0.0.0-20181025140707-c8b2ac68906a/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 "cmd/internal/objabi" 10 "fmt" 11 "strings" 12 ) 13 14 const REG_NONE = 0 15 16 // Line returns a string containing the filename and line number for p 17 func (p *Prog) Line() string { 18 return p.Ctxt.OutermostPos(p.Pos).Format(false, true) 19 } 20 21 // InnermostLineNumber returns a string containing the line number for the 22 // innermost inlined function (if any inlining) at p's position 23 func (p *Prog) InnermostLineNumber() string { 24 return p.Ctxt.InnermostPos(p.Pos).LineNumber() 25 } 26 27 // InnermostLineNumberHTML returns a string containing the line number for the 28 // innermost inlined function (if any inlining) at p's position 29 func (p *Prog) InnermostLineNumberHTML() string { 30 return p.Ctxt.InnermostPos(p.Pos).LineNumberHTML() 31 } 32 33 // InnermostFilename returns a string containing the innermost 34 // (in inlining) filename at p's position 35 func (p *Prog) InnermostFilename() string { 36 // TODO For now, this is only used for debugging output, and if we need more/better information, it might change. 37 // An example of what we might want to see is the full stack of positions for inlined code, so we get some visibility into what is recorded there. 38 pos := p.Ctxt.InnermostPos(p.Pos) 39 if !pos.IsKnown() { 40 return "<unknown file name>" 41 } 42 return pos.Filename() 43 } 44 45 var armCondCode = []string{ 46 ".EQ", 47 ".NE", 48 ".CS", 49 ".CC", 50 ".MI", 51 ".PL", 52 ".VS", 53 ".VC", 54 ".HI", 55 ".LS", 56 ".GE", 57 ".LT", 58 ".GT", 59 ".LE", 60 "", 61 ".NV", 62 } 63 64 /* ARM scond byte */ 65 const ( 66 C_SCOND = (1 << 4) - 1 67 C_SBIT = 1 << 4 68 C_PBIT = 1 << 5 69 C_WBIT = 1 << 6 70 C_FBIT = 1 << 7 71 C_UBIT = 1 << 7 72 C_SCOND_XOR = 14 73 ) 74 75 // CConv formats opcode suffix bits (Prog.Scond). 76 func CConv(s uint8) string { 77 if s == 0 { 78 return "" 79 } 80 for i := range opSuffixSpace { 81 sset := &opSuffixSpace[i] 82 if sset.arch == objabi.GOARCH { 83 return sset.cconv(s) 84 } 85 } 86 return fmt.Sprintf("SC???%d", s) 87 } 88 89 // CConvARM formats ARM opcode suffix bits (mostly condition codes). 90 func CConvARM(s uint8) string { 91 // TODO: could be great to move suffix-related things into 92 // ARM asm backends some day. 93 // obj/x86 can be used as an example. 94 95 sc := armCondCode[(s&C_SCOND)^C_SCOND_XOR] 96 if s&C_SBIT != 0 { 97 sc += ".S" 98 } 99 if s&C_PBIT != 0 { 100 sc += ".P" 101 } 102 if s&C_WBIT != 0 { 103 sc += ".W" 104 } 105 if s&C_UBIT != 0 { /* ambiguous with FBIT */ 106 sc += ".U" 107 } 108 return sc 109 } 110 111 func (p *Prog) String() string { 112 if p == nil { 113 return "<nil Prog>" 114 } 115 if p.Ctxt == nil { 116 return "<Prog without ctxt>" 117 } 118 return fmt.Sprintf("%.5d (%v)\t%s", p.Pc, p.Line(), p.InstructionString()) 119 } 120 121 // InstructionString returns a string representation of the instruction without preceding 122 // program counter or file and line number. 123 func (p *Prog) InstructionString() string { 124 if p == nil { 125 return "<nil Prog>" 126 } 127 128 if p.Ctxt == nil { 129 return "<Prog without ctxt>" 130 } 131 132 sc := CConv(p.Scond) 133 134 var buf bytes.Buffer 135 136 fmt.Fprintf(&buf, "%v%s", p.As, sc) 137 sep := "\t" 138 139 if p.From.Type != TYPE_NONE { 140 fmt.Fprintf(&buf, "%s%v", sep, Dconv(p, &p.From)) 141 sep = ", " 142 } 143 if p.Reg != REG_NONE { 144 // Should not happen but might as well show it if it does. 145 fmt.Fprintf(&buf, "%s%v", sep, Rconv(int(p.Reg))) 146 sep = ", " 147 } 148 for i := range p.RestArgs { 149 fmt.Fprintf(&buf, "%s%v", sep, Dconv(p, &p.RestArgs[i])) 150 sep = ", " 151 } 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 { 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 Dconv(p *Prog, a *Addr) string { 184 var str string 185 186 switch a.Type { 187 default: 188 str = fmt.Sprintf("type=%d", a.Type) 189 190 case TYPE_NONE: 191 str = "" 192 if a.Name != NAME_NONE || a.Reg != 0 || a.Sym != nil { 193 str = fmt.Sprintf("%v(%v)(NONE)", Mconv(a), Rconv(int(a.Reg))) 194 } 195 196 case TYPE_REG: 197 // TODO(rsc): This special case is for x86 instructions like 198 // PINSRQ CX,$1,X6 199 // where the $1 is included in the p->to Addr. 200 // Move into a new field. 201 if a.Offset != 0 && (a.Reg < RBaseARM64 || a.Reg >= RBaseMIPS) { 202 str = fmt.Sprintf("$%d,%v", a.Offset, Rconv(int(a.Reg))) 203 break 204 } 205 206 str = Rconv(int(a.Reg)) 207 if a.Name != NAME_NONE || a.Sym != nil { 208 str = fmt.Sprintf("%v(%v)(REG)", Mconv(a), Rconv(int(a.Reg))) 209 } 210 if (RBaseARM64+1<<10+1<<9) /* arm64.REG_ELEM */ <= a.Reg && 211 a.Reg < (RBaseARM64+1<<11) /* arm64.REG_ELEM_END */ { 212 str += fmt.Sprintf("[%d]", a.Index) 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 if a.Scale == 0 { 233 // arm64 shifted or extended register offset, scale = 0. 234 str += fmt.Sprintf("(%v)", Rconv(int(a.Index))) 235 } else { 236 str += fmt.Sprintf("(%v*%d)", Rconv(int(a.Index)), int(a.Scale)) 237 } 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) == objabi.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 objabi.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 r := (v >> 16) & 31 285 str = fmt.Sprintf("%s%c%c%d", Rconv(r+RBaseARM64), op[0], op[1], (v>>10)&63) 286 default: 287 panic("TYPE_SHIFT is not supported on " + objabi.GOARCH) 288 } 289 290 case TYPE_REGREG: 291 str = fmt.Sprintf("(%v, %v)", Rconv(int(a.Reg)), Rconv(int(a.Offset))) 292 293 case TYPE_REGREG2: 294 str = fmt.Sprintf("%v, %v", Rconv(int(a.Offset)), Rconv(int(a.Reg))) 295 296 case TYPE_REGLIST: 297 str = RLconv(a.Offset) 298 } 299 300 return str 301 } 302 303 func Mconv(a *Addr) string { 304 var str string 305 306 switch a.Name { 307 default: 308 str = fmt.Sprintf("name=%d", a.Name) 309 310 case NAME_NONE: 311 switch { 312 case a.Reg == REG_NONE: 313 str = fmt.Sprint(a.Offset) 314 case a.Offset == 0: 315 str = fmt.Sprintf("(%v)", Rconv(int(a.Reg))) 316 case a.Offset != 0: 317 str = fmt.Sprintf("%d(%v)", a.Offset, Rconv(int(a.Reg))) 318 } 319 320 // Note: a.Reg == REG_NONE encodes the default base register for the NAME_ type. 321 case NAME_EXTERN: 322 reg := "SB" 323 if a.Reg != REG_NONE { 324 reg = Rconv(int(a.Reg)) 325 } 326 if a.Sym != nil { 327 str = fmt.Sprintf("%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg) 328 } else { 329 str = fmt.Sprintf("%s(%s)", offConv(a.Offset), reg) 330 } 331 332 case NAME_GOTREF: 333 reg := "SB" 334 if a.Reg != REG_NONE { 335 reg = Rconv(int(a.Reg)) 336 } 337 if a.Sym != nil { 338 str = fmt.Sprintf("%s%s@GOT(%s)", a.Sym.Name, offConv(a.Offset), reg) 339 } else { 340 str = fmt.Sprintf("%s@GOT(%s)", offConv(a.Offset), reg) 341 } 342 343 case NAME_STATIC: 344 reg := "SB" 345 if a.Reg != REG_NONE { 346 reg = Rconv(int(a.Reg)) 347 } 348 if a.Sym != nil { 349 str = fmt.Sprintf("%s<>%s(%s)", a.Sym.Name, offConv(a.Offset), reg) 350 } else { 351 str = fmt.Sprintf("<>%s(%s)", offConv(a.Offset), reg) 352 } 353 354 case NAME_AUTO: 355 reg := "SP" 356 if a.Reg != REG_NONE { 357 reg = Rconv(int(a.Reg)) 358 } 359 if a.Sym != nil { 360 str = fmt.Sprintf("%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg) 361 } else { 362 str = fmt.Sprintf("%s(%s)", offConv(a.Offset), reg) 363 } 364 365 case NAME_PARAM: 366 reg := "FP" 367 if a.Reg != REG_NONE { 368 reg = Rconv(int(a.Reg)) 369 } 370 if a.Sym != nil { 371 str = fmt.Sprintf("%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg) 372 } else { 373 str = fmt.Sprintf("%s(%s)", offConv(a.Offset), reg) 374 } 375 } 376 return str 377 } 378 379 func offConv(off int64) string { 380 if off == 0 { 381 return "" 382 } 383 return fmt.Sprintf("%+d", off) 384 } 385 386 // opSuffixSet is like regListSet, but for opcode suffixes. 387 // 388 // Unlike some other similar structures, uint8 space is not 389 // divided by its own values set (because there are only 256 of them). 390 // Instead, every arch may interpret/format all 8 bits as they like, 391 // as long as they register proper cconv function for it. 392 type opSuffixSet struct { 393 arch string 394 cconv func(suffix uint8) string 395 } 396 397 var opSuffixSpace []opSuffixSet 398 399 // RegisterOpSuffix assigns cconv function for formatting opcode suffixes 400 // when compiling for GOARCH=arch. 401 // 402 // cconv is never called with 0 argument. 403 func RegisterOpSuffix(arch string, cconv func(uint8) string) { 404 opSuffixSpace = append(opSuffixSpace, opSuffixSet{ 405 arch: arch, 406 cconv: cconv, 407 }) 408 } 409 410 type regSet struct { 411 lo int 412 hi int 413 Rconv func(int) string 414 } 415 416 // Few enough architectures that a linear scan is fastest. 417 // Not even worth sorting. 418 var regSpace []regSet 419 420 /* 421 Each architecture defines a register space as a unique 422 integer range. 423 Here is the list of architectures and the base of their register spaces. 424 */ 425 426 const ( 427 // Because of masking operations in the encodings, each register 428 // space should start at 0 modulo some power of 2. 429 RBase386 = 1 * 1024 430 RBaseAMD64 = 2 * 1024 431 RBaseARM = 3 * 1024 432 RBasePPC64 = 4 * 1024 // range [4k, 8k) 433 RBaseARM64 = 8 * 1024 // range [8k, 13k) 434 RBaseMIPS = 13 * 1024 // range [13k, 14k) 435 RBaseS390X = 14 * 1024 // range [14k, 15k) 436 RBaseWasm = 16 * 1024 437 ) 438 439 // RegisterRegister binds a pretty-printer (Rconv) for register 440 // numbers to a given register number range. Lo is inclusive, 441 // hi exclusive (valid registers are lo through hi-1). 442 func RegisterRegister(lo, hi int, Rconv func(int) string) { 443 regSpace = append(regSpace, regSet{lo, hi, Rconv}) 444 } 445 446 func Rconv(reg int) string { 447 if reg == REG_NONE { 448 return "NONE" 449 } 450 for i := range regSpace { 451 rs := ®Space[i] 452 if rs.lo <= reg && reg < rs.hi { 453 return rs.Rconv(reg) 454 } 455 } 456 return fmt.Sprintf("R???%d", reg) 457 } 458 459 type regListSet struct { 460 lo int64 461 hi int64 462 RLconv func(int64) string 463 } 464 465 var regListSpace []regListSet 466 467 // Each architecture is allotted a distinct subspace: [Lo, Hi) for declaring its 468 // arch-specific register list numbers. 469 const ( 470 RegListARMLo = 0 471 RegListARMHi = 1 << 16 472 473 // arm64 uses the 60th bit to differentiate from other archs 474 RegListARM64Lo = 1 << 60 475 RegListARM64Hi = 1<<61 - 1 476 477 // x86 uses the 61th bit to differentiate from other archs 478 RegListX86Lo = 1 << 61 479 RegListX86Hi = 1<<62 - 1 480 ) 481 482 // RegisterRegisterList binds a pretty-printer (RLconv) for register list 483 // numbers to a given register list number range. Lo is inclusive, 484 // hi exclusive (valid register list are lo through hi-1). 485 func RegisterRegisterList(lo, hi int64, rlconv func(int64) string) { 486 regListSpace = append(regListSpace, regListSet{lo, hi, rlconv}) 487 } 488 489 func RLconv(list int64) string { 490 for i := range regListSpace { 491 rls := ®ListSpace[i] 492 if rls.lo <= list && list < rls.hi { 493 return rls.RLconv(list) 494 } 495 } 496 return fmt.Sprintf("RL???%d", list) 497 } 498 499 type opSet struct { 500 lo As 501 names []string 502 } 503 504 // Not even worth sorting 505 var aSpace []opSet 506 507 // RegisterOpcode binds a list of instruction names 508 // to a given instruction number range. 509 func RegisterOpcode(lo As, Anames []string) { 510 if len(Anames) > AllowedOpCodes { 511 panic(fmt.Sprintf("too many instructions, have %d max %d", len(Anames), AllowedOpCodes)) 512 } 513 aSpace = append(aSpace, opSet{lo, Anames}) 514 } 515 516 func (a As) String() string { 517 if 0 <= a && int(a) < len(Anames) { 518 return Anames[a] 519 } 520 for i := range aSpace { 521 as := &aSpace[i] 522 if as.lo <= a && int(a-as.lo) < len(as.names) { 523 return as.names[a-as.lo] 524 } 525 } 526 return fmt.Sprintf("A???%d", a) 527 } 528 529 var Anames = []string{ 530 "XXX", 531 "CALL", 532 "DUFFCOPY", 533 "DUFFZERO", 534 "END", 535 "FUNCDATA", 536 "JMP", 537 "NOP", 538 "PCALIGN", 539 "PCDATA", 540 "RET", 541 "GETCALLERPC", 542 "TEXT", 543 "UNDEF", 544 } 545 546 func Bool2int(b bool) int { 547 // The compiler currently only optimizes this form. 548 // See issue 6011. 549 var i int 550 if b { 551 i = 1 552 } else { 553 i = 0 554 } 555 return i 556 }