podman
598 строк · 13.5 Кб
1// Copyright 2015 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package obj6
7import (8"bytes"9"github.com/twitchyliquid64/golang-asm/objabi"10"fmt"11"io"12"strings"13)
14
15const REG_NONE = 016
17// Line returns a string containing the filename and line number for p
18func (p *Prog) Line() string {19return p.Ctxt.OutermostPos(p.Pos).Format(false, true)20}
21func (p *Prog) InnermostLine(w io.Writer) {22p.Ctxt.InnermostPos(p.Pos).WriteTo(w, false, true)23}
24
25// InnermostLineNumber returns a string containing the line number for the
26// innermost inlined function (if any inlining) at p's position
27func (p *Prog) InnermostLineNumber() string {28return p.Ctxt.InnermostPos(p.Pos).LineNumber()29}
30
31// InnermostLineNumberHTML returns a string containing the line number for the
32// innermost inlined function (if any inlining) at p's position
33func (p *Prog) InnermostLineNumberHTML() string {34return p.Ctxt.InnermostPos(p.Pos).LineNumberHTML()35}
36
37// InnermostFilename returns a string containing the innermost
38// (in inlining) filename at p's position
39func (p *Prog) InnermostFilename() string {40// TODO For now, this is only used for debugging output, and if we need more/better information, it might change.41// 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.42pos := p.Ctxt.InnermostPos(p.Pos)43if !pos.IsKnown() {44return "<unknown file name>"45}46return pos.Filename()47}
48
49var armCondCode = []string{50".EQ",51".NE",52".CS",53".CC",54".MI",55".PL",56".VS",57".VC",58".HI",59".LS",60".GE",61".LT",62".GT",63".LE",64"",65".NV",66}
67
68/* ARM scond byte */
69const (70C_SCOND = (1 << 4) - 171C_SBIT = 1 << 472C_PBIT = 1 << 573C_WBIT = 1 << 674C_FBIT = 1 << 775C_UBIT = 1 << 776C_SCOND_XOR = 1477)
78
79// CConv formats opcode suffix bits (Prog.Scond).
80func CConv(s uint8) string {81if s == 0 {82return ""83}84for i := range opSuffixSpace {85sset := &opSuffixSpace[i]86if sset.arch == objabi.GOARCH {87return sset.cconv(s)88}89}90return fmt.Sprintf("SC???%d", s)91}
92
93// CConvARM formats ARM opcode suffix bits (mostly condition codes).
94func CConvARM(s uint8) string {95// TODO: could be great to move suffix-related things into96// ARM asm backends some day.97// obj/x86 can be used as an example.98
99sc := armCondCode[(s&C_SCOND)^C_SCOND_XOR]100if s&C_SBIT != 0 {101sc += ".S"102}103if s&C_PBIT != 0 {104sc += ".P"105}106if s&C_WBIT != 0 {107sc += ".W"108}109if s&C_UBIT != 0 { /* ambiguous with FBIT */110sc += ".U"111}112return sc113}
114
115func (p *Prog) String() string {116if p == nil {117return "<nil Prog>"118}119if p.Ctxt == nil {120return "<Prog without ctxt>"121}122return fmt.Sprintf("%.5d (%v)\t%s", p.Pc, p.Line(), p.InstructionString())123}
124
125func (p *Prog) InnermostString(w io.Writer) {126if p == nil {127io.WriteString(w, "<nil Prog>")128return129}130if p.Ctxt == nil {131io.WriteString(w, "<Prog without ctxt>")132return133}134fmt.Fprintf(w, "%.5d (", p.Pc)135p.InnermostLine(w)136io.WriteString(w, ")\t")137p.WriteInstructionString(w)138}
139
140// InstructionString returns a string representation of the instruction without preceding
141// program counter or file and line number.
142func (p *Prog) InstructionString() string {143buf := new(bytes.Buffer)144p.WriteInstructionString(buf)145return buf.String()146}
147
148// WriteInstructionString writes a string representation of the instruction without preceding
149// program counter or file and line number.
150func (p *Prog) WriteInstructionString(w io.Writer) {151if p == nil {152io.WriteString(w, "<nil Prog>")153return154}155
156if p.Ctxt == nil {157io.WriteString(w, "<Prog without ctxt>")158return159}160
161sc := CConv(p.Scond)162
163io.WriteString(w, p.As.String())164io.WriteString(w, sc)165sep := "\t"166
167if p.From.Type != TYPE_NONE {168io.WriteString(w, sep)169WriteDconv(w, p, &p.From)170sep = ", "171}172if p.Reg != REG_NONE {173// Should not happen but might as well show it if it does.174fmt.Fprintf(w, "%s%v", sep, Rconv(int(p.Reg)))175sep = ", "176}177for i := range p.RestArgs {178io.WriteString(w, sep)179WriteDconv(w, p, &p.RestArgs[i])180sep = ", "181}182
183if p.As == ATEXT {184// If there are attributes, print them. Otherwise, skip the comma.185// In short, print one of these two:186// TEXT foo(SB), DUPOK|NOSPLIT, $0187// TEXT foo(SB), $0188s := p.From.Sym.Attribute.TextAttrString()189if s != "" {190fmt.Fprintf(w, "%s%s", sep, s)191sep = ", "192}193}194if p.To.Type != TYPE_NONE {195io.WriteString(w, sep)196WriteDconv(w, p, &p.To)197}198if p.RegTo2 != REG_NONE {199fmt.Fprintf(w, "%s%v", sep, Rconv(int(p.RegTo2)))200}201}
202
203func (ctxt *Link) NewProg() *Prog {204p := new(Prog)205p.Ctxt = ctxt206return p207}
208
209func (ctxt *Link) CanReuseProgs() bool {210return ctxt.Debugasm == 0211}
212
213func Dconv(p *Prog, a *Addr) string {214buf := new(bytes.Buffer)215WriteDconv(buf, p, a)216return buf.String()217}
218
219func WriteDconv(w io.Writer, p *Prog, a *Addr) {220switch a.Type {221default:222fmt.Fprintf(w, "type=%d", a.Type)223
224case TYPE_NONE:225if a.Name != NAME_NONE || a.Reg != 0 || a.Sym != nil {226a.WriteNameTo(w)227fmt.Fprintf(w, "(%v)(NONE)", Rconv(int(a.Reg)))228}229
230case TYPE_REG:231// TODO(rsc): This special case is for x86 instructions like232// PINSRQ CX,$1,X6233// where the $1 is included in the p->to Addr.234// Move into a new field.235if a.Offset != 0 && (a.Reg < RBaseARM64 || a.Reg >= RBaseMIPS) {236fmt.Fprintf(w, "$%d,%v", a.Offset, Rconv(int(a.Reg)))237return238}239
240if a.Name != NAME_NONE || a.Sym != nil {241a.WriteNameTo(w)242fmt.Fprintf(w, "(%v)(REG)", Rconv(int(a.Reg)))243} else {244io.WriteString(w, Rconv(int(a.Reg)))245}246if (RBaseARM64+1<<10+1<<9) /* arm64.REG_ELEM */ <= a.Reg &&247a.Reg < (RBaseARM64+1<<11) /* arm64.REG_ELEM_END */ {248fmt.Fprintf(w, "[%d]", a.Index)249}250
251case TYPE_BRANCH:252if a.Sym != nil {253fmt.Fprintf(w, "%s(SB)", a.Sym.Name)254} else if a.Target() != nil {255fmt.Fprint(w, a.Target().Pc)256} else {257fmt.Fprintf(w, "%d(PC)", a.Offset)258}259
260case TYPE_INDIR:261io.WriteString(w, "*")262a.WriteNameTo(w)263
264case TYPE_MEM:265a.WriteNameTo(w)266if a.Index != REG_NONE {267if a.Scale == 0 {268// arm64 shifted or extended register offset, scale = 0.269fmt.Fprintf(w, "(%v)", Rconv(int(a.Index)))270} else {271fmt.Fprintf(w, "(%v*%d)", Rconv(int(a.Index)), int(a.Scale))272}273}274
275case TYPE_CONST:276io.WriteString(w, "$")277a.WriteNameTo(w)278if a.Reg != 0 {279fmt.Fprintf(w, "(%v)", Rconv(int(a.Reg)))280}281
282case TYPE_TEXTSIZE:283if a.Val.(int32) == objabi.ArgsSizeUnknown {284fmt.Fprintf(w, "$%d", a.Offset)285} else {286fmt.Fprintf(w, "$%d-%d", a.Offset, a.Val.(int32))287}288
289case TYPE_FCONST:290str := fmt.Sprintf("%.17g", a.Val.(float64))291// Make sure 1 prints as 1.0292if !strings.ContainsAny(str, ".e") {293str += ".0"294}295fmt.Fprintf(w, "$(%s)", str)296
297case TYPE_SCONST:298fmt.Fprintf(w, "$%q", a.Val.(string))299
300case TYPE_ADDR:301io.WriteString(w, "$")302a.WriteNameTo(w)303
304case TYPE_SHIFT:305v := int(a.Offset)306ops := "<<>>->@>"307switch objabi.GOARCH {308case "arm":309op := ops[((v>>5)&3)<<1:]310if v&(1<<4) != 0 {311fmt.Fprintf(w, "R%d%c%cR%d", v&15, op[0], op[1], (v>>8)&15)312} else {313fmt.Fprintf(w, "R%d%c%c%d", v&15, op[0], op[1], (v>>7)&31)314}315if a.Reg != 0 {316fmt.Fprintf(w, "(%v)", Rconv(int(a.Reg)))317}318case "arm64":319op := ops[((v>>22)&3)<<1:]320r := (v >> 16) & 31321fmt.Fprintf(w, "%s%c%c%d", Rconv(r+RBaseARM64), op[0], op[1], (v>>10)&63)322default:323panic("TYPE_SHIFT is not supported on " + objabi.GOARCH)324}325
326case TYPE_REGREG:327fmt.Fprintf(w, "(%v, %v)", Rconv(int(a.Reg)), Rconv(int(a.Offset)))328
329case TYPE_REGREG2:330fmt.Fprintf(w, "%v, %v", Rconv(int(a.Offset)), Rconv(int(a.Reg)))331
332case TYPE_REGLIST:333io.WriteString(w, RLconv(a.Offset))334}335}
336
337func (a *Addr) WriteNameTo(w io.Writer) {338switch a.Name {339default:340fmt.Fprintf(w, "name=%d", a.Name)341
342case NAME_NONE:343switch {344case a.Reg == REG_NONE:345fmt.Fprint(w, a.Offset)346case a.Offset == 0:347fmt.Fprintf(w, "(%v)", Rconv(int(a.Reg)))348case a.Offset != 0:349fmt.Fprintf(w, "%d(%v)", a.Offset, Rconv(int(a.Reg)))350}351
352// Note: a.Reg == REG_NONE encodes the default base register for the NAME_ type.353case NAME_EXTERN:354reg := "SB"355if a.Reg != REG_NONE {356reg = Rconv(int(a.Reg))357}358if a.Sym != nil {359fmt.Fprintf(w, "%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg)360} else {361fmt.Fprintf(w, "%s(%s)", offConv(a.Offset), reg)362}363
364case NAME_GOTREF:365reg := "SB"366if a.Reg != REG_NONE {367reg = Rconv(int(a.Reg))368}369if a.Sym != nil {370fmt.Fprintf(w, "%s%s@GOT(%s)", a.Sym.Name, offConv(a.Offset), reg)371} else {372fmt.Fprintf(w, "%s@GOT(%s)", offConv(a.Offset), reg)373}374
375case NAME_STATIC:376reg := "SB"377if a.Reg != REG_NONE {378reg = Rconv(int(a.Reg))379}380if a.Sym != nil {381fmt.Fprintf(w, "%s<>%s(%s)", a.Sym.Name, offConv(a.Offset), reg)382} else {383fmt.Fprintf(w, "<>%s(%s)", offConv(a.Offset), reg)384}385
386case NAME_AUTO:387reg := "SP"388if a.Reg != REG_NONE {389reg = Rconv(int(a.Reg))390}391if a.Sym != nil {392fmt.Fprintf(w, "%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg)393} else {394fmt.Fprintf(w, "%s(%s)", offConv(a.Offset), reg)395}396
397case NAME_PARAM:398reg := "FP"399if a.Reg != REG_NONE {400reg = Rconv(int(a.Reg))401}402if a.Sym != nil {403fmt.Fprintf(w, "%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg)404} else {405fmt.Fprintf(w, "%s(%s)", offConv(a.Offset), reg)406}407case NAME_TOCREF:408reg := "SB"409if a.Reg != REG_NONE {410reg = Rconv(int(a.Reg))411}412if a.Sym != nil {413fmt.Fprintf(w, "%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg)414} else {415fmt.Fprintf(w, "%s(%s)", offConv(a.Offset), reg)416}417}418}
419
420func offConv(off int64) string {421if off == 0 {422return ""423}424return fmt.Sprintf("%+d", off)425}
426
427// opSuffixSet is like regListSet, but for opcode suffixes.
428//
429// Unlike some other similar structures, uint8 space is not
430// divided by its own values set (because there are only 256 of them).
431// Instead, every arch may interpret/format all 8 bits as they like,
432// as long as they register proper cconv function for it.
433type opSuffixSet struct {434arch string435cconv func(suffix uint8) string436}
437
438var opSuffixSpace []opSuffixSet439
440// RegisterOpSuffix assigns cconv function for formatting opcode suffixes
441// when compiling for GOARCH=arch.
442//
443// cconv is never called with 0 argument.
444func RegisterOpSuffix(arch string, cconv func(uint8) string) {445opSuffixSpace = append(opSuffixSpace, opSuffixSet{446arch: arch,447cconv: cconv,448})449}
450
451type regSet struct {452lo int453hi int454Rconv func(int) string455}
456
457// Few enough architectures that a linear scan is fastest.
458// Not even worth sorting.
459var regSpace []regSet460
461/*
462Each architecture defines a register space as a unique
463integer range.
464Here is the list of architectures and the base of their register spaces.
465*/
466
467const (468// Because of masking operations in the encodings, each register469// space should start at 0 modulo some power of 2.470RBase386 = 1 * 1024471RBaseAMD64 = 2 * 1024472RBaseARM = 3 * 1024473RBasePPC64 = 4 * 1024 // range [4k, 8k)474RBaseARM64 = 8 * 1024 // range [8k, 13k)475RBaseMIPS = 13 * 1024 // range [13k, 14k)476RBaseS390X = 14 * 1024 // range [14k, 15k)477RBaseRISCV = 15 * 1024 // range [15k, 16k)478RBaseWasm = 16 * 1024479)
480
481// RegisterRegister binds a pretty-printer (Rconv) for register
482// numbers to a given register number range. Lo is inclusive,
483// hi exclusive (valid registers are lo through hi-1).
484func RegisterRegister(lo, hi int, Rconv func(int) string) {485regSpace = append(regSpace, regSet{lo, hi, Rconv})486}
487
488func Rconv(reg int) string {489if reg == REG_NONE {490return "NONE"491}492for i := range regSpace {493rs := ®Space[i]494if rs.lo <= reg && reg < rs.hi {495return rs.Rconv(reg)496}497}498return fmt.Sprintf("R???%d", reg)499}
500
501type regListSet struct {502lo int64503hi int64504RLconv func(int64) string505}
506
507var regListSpace []regListSet508
509// Each architecture is allotted a distinct subspace: [Lo, Hi) for declaring its
510// arch-specific register list numbers.
511const (512RegListARMLo = 0513RegListARMHi = 1 << 16514
515// arm64 uses the 60th bit to differentiate from other archs516RegListARM64Lo = 1 << 60517RegListARM64Hi = 1<<61 - 1518
519// x86 uses the 61th bit to differentiate from other archs520RegListX86Lo = 1 << 61521RegListX86Hi = 1<<62 - 1522)
523
524// RegisterRegisterList binds a pretty-printer (RLconv) for register list
525// numbers to a given register list number range. Lo is inclusive,
526// hi exclusive (valid register list are lo through hi-1).
527func RegisterRegisterList(lo, hi int64, rlconv func(int64) string) {528regListSpace = append(regListSpace, regListSet{lo, hi, rlconv})529}
530
531func RLconv(list int64) string {532for i := range regListSpace {533rls := ®ListSpace[i]534if rls.lo <= list && list < rls.hi {535return rls.RLconv(list)536}537}538return fmt.Sprintf("RL???%d", list)539}
540
541type opSet struct {542lo As
543names []string544}
545
546// Not even worth sorting
547var aSpace []opSet548
549// RegisterOpcode binds a list of instruction names
550// to a given instruction number range.
551func RegisterOpcode(lo As, Anames []string) {552if len(Anames) > AllowedOpCodes {553panic(fmt.Sprintf("too many instructions, have %d max %d", len(Anames), AllowedOpCodes))554}555aSpace = append(aSpace, opSet{lo, Anames})556}
557
558func (a As) String() string {559if 0 <= a && int(a) < len(Anames) {560return Anames[a]561}562for i := range aSpace {563as := &aSpace[i]564if as.lo <= a && int(a-as.lo) < len(as.names) {565return as.names[a-as.lo]566}567}568return fmt.Sprintf("A???%d", a)569}
570
571var Anames = []string{572"XXX",573"CALL",574"DUFFCOPY",575"DUFFZERO",576"END",577"FUNCDATA",578"JMP",579"NOP",580"PCALIGN",581"PCDATA",582"RET",583"GETCALLERPC",584"TEXT",585"UNDEF",586}
587
588func Bool2int(b bool) int {589// The compiler currently only optimizes this form.590// See issue 6011.591var i int592if b {593i = 1594} else {595i = 0596}597return i598}
599