diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 13:14:23 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 13:14:23 +0000 |
commit | 73df946d56c74384511a194dd01dbe099584fd1a (patch) | |
tree | fd0bcea490dd81327ddfbb31e215439672c9a068 /src/cmd/internal/obj/util.go | |
parent | Initial commit. (diff) | |
download | golang-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.go | 636 |
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 := ®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 +} + +func abiDecorate(a *Addr, abiDetail bool) string { + if !abiDetail || a.Sym == nil { + return "" + } + return fmt.Sprintf("<%s>", a.Sym.ABI()) +} |