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