| // 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 "<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 == 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 "<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.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 | |
| } | |
| func isZReg(r int) bool { | |
| return (r >= RBaseARM64+96 && r <= RBaseARM64+127) || | |
| (r >= RBaseARM64+2048 && r < RBaseARM64+3072) | |
| } | |
| // 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: | |
| if buildcfg.GOARCH == "arm64" && a.Offset&(int64(1)<<62) != 0 { | |
| preg := int((a.Offset)&31) + RBaseARM64 + 128 | |
| arng2 := (a.Offset >> 5) & 15 | |
| selreg := int((a.Offset>>9)&31) + RBaseARM64 | |
| idximm := (a.Offset >> 14) & 63 | |
| arngStr := "" | |
| switch arng2 { | |
| case 9: | |
| arngStr = ".B" | |
| case 10: | |
| arngStr = ".H" | |
| case 11: | |
| arngStr = ".S" | |
| case 12: | |
| arngStr = ".D" | |
| case 13: | |
| arngStr = ".Q" | |
| } | |
| fmt.Fprintf(w, "[%s,$%d](%s%s)", Rconv(selreg), idximm, Rconv(preg), arngStr) | |
| return | |
| } | |
| // 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_ZARNG */) || | |
| ((RBaseARM64+1<<11+1<<9) /* arm64.REG_ZARNGELEM */ <= a.Reg && a.Reg < (RBaseARM64+1<<11+1<<10+1<<9) /* arm64.REG_PARNGZM */) { | |
| fmt.Fprintf(w, "[%d]", a.Index) | |
| } | |
| if (RBaseLOONG64+(1<<12)+(1<<11)) /* loong64.REG_ELEM */ <= a.Reg && | |
| a.Reg < (RBaseLOONG64+(1<<12)+(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: | |
| if buildcfg.GOARCH == "arm64" && (a.Scale < 0 || (a.Index != REG_NONE && (isZReg(int(a.Reg)) || isZReg(int(a.Index))))) { | |
| // SVE extended addressing pattern | |
| if a.Index == REG_NONE { | |
| if a.Offset < 0 { | |
| fmt.Fprintf(w, "(-VL*%d)(%v)", -a.Offset, Rconv(int(a.Reg))) | |
| } else { | |
| fmt.Fprintf(w, "(VL*%d)(%v)", a.Offset, Rconv(int(a.Reg))) | |
| } | |
| } else { | |
| amount := 0 | |
| mod := 0 | |
| if a.Scale < 0 { | |
| amount = int((a.Scale >> 12) & 0x7) | |
| mod = int((a.Scale >> 9) & 0x7) | |
| } | |
| modStr := "" | |
| switch mod { | |
| case 1: | |
| modStr = ".UXTW" | |
| case 2: | |
| modStr = ".SXTW" | |
| } | |
| amountStr := "" | |
| if amount != 0 { | |
| amountStr = fmt.Sprintf("<<%d", amount) | |
| } | |
| fmt.Fprintf(w, "(%v%s%s)(%v)", Rconv(int(a.Reg)), modStr, amountStr, Rconv(int(a.Index))) | |
| } | |
| } else { | |
| 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, 18k) | |
| RBaseMIPS = 18 * 1024 // range [18k, 19k) | |
| RBaseS390X = 19 * 1024 // range [19k, 20k) | |
| RBaseRISCV = 20 * 1024 // range [20k, 21k) | |
| RBaseWasm = 21 * 1024 | |
| RBaseLOONG64 = 22 * 1024 // range [22K, 30k) | |
| ) | |
| // 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 | |
| // Each architecture is allotted a distinct subspace: [Lo, Hi) for declaring its | |
| // arch-specific special operands. | |
| const ( | |
| SpecialOperandARM64Base = 0 << 16 | |
| SpecialOperandRISCVBase = 1 << 16 | |
| ) | |
| // 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 alignment %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.Align < int16(a) { | |
| cursym.Align = int16(a) | |
| } | |
| } |