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

View as plain text