github.com/4ad/go@v0.0.0-20161219182952-69a12818b605/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  	"cmd/internal/sys"
    16  )
    17  
    18  const REG_NONE = 0
    19  
    20  var start time.Time
    21  
    22  func Cputime() float64 {
    23  	if start.IsZero() {
    24  		start = time.Now()
    25  	}
    26  	return time.Since(start).Seconds()
    27  }
    28  
    29  func envOr(key, value string) string {
    30  	if x := os.Getenv(key); x != "" {
    31  		return x
    32  	}
    33  	return value
    34  }
    35  
    36  func Getgoroot() string {
    37  	return envOr("GOROOT", defaultGOROOT)
    38  }
    39  
    40  func Getgoarch() string {
    41  	return envOr("GOARCH", defaultGOARCH)
    42  }
    43  
    44  func Getgoos() string {
    45  	return envOr("GOOS", defaultGOOS)
    46  }
    47  
    48  func Getgoarm() int32 {
    49  	switch v := envOr("GOARM", defaultGOARM); v {
    50  	case "5":
    51  		return 5
    52  	case "6":
    53  		return 6
    54  	case "7":
    55  		return 7
    56  	}
    57  	// Fail here, rather than validate at multiple call sites.
    58  	log.Fatalf("Invalid GOARM value. Must be 5, 6, or 7.")
    59  	panic("unreachable")
    60  }
    61  
    62  func Getgo386() string {
    63  	// Validated by cmd/compile.
    64  	return envOr("GO386", defaultGO386)
    65  }
    66  
    67  func Getgoextlinkenabled() string {
    68  	return envOr("GO_EXTLINK_ENABLED", defaultGO_EXTLINK_ENABLED)
    69  }
    70  
    71  func Getgoversion() string {
    72  	return version
    73  }
    74  
    75  func (p *Prog) Line() string {
    76  	return p.Ctxt.LineHist.LineString(int(p.Lineno))
    77  }
    78  
    79  var armCondCode = []string{
    80  	".EQ",
    81  	".NE",
    82  	".CS",
    83  	".CC",
    84  	".MI",
    85  	".PL",
    86  	".VS",
    87  	".VC",
    88  	".HI",
    89  	".LS",
    90  	".GE",
    91  	".LT",
    92  	".GT",
    93  	".LE",
    94  	"",
    95  	".NV",
    96  }
    97  
    98  /* ARM scond byte */
    99  const (
   100  	C_SCOND     = (1 << 4) - 1
   101  	C_SBIT      = 1 << 4
   102  	C_PBIT      = 1 << 5
   103  	C_WBIT      = 1 << 6
   104  	C_FBIT      = 1 << 7
   105  	C_UBIT      = 1 << 7
   106  	C_SCOND_XOR = 14
   107  )
   108  
   109  // CConv formats ARM condition codes and SPARC64 instruction suffixes.
   110  func CConv(ctxt *Link, s uint8) (sc string) {
   111  	if s == 0 {
   112  		return ""
   113  	}
   114  	switch ctxt.Arch.Family {
   115  	case sys.ARM, sys.ARM64:
   116  		sc = armCondCode[(s&C_SCOND)^C_SCOND_XOR]
   117  		if s&C_SBIT != 0 {
   118  			sc += ".S"
   119  		}
   120  		if s&C_PBIT != 0 {
   121  			sc += ".P"
   122  		}
   123  		if s&C_WBIT != 0 {
   124  			sc += ".W"
   125  		}
   126  		if s&C_UBIT != 0 { /* ambiguous with FBIT */
   127  			sc += ".U"
   128  		}
   129  	case sys.SPARC64:
   130  		if s == 1 {
   131  			sc = ".PN"
   132  		}
   133  	}
   134  	return sc
   135  }
   136  
   137  func (p *Prog) String() string {
   138  	if p == nil {
   139  		return "<nil Prog>"
   140  	}
   141  
   142  	if p.Ctxt == nil {
   143  		return "<Prog without ctxt>"
   144  	}
   145  
   146  	sc := CConv(p.Ctxt, p.Scond)
   147  
   148  	var buf bytes.Buffer
   149  
   150  	fmt.Fprintf(&buf, "%.5d (%v)\t%v%s", p.Pc, p.Line(), Aconv(p.As), sc)
   151  	sep := "\t"
   152  	quadOpAmd64 := p.RegTo2 == -1
   153  	if quadOpAmd64 {
   154  		fmt.Fprintf(&buf, "%s$%d", sep, p.From3.Offset)
   155  		sep = ", "
   156  	}
   157  	if p.From.Type != TYPE_NONE {
   158  		fmt.Fprintf(&buf, "%s%v", sep, Dconv(p, &p.From))
   159  		sep = ", "
   160  	}
   161  	if p.Reg != REG_NONE {
   162  		// Should not happen but might as well show it if it does.
   163  		fmt.Fprintf(&buf, "%s%v", sep, Rconv(int(p.Reg)))
   164  		sep = ", "
   165  	}
   166  	if p.From3Type() != TYPE_NONE {
   167  		if p.From3.Type == TYPE_CONST && (p.As == ATEXT || p.As == AGLOBL) {
   168  			// Special case - omit $.
   169  			fmt.Fprintf(&buf, "%s%d", sep, p.From3.Offset)
   170  		} else if quadOpAmd64 {
   171  			fmt.Fprintf(&buf, "%s%v", sep, Rconv(int(p.From3.Reg)))
   172  		} else {
   173  			fmt.Fprintf(&buf, "%s%v", sep, Dconv(p, p.From3))
   174  		}
   175  		sep = ", "
   176  	}
   177  	if p.To.Type != TYPE_NONE {
   178  		fmt.Fprintf(&buf, "%s%v", sep, Dconv(p, &p.To))
   179  	}
   180  	if p.RegTo2 != REG_NONE && !quadOpAmd64 {
   181  		fmt.Fprintf(&buf, "%s%v", sep, Rconv(int(p.RegTo2)))
   182  	}
   183  	return buf.String()
   184  }
   185  
   186  func (ctxt *Link) NewProg() *Prog {
   187  	var p *Prog
   188  	if i := ctxt.allocIdx; i < len(ctxt.progs) {
   189  		p = &ctxt.progs[i]
   190  		ctxt.allocIdx = i + 1
   191  	} else {
   192  		p = new(Prog) // should be the only call to this; all others should use ctxt.NewProg
   193  	}
   194  	p.Ctxt = ctxt
   195  	return p
   196  }
   197  func (ctxt *Link) freeProgs() {
   198  	s := ctxt.progs[:ctxt.allocIdx]
   199  	for i := range s {
   200  		s[i] = Prog{}
   201  	}
   202  	ctxt.allocIdx = 0
   203  }
   204  
   205  func (ctxt *Link) Line(n int) string {
   206  	return ctxt.LineHist.LineString(n)
   207  }
   208  
   209  func Getcallerpc(interface{}) uintptr {
   210  	return 1
   211  }
   212  
   213  func (ctxt *Link) Dconv(a *Addr) string {
   214  	return Dconv(nil, a)
   215  }
   216  
   217  func Dconv(p *Prog, a *Addr) string {
   218  	var str string
   219  
   220  	switch a.Type {
   221  	default:
   222  		str = fmt.Sprintf("type=%d", a.Type)
   223  
   224  	case TYPE_NONE:
   225  		str = ""
   226  		if a.Name != NAME_NONE || a.Reg != 0 || a.Sym != nil {
   227  			str = fmt.Sprintf("%v(%v)(NONE)", Mconv(a), 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 {
   236  			str = fmt.Sprintf("$%d,%v", a.Offset, Rconv(int(a.Reg)))
   237  			break
   238  		}
   239  
   240  		str = Rconv(int(a.Reg))
   241  		if a.Name != NAME_NONE || a.Sym != nil {
   242  			str = fmt.Sprintf("%v(%v)(REG)", Mconv(a), Rconv(int(a.Reg)))
   243  		}
   244  
   245  	case TYPE_BRANCH:
   246  		if a.Sym != nil {
   247  			str = fmt.Sprintf("%s(SB)", a.Sym.Name)
   248  		} else if p != nil && p.Pcond != nil {
   249  			str = fmt.Sprint(p.Pcond.Pc)
   250  		} else if a.Val != nil {
   251  			str = fmt.Sprint(a.Val.(*Prog).Pc)
   252  		} else {
   253  			str = fmt.Sprintf("%d(PC)", a.Offset)
   254  		}
   255  
   256  	case TYPE_INDIR:
   257  		str = fmt.Sprintf("*%s", Mconv(a))
   258  
   259  	case TYPE_MEM:
   260  		str = Mconv(a)
   261  		if a.Index != REG_NONE {
   262  			str += fmt.Sprintf("(%v*%d)", Rconv(int(a.Index)), int(a.Scale))
   263  		}
   264  		if p != nil && p.As == ATYPE && a.Gotype != nil {
   265  			str += fmt.Sprintf("%s", a.Gotype.Name)
   266  		}
   267  
   268  	case TYPE_CONST:
   269  		if a.Reg != 0 {
   270  			str = fmt.Sprintf("$%v(%v)", Mconv(a), Rconv(int(a.Reg)))
   271  		} else {
   272  			str = fmt.Sprintf("$%v", Mconv(a))
   273  		}
   274  
   275  	case TYPE_TEXTSIZE:
   276  		if a.Val.(int32) == ArgsSizeUnknown {
   277  			str = fmt.Sprintf("$%d", a.Offset)
   278  		} else {
   279  			str = fmt.Sprintf("$%d-%d", a.Offset, a.Val.(int32))
   280  		}
   281  
   282  	case TYPE_FCONST:
   283  		str = fmt.Sprintf("%.17g", a.Val.(float64))
   284  		// Make sure 1 prints as 1.0
   285  		if !strings.ContainsAny(str, ".e") {
   286  			str += ".0"
   287  		}
   288  		str = fmt.Sprintf("$(%s)", str)
   289  
   290  	case TYPE_SCONST:
   291  		str = fmt.Sprintf("$%q", a.Val.(string))
   292  
   293  	case TYPE_ADDR:
   294  		str = fmt.Sprintf("$%s", Mconv(a))
   295  		if a.Index != REG_NONE {
   296  			str += fmt.Sprintf("(%v*%d)", Rconv(int(a.Index)), int(a.Scale))
   297  		}
   298  
   299  	case TYPE_SHIFT:
   300  		v := int(a.Offset)
   301  		op := "<<>>->@>"[((v>>5)&3)<<1:]
   302  		if v&(1<<4) != 0 {
   303  			str = fmt.Sprintf("R%d%c%cR%d", v&15, op[0], op[1], (v>>8)&15)
   304  		} else {
   305  			str = fmt.Sprintf("R%d%c%c%d", v&15, op[0], op[1], (v>>7)&31)
   306  		}
   307  		if a.Reg != 0 {
   308  			str += fmt.Sprintf("(%v)", Rconv(int(a.Reg)))
   309  		}
   310  
   311  	case TYPE_REGREG:
   312  		str = fmt.Sprintf("(%v, %v)", Rconv(int(a.Reg)), Rconv(int(a.Offset)))
   313  
   314  	case TYPE_REGREG2:
   315  		str = fmt.Sprintf("%v, %v", Rconv(int(a.Reg)), Rconv(int(a.Offset)))
   316  
   317  	case TYPE_REGLIST:
   318  		str = regListConv(int(a.Offset))
   319  	}
   320  
   321  	return str
   322  }
   323  
   324  func Mconv(a *Addr) string {
   325  	var str string
   326  
   327  	switch a.Name {
   328  	default:
   329  		str = fmt.Sprintf("name=%d", a.Name)
   330  
   331  	case NAME_NONE:
   332  		switch {
   333  		case a.Reg == REG_NONE:
   334  			str = fmt.Sprint(a.Offset)
   335  		case a.Offset == 0:
   336  			str = fmt.Sprintf("(%v)", Rconv(int(a.Reg)))
   337  		case a.Offset != 0:
   338  			// TODO(aram): remove hack
   339  			reg := Rconv(int(a.Reg))
   340  			if (reg == "RSP" || reg == "RFP") && a.Offset > 0x7ff {
   341  				return fmt.Sprintf("%d+176+2047(%v)", a.Offset-0x7ff-176, reg)
   342  			}
   343  			str = fmt.Sprintf("%d(%v)", a.Offset, reg)
   344  		}
   345  
   346  	case NAME_EXTERN:
   347  		if a.Sym != nil {
   348  			str = fmt.Sprintf("%s%s(SB)", a.Sym.Name, offConv(a.Offset))
   349  		} else {
   350  			str = fmt.Sprintf("%s(SB)", offConv(a.Offset))
   351  		}
   352  
   353  	case NAME_GOTREF:
   354  		if a.Sym != nil {
   355  			str = fmt.Sprintf("%s%s@GOT(SB)", a.Sym.Name, offConv(a.Offset))
   356  		} else {
   357  			str = fmt.Sprintf("%s@GOT(SB)", offConv(a.Offset))
   358  		}
   359  
   360  	case NAME_STATIC:
   361  		if a.Sym != nil {
   362  			str = fmt.Sprintf("%s<>%s(SB)", a.Sym.Name, offConv(a.Offset))
   363  		} else {
   364  			str = fmt.Sprintf("<>%s(SB)", offConv(a.Offset))
   365  		}
   366  
   367  	case NAME_AUTO:
   368  		if a.Sym != nil {
   369  			str = fmt.Sprintf("%s%s(SP)", a.Sym.Name, offConv(a.Offset))
   370  		} else {
   371  			str = fmt.Sprintf("%s(SP)", offConv(a.Offset))
   372  		}
   373  
   374  	case NAME_PARAM:
   375  		if a.Sym != nil {
   376  			str = fmt.Sprintf("%s%s(FP)", a.Sym.Name, offConv(a.Offset))
   377  		} else {
   378  			str = fmt.Sprintf("%s(FP)", offConv(a.Offset))
   379  		}
   380  	}
   381  	return str
   382  }
   383  
   384  func offConv(off int64) string {
   385  	if off == 0 {
   386  		return ""
   387  	}
   388  	return fmt.Sprintf("%+d", off)
   389  }
   390  
   391  type regSet struct {
   392  	lo    int
   393  	hi    int
   394  	Rconv func(int) string
   395  }
   396  
   397  // Few enough architectures that a linear scan is fastest.
   398  // Not even worth sorting.
   399  var regSpace []regSet
   400  
   401  /*
   402  	Each architecture defines a register space as a unique
   403  	integer range.
   404  	Here is the list of architectures and the base of their register spaces.
   405  */
   406  
   407  const (
   408  	// Because of masking operations in the encodings, each register
   409  	// space should start at 0 modulo some power of 2.
   410  	RBase386     = 1 * 1024
   411  	RBaseAMD64   = 2 * 1024
   412  	RBaseARM     = 3 * 1024
   413  	RBasePPC64   = 4 * 1024  // range [4k, 8k)
   414  	RBaseARM64   = 8 * 1024  // range [8k, 13k)
   415  	RBaseMIPS64  = 13 * 1024 // range [13k, 14k)
   416  	RBaseS390X   = 14 * 1024 // range [14k, 15k)
   417  	RBaseSPARC64 = 15 * 1024 // range [15k, 16k)
   418  )
   419  
   420  // RegisterRegister binds a pretty-printer (Rconv) for register
   421  // numbers to a given register number range. Lo is inclusive,
   422  // hi exclusive (valid registers are lo through hi-1).
   423  func RegisterRegister(lo, hi int, Rconv func(int) string) {
   424  	regSpace = append(regSpace, regSet{lo, hi, Rconv})
   425  }
   426  
   427  func Rconv(reg int) string {
   428  	if reg == REG_NONE {
   429  		return "NONE"
   430  	}
   431  	for i := range regSpace {
   432  		rs := &regSpace[i]
   433  		if rs.lo <= reg && reg < rs.hi {
   434  			return rs.Rconv(reg)
   435  		}
   436  	}
   437  	return fmt.Sprintf("R???%d", reg)
   438  }
   439  
   440  func regListConv(list int) string {
   441  	str := ""
   442  
   443  	for i := 0; i < 16; i++ { // TODO: 16 is ARM-specific.
   444  		if list&(1<<uint(i)) != 0 {
   445  			if str == "" {
   446  				str += "["
   447  			} else {
   448  				str += ","
   449  			}
   450  			// This is ARM-specific; R10 is g.
   451  			if i == 10 {
   452  				str += "g"
   453  			} else {
   454  				str += fmt.Sprintf("R%d", i)
   455  			}
   456  		}
   457  	}
   458  
   459  	str += "]"
   460  	return str
   461  }
   462  
   463  type opSet struct {
   464  	lo    As
   465  	names []string
   466  }
   467  
   468  // Not even worth sorting
   469  var aSpace []opSet
   470  
   471  // RegisterOpcode binds a list of instruction names
   472  // to a given instruction number range.
   473  func RegisterOpcode(lo As, Anames []string) {
   474  	aSpace = append(aSpace, opSet{lo, Anames})
   475  }
   476  
   477  func Aconv(a As) string {
   478  	if 0 <= a && int(a) < len(Anames) {
   479  		return Anames[a]
   480  	}
   481  	for i := range aSpace {
   482  		as := &aSpace[i]
   483  		if as.lo <= a && int(a-as.lo) < len(as.names) {
   484  			return as.names[a-as.lo]
   485  		}
   486  	}
   487  	return fmt.Sprintf("A???%d", a)
   488  }
   489  
   490  var Anames = []string{
   491  	"XXX",
   492  	"CALL",
   493  	"CHECKNIL",
   494  	"DUFFCOPY",
   495  	"DUFFZERO",
   496  	"END",
   497  	"FUNCDATA",
   498  	"GLOBL",
   499  	"JMP",
   500  	"NOP",
   501  	"PCDATA",
   502  	"RET",
   503  	"TEXT",
   504  	"TYPE",
   505  	"UNDEF",
   506  	"USEFIELD",
   507  	"VARDEF",
   508  	"VARKILL",
   509  	"VARLIVE",
   510  }
   511  
   512  func Bool2int(b bool) int {
   513  	if b {
   514  		return 1
   515  	}
   516  	return 0
   517  }