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