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