123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598 |
- // 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"
- "github.com/twitchyliquid64/golang-asm/objabi"
- "fmt"
- "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 "<unknown file name>"
- }
- 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 == objabi.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 "<nil Prog>"
- }
- if p.Ctxt == nil {
- return "<Prog without ctxt>"
- }
- 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, "<nil Prog>")
- return
- }
- if p.Ctxt == nil {
- io.WriteString(w, "<Prog without ctxt>")
- 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, "<nil Prog>")
- return
- }
- if p.Ctxt == nil {
- io.WriteString(w, "<Prog without ctxt>")
- 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 {
- io.WriteString(w, sep)
- WriteDconv(w, p, &p.RestArgs[i])
- 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.Attribute.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)
- }
- if p.RegTo2 != REG_NONE {
- fmt.Fprintf(w, "%s%v", sep, Rconv(int(p.RegTo2)))
- }
- }
- func (ctxt *Link) NewProg() *Prog {
- p := new(Prog)
- p.Ctxt = ctxt
- return p
- }
- func (ctxt *Link) CanReuseProgs() bool {
- return ctxt.Debugasm == 0
- }
- func Dconv(p *Prog, a *Addr) string {
- buf := new(bytes.Buffer)
- WriteDconv(buf, p, a)
- return buf.String()
- }
- func WriteDconv(w io.Writer, p *Prog, a *Addr) {
- 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)
- }
- case TYPE_BRANCH:
- if a.Sym != nil {
- fmt.Fprintf(w, "%s(SB)", a.Sym.Name)
- } 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)
- 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) == objabi.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)
- case TYPE_SHIFT:
- v := int(a.Offset)
- ops := "<<>>->@>"
- switch objabi.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 " + objabi.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))
- }
- }
- func (a *Addr) WriteNameTo(w io.Writer) {
- 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)", a.Sym.Name, 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
- )
- // 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)
- }
- 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",
- "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
- }
|