...

Source file src/cmd/internal/obj/util.go

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

View as plain text