...

Source file src/github.com/twitchyliquid64/golang-asm/obj/util.go

Documentation: github.com/twitchyliquid64/golang-asm/obj

     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  
     5  package obj
     6  
     7  import (
     8  	"bytes"
     9  	"github.com/twitchyliquid64/golang-asm/objabi"
    10  	"fmt"
    11  	"io"
    12  	"strings"
    13  )
    14  
    15  const REG_NONE = 0
    16  
    17  // Line returns a string containing the filename and line number for p
    18  func (p *Prog) Line() string {
    19  	return p.Ctxt.OutermostPos(p.Pos).Format(false, true)
    20  }
    21  func (p *Prog) InnermostLine(w io.Writer) {
    22  	p.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
    27  func (p *Prog) InnermostLineNumber() string {
    28  	return 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
    33  func (p *Prog) InnermostLineNumberHTML() string {
    34  	return p.Ctxt.InnermostPos(p.Pos).LineNumberHTML()
    35  }
    36  
    37  // InnermostFilename returns a string containing the innermost
    38  // (in inlining) filename at p's position
    39  func (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.
    42  	pos := p.Ctxt.InnermostPos(p.Pos)
    43  	if !pos.IsKnown() {
    44  		return "<unknown file name>"
    45  	}
    46  	return pos.Filename()
    47  }
    48  
    49  var 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 */
    69  const (
    70  	C_SCOND     = (1 << 4) - 1
    71  	C_SBIT      = 1 << 4
    72  	C_PBIT      = 1 << 5
    73  	C_WBIT      = 1 << 6
    74  	C_FBIT      = 1 << 7
    75  	C_UBIT      = 1 << 7
    76  	C_SCOND_XOR = 14
    77  )
    78  
    79  // CConv formats opcode suffix bits (Prog.Scond).
    80  func CConv(s uint8) string {
    81  	if s == 0 {
    82  		return ""
    83  	}
    84  	for i := range opSuffixSpace {
    85  		sset := &opSuffixSpace[i]
    86  		if sset.arch == objabi.GOARCH {
    87  			return sset.cconv(s)
    88  		}
    89  	}
    90  	return fmt.Sprintf("SC???%d", s)
    91  }
    92  
    93  // CConvARM formats ARM opcode suffix bits (mostly condition codes).
    94  func CConvARM(s uint8) string {
    95  	// TODO: could be great to move suffix-related things into
    96  	// ARM asm backends some day.
    97  	// obj/x86 can be used as an example.
    98  
    99  	sc := armCondCode[(s&C_SCOND)^C_SCOND_XOR]
   100  	if s&C_SBIT != 0 {
   101  		sc += ".S"
   102  	}
   103  	if s&C_PBIT != 0 {
   104  		sc += ".P"
   105  	}
   106  	if s&C_WBIT != 0 {
   107  		sc += ".W"
   108  	}
   109  	if s&C_UBIT != 0 { /* ambiguous with FBIT */
   110  		sc += ".U"
   111  	}
   112  	return sc
   113  }
   114  
   115  func (p *Prog) String() string {
   116  	if p == nil {
   117  		return "<nil Prog>"
   118  	}
   119  	if p.Ctxt == nil {
   120  		return "<Prog without ctxt>"
   121  	}
   122  	return fmt.Sprintf("%.5d (%v)\t%s", p.Pc, p.Line(), p.InstructionString())
   123  }
   124  
   125  func (p *Prog) InnermostString(w io.Writer) {
   126  	if p == nil {
   127  		io.WriteString(w, "<nil Prog>")
   128  		return
   129  	}
   130  	if p.Ctxt == nil {
   131  		io.WriteString(w, "<Prog without ctxt>")
   132  		return
   133  	}
   134  	fmt.Fprintf(w, "%.5d (", p.Pc)
   135  	p.InnermostLine(w)
   136  	io.WriteString(w, ")\t")
   137  	p.WriteInstructionString(w)
   138  }
   139  
   140  // InstructionString returns a string representation of the instruction without preceding
   141  // program counter or file and line number.
   142  func (p *Prog) InstructionString() string {
   143  	buf := new(bytes.Buffer)
   144  	p.WriteInstructionString(buf)
   145  	return buf.String()
   146  }
   147  
   148  // WriteInstructionString writes a string representation of the instruction without preceding
   149  // program counter or file and line number.
   150  func (p *Prog) WriteInstructionString(w io.Writer) {
   151  	if p == nil {
   152  		io.WriteString(w, "<nil Prog>")
   153  		return
   154  	}
   155  
   156  	if p.Ctxt == nil {
   157  		io.WriteString(w, "<Prog without ctxt>")
   158  		return
   159  	}
   160  
   161  	sc := CConv(p.Scond)
   162  
   163  	io.WriteString(w, p.As.String())
   164  	io.WriteString(w, sc)
   165  	sep := "\t"
   166  
   167  	if p.From.Type != TYPE_NONE {
   168  		io.WriteString(w, sep)
   169  		WriteDconv(w, p, &p.From)
   170  		sep = ", "
   171  	}
   172  	if p.Reg != REG_NONE {
   173  		// Should not happen but might as well show it if it does.
   174  		fmt.Fprintf(w, "%s%v", sep, Rconv(int(p.Reg)))
   175  		sep = ", "
   176  	}
   177  	for i := range p.RestArgs {
   178  		io.WriteString(w, sep)
   179  		WriteDconv(w, p, &p.RestArgs[i])
   180  		sep = ", "
   181  	}
   182  
   183  	if 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, $0
   187  		// TEXT	foo(SB), $0
   188  		s := p.From.Sym.Attribute.TextAttrString()
   189  		if s != "" {
   190  			fmt.Fprintf(w, "%s%s", sep, s)
   191  			sep = ", "
   192  		}
   193  	}
   194  	if p.To.Type != TYPE_NONE {
   195  		io.WriteString(w, sep)
   196  		WriteDconv(w, p, &p.To)
   197  	}
   198  	if p.RegTo2 != REG_NONE {
   199  		fmt.Fprintf(w, "%s%v", sep, Rconv(int(p.RegTo2)))
   200  	}
   201  }
   202  
   203  func (ctxt *Link) NewProg() *Prog {
   204  	p := new(Prog)
   205  	p.Ctxt = ctxt
   206  	return p
   207  }
   208  
   209  func (ctxt *Link) CanReuseProgs() bool {
   210  	return ctxt.Debugasm == 0
   211  }
   212  
   213  func Dconv(p *Prog, a *Addr) string {
   214  	buf := new(bytes.Buffer)
   215  	WriteDconv(buf, p, a)
   216  	return buf.String()
   217  }
   218  
   219  func WriteDconv(w io.Writer, p *Prog, a *Addr) {
   220  	switch a.Type {
   221  	default:
   222  		fmt.Fprintf(w, "type=%d", a.Type)
   223  
   224  	case TYPE_NONE:
   225  		if a.Name != NAME_NONE || a.Reg != 0 || a.Sym != nil {
   226  			a.WriteNameTo(w)
   227  			fmt.Fprintf(w, "(%v)(NONE)", Rconv(int(a.Reg)))
   228  		}
   229  
   230  	case TYPE_REG:
   231  		// TODO(rsc): This special case is for x86 instructions like
   232  		//	PINSRQ	CX,$1,X6
   233  		// where the $1 is included in the p->to Addr.
   234  		// Move into a new field.
   235  		if a.Offset != 0 && (a.Reg < RBaseARM64 || a.Reg >= RBaseMIPS) {
   236  			fmt.Fprintf(w, "$%d,%v", a.Offset, Rconv(int(a.Reg)))
   237  			return
   238  		}
   239  
   240  		if a.Name != NAME_NONE || a.Sym != nil {
   241  			a.WriteNameTo(w)
   242  			fmt.Fprintf(w, "(%v)(REG)", Rconv(int(a.Reg)))
   243  		} else {
   244  			io.WriteString(w, Rconv(int(a.Reg)))
   245  		}
   246  		if (RBaseARM64+1<<10+1<<9) /* arm64.REG_ELEM */ <= a.Reg &&
   247  			a.Reg < (RBaseARM64+1<<11) /* arm64.REG_ELEM_END */ {
   248  			fmt.Fprintf(w, "[%d]", a.Index)
   249  		}
   250  
   251  	case TYPE_BRANCH:
   252  		if a.Sym != nil {
   253  			fmt.Fprintf(w, "%s(SB)", a.Sym.Name)
   254  		} else if a.Target() != nil {
   255  			fmt.Fprint(w, a.Target().Pc)
   256  		} else {
   257  			fmt.Fprintf(w, "%d(PC)", a.Offset)
   258  		}
   259  
   260  	case TYPE_INDIR:
   261  		io.WriteString(w, "*")
   262  		a.WriteNameTo(w)
   263  
   264  	case TYPE_MEM:
   265  		a.WriteNameTo(w)
   266  		if a.Index != REG_NONE {
   267  			if a.Scale == 0 {
   268  				// arm64 shifted or extended register offset, scale = 0.
   269  				fmt.Fprintf(w, "(%v)", Rconv(int(a.Index)))
   270  			} else {
   271  				fmt.Fprintf(w, "(%v*%d)", Rconv(int(a.Index)), int(a.Scale))
   272  			}
   273  		}
   274  
   275  	case TYPE_CONST:
   276  		io.WriteString(w, "$")
   277  		a.WriteNameTo(w)
   278  		if a.Reg != 0 {
   279  			fmt.Fprintf(w, "(%v)", Rconv(int(a.Reg)))
   280  		}
   281  
   282  	case TYPE_TEXTSIZE:
   283  		if a.Val.(int32) == objabi.ArgsSizeUnknown {
   284  			fmt.Fprintf(w, "$%d", a.Offset)
   285  		} else {
   286  			fmt.Fprintf(w, "$%d-%d", a.Offset, a.Val.(int32))
   287  		}
   288  
   289  	case TYPE_FCONST:
   290  		str := fmt.Sprintf("%.17g", a.Val.(float64))
   291  		// Make sure 1 prints as 1.0
   292  		if !strings.ContainsAny(str, ".e") {
   293  			str += ".0"
   294  		}
   295  		fmt.Fprintf(w, "$(%s)", str)
   296  
   297  	case TYPE_SCONST:
   298  		fmt.Fprintf(w, "$%q", a.Val.(string))
   299  
   300  	case TYPE_ADDR:
   301  		io.WriteString(w, "$")
   302  		a.WriteNameTo(w)
   303  
   304  	case TYPE_SHIFT:
   305  		v := int(a.Offset)
   306  		ops := "<<>>->@>"
   307  		switch objabi.GOARCH {
   308  		case "arm":
   309  			op := ops[((v>>5)&3)<<1:]
   310  			if v&(1<<4) != 0 {
   311  				fmt.Fprintf(w, "R%d%c%cR%d", v&15, op[0], op[1], (v>>8)&15)
   312  			} else {
   313  				fmt.Fprintf(w, "R%d%c%c%d", v&15, op[0], op[1], (v>>7)&31)
   314  			}
   315  			if a.Reg != 0 {
   316  				fmt.Fprintf(w, "(%v)", Rconv(int(a.Reg)))
   317  			}
   318  		case "arm64":
   319  			op := ops[((v>>22)&3)<<1:]
   320  			r := (v >> 16) & 31
   321  			fmt.Fprintf(w, "%s%c%c%d", Rconv(r+RBaseARM64), op[0], op[1], (v>>10)&63)
   322  		default:
   323  			panic("TYPE_SHIFT is not supported on " + objabi.GOARCH)
   324  		}
   325  
   326  	case TYPE_REGREG:
   327  		fmt.Fprintf(w, "(%v, %v)", Rconv(int(a.Reg)), Rconv(int(a.Offset)))
   328  
   329  	case TYPE_REGREG2:
   330  		fmt.Fprintf(w, "%v, %v", Rconv(int(a.Offset)), Rconv(int(a.Reg)))
   331  
   332  	case TYPE_REGLIST:
   333  		io.WriteString(w, RLconv(a.Offset))
   334  	}
   335  }
   336  
   337  func (a *Addr) WriteNameTo(w io.Writer) {
   338  	switch a.Name {
   339  	default:
   340  		fmt.Fprintf(w, "name=%d", a.Name)
   341  
   342  	case NAME_NONE:
   343  		switch {
   344  		case a.Reg == REG_NONE:
   345  			fmt.Fprint(w, a.Offset)
   346  		case a.Offset == 0:
   347  			fmt.Fprintf(w, "(%v)", Rconv(int(a.Reg)))
   348  		case a.Offset != 0:
   349  			fmt.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.
   353  	case NAME_EXTERN:
   354  		reg := "SB"
   355  		if a.Reg != REG_NONE {
   356  			reg = Rconv(int(a.Reg))
   357  		}
   358  		if a.Sym != nil {
   359  			fmt.Fprintf(w, "%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg)
   360  		} else {
   361  			fmt.Fprintf(w, "%s(%s)", offConv(a.Offset), reg)
   362  		}
   363  
   364  	case NAME_GOTREF:
   365  		reg := "SB"
   366  		if a.Reg != REG_NONE {
   367  			reg = Rconv(int(a.Reg))
   368  		}
   369  		if a.Sym != nil {
   370  			fmt.Fprintf(w, "%s%s@GOT(%s)", a.Sym.Name, offConv(a.Offset), reg)
   371  		} else {
   372  			fmt.Fprintf(w, "%s@GOT(%s)", offConv(a.Offset), reg)
   373  		}
   374  
   375  	case NAME_STATIC:
   376  		reg := "SB"
   377  		if a.Reg != REG_NONE {
   378  			reg = Rconv(int(a.Reg))
   379  		}
   380  		if a.Sym != nil {
   381  			fmt.Fprintf(w, "%s<>%s(%s)", a.Sym.Name, offConv(a.Offset), reg)
   382  		} else {
   383  			fmt.Fprintf(w, "<>%s(%s)", offConv(a.Offset), reg)
   384  		}
   385  
   386  	case NAME_AUTO:
   387  		reg := "SP"
   388  		if a.Reg != REG_NONE {
   389  			reg = Rconv(int(a.Reg))
   390  		}
   391  		if a.Sym != nil {
   392  			fmt.Fprintf(w, "%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg)
   393  		} else {
   394  			fmt.Fprintf(w, "%s(%s)", offConv(a.Offset), reg)
   395  		}
   396  
   397  	case NAME_PARAM:
   398  		reg := "FP"
   399  		if a.Reg != REG_NONE {
   400  			reg = Rconv(int(a.Reg))
   401  		}
   402  		if a.Sym != nil {
   403  			fmt.Fprintf(w, "%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg)
   404  		} else {
   405  			fmt.Fprintf(w, "%s(%s)", offConv(a.Offset), reg)
   406  		}
   407  	case NAME_TOCREF:
   408  		reg := "SB"
   409  		if a.Reg != REG_NONE {
   410  			reg = Rconv(int(a.Reg))
   411  		}
   412  		if a.Sym != nil {
   413  			fmt.Fprintf(w, "%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg)
   414  		} else {
   415  			fmt.Fprintf(w, "%s(%s)", offConv(a.Offset), reg)
   416  		}
   417  	}
   418  }
   419  
   420  func offConv(off int64) string {
   421  	if off == 0 {
   422  		return ""
   423  	}
   424  	return 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.
   433  type opSuffixSet struct {
   434  	arch  string
   435  	cconv func(suffix uint8) string
   436  }
   437  
   438  var opSuffixSpace []opSuffixSet
   439  
   440  // RegisterOpSuffix assigns cconv function for formatting opcode suffixes
   441  // when compiling for GOARCH=arch.
   442  //
   443  // cconv is never called with 0 argument.
   444  func RegisterOpSuffix(arch string, cconv func(uint8) string) {
   445  	opSuffixSpace = append(opSuffixSpace, opSuffixSet{
   446  		arch:  arch,
   447  		cconv: cconv,
   448  	})
   449  }
   450  
   451  type regSet struct {
   452  	lo    int
   453  	hi    int
   454  	Rconv func(int) string
   455  }
   456  
   457  // Few enough architectures that a linear scan is fastest.
   458  // Not even worth sorting.
   459  var regSpace []regSet
   460  
   461  /*
   462  	Each architecture defines a register space as a unique
   463  	integer range.
   464  	Here is the list of architectures and the base of their register spaces.
   465  */
   466  
   467  const (
   468  	// Because of masking operations in the encodings, each register
   469  	// space should start at 0 modulo some power of 2.
   470  	RBase386   = 1 * 1024
   471  	RBaseAMD64 = 2 * 1024
   472  	RBaseARM   = 3 * 1024
   473  	RBasePPC64 = 4 * 1024  // range [4k, 8k)
   474  	RBaseARM64 = 8 * 1024  // range [8k, 13k)
   475  	RBaseMIPS  = 13 * 1024 // range [13k, 14k)
   476  	RBaseS390X = 14 * 1024 // range [14k, 15k)
   477  	RBaseRISCV = 15 * 1024 // range [15k, 16k)
   478  	RBaseWasm  = 16 * 1024
   479  )
   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).
   484  func RegisterRegister(lo, hi int, Rconv func(int) string) {
   485  	regSpace = append(regSpace, regSet{lo, hi, Rconv})
   486  }
   487  
   488  func Rconv(reg int) string {
   489  	if reg == REG_NONE {
   490  		return "NONE"
   491  	}
   492  	for i := range regSpace {
   493  		rs := &regSpace[i]
   494  		if rs.lo <= reg && reg < rs.hi {
   495  			return rs.Rconv(reg)
   496  		}
   497  	}
   498  	return fmt.Sprintf("R???%d", reg)
   499  }
   500  
   501  type regListSet struct {
   502  	lo     int64
   503  	hi     int64
   504  	RLconv func(int64) string
   505  }
   506  
   507  var regListSpace []regListSet
   508  
   509  // Each architecture is allotted a distinct subspace: [Lo, Hi) for declaring its
   510  // arch-specific register list numbers.
   511  const (
   512  	RegListARMLo = 0
   513  	RegListARMHi = 1 << 16
   514  
   515  	// arm64 uses the 60th bit to differentiate from other archs
   516  	RegListARM64Lo = 1 << 60
   517  	RegListARM64Hi = 1<<61 - 1
   518  
   519  	// x86 uses the 61th bit to differentiate from other archs
   520  	RegListX86Lo = 1 << 61
   521  	RegListX86Hi = 1<<62 - 1
   522  )
   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).
   527  func RegisterRegisterList(lo, hi int64, rlconv func(int64) string) {
   528  	regListSpace = append(regListSpace, regListSet{lo, hi, rlconv})
   529  }
   530  
   531  func RLconv(list int64) string {
   532  	for i := range regListSpace {
   533  		rls := &regListSpace[i]
   534  		if rls.lo <= list && list < rls.hi {
   535  			return rls.RLconv(list)
   536  		}
   537  	}
   538  	return fmt.Sprintf("RL???%d", list)
   539  }
   540  
   541  type opSet struct {
   542  	lo    As
   543  	names []string
   544  }
   545  
   546  // Not even worth sorting
   547  var aSpace []opSet
   548  
   549  // RegisterOpcode binds a list of instruction names
   550  // to a given instruction number range.
   551  func RegisterOpcode(lo As, Anames []string) {
   552  	if len(Anames) > AllowedOpCodes {
   553  		panic(fmt.Sprintf("too many instructions, have %d max %d", len(Anames), AllowedOpCodes))
   554  	}
   555  	aSpace = append(aSpace, opSet{lo, Anames})
   556  }
   557  
   558  func (a As) String() string {
   559  	if 0 <= a && int(a) < len(Anames) {
   560  		return Anames[a]
   561  	}
   562  	for i := range aSpace {
   563  		as := &aSpace[i]
   564  		if as.lo <= a && int(a-as.lo) < len(as.names) {
   565  			return as.names[a-as.lo]
   566  		}
   567  	}
   568  	return fmt.Sprintf("A???%d", a)
   569  }
   570  
   571  var 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  
   588  func Bool2int(b bool) int {
   589  	// The compiler currently only optimizes this form.
   590  	// See issue 6011.
   591  	var i int
   592  	if b {
   593  		i = 1
   594  	} else {
   595  		i = 0
   596  	}
   597  	return i
   598  }
   599  

View as plain text