summaryrefslogtreecommitdiffstats
path: root/src/cmd/internal/obj/util.go
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 13:14:23 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 13:14:23 +0000
commit73df946d56c74384511a194dd01dbe099584fd1a (patch)
treefd0bcea490dd81327ddfbb31e215439672c9a068 /src/cmd/internal/obj/util.go
parentInitial commit. (diff)
downloadgolang-1.16-upstream.tar.xz
golang-1.16-upstream.zip
Adding upstream version 1.16.10.upstream/1.16.10upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--src/cmd/internal/obj/util.go636
1 files changed, 636 insertions, 0 deletions
diff --git a/src/cmd/internal/obj/util.go b/src/cmd/internal/obj/util.go
new file mode 100644
index 0000000..b9bacb7
--- /dev/null
+++ b/src/cmd/internal/obj/util.go
@@ -0,0 +1,636 @@
+// 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"
+ "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 {
+ 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.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)))
+ }
+ 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()
+}
+
+// DconvDconvWithABIDetail 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)
+ }
+
+ 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) == 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, abiDetail)
+
+ 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) {
+ 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
+)
+
+// 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 := &regSpace[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 := &regListSpace[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
+}
+
+func abiDecorate(a *Addr, abiDetail bool) string {
+ if !abiDetail || a.Sym == nil {
+ return ""
+ }
+ return fmt.Sprintf("<%s>", a.Sym.ABI())
+}