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