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

View as plain text