github.com/tidwall/go@v0.0.0-20170415222209-6694a6888b7d/src/cmd/internal/obj/util.go (about)

     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  	"fmt"
    10  	"log"
    11  	"os"
    12  	"strings"
    13  	"time"
    14  )
    15  
    16  const REG_NONE = 0
    17  
    18  var start time.Time
    19  
    20  func Cputime() float64 {
    21  	if start.IsZero() {
    22  		start = time.Now()
    23  	}
    24  	return time.Since(start).Seconds()
    25  }
    26  
    27  func envOr(key, value string) string {
    28  	if x := os.Getenv(key); x != "" {
    29  		return x
    30  	}
    31  	return value
    32  }
    33  
    34  var (
    35  	GOROOT  = envOr("GOROOT", defaultGOROOT)
    36  	GOARCH  = envOr("GOARCH", defaultGOARCH)
    37  	GOOS    = envOr("GOOS", defaultGOOS)
    38  	GO386   = envOr("GO386", defaultGO386)
    39  	GOARM   = goarm()
    40  	Version = version
    41  )
    42  
    43  func goarm() int {
    44  	switch v := envOr("GOARM", defaultGOARM); v {
    45  	case "5":
    46  		return 5
    47  	case "6":
    48  		return 6
    49  	case "7":
    50  		return 7
    51  	}
    52  	// Fail here, rather than validate at multiple call sites.
    53  	log.Fatalf("Invalid GOARM value. Must be 5, 6, or 7.")
    54  	panic("unreachable")
    55  }
    56  
    57  func Getgoextlinkenabled() string {
    58  	return envOr("GO_EXTLINK_ENABLED", defaultGO_EXTLINK_ENABLED)
    59  }
    60  
    61  func (p *Prog) Line() string {
    62  	return p.Ctxt.OutermostPos(p.Pos).Format(false)
    63  }
    64  
    65  var armCondCode = []string{
    66  	".EQ",
    67  	".NE",
    68  	".CS",
    69  	".CC",
    70  	".MI",
    71  	".PL",
    72  	".VS",
    73  	".VC",
    74  	".HI",
    75  	".LS",
    76  	".GE",
    77  	".LT",
    78  	".GT",
    79  	".LE",
    80  	"",
    81  	".NV",
    82  }
    83  
    84  /* ARM scond byte */
    85  const (
    86  	C_SCOND     = (1 << 4) - 1
    87  	C_SBIT      = 1 << 4
    88  	C_PBIT      = 1 << 5
    89  	C_WBIT      = 1 << 6
    90  	C_FBIT      = 1 << 7
    91  	C_UBIT      = 1 << 7
    92  	C_SCOND_XOR = 14
    93  )
    94  
    95  // CConv formats ARM condition codes.
    96  func CConv(s uint8) string {
    97  	if s == 0 {
    98  		return ""
    99  	}
   100  	sc := armCondCode[(s&C_SCOND)^C_SCOND_XOR]
   101  	if s&C_SBIT != 0 {
   102  		sc += ".S"
   103  	}
   104  	if s&C_PBIT != 0 {
   105  		sc += ".P"
   106  	}
   107  	if s&C_WBIT != 0 {
   108  		sc += ".W"
   109  	}
   110  	if s&C_UBIT != 0 { /* ambiguous with FBIT */
   111  		sc += ".U"
   112  	}
   113  	return sc
   114  }
   115  
   116  func (p *Prog) String() string {
   117  	if p == nil {
   118  		return "<nil Prog>"
   119  	}
   120  
   121  	if p.Ctxt == nil {
   122  		return "<Prog without ctxt>"
   123  	}
   124  
   125  	sc := CConv(p.Scond)
   126  
   127  	var buf bytes.Buffer
   128  
   129  	fmt.Fprintf(&buf, "%.5d (%v)\t%v%s", p.Pc, p.Line(), p.As, sc)
   130  	sep := "\t"
   131  	quadOpAmd64 := p.RegTo2 == -1
   132  	if quadOpAmd64 {
   133  		fmt.Fprintf(&buf, "%s$%d", sep, p.From3.Offset)
   134  		sep = ", "
   135  	}
   136  	if p.From.Type != TYPE_NONE {
   137  		fmt.Fprintf(&buf, "%s%v", sep, Dconv(p, &p.From))
   138  		sep = ", "
   139  	}
   140  	if p.Reg != REG_NONE {
   141  		// Should not happen but might as well show it if it does.
   142  		fmt.Fprintf(&buf, "%s%v", sep, Rconv(int(p.Reg)))
   143  		sep = ", "
   144  	}
   145  	if p.From3Type() != TYPE_NONE {
   146  		if quadOpAmd64 {
   147  			fmt.Fprintf(&buf, "%s%v", sep, Rconv(int(p.From3.Reg)))
   148  		} else {
   149  			fmt.Fprintf(&buf, "%s%v", sep, Dconv(p, p.From3))
   150  		}
   151  		sep = ", "
   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 && !quadOpAmd64 {
   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 (ctxt *Link) Dconv(a *Addr) string {
   184  	return Dconv(nil, a)
   185  }
   186  
   187  func Dconv(p *Prog, a *Addr) string {
   188  	var str string
   189  
   190  	switch a.Type {
   191  	default:
   192  		str = fmt.Sprintf("type=%d", a.Type)
   193  
   194  	case TYPE_NONE:
   195  		str = ""
   196  		if a.Name != NAME_NONE || a.Reg != 0 || a.Sym != nil {
   197  			str = fmt.Sprintf("%v(%v)(NONE)", Mconv(a), Rconv(int(a.Reg)))
   198  		}
   199  
   200  	case TYPE_REG:
   201  		// TODO(rsc): This special case is for x86 instructions like
   202  		//	PINSRQ	CX,$1,X6
   203  		// where the $1 is included in the p->to Addr.
   204  		// Move into a new field.
   205  		if a.Offset != 0 {
   206  			str = fmt.Sprintf("$%d,%v", a.Offset, Rconv(int(a.Reg)))
   207  			break
   208  		}
   209  
   210  		str = Rconv(int(a.Reg))
   211  		if a.Name != NAME_NONE || a.Sym != nil {
   212  			str = fmt.Sprintf("%v(%v)(REG)", Mconv(a), Rconv(int(a.Reg)))
   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  			str += fmt.Sprintf("(%v*%d)", Rconv(int(a.Index)), int(a.Scale))
   233  		}
   234  
   235  	case TYPE_CONST:
   236  		if a.Reg != 0 {
   237  			str = fmt.Sprintf("$%v(%v)", Mconv(a), Rconv(int(a.Reg)))
   238  		} else {
   239  			str = fmt.Sprintf("$%v", Mconv(a))
   240  		}
   241  
   242  	case TYPE_TEXTSIZE:
   243  		if a.Val.(int32) == ArgsSizeUnknown {
   244  			str = fmt.Sprintf("$%d", a.Offset)
   245  		} else {
   246  			str = fmt.Sprintf("$%d-%d", a.Offset, a.Val.(int32))
   247  		}
   248  
   249  	case TYPE_FCONST:
   250  		str = fmt.Sprintf("%.17g", a.Val.(float64))
   251  		// Make sure 1 prints as 1.0
   252  		if !strings.ContainsAny(str, ".e") {
   253  			str += ".0"
   254  		}
   255  		str = fmt.Sprintf("$(%s)", str)
   256  
   257  	case TYPE_SCONST:
   258  		str = fmt.Sprintf("$%q", a.Val.(string))
   259  
   260  	case TYPE_ADDR:
   261  		str = fmt.Sprintf("$%s", Mconv(a))
   262  
   263  	case TYPE_SHIFT:
   264  		v := int(a.Offset)
   265  		ops := "<<>>->@>"
   266  		switch GOARCH {
   267  		case "arm":
   268  			op := ops[((v>>5)&3)<<1:]
   269  			if v&(1<<4) != 0 {
   270  				str = fmt.Sprintf("R%d%c%cR%d", v&15, op[0], op[1], (v>>8)&15)
   271  			} else {
   272  				str = fmt.Sprintf("R%d%c%c%d", v&15, op[0], op[1], (v>>7)&31)
   273  			}
   274  			if a.Reg != 0 {
   275  				str += fmt.Sprintf("(%v)", Rconv(int(a.Reg)))
   276  			}
   277  		case "arm64":
   278  			op := ops[((v>>22)&3)<<1:]
   279  			str = fmt.Sprintf("R%d%c%c%d", (v>>16)&31, op[0], op[1], (v>>10)&63)
   280  		default:
   281  			panic("TYPE_SHIFT is not supported on " + GOARCH)
   282  		}
   283  
   284  	case TYPE_REGREG:
   285  		str = fmt.Sprintf("(%v, %v)", Rconv(int(a.Reg)), Rconv(int(a.Offset)))
   286  
   287  	case TYPE_REGREG2:
   288  		str = fmt.Sprintf("%v, %v", Rconv(int(a.Reg)), Rconv(int(a.Offset)))
   289  
   290  	case TYPE_REGLIST:
   291  		str = regListConv(int(a.Offset))
   292  	}
   293  
   294  	return str
   295  }
   296  
   297  func Mconv(a *Addr) string {
   298  	var str string
   299  
   300  	switch a.Name {
   301  	default:
   302  		str = fmt.Sprintf("name=%d", a.Name)
   303  
   304  	case NAME_NONE:
   305  		switch {
   306  		case a.Reg == REG_NONE:
   307  			str = fmt.Sprint(a.Offset)
   308  		case a.Offset == 0:
   309  			str = fmt.Sprintf("(%v)", Rconv(int(a.Reg)))
   310  		case a.Offset != 0:
   311  			str = fmt.Sprintf("%d(%v)", a.Offset, Rconv(int(a.Reg)))
   312  		}
   313  
   314  		// Note: a.Reg == REG_NONE encodes the default base register for the NAME_ type.
   315  	case NAME_EXTERN:
   316  		reg := "SB"
   317  		if a.Reg != REG_NONE {
   318  			reg = Rconv(int(a.Reg))
   319  		}
   320  		if a.Sym != nil {
   321  			str = fmt.Sprintf("%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg)
   322  		} else {
   323  			str = fmt.Sprintf("%s(%s)", offConv(a.Offset), reg)
   324  		}
   325  
   326  	case NAME_GOTREF:
   327  		reg := "SB"
   328  		if a.Reg != REG_NONE {
   329  			reg = Rconv(int(a.Reg))
   330  		}
   331  		if a.Sym != nil {
   332  			str = fmt.Sprintf("%s%s@GOT(%s)", a.Sym.Name, offConv(a.Offset), reg)
   333  		} else {
   334  			str = fmt.Sprintf("%s@GOT(%s)", offConv(a.Offset), reg)
   335  		}
   336  
   337  	case NAME_STATIC:
   338  		reg := "SB"
   339  		if a.Reg != REG_NONE {
   340  			reg = Rconv(int(a.Reg))
   341  		}
   342  		if a.Sym != nil {
   343  			str = fmt.Sprintf("%s<>%s(%s)", a.Sym.Name, offConv(a.Offset), reg)
   344  		} else {
   345  			str = fmt.Sprintf("<>%s(%s)", offConv(a.Offset), reg)
   346  		}
   347  
   348  	case NAME_AUTO:
   349  		reg := "SP"
   350  		if a.Reg != REG_NONE {
   351  			reg = Rconv(int(a.Reg))
   352  		}
   353  		if a.Sym != nil {
   354  			str = fmt.Sprintf("%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg)
   355  		} else {
   356  			str = fmt.Sprintf("%s(%s)", offConv(a.Offset), reg)
   357  		}
   358  
   359  	case NAME_PARAM:
   360  		reg := "FP"
   361  		if a.Reg != REG_NONE {
   362  			reg = Rconv(int(a.Reg))
   363  		}
   364  		if a.Sym != nil {
   365  			str = fmt.Sprintf("%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg)
   366  		} else {
   367  			str = fmt.Sprintf("%s(%s)", offConv(a.Offset), reg)
   368  		}
   369  	}
   370  	return str
   371  }
   372  
   373  func offConv(off int64) string {
   374  	if off == 0 {
   375  		return ""
   376  	}
   377  	return fmt.Sprintf("%+d", off)
   378  }
   379  
   380  type regSet struct {
   381  	lo    int
   382  	hi    int
   383  	Rconv func(int) string
   384  }
   385  
   386  // Few enough architectures that a linear scan is fastest.
   387  // Not even worth sorting.
   388  var regSpace []regSet
   389  
   390  /*
   391  	Each architecture defines a register space as a unique
   392  	integer range.
   393  	Here is the list of architectures and the base of their register spaces.
   394  */
   395  
   396  const (
   397  	// Because of masking operations in the encodings, each register
   398  	// space should start at 0 modulo some power of 2.
   399  	RBase386   = 1 * 1024
   400  	RBaseAMD64 = 2 * 1024
   401  	RBaseARM   = 3 * 1024
   402  	RBasePPC64 = 4 * 1024  // range [4k, 8k)
   403  	RBaseARM64 = 8 * 1024  // range [8k, 13k)
   404  	RBaseMIPS  = 13 * 1024 // range [13k, 14k)
   405  	RBaseS390X = 14 * 1024 // range [14k, 15k)
   406  )
   407  
   408  // RegisterRegister binds a pretty-printer (Rconv) for register
   409  // numbers to a given register number range. Lo is inclusive,
   410  // hi exclusive (valid registers are lo through hi-1).
   411  func RegisterRegister(lo, hi int, Rconv func(int) string) {
   412  	regSpace = append(regSpace, regSet{lo, hi, Rconv})
   413  }
   414  
   415  func Rconv(reg int) string {
   416  	if reg == REG_NONE {
   417  		return "NONE"
   418  	}
   419  	for i := range regSpace {
   420  		rs := &regSpace[i]
   421  		if rs.lo <= reg && reg < rs.hi {
   422  			return rs.Rconv(reg)
   423  		}
   424  	}
   425  	return fmt.Sprintf("R???%d", reg)
   426  }
   427  
   428  func regListConv(list int) string {
   429  	str := ""
   430  
   431  	for i := 0; i < 16; i++ { // TODO: 16 is ARM-specific.
   432  		if list&(1<<uint(i)) != 0 {
   433  			if str == "" {
   434  				str += "["
   435  			} else {
   436  				str += ","
   437  			}
   438  			// This is ARM-specific; R10 is g.
   439  			if i == 10 {
   440  				str += "g"
   441  			} else {
   442  				str += fmt.Sprintf("R%d", i)
   443  			}
   444  		}
   445  	}
   446  
   447  	str += "]"
   448  	return str
   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  }