// Copyright 2015 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package obj import ( "bytes" "cmd/internal/objabi" "fmt" "internal/abi" "internal/buildcfg" "io" "strings" ) const REG_NONE = 0 // Line returns a string containing the filename and line number for p func (p *Prog) Line() string { return p.Ctxt.OutermostPos(p.Pos).Format(false, true) } func (p *Prog) InnermostLine(w io.Writer) { p.Ctxt.InnermostPos(p.Pos).WriteTo(w, false, true) } // InnermostLineNumber returns a string containing the line number for the // innermost inlined function (if any inlining) at p's position func (p *Prog) InnermostLineNumber() string { return p.Ctxt.InnermostPos(p.Pos).LineNumber() } // InnermostLineNumberHTML returns a string containing the line number for the // innermost inlined function (if any inlining) at p's position func (p *Prog) InnermostLineNumberHTML() string { return p.Ctxt.InnermostPos(p.Pos).LineNumberHTML() } // InnermostFilename returns a string containing the innermost // (in inlining) filename at p's position func (p *Prog) InnermostFilename() string { // TODO For now, this is only used for debugging output, and if we need more/better information, it might change. // 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. pos := p.Ctxt.InnermostPos(p.Pos) if !pos.IsKnown() { return "" } return pos.Filename() } var armCondCode = []string{ ".EQ", ".NE", ".CS", ".CC", ".MI", ".PL", ".VS", ".VC", ".HI", ".LS", ".GE", ".LT", ".GT", ".LE", "", ".NV", } /* ARM scond byte */ const ( C_SCOND = (1 << 4) - 1 C_SBIT = 1 << 4 C_PBIT = 1 << 5 C_WBIT = 1 << 6 C_FBIT = 1 << 7 C_UBIT = 1 << 7 C_SCOND_XOR = 14 ) // CConv formats opcode suffix bits (Prog.Scond). func CConv(s uint8) string { if s == 0 { return "" } for i := range opSuffixSpace { sset := &opSuffixSpace[i] if sset.arch == buildcfg.GOARCH { return sset.cconv(s) } } return fmt.Sprintf("SC???%d", s) } // CConvARM formats ARM opcode suffix bits (mostly condition codes). func CConvARM(s uint8) string { // TODO: could be great to move suffix-related things into // ARM asm backends some day. // obj/x86 can be used as an example. sc := armCondCode[(s&C_SCOND)^C_SCOND_XOR] if s&C_SBIT != 0 { sc += ".S" } if s&C_PBIT != 0 { sc += ".P" } if s&C_WBIT != 0 { sc += ".W" } if s&C_UBIT != 0 { /* ambiguous with FBIT */ sc += ".U" } return sc } func (p *Prog) String() string { if p == nil { return "" } if p.Ctxt == nil { return "" } return fmt.Sprintf("%.5d (%v)\t%s", p.Pc, p.Line(), p.InstructionString()) } func (p *Prog) InnermostString(w io.Writer) { if p == nil { io.WriteString(w, "") return } if p.Ctxt == nil { io.WriteString(w, "") return } fmt.Fprintf(w, "%.5d (", p.Pc) p.InnermostLine(w) io.WriteString(w, ")\t") p.WriteInstructionString(w) } // InstructionString returns a string representation of the instruction without preceding // program counter or file and line number. func (p *Prog) InstructionString() string { buf := new(bytes.Buffer) p.WriteInstructionString(buf) return buf.String() } // WriteInstructionString writes a string representation of the instruction without preceding // program counter or file and line number. func (p *Prog) WriteInstructionString(w io.Writer) { if p == nil { io.WriteString(w, "") return } if p.Ctxt == nil { io.WriteString(w, "") return } sc := CConv(p.Scond) io.WriteString(w, p.As.String()) io.WriteString(w, sc) sep := "\t" if p.From.Type != TYPE_NONE { io.WriteString(w, sep) WriteDconv(w, p, &p.From) sep = ", " } if p.Reg != REG_NONE { // Should not happen but might as well show it if it does. fmt.Fprintf(w, "%s%v", sep, Rconv(int(p.Reg))) sep = ", " } for i := range p.RestArgs { if p.RestArgs[i].Pos == Source { io.WriteString(w, sep) WriteDconv(w, p, &p.RestArgs[i].Addr) sep = ", " } } if p.As == ATEXT { // If there are attributes, print them. Otherwise, skip the comma. // In short, print one of these two: // TEXT foo(SB), DUPOK|NOSPLIT, $0 // TEXT foo(SB), $0 s := p.From.Sym.TextAttrString() if s != "" { fmt.Fprintf(w, "%s%s", sep, s) sep = ", " } } if p.To.Type != TYPE_NONE { io.WriteString(w, sep) WriteDconv(w, p, &p.To) sep = ", " } if p.RegTo2 != REG_NONE { fmt.Fprintf(w, "%s%v", sep, Rconv(int(p.RegTo2))) } for i := range p.RestArgs { if p.RestArgs[i].Pos == Destination { io.WriteString(w, sep) WriteDconv(w, p, &p.RestArgs[i].Addr) sep = ", " } } } func (ctxt *Link) NewProg() *Prog { p := new(Prog) p.Ctxt = ctxt return p } func (ctxt *Link) CanReuseProgs() bool { return ctxt.Debugasm == 0 } // Dconv accepts an argument 'a' within a prog 'p' and returns a string // with a formatted version of the argument. func Dconv(p *Prog, a *Addr) string { buf := new(bytes.Buffer) writeDconv(buf, p, a, false) return buf.String() } // DconvWithABIDetail accepts an argument 'a' within a prog 'p' // and returns a string with a formatted version of the argument, in // which text symbols are rendered with explicit ABI selectors. func DconvWithABIDetail(p *Prog, a *Addr) string { buf := new(bytes.Buffer) writeDconv(buf, p, a, true) return buf.String() } // WriteDconv accepts an argument 'a' within a prog 'p' // and writes a formatted version of the arg to the writer. func WriteDconv(w io.Writer, p *Prog, a *Addr) { writeDconv(w, p, a, false) } func writeDconv(w io.Writer, p *Prog, a *Addr, abiDetail bool) { switch a.Type { default: fmt.Fprintf(w, "type=%d", a.Type) case TYPE_NONE: if a.Name != NAME_NONE || a.Reg != 0 || a.Sym != nil { a.WriteNameTo(w) fmt.Fprintf(w, "(%v)(NONE)", Rconv(int(a.Reg))) } case TYPE_REG: // TODO(rsc): This special case is for x86 instructions like // PINSRQ CX,$1,X6 // where the $1 is included in the p->to Addr. // Move into a new field. if a.Offset != 0 && (a.Reg < RBaseARM64 || a.Reg >= RBaseMIPS) { fmt.Fprintf(w, "$%d,%v", a.Offset, Rconv(int(a.Reg))) return } if a.Name != NAME_NONE || a.Sym != nil { a.WriteNameTo(w) fmt.Fprintf(w, "(%v)(REG)", Rconv(int(a.Reg))) } else { io.WriteString(w, Rconv(int(a.Reg))) } if (RBaseARM64+1<<10+1<<9) /* arm64.REG_ELEM */ <= a.Reg && a.Reg < (RBaseARM64+1<<11) /* arm64.REG_ELEM_END */ { fmt.Fprintf(w, "[%d]", a.Index) } if (RBaseLOONG64+(1<<10)+(1<<11)) /* loong64.REG_ELEM */ <= a.Reg && a.Reg < (RBaseLOONG64+(1<<10)+(2<<11)) /* loong64.REG_ELEM_END */ { fmt.Fprintf(w, "[%d]", a.Index) } case TYPE_BRANCH: if a.Sym != nil { fmt.Fprintf(w, "%s%s(SB)", a.Sym.Name, abiDecorate(a, abiDetail)) } else if a.Target() != nil { fmt.Fprint(w, a.Target().Pc) } else { fmt.Fprintf(w, "%d(PC)", a.Offset) } case TYPE_INDIR: io.WriteString(w, "*") a.writeNameTo(w, abiDetail) case TYPE_MEM: a.WriteNameTo(w) if a.Index != REG_NONE { if a.Scale == 0 { // arm64 shifted or extended register offset, scale = 0. fmt.Fprintf(w, "(%v)", Rconv(int(a.Index))) } else { fmt.Fprintf(w, "(%v*%d)", Rconv(int(a.Index)), int(a.Scale)) } } case TYPE_CONST: io.WriteString(w, "$") a.WriteNameTo(w) if a.Reg != 0 { fmt.Fprintf(w, "(%v)", Rconv(int(a.Reg))) } case TYPE_TEXTSIZE: if a.Val.(int32) == abi.ArgsSizeUnknown { fmt.Fprintf(w, "$%d", a.Offset) } else { fmt.Fprintf(w, "$%d-%d", a.Offset, a.Val.(int32)) } case TYPE_FCONST: str := fmt.Sprintf("%.17g", a.Val.(float64)) // Make sure 1 prints as 1.0 if !strings.ContainsAny(str, ".e") { str += ".0" } fmt.Fprintf(w, "$(%s)", str) case TYPE_SCONST: fmt.Fprintf(w, "$%q", a.Val.(string)) case TYPE_ADDR: io.WriteString(w, "$") a.writeNameTo(w, abiDetail) case TYPE_SHIFT: v := int(a.Offset) ops := "<<>>->@>" switch buildcfg.GOARCH { case "arm": op := ops[((v>>5)&3)<<1:] if v&(1<<4) != 0 { fmt.Fprintf(w, "R%d%c%cR%d", v&15, op[0], op[1], (v>>8)&15) } else { fmt.Fprintf(w, "R%d%c%c%d", v&15, op[0], op[1], (v>>7)&31) } if a.Reg != 0 { fmt.Fprintf(w, "(%v)", Rconv(int(a.Reg))) } case "arm64": op := ops[((v>>22)&3)<<1:] r := (v >> 16) & 31 fmt.Fprintf(w, "%s%c%c%d", Rconv(r+RBaseARM64), op[0], op[1], (v>>10)&63) default: panic("TYPE_SHIFT is not supported on " + buildcfg.GOARCH) } case TYPE_REGREG: fmt.Fprintf(w, "(%v, %v)", Rconv(int(a.Reg)), Rconv(int(a.Offset))) case TYPE_REGREG2: fmt.Fprintf(w, "%v, %v", Rconv(int(a.Offset)), Rconv(int(a.Reg))) case TYPE_REGLIST: io.WriteString(w, RLconv(a.Offset)) case TYPE_SPECIAL: io.WriteString(w, SPCconv(a.Offset)) } } func (a *Addr) WriteNameTo(w io.Writer) { a.writeNameTo(w, false) } func (a *Addr) writeNameTo(w io.Writer, abiDetail bool) { switch a.Name { default: fmt.Fprintf(w, "name=%d", a.Name) case NAME_NONE: switch { case a.Reg == REG_NONE: fmt.Fprint(w, a.Offset) case a.Offset == 0: fmt.Fprintf(w, "(%v)", Rconv(int(a.Reg))) case a.Offset != 0: fmt.Fprintf(w, "%d(%v)", a.Offset, Rconv(int(a.Reg))) } // Note: a.Reg == REG_NONE encodes the default base register for the NAME_ type. case NAME_EXTERN: reg := "SB" if a.Reg != REG_NONE { reg = Rconv(int(a.Reg)) } if a.Sym != nil { fmt.Fprintf(w, "%s%s%s(%s)", a.Sym.Name, abiDecorate(a, abiDetail), offConv(a.Offset), reg) } else { fmt.Fprintf(w, "%s(%s)", offConv(a.Offset), reg) } case NAME_GOTREF: reg := "SB" if a.Reg != REG_NONE { reg = Rconv(int(a.Reg)) } if a.Sym != nil { fmt.Fprintf(w, "%s%s@GOT(%s)", a.Sym.Name, offConv(a.Offset), reg) } else { fmt.Fprintf(w, "%s@GOT(%s)", offConv(a.Offset), reg) } case NAME_STATIC: reg := "SB" if a.Reg != REG_NONE { reg = Rconv(int(a.Reg)) } if a.Sym != nil { fmt.Fprintf(w, "%s<>%s(%s)", a.Sym.Name, offConv(a.Offset), reg) } else { fmt.Fprintf(w, "<>%s(%s)", offConv(a.Offset), reg) } case NAME_AUTO: reg := "SP" if a.Reg != REG_NONE { reg = Rconv(int(a.Reg)) } if a.Sym != nil { fmt.Fprintf(w, "%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg) } else { fmt.Fprintf(w, "%s(%s)", offConv(a.Offset), reg) } case NAME_PARAM: reg := "FP" if a.Reg != REG_NONE { reg = Rconv(int(a.Reg)) } if a.Sym != nil { fmt.Fprintf(w, "%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg) } else { fmt.Fprintf(w, "%s(%s)", offConv(a.Offset), reg) } case NAME_TOCREF: reg := "SB" if a.Reg != REG_NONE { reg = Rconv(int(a.Reg)) } if a.Sym != nil { fmt.Fprintf(w, "%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg) } else { fmt.Fprintf(w, "%s(%s)", offConv(a.Offset), reg) } } } func offConv(off int64) string { if off == 0 { return "" } return fmt.Sprintf("%+d", off) } // opSuffixSet is like regListSet, but for opcode suffixes. // // Unlike some other similar structures, uint8 space is not // divided by its own values set (because there are only 256 of them). // Instead, every arch may interpret/format all 8 bits as they like, // as long as they register proper cconv function for it. type opSuffixSet struct { arch string cconv func(suffix uint8) string } var opSuffixSpace []opSuffixSet // RegisterOpSuffix assigns cconv function for formatting opcode suffixes // when compiling for GOARCH=arch. // // cconv is never called with 0 argument. func RegisterOpSuffix(arch string, cconv func(uint8) string) { opSuffixSpace = append(opSuffixSpace, opSuffixSet{ arch: arch, cconv: cconv, }) } type regSet struct { lo int hi int Rconv func(int) string } // Few enough architectures that a linear scan is fastest. // Not even worth sorting. var regSpace []regSet /* Each architecture defines a register space as a unique integer range. Here is the list of architectures and the base of their register spaces. */ const ( // Because of masking operations in the encodings, each register // space should start at 0 modulo some power of 2. RBase386 = 1 * 1024 RBaseAMD64 = 2 * 1024 RBaseARM = 3 * 1024 RBasePPC64 = 4 * 1024 // range [4k, 8k) RBaseARM64 = 8 * 1024 // range [8k, 13k) RBaseMIPS = 13 * 1024 // range [13k, 14k) RBaseS390X = 14 * 1024 // range [14k, 15k) RBaseRISCV = 15 * 1024 // range [15k, 16k) RBaseWasm = 16 * 1024 RBaseLOONG64 = 19 * 1024 // range [19K, 22k) ) // RegisterRegister binds a pretty-printer (Rconv) for register // numbers to a given register number range. Lo is inclusive, // hi exclusive (valid registers are lo through hi-1). func RegisterRegister(lo, hi int, Rconv func(int) string) { regSpace = append(regSpace, regSet{lo, hi, Rconv}) } func Rconv(reg int) string { if reg == REG_NONE { return "NONE" } for i := range regSpace { rs := ®Space[i] if rs.lo <= reg && reg < rs.hi { return rs.Rconv(reg) } } return fmt.Sprintf("R???%d", reg) } type regListSet struct { lo int64 hi int64 RLconv func(int64) string } var regListSpace []regListSet // Each architecture is allotted a distinct subspace: [Lo, Hi) for declaring its // arch-specific register list numbers. const ( RegListARMLo = 0 RegListARMHi = 1 << 16 // arm64 uses the 60th bit to differentiate from other archs RegListARM64Lo = 1 << 60 RegListARM64Hi = 1<<61 - 1 // x86 uses the 61th bit to differentiate from other archs RegListX86Lo = 1 << 61 RegListX86Hi = 1<<62 - 1 ) // RegisterRegisterList binds a pretty-printer (RLconv) for register list // numbers to a given register list number range. Lo is inclusive, // hi exclusive (valid register list are lo through hi-1). func RegisterRegisterList(lo, hi int64, rlconv func(int64) string) { regListSpace = append(regListSpace, regListSet{lo, hi, rlconv}) } func RLconv(list int64) string { for i := range regListSpace { rls := ®ListSpace[i] if rls.lo <= list && list < rls.hi { return rls.RLconv(list) } } return fmt.Sprintf("RL???%d", list) } // Special operands type spcSet struct { lo int64 hi int64 SPCconv func(int64) string } var spcSpace []spcSet // RegisterSpecialOperands binds a pretty-printer (SPCconv) for special // operand numbers to a given special operand number range. Lo is inclusive, // hi is exclusive (valid special operands are lo through hi-1). func RegisterSpecialOperands(lo, hi int64, rlconv func(int64) string) { spcSpace = append(spcSpace, spcSet{lo, hi, rlconv}) } // SPCconv returns the string representation of the special operand spc. func SPCconv(spc int64) string { for i := range spcSpace { spcs := &spcSpace[i] if spcs.lo <= spc && spc < spcs.hi { return spcs.SPCconv(spc) } } return fmt.Sprintf("SPC???%d", spc) } type opSet struct { lo As names []string } // Not even worth sorting var aSpace []opSet // RegisterOpcode binds a list of instruction names // to a given instruction number range. func RegisterOpcode(lo As, Anames []string) { if len(Anames) > AllowedOpCodes { panic(fmt.Sprintf("too many instructions, have %d max %d", len(Anames), AllowedOpCodes)) } aSpace = append(aSpace, opSet{lo, Anames}) } func (a As) String() string { if 0 <= a && int(a) < len(Anames) { return Anames[a] } for i := range aSpace { as := &aSpace[i] if as.lo <= a && int(a-as.lo) < len(as.names) { return as.names[a-as.lo] } } return fmt.Sprintf("A???%d", a) } var Anames = []string{ "XXX", "CALL", "DUFFCOPY", "DUFFZERO", "END", "FUNCDATA", "JMP", "NOP", "PCALIGN", "PCALIGNMAX", "PCDATA", "RET", "GETCALLERPC", "TEXT", "UNDEF", } func Bool2int(b bool) int { // The compiler currently only optimizes this form. // See issue 6011. var i int if b { i = 1 } else { i = 0 } return i } func abiDecorate(a *Addr, abiDetail bool) string { if !abiDetail || a.Sym == nil { return "" } return fmt.Sprintf("<%s>", a.Sym.ABI()) } // AlignmentPadding bytes to add to align code as requested. // Alignment is restricted to powers of 2 between 8 and 2048 inclusive. // // pc_: current offset in function, in bytes // p: a PCALIGN or PCALIGNMAX prog // ctxt: the context, for current function // cursym: current function being assembled // returns number of bytes of padding needed, // updates minimum alignment for the function. func AlignmentPadding(pc int32, p *Prog, ctxt *Link, cursym *LSym) int { v := AlignmentPaddingLength(pc, p, ctxt) requireAlignment(p.From.Offset, ctxt, cursym) return v } // AlignmentPaddingLength is the number of bytes to add to align code as requested. // Alignment is restricted to powers of 2 between 8 and 2048 inclusive. // This only computes the length and does not update the (missing parameter) // current function's own required alignment. // // pc: current offset in function, in bytes // p: a PCALIGN or PCALIGNMAX prog // ctxt: the context, for current function // returns number of bytes of padding needed, func AlignmentPaddingLength(pc int32, p *Prog, ctxt *Link) int { a := p.From.Offset if !((a&(a-1) == 0) && 8 <= a && a <= 2048) { ctxt.Diag("alignment value of an instruction must be a power of two and in the range [8, 2048], got %d\n", a) return 0 } pc64 := int64(pc) lob := pc64 & (a - 1) // Low Order Bits -- if not zero, then not aligned if p.As == APCALIGN { if lob != 0 { return int(a - lob) } return 0 } // emit as many as s bytes of padding to obtain alignment s := p.To.Offset if s < 0 || s >= a { ctxt.Diag("PCALIGNMAX 'amount' %d must be non-negative and smaller than the aligment %d\n", s, a) return 0 } if s >= a-lob { return int(a - lob) } return 0 } // requireAlignment ensures that the function is aligned enough to support // the required code alignment func requireAlignment(a int64, ctxt *Link, cursym *LSym) { // TODO remove explicit knowledge about AIX. if ctxt.Headtype != objabi.Haix && cursym.Func().Align < int32(a) { cursym.Func().Align = int32(a) } }