...
Run Format

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

View as plain text