Black Lives Matter. Support the Equal Justice Initiative.

Source file src/cmd/internal/obj/util.go

Documentation: cmd/internal/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  	"cmd/internal/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  		if p.RestArgs[i].Pos == Source {
   179  			io.WriteString(w, sep)
   180  			WriteDconv(w, p, &p.RestArgs[i].Addr)
   181  			sep = ", "
   182  		}
   183  	}
   184  
   185  	if p.As == ATEXT {
   186  		// If there are attributes, print them. Otherwise, skip the comma.
   187  		// In short, print one of these two:
   188  		// TEXT	foo(SB), DUPOK|NOSPLIT, $0
   189  		// TEXT	foo(SB), $0
   190  		s := p.From.Sym.Attribute.TextAttrString()
   191  		if s != "" {
   192  			fmt.Fprintf(w, "%s%s", sep, s)
   193  			sep = ", "
   194  		}
   195  	}
   196  	if p.To.Type != TYPE_NONE {
   197  		io.WriteString(w, sep)
   198  		WriteDconv(w, p, &p.To)
   199  	}
   200  	if p.RegTo2 != REG_NONE {
   201  		fmt.Fprintf(w, "%s%v", sep, Rconv(int(p.RegTo2)))
   202  	}
   203  	for i := range p.RestArgs {
   204  		if p.RestArgs[i].Pos == Destination {
   205  			io.WriteString(w, sep)
   206  			WriteDconv(w, p, &p.RestArgs[i].Addr)
   207  			sep = ", "
   208  		}
   209  	}
   210  }
   211  
   212  func (ctxt *Link) NewProg() *Prog {
   213  	p := new(Prog)
   214  	p.Ctxt = ctxt
   215  	return p
   216  }
   217  
   218  func (ctxt *Link) CanReuseProgs() bool {
   219  	return ctxt.Debugasm == 0
   220  }
   221  
   222  // Dconv accepts an argument 'a' within a prog 'p' and returns a string
   223  // with a formatted version of the argument.
   224  func Dconv(p *Prog, a *Addr) string {
   225  	buf := new(bytes.Buffer)
   226  	writeDconv(buf, p, a, false)
   227  	return buf.String()
   228  }
   229  
   230  // DconvDconvWithABIDetail accepts an argument 'a' within a prog 'p'
   231  // and returns a string with a formatted version of the argument, in
   232  // which text symbols are rendered with explicit ABI selectors.
   233  func DconvWithABIDetail(p *Prog, a *Addr) string {
   234  	buf := new(bytes.Buffer)
   235  	writeDconv(buf, p, a, true)
   236  	return buf.String()
   237  }
   238  
   239  // WriteDconv accepts an argument 'a' within a prog 'p'
   240  // and writes a formatted version of the arg to the writer.
   241  func WriteDconv(w io.Writer, p *Prog, a *Addr) {
   242  	writeDconv(w, p, a, false)
   243  }
   244  
   245  func writeDconv(w io.Writer, p *Prog, a *Addr, abiDetail bool) {
   246  	switch a.Type {
   247  	default:
   248  		fmt.Fprintf(w, "type=%d", a.Type)
   249  
   250  	case TYPE_NONE:
   251  		if a.Name != NAME_NONE || a.Reg != 0 || a.Sym != nil {
   252  			a.WriteNameTo(w)
   253  			fmt.Fprintf(w, "(%v)(NONE)", Rconv(int(a.Reg)))
   254  		}
   255  
   256  	case TYPE_REG:
   257  		// TODO(rsc): This special case is for x86 instructions like
   258  		//	PINSRQ	CX,$1,X6
   259  		// where the $1 is included in the p->to Addr.
   260  		// Move into a new field.
   261  		if a.Offset != 0 && (a.Reg < RBaseARM64 || a.Reg >= RBaseMIPS) {
   262  			fmt.Fprintf(w, "$%d,%v", a.Offset, Rconv(int(a.Reg)))
   263  			return
   264  		}
   265  
   266  		if a.Name != NAME_NONE || a.Sym != nil {
   267  			a.WriteNameTo(w)
   268  			fmt.Fprintf(w, "(%v)(REG)", Rconv(int(a.Reg)))
   269  		} else {
   270  			io.WriteString(w, Rconv(int(a.Reg)))
   271  		}
   272  		if (RBaseARM64+1<<10+1<<9) /* arm64.REG_ELEM */ <= a.Reg &&
   273  			a.Reg < (RBaseARM64+1<<11) /* arm64.REG_ELEM_END */ {
   274  			fmt.Fprintf(w, "[%d]", a.Index)
   275  		}
   276  
   277  	case TYPE_BRANCH:
   278  		if a.Sym != nil {
   279  			fmt.Fprintf(w, "%s%s(SB)", a.Sym.Name, abiDecorate(a, abiDetail))
   280  		} else if a.Target() != nil {
   281  			fmt.Fprint(w, a.Target().Pc)
   282  		} else {
   283  			fmt.Fprintf(w, "%d(PC)", a.Offset)
   284  		}
   285  
   286  	case TYPE_INDIR:
   287  		io.WriteString(w, "*")
   288  		a.writeNameTo(w, abiDetail)
   289  
   290  	case TYPE_MEM:
   291  		a.WriteNameTo(w)
   292  		if a.Index != REG_NONE {
   293  			if a.Scale == 0 {
   294  				// arm64 shifted or extended register offset, scale = 0.
   295  				fmt.Fprintf(w, "(%v)", Rconv(int(a.Index)))
   296  			} else {
   297  				fmt.Fprintf(w, "(%v*%d)", Rconv(int(a.Index)), int(a.Scale))
   298  			}
   299  		}
   300  
   301  	case TYPE_CONST:
   302  		io.WriteString(w, "$")
   303  		a.WriteNameTo(w)
   304  		if a.Reg != 0 {
   305  			fmt.Fprintf(w, "(%v)", Rconv(int(a.Reg)))
   306  		}
   307  
   308  	case TYPE_TEXTSIZE:
   309  		if a.Val.(int32) == objabi.ArgsSizeUnknown {
   310  			fmt.Fprintf(w, "$%d", a.Offset)
   311  		} else {
   312  			fmt.Fprintf(w, "$%d-%d", a.Offset, a.Val.(int32))
   313  		}
   314  
   315  	case TYPE_FCONST:
   316  		str := fmt.Sprintf("%.17g", a.Val.(float64))
   317  		// Make sure 1 prints as 1.0
   318  		if !strings.ContainsAny(str, ".e") {
   319  			str += ".0"
   320  		}
   321  		fmt.Fprintf(w, "$(%s)", str)
   322  
   323  	case TYPE_SCONST:
   324  		fmt.Fprintf(w, "$%q", a.Val.(string))
   325  
   326  	case TYPE_ADDR:
   327  		io.WriteString(w, "$")
   328  		a.writeNameTo(w, abiDetail)
   329  
   330  	case TYPE_SHIFT:
   331  		v := int(a.Offset)
   332  		ops := "<<>>->@>"
   333  		switch objabi.GOARCH {
   334  		case "arm":
   335  			op := ops[((v>>5)&3)<<1:]
   336  			if v&(1<<4) != 0 {
   337  				fmt.Fprintf(w, "R%d%c%cR%d", v&15, op[0], op[1], (v>>8)&15)
   338  			} else {
   339  				fmt.Fprintf(w, "R%d%c%c%d", v&15, op[0], op[1], (v>>7)&31)
   340  			}
   341  			if a.Reg != 0 {
   342  				fmt.Fprintf(w, "(%v)", Rconv(int(a.Reg)))
   343  			}
   344  		case "arm64":
   345  			op := ops[((v>>22)&3)<<1:]
   346  			r := (v >> 16) & 31
   347  			fmt.Fprintf(w, "%s%c%c%d", Rconv(r+RBaseARM64), op[0], op[1], (v>>10)&63)
   348  		default:
   349  			panic("TYPE_SHIFT is not supported on " + objabi.GOARCH)
   350  		}
   351  
   352  	case TYPE_REGREG:
   353  		fmt.Fprintf(w, "(%v, %v)", Rconv(int(a.Reg)), Rconv(int(a.Offset)))
   354  
   355  	case TYPE_REGREG2:
   356  		fmt.Fprintf(w, "%v, %v", Rconv(int(a.Offset)), Rconv(int(a.Reg)))
   357  
   358  	case TYPE_REGLIST:
   359  		io.WriteString(w, RLconv(a.Offset))
   360  	}
   361  }
   362  
   363  func (a *Addr) WriteNameTo(w io.Writer) {
   364  	a.writeNameTo(w, false)
   365  }
   366  
   367  func (a *Addr) writeNameTo(w io.Writer, abiDetail bool) {
   368  
   369  	switch a.Name {
   370  	default:
   371  		fmt.Fprintf(w, "name=%d", a.Name)
   372  
   373  	case NAME_NONE:
   374  		switch {
   375  		case a.Reg == REG_NONE:
   376  			fmt.Fprint(w, a.Offset)
   377  		case a.Offset == 0:
   378  			fmt.Fprintf(w, "(%v)", Rconv(int(a.Reg)))
   379  		case a.Offset != 0:
   380  			fmt.Fprintf(w, "%d(%v)", a.Offset, Rconv(int(a.Reg)))
   381  		}
   382  
   383  		// Note: a.Reg == REG_NONE encodes the default base register for the NAME_ type.
   384  	case NAME_EXTERN:
   385  		reg := "SB"
   386  		if a.Reg != REG_NONE {
   387  			reg = Rconv(int(a.Reg))
   388  		}
   389  		if a.Sym != nil {
   390  			fmt.Fprintf(w, "%s%s%s(%s)", a.Sym.Name, abiDecorate(a, abiDetail), offConv(a.Offset), reg)
   391  		} else {
   392  			fmt.Fprintf(w, "%s(%s)", offConv(a.Offset), reg)
   393  		}
   394  
   395  	case NAME_GOTREF:
   396  		reg := "SB"
   397  		if a.Reg != REG_NONE {
   398  			reg = Rconv(int(a.Reg))
   399  		}
   400  		if a.Sym != nil {
   401  			fmt.Fprintf(w, "%s%s@GOT(%s)", a.Sym.Name, offConv(a.Offset), reg)
   402  		} else {
   403  			fmt.Fprintf(w, "%s@GOT(%s)", offConv(a.Offset), reg)
   404  		}
   405  
   406  	case NAME_STATIC:
   407  		reg := "SB"
   408  		if a.Reg != REG_NONE {
   409  			reg = Rconv(int(a.Reg))
   410  		}
   411  		if a.Sym != nil {
   412  			fmt.Fprintf(w, "%s<>%s(%s)", a.Sym.Name, offConv(a.Offset), reg)
   413  		} else {
   414  			fmt.Fprintf(w, "<>%s(%s)", offConv(a.Offset), reg)
   415  		}
   416  
   417  	case NAME_AUTO:
   418  		reg := "SP"
   419  		if a.Reg != REG_NONE {
   420  			reg = Rconv(int(a.Reg))
   421  		}
   422  		if a.Sym != nil {
   423  			fmt.Fprintf(w, "%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg)
   424  		} else {
   425  			fmt.Fprintf(w, "%s(%s)", offConv(a.Offset), reg)
   426  		}
   427  
   428  	case NAME_PARAM:
   429  		reg := "FP"
   430  		if a.Reg != REG_NONE {
   431  			reg = Rconv(int(a.Reg))
   432  		}
   433  		if a.Sym != nil {
   434  			fmt.Fprintf(w, "%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg)
   435  		} else {
   436  			fmt.Fprintf(w, "%s(%s)", offConv(a.Offset), reg)
   437  		}
   438  	case NAME_TOCREF:
   439  		reg := "SB"
   440  		if a.Reg != REG_NONE {
   441  			reg = Rconv(int(a.Reg))
   442  		}
   443  		if a.Sym != nil {
   444  			fmt.Fprintf(w, "%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg)
   445  		} else {
   446  			fmt.Fprintf(w, "%s(%s)", offConv(a.Offset), reg)
   447  		}
   448  	}
   449  }
   450  
   451  func offConv(off int64) string {
   452  	if off == 0 {
   453  		return ""
   454  	}
   455  	return fmt.Sprintf("%+d", off)
   456  }
   457  
   458  // opSuffixSet is like regListSet, but for opcode suffixes.
   459  //
   460  // Unlike some other similar structures, uint8 space is not
   461  // divided by its own values set (because there are only 256 of them).
   462  // Instead, every arch may interpret/format all 8 bits as they like,
   463  // as long as they register proper cconv function for it.
   464  type opSuffixSet struct {
   465  	arch  string
   466  	cconv func(suffix uint8) string
   467  }
   468  
   469  var opSuffixSpace []opSuffixSet
   470  
   471  // RegisterOpSuffix assigns cconv function for formatting opcode suffixes
   472  // when compiling for GOARCH=arch.
   473  //
   474  // cconv is never called with 0 argument.
   475  func RegisterOpSuffix(arch string, cconv func(uint8) string) {
   476  	opSuffixSpace = append(opSuffixSpace, opSuffixSet{
   477  		arch:  arch,
   478  		cconv: cconv,
   479  	})
   480  }
   481  
   482  type regSet struct {
   483  	lo    int
   484  	hi    int
   485  	Rconv func(int) string
   486  }
   487  
   488  // Few enough architectures that a linear scan is fastest.
   489  // Not even worth sorting.
   490  var regSpace []regSet
   491  
   492  /*
   493  	Each architecture defines a register space as a unique
   494  	integer range.
   495  	Here is the list of architectures and the base of their register spaces.
   496  */
   497  
   498  const (
   499  	// Because of masking operations in the encodings, each register
   500  	// space should start at 0 modulo some power of 2.
   501  	RBase386   = 1 * 1024
   502  	RBaseAMD64 = 2 * 1024
   503  	RBaseARM   = 3 * 1024
   504  	RBasePPC64 = 4 * 1024  // range [4k, 8k)
   505  	RBaseARM64 = 8 * 1024  // range [8k, 13k)
   506  	RBaseMIPS  = 13 * 1024 // range [13k, 14k)
   507  	RBaseS390X = 14 * 1024 // range [14k, 15k)
   508  	RBaseRISCV = 15 * 1024 // range [15k, 16k)
   509  	RBaseWasm  = 16 * 1024
   510  )
   511  
   512  // RegisterRegister binds a pretty-printer (Rconv) for register
   513  // numbers to a given register number range. Lo is inclusive,
   514  // hi exclusive (valid registers are lo through hi-1).
   515  func RegisterRegister(lo, hi int, Rconv func(int) string) {
   516  	regSpace = append(regSpace, regSet{lo, hi, Rconv})
   517  }
   518  
   519  func Rconv(reg int) string {
   520  	if reg == REG_NONE {
   521  		return "NONE"
   522  	}
   523  	for i := range regSpace {
   524  		rs := &regSpace[i]
   525  		if rs.lo <= reg && reg < rs.hi {
   526  			return rs.Rconv(reg)
   527  		}
   528  	}
   529  	return fmt.Sprintf("R???%d", reg)
   530  }
   531  
   532  type regListSet struct {
   533  	lo     int64
   534  	hi     int64
   535  	RLconv func(int64) string
   536  }
   537  
   538  var regListSpace []regListSet
   539  
   540  // Each architecture is allotted a distinct subspace: [Lo, Hi) for declaring its
   541  // arch-specific register list numbers.
   542  const (
   543  	RegListARMLo = 0
   544  	RegListARMHi = 1 << 16
   545  
   546  	// arm64 uses the 60th bit to differentiate from other archs
   547  	RegListARM64Lo = 1 << 60
   548  	RegListARM64Hi = 1<<61 - 1
   549  
   550  	// x86 uses the 61th bit to differentiate from other archs
   551  	RegListX86Lo = 1 << 61
   552  	RegListX86Hi = 1<<62 - 1
   553  )
   554  
   555  // RegisterRegisterList binds a pretty-printer (RLconv) for register list
   556  // numbers to a given register list number range. Lo is inclusive,
   557  // hi exclusive (valid register list are lo through hi-1).
   558  func RegisterRegisterList(lo, hi int64, rlconv func(int64) string) {
   559  	regListSpace = append(regListSpace, regListSet{lo, hi, rlconv})
   560  }
   561  
   562  func RLconv(list int64) string {
   563  	for i := range regListSpace {
   564  		rls := &regListSpace[i]
   565  		if rls.lo <= list && list < rls.hi {
   566  			return rls.RLconv(list)
   567  		}
   568  	}
   569  	return fmt.Sprintf("RL???%d", list)
   570  }
   571  
   572  type opSet struct {
   573  	lo    As
   574  	names []string
   575  }
   576  
   577  // Not even worth sorting
   578  var aSpace []opSet
   579  
   580  // RegisterOpcode binds a list of instruction names
   581  // to a given instruction number range.
   582  func RegisterOpcode(lo As, Anames []string) {
   583  	if len(Anames) > AllowedOpCodes {
   584  		panic(fmt.Sprintf("too many instructions, have %d max %d", len(Anames), AllowedOpCodes))
   585  	}
   586  	aSpace = append(aSpace, opSet{lo, Anames})
   587  }
   588  
   589  func (a As) String() string {
   590  	if 0 <= a && int(a) < len(Anames) {
   591  		return Anames[a]
   592  	}
   593  	for i := range aSpace {
   594  		as := &aSpace[i]
   595  		if as.lo <= a && int(a-as.lo) < len(as.names) {
   596  			return as.names[a-as.lo]
   597  		}
   598  	}
   599  	return fmt.Sprintf("A???%d", a)
   600  }
   601  
   602  var Anames = []string{
   603  	"XXX",
   604  	"CALL",
   605  	"DUFFCOPY",
   606  	"DUFFZERO",
   607  	"END",
   608  	"FUNCDATA",
   609  	"JMP",
   610  	"NOP",
   611  	"PCALIGN",
   612  	"PCDATA",
   613  	"RET",
   614  	"GETCALLERPC",
   615  	"TEXT",
   616  	"UNDEF",
   617  }
   618  
   619  func Bool2int(b bool) int {
   620  	// The compiler currently only optimizes this form.
   621  	// See issue 6011.
   622  	var i int
   623  	if b {
   624  		i = 1
   625  	} else {
   626  		i = 0
   627  	}
   628  	return i
   629  }
   630  
   631  func abiDecorate(a *Addr, abiDetail bool) string {
   632  	if !abiDetail || a.Sym == nil {
   633  		return ""
   634  	}
   635  	return fmt.Sprintf("<%s>", a.Sym.ABI())
   636  }
   637  

View as plain text