util.go (13849B)
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 "github.com/twitchyliquid64/golang-asm/objabi" 10 "fmt" 11 "io" 12 "strings" 13 ) 14 15 const REG_NONE = 0 16 17 // Line returns a string containing the filename and line number for p 18 func (p *Prog) Line() string { 19 return p.Ctxt.OutermostPos(p.Pos).Format(false, true) 20 } 21 func (p *Prog) InnermostLine(w io.Writer) { 22 p.Ctxt.InnermostPos(p.Pos).WriteTo(w, false, true) 23 } 24 25 // InnermostLineNumber returns a string containing the line number for the 26 // innermost inlined function (if any inlining) at p's position 27 func (p *Prog) InnermostLineNumber() string { 28 return p.Ctxt.InnermostPos(p.Pos).LineNumber() 29 } 30 31 // InnermostLineNumberHTML returns a string containing the line number for the 32 // innermost inlined function (if any inlining) at p's position 33 func (p *Prog) InnermostLineNumberHTML() string { 34 return p.Ctxt.InnermostPos(p.Pos).LineNumberHTML() 35 } 36 37 // InnermostFilename returns a string containing the innermost 38 // (in inlining) filename at p's position 39 func (p *Prog) InnermostFilename() string { 40 // TODO For now, this is only used for debugging output, and if we need more/better information, it might change. 41 // 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. 42 pos := p.Ctxt.InnermostPos(p.Pos) 43 if !pos.IsKnown() { 44 return "<unknown file name>" 45 } 46 return pos.Filename() 47 } 48 49 var armCondCode = []string{ 50 ".EQ", 51 ".NE", 52 ".CS", 53 ".CC", 54 ".MI", 55 ".PL", 56 ".VS", 57 ".VC", 58 ".HI", 59 ".LS", 60 ".GE", 61 ".LT", 62 ".GT", 63 ".LE", 64 "", 65 ".NV", 66 } 67 68 /* ARM scond byte */ 69 const ( 70 C_SCOND = (1 << 4) - 1 71 C_SBIT = 1 << 4 72 C_PBIT = 1 << 5 73 C_WBIT = 1 << 6 74 C_FBIT = 1 << 7 75 C_UBIT = 1 << 7 76 C_SCOND_XOR = 14 77 ) 78 79 // CConv formats opcode suffix bits (Prog.Scond). 80 func CConv(s uint8) string { 81 if s == 0 { 82 return "" 83 } 84 for i := range opSuffixSpace { 85 sset := &opSuffixSpace[i] 86 if sset.arch == objabi.GOARCH { 87 return sset.cconv(s) 88 } 89 } 90 return fmt.Sprintf("SC???%d", s) 91 } 92 93 // CConvARM formats ARM opcode suffix bits (mostly condition codes). 94 func CConvARM(s uint8) string { 95 // TODO: could be great to move suffix-related things into 96 // ARM asm backends some day. 97 // obj/x86 can be used as an example. 98 99 sc := armCondCode[(s&C_SCOND)^C_SCOND_XOR] 100 if s&C_SBIT != 0 { 101 sc += ".S" 102 } 103 if s&C_PBIT != 0 { 104 sc += ".P" 105 } 106 if s&C_WBIT != 0 { 107 sc += ".W" 108 } 109 if s&C_UBIT != 0 { /* ambiguous with FBIT */ 110 sc += ".U" 111 } 112 return sc 113 } 114 115 func (p *Prog) String() string { 116 if p == nil { 117 return "<nil Prog>" 118 } 119 if p.Ctxt == nil { 120 return "<Prog without ctxt>" 121 } 122 return fmt.Sprintf("%.5d (%v)\t%s", p.Pc, p.Line(), p.InstructionString()) 123 } 124 125 func (p *Prog) InnermostString(w io.Writer) { 126 if p == nil { 127 io.WriteString(w, "<nil Prog>") 128 return 129 } 130 if p.Ctxt == nil { 131 io.WriteString(w, "<Prog without ctxt>") 132 return 133 } 134 fmt.Fprintf(w, "%.5d (", p.Pc) 135 p.InnermostLine(w) 136 io.WriteString(w, ")\t") 137 p.WriteInstructionString(w) 138 } 139 140 // InstructionString returns a string representation of the instruction without preceding 141 // program counter or file and line number. 142 func (p *Prog) InstructionString() string { 143 buf := new(bytes.Buffer) 144 p.WriteInstructionString(buf) 145 return buf.String() 146 } 147 148 // WriteInstructionString writes a string representation of the instruction without preceding 149 // program counter or file and line number. 150 func (p *Prog) WriteInstructionString(w io.Writer) { 151 if p == nil { 152 io.WriteString(w, "<nil Prog>") 153 return 154 } 155 156 if p.Ctxt == nil { 157 io.WriteString(w, "<Prog without ctxt>") 158 return 159 } 160 161 sc := CConv(p.Scond) 162 163 io.WriteString(w, p.As.String()) 164 io.WriteString(w, sc) 165 sep := "\t" 166 167 if p.From.Type != TYPE_NONE { 168 io.WriteString(w, sep) 169 WriteDconv(w, p, &p.From) 170 sep = ", " 171 } 172 if p.Reg != REG_NONE { 173 // Should not happen but might as well show it if it does. 174 fmt.Fprintf(w, "%s%v", sep, Rconv(int(p.Reg))) 175 sep = ", " 176 } 177 for i := range p.RestArgs { 178 io.WriteString(w, sep) 179 WriteDconv(w, p, &p.RestArgs[i]) 180 sep = ", " 181 } 182 183 if p.As == ATEXT { 184 // If there are attributes, print them. Otherwise, skip the comma. 185 // In short, print one of these two: 186 // TEXT foo(SB), DUPOK|NOSPLIT, $0 187 // TEXT foo(SB), $0 188 s := p.From.Sym.Attribute.TextAttrString() 189 if s != "" { 190 fmt.Fprintf(w, "%s%s", sep, s) 191 sep = ", " 192 } 193 } 194 if p.To.Type != TYPE_NONE { 195 io.WriteString(w, sep) 196 WriteDconv(w, p, &p.To) 197 } 198 if p.RegTo2 != REG_NONE { 199 fmt.Fprintf(w, "%s%v", sep, Rconv(int(p.RegTo2))) 200 } 201 } 202 203 func (ctxt *Link) NewProg() *Prog { 204 p := new(Prog) 205 p.Ctxt = ctxt 206 return p 207 } 208 209 func (ctxt *Link) CanReuseProgs() bool { 210 return ctxt.Debugasm == 0 211 } 212 213 func Dconv(p *Prog, a *Addr) string { 214 buf := new(bytes.Buffer) 215 WriteDconv(buf, p, a) 216 return buf.String() 217 } 218 219 func WriteDconv(w io.Writer, p *Prog, a *Addr) { 220 switch a.Type { 221 default: 222 fmt.Fprintf(w, "type=%d", a.Type) 223 224 case TYPE_NONE: 225 if a.Name != NAME_NONE || a.Reg != 0 || a.Sym != nil { 226 a.WriteNameTo(w) 227 fmt.Fprintf(w, "(%v)(NONE)", 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 && (a.Reg < RBaseARM64 || a.Reg >= RBaseMIPS) { 236 fmt.Fprintf(w, "$%d,%v", a.Offset, Rconv(int(a.Reg))) 237 return 238 } 239 240 if a.Name != NAME_NONE || a.Sym != nil { 241 a.WriteNameTo(w) 242 fmt.Fprintf(w, "(%v)(REG)", Rconv(int(a.Reg))) 243 } else { 244 io.WriteString(w, Rconv(int(a.Reg))) 245 } 246 if (RBaseARM64+1<<10+1<<9) /* arm64.REG_ELEM */ <= a.Reg && 247 a.Reg < (RBaseARM64+1<<11) /* arm64.REG_ELEM_END */ { 248 fmt.Fprintf(w, "[%d]", a.Index) 249 } 250 251 case TYPE_BRANCH: 252 if a.Sym != nil { 253 fmt.Fprintf(w, "%s(SB)", a.Sym.Name) 254 } else if a.Target() != nil { 255 fmt.Fprint(w, a.Target().Pc) 256 } else { 257 fmt.Fprintf(w, "%d(PC)", a.Offset) 258 } 259 260 case TYPE_INDIR: 261 io.WriteString(w, "*") 262 a.WriteNameTo(w) 263 264 case TYPE_MEM: 265 a.WriteNameTo(w) 266 if a.Index != REG_NONE { 267 if a.Scale == 0 { 268 // arm64 shifted or extended register offset, scale = 0. 269 fmt.Fprintf(w, "(%v)", Rconv(int(a.Index))) 270 } else { 271 fmt.Fprintf(w, "(%v*%d)", Rconv(int(a.Index)), int(a.Scale)) 272 } 273 } 274 275 case TYPE_CONST: 276 io.WriteString(w, "$") 277 a.WriteNameTo(w) 278 if a.Reg != 0 { 279 fmt.Fprintf(w, "(%v)", Rconv(int(a.Reg))) 280 } 281 282 case TYPE_TEXTSIZE: 283 if a.Val.(int32) == objabi.ArgsSizeUnknown { 284 fmt.Fprintf(w, "$%d", a.Offset) 285 } else { 286 fmt.Fprintf(w, "$%d-%d", a.Offset, a.Val.(int32)) 287 } 288 289 case TYPE_FCONST: 290 str := fmt.Sprintf("%.17g", a.Val.(float64)) 291 // Make sure 1 prints as 1.0 292 if !strings.ContainsAny(str, ".e") { 293 str += ".0" 294 } 295 fmt.Fprintf(w, "$(%s)", str) 296 297 case TYPE_SCONST: 298 fmt.Fprintf(w, "$%q", a.Val.(string)) 299 300 case TYPE_ADDR: 301 io.WriteString(w, "$") 302 a.WriteNameTo(w) 303 304 case TYPE_SHIFT: 305 v := int(a.Offset) 306 ops := "<<>>->@>" 307 switch objabi.GOARCH { 308 case "arm": 309 op := ops[((v>>5)&3)<<1:] 310 if v&(1<<4) != 0 { 311 fmt.Fprintf(w, "R%d%c%cR%d", v&15, op[0], op[1], (v>>8)&15) 312 } else { 313 fmt.Fprintf(w, "R%d%c%c%d", v&15, op[0], op[1], (v>>7)&31) 314 } 315 if a.Reg != 0 { 316 fmt.Fprintf(w, "(%v)", Rconv(int(a.Reg))) 317 } 318 case "arm64": 319 op := ops[((v>>22)&3)<<1:] 320 r := (v >> 16) & 31 321 fmt.Fprintf(w, "%s%c%c%d", Rconv(r+RBaseARM64), op[0], op[1], (v>>10)&63) 322 default: 323 panic("TYPE_SHIFT is not supported on " + objabi.GOARCH) 324 } 325 326 case TYPE_REGREG: 327 fmt.Fprintf(w, "(%v, %v)", Rconv(int(a.Reg)), Rconv(int(a.Offset))) 328 329 case TYPE_REGREG2: 330 fmt.Fprintf(w, "%v, %v", Rconv(int(a.Offset)), Rconv(int(a.Reg))) 331 332 case TYPE_REGLIST: 333 io.WriteString(w, RLconv(a.Offset)) 334 } 335 } 336 337 func (a *Addr) WriteNameTo(w io.Writer) { 338 switch a.Name { 339 default: 340 fmt.Fprintf(w, "name=%d", a.Name) 341 342 case NAME_NONE: 343 switch { 344 case a.Reg == REG_NONE: 345 fmt.Fprint(w, a.Offset) 346 case a.Offset == 0: 347 fmt.Fprintf(w, "(%v)", Rconv(int(a.Reg))) 348 case a.Offset != 0: 349 fmt.Fprintf(w, "%d(%v)", a.Offset, Rconv(int(a.Reg))) 350 } 351 352 // Note: a.Reg == REG_NONE encodes the default base register for the NAME_ type. 353 case NAME_EXTERN: 354 reg := "SB" 355 if a.Reg != REG_NONE { 356 reg = Rconv(int(a.Reg)) 357 } 358 if a.Sym != nil { 359 fmt.Fprintf(w, "%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg) 360 } else { 361 fmt.Fprintf(w, "%s(%s)", offConv(a.Offset), reg) 362 } 363 364 case NAME_GOTREF: 365 reg := "SB" 366 if a.Reg != REG_NONE { 367 reg = Rconv(int(a.Reg)) 368 } 369 if a.Sym != nil { 370 fmt.Fprintf(w, "%s%s@GOT(%s)", a.Sym.Name, offConv(a.Offset), reg) 371 } else { 372 fmt.Fprintf(w, "%s@GOT(%s)", offConv(a.Offset), reg) 373 } 374 375 case NAME_STATIC: 376 reg := "SB" 377 if a.Reg != REG_NONE { 378 reg = Rconv(int(a.Reg)) 379 } 380 if a.Sym != nil { 381 fmt.Fprintf(w, "%s<>%s(%s)", a.Sym.Name, offConv(a.Offset), reg) 382 } else { 383 fmt.Fprintf(w, "<>%s(%s)", offConv(a.Offset), reg) 384 } 385 386 case NAME_AUTO: 387 reg := "SP" 388 if a.Reg != REG_NONE { 389 reg = Rconv(int(a.Reg)) 390 } 391 if a.Sym != nil { 392 fmt.Fprintf(w, "%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg) 393 } else { 394 fmt.Fprintf(w, "%s(%s)", offConv(a.Offset), reg) 395 } 396 397 case NAME_PARAM: 398 reg := "FP" 399 if a.Reg != REG_NONE { 400 reg = Rconv(int(a.Reg)) 401 } 402 if a.Sym != nil { 403 fmt.Fprintf(w, "%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg) 404 } else { 405 fmt.Fprintf(w, "%s(%s)", offConv(a.Offset), reg) 406 } 407 case NAME_TOCREF: 408 reg := "SB" 409 if a.Reg != REG_NONE { 410 reg = Rconv(int(a.Reg)) 411 } 412 if a.Sym != nil { 413 fmt.Fprintf(w, "%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg) 414 } else { 415 fmt.Fprintf(w, "%s(%s)", offConv(a.Offset), reg) 416 } 417 } 418 } 419 420 func offConv(off int64) string { 421 if off == 0 { 422 return "" 423 } 424 return fmt.Sprintf("%+d", off) 425 } 426 427 // opSuffixSet is like regListSet, but for opcode suffixes. 428 // 429 // Unlike some other similar structures, uint8 space is not 430 // divided by its own values set (because there are only 256 of them). 431 // Instead, every arch may interpret/format all 8 bits as they like, 432 // as long as they register proper cconv function for it. 433 type opSuffixSet struct { 434 arch string 435 cconv func(suffix uint8) string 436 } 437 438 var opSuffixSpace []opSuffixSet 439 440 // RegisterOpSuffix assigns cconv function for formatting opcode suffixes 441 // when compiling for GOARCH=arch. 442 // 443 // cconv is never called with 0 argument. 444 func RegisterOpSuffix(arch string, cconv func(uint8) string) { 445 opSuffixSpace = append(opSuffixSpace, opSuffixSet{ 446 arch: arch, 447 cconv: cconv, 448 }) 449 } 450 451 type regSet struct { 452 lo int 453 hi int 454 Rconv func(int) string 455 } 456 457 // Few enough architectures that a linear scan is fastest. 458 // Not even worth sorting. 459 var regSpace []regSet 460 461 /* 462 Each architecture defines a register space as a unique 463 integer range. 464 Here is the list of architectures and the base of their register spaces. 465 */ 466 467 const ( 468 // Because of masking operations in the encodings, each register 469 // space should start at 0 modulo some power of 2. 470 RBase386 = 1 * 1024 471 RBaseAMD64 = 2 * 1024 472 RBaseARM = 3 * 1024 473 RBasePPC64 = 4 * 1024 // range [4k, 8k) 474 RBaseARM64 = 8 * 1024 // range [8k, 13k) 475 RBaseMIPS = 13 * 1024 // range [13k, 14k) 476 RBaseS390X = 14 * 1024 // range [14k, 15k) 477 RBaseRISCV = 15 * 1024 // range [15k, 16k) 478 RBaseWasm = 16 * 1024 479 ) 480 481 // RegisterRegister binds a pretty-printer (Rconv) for register 482 // numbers to a given register number range. Lo is inclusive, 483 // hi exclusive (valid registers are lo through hi-1). 484 func RegisterRegister(lo, hi int, Rconv func(int) string) { 485 regSpace = append(regSpace, regSet{lo, hi, Rconv}) 486 } 487 488 func Rconv(reg int) string { 489 if reg == REG_NONE { 490 return "NONE" 491 } 492 for i := range regSpace { 493 rs := ®Space[i] 494 if rs.lo <= reg && reg < rs.hi { 495 return rs.Rconv(reg) 496 } 497 } 498 return fmt.Sprintf("R???%d", reg) 499 } 500 501 type regListSet struct { 502 lo int64 503 hi int64 504 RLconv func(int64) string 505 } 506 507 var regListSpace []regListSet 508 509 // Each architecture is allotted a distinct subspace: [Lo, Hi) for declaring its 510 // arch-specific register list numbers. 511 const ( 512 RegListARMLo = 0 513 RegListARMHi = 1 << 16 514 515 // arm64 uses the 60th bit to differentiate from other archs 516 RegListARM64Lo = 1 << 60 517 RegListARM64Hi = 1<<61 - 1 518 519 // x86 uses the 61th bit to differentiate from other archs 520 RegListX86Lo = 1 << 61 521 RegListX86Hi = 1<<62 - 1 522 ) 523 524 // RegisterRegisterList binds a pretty-printer (RLconv) for register list 525 // numbers to a given register list number range. Lo is inclusive, 526 // hi exclusive (valid register list are lo through hi-1). 527 func RegisterRegisterList(lo, hi int64, rlconv func(int64) string) { 528 regListSpace = append(regListSpace, regListSet{lo, hi, rlconv}) 529 } 530 531 func RLconv(list int64) string { 532 for i := range regListSpace { 533 rls := ®ListSpace[i] 534 if rls.lo <= list && list < rls.hi { 535 return rls.RLconv(list) 536 } 537 } 538 return fmt.Sprintf("RL???%d", list) 539 } 540 541 type opSet struct { 542 lo As 543 names []string 544 } 545 546 // Not even worth sorting 547 var aSpace []opSet 548 549 // RegisterOpcode binds a list of instruction names 550 // to a given instruction number range. 551 func RegisterOpcode(lo As, Anames []string) { 552 if len(Anames) > AllowedOpCodes { 553 panic(fmt.Sprintf("too many instructions, have %d max %d", len(Anames), AllowedOpCodes)) 554 } 555 aSpace = append(aSpace, opSet{lo, Anames}) 556 } 557 558 func (a As) String() string { 559 if 0 <= a && int(a) < len(Anames) { 560 return Anames[a] 561 } 562 for i := range aSpace { 563 as := &aSpace[i] 564 if as.lo <= a && int(a-as.lo) < len(as.names) { 565 return as.names[a-as.lo] 566 } 567 } 568 return fmt.Sprintf("A???%d", a) 569 } 570 571 var Anames = []string{ 572 "XXX", 573 "CALL", 574 "DUFFCOPY", 575 "DUFFZERO", 576 "END", 577 "FUNCDATA", 578 "JMP", 579 "NOP", 580 "PCALIGN", 581 "PCDATA", 582 "RET", 583 "GETCALLERPC", 584 "TEXT", 585 "UNDEF", 586 } 587 588 func Bool2int(b bool) int { 589 // The compiler currently only optimizes this form. 590 // See issue 6011. 591 var i int 592 if b { 593 i = 1 594 } else { 595 i = 0 596 } 597 return i 598 }