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  	"internal/abi"
    12  	"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  var armCondCode = []string{
    52  	".EQ",
    53  	".NE",
    54  	".CS",
    55  	".CC",
    56  	".MI",
    57  	".PL",
    58  	".VS",
    59  	".VC",
    60  	".HI",
    61  	".LS",
    62  	".GE",
    63  	".LT",
    64  	".GT",
    65  	".LE",
    66  	"",
    67  	".NV",
    68  }
    69  
    70  /* ARM scond byte */
    71  const (
    72  	C_SCOND     = (1 << 4) - 1
    73  	C_SBIT      = 1 << 4
    74  	C_PBIT      = 1 << 5
    75  	C_WBIT      = 1 << 6
    76  	C_FBIT      = 1 << 7
    77  	C_UBIT      = 1 << 7
    78  	C_SCOND_XOR = 14
    79  )
    80  
    81  // CConv formats opcode suffix bits (Prog.Scond).
    82  func CConv(s uint8) string {
    83  	if s == 0 {
    84  		return ""
    85  	}
    86  	for i := range opSuffixSpace {
    87  		sset := &opSuffixSpace[i]
    88  		if sset.arch == buildcfg.GOARCH {
    89  			return sset.cconv(s)
    90  		}
    91  	}
    92  	return fmt.Sprintf("SC???%d", s)
    93  }
    94  
    95  // CConvARM formats ARM opcode suffix bits (mostly condition codes).
    96  func CConvARM(s uint8) string {
    97  	// TODO: could be great to move suffix-related things into
    98  	// ARM asm backends some day.
    99  	// obj/x86 can be used as an example.
   100  
   101  	sc := armCondCode[(s&C_SCOND)^C_SCOND_XOR]
   102  	if s&C_SBIT != 0 {
   103  		sc += ".S"
   104  	}
   105  	if s&C_PBIT != 0 {
   106  		sc += ".P"
   107  	}
   108  	if s&C_WBIT != 0 {
   109  		sc += ".W"
   110  	}
   111  	if s&C_UBIT != 0 { /* ambiguous with FBIT */
   112  		sc += ".U"
   113  	}
   114  	return sc
   115  }
   116  
   117  func (p *Prog) String() string {
   118  	if p == nil {
   119  		return "<nil Prog>"
   120  	}
   121  	if p.Ctxt == nil {
   122  		return "<Prog without ctxt>"
   123  	}
   124  	return fmt.Sprintf("%.5d (%v)\t%s", p.Pc, p.Line(), p.InstructionString())
   125  }
   126  
   127  func (p *Prog) InnermostString(w io.Writer) {
   128  	if p == nil {
   129  		io.WriteString(w, "<nil Prog>")
   130  		return
   131  	}
   132  	if p.Ctxt == nil {
   133  		io.WriteString(w, "<Prog without ctxt>")
   134  		return
   135  	}
   136  	fmt.Fprintf(w, "%.5d (", p.Pc)
   137  	p.InnermostLine(w)
   138  	io.WriteString(w, ")\t")
   139  	p.WriteInstructionString(w)
   140  }
   141  
   142  // InstructionString returns a string representation of the instruction without preceding
   143  // program counter or file and line number.
   144  func (p *Prog) InstructionString() string {
   145  	buf := new(bytes.Buffer)
   146  	p.WriteInstructionString(buf)
   147  	return buf.String()
   148  }
   149  
   150  // WriteInstructionString writes a string representation of the instruction without preceding
   151  // program counter or file and line number.
   152  func (p *Prog) WriteInstructionString(w io.Writer) {
   153  	if p == nil {
   154  		io.WriteString(w, "<nil Prog>")
   155  		return
   156  	}
   157  
   158  	if p.Ctxt == nil {
   159  		io.WriteString(w, "<Prog without ctxt>")
   160  		return
   161  	}
   162  
   163  	sc := CConv(p.Scond)
   164  
   165  	io.WriteString(w, p.As.String())
   166  	io.WriteString(w, sc)
   167  	sep := "\t"
   168  
   169  	if p.From.Type != TYPE_NONE {
   170  		io.WriteString(w, sep)
   171  		WriteDconv(w, p, &p.From)
   172  		sep = ", "
   173  	}
   174  	if p.Reg != REG_NONE {
   175  		// Should not happen but might as well show it if it does.
   176  		fmt.Fprintf(w, "%s%v", sep, Rconv(int(p.Reg)))
   177  		sep = ", "
   178  	}
   179  	for i := range p.RestArgs {
   180  		if p.RestArgs[i].Pos == Source {
   181  			io.WriteString(w, sep)
   182  			WriteDconv(w, p, &p.RestArgs[i].Addr)
   183  			sep = ", "
   184  		}
   185  	}
   186  
   187  	if p.As == ATEXT {
   188  		// If there are attributes, print them. Otherwise, skip the comma.
   189  		// In short, print one of these two:
   190  		// TEXT	foo(SB), DUPOK|NOSPLIT, $0
   191  		// TEXT	foo(SB), $0
   192  		s := p.From.Sym.TextAttrString()
   193  		if s != "" {
   194  			fmt.Fprintf(w, "%s%s", sep, s)
   195  			sep = ", "
   196  		}
   197  	}
   198  	if p.To.Type != TYPE_NONE {
   199  		io.WriteString(w, sep)
   200  		WriteDconv(w, p, &p.To)
   201  		sep = ", "
   202  	}
   203  	if p.RegTo2 != REG_NONE {
   204  		fmt.Fprintf(w, "%s%v", sep, Rconv(int(p.RegTo2)))
   205  	}
   206  	for i := range p.RestArgs {
   207  		if p.RestArgs[i].Pos == Destination {
   208  			io.WriteString(w, sep)
   209  			WriteDconv(w, p, &p.RestArgs[i].Addr)
   210  			sep = ", "
   211  		}
   212  	}
   213  }
   214  
   215  func (ctxt *Link) NewProg() *Prog {
   216  	p := new(Prog)
   217  	p.Ctxt = ctxt
   218  	return p
   219  }
   220  
   221  func (ctxt *Link) CanReuseProgs() bool {
   222  	return ctxt.Debugasm == 0
   223  }
   224  
   225  func isZReg(r int) bool {
   226  	return (r >= RBaseARM64+96 && r <= RBaseARM64+127) ||
   227  		(r >= RBaseARM64+2048 && r < RBaseARM64+3072)
   228  }
   229  
   230  // Dconv accepts an argument 'a' within a prog 'p' and returns a string
   231  // with a formatted version of the argument.
   232  func Dconv(p *Prog, a *Addr) string {
   233  	buf := new(bytes.Buffer)
   234  	writeDconv(buf, p, a, false)
   235  	return buf.String()
   236  }
   237  
   238  // DconvWithABIDetail accepts an argument 'a' within a prog 'p'
   239  // and returns a string with a formatted version of the argument, in
   240  // which text symbols are rendered with explicit ABI selectors.
   241  func DconvWithABIDetail(p *Prog, a *Addr) string {
   242  	buf := new(bytes.Buffer)
   243  	writeDconv(buf, p, a, true)
   244  	return buf.String()
   245  }
   246  
   247  // WriteDconv accepts an argument 'a' within a prog 'p'
   248  // and writes a formatted version of the arg to the writer.
   249  func WriteDconv(w io.Writer, p *Prog, a *Addr) {
   250  	writeDconv(w, p, a, false)
   251  }
   252  
   253  func writeDconv(w io.Writer, p *Prog, a *Addr, abiDetail bool) {
   254  	switch a.Type {
   255  	default:
   256  		fmt.Fprintf(w, "type=%d", a.Type)
   257  
   258  	case TYPE_NONE:
   259  		if a.Name != NAME_NONE || a.Reg != 0 || a.Sym != nil {
   260  			a.WriteNameTo(w)
   261  			fmt.Fprintf(w, "(%v)(NONE)", Rconv(int(a.Reg)))
   262  		}
   263  
   264  	case TYPE_REG:
   265  		if buildcfg.GOARCH == "arm64" && a.Offset&(int64(1)<<62) != 0 {
   266  			preg := int((a.Offset)&31) + RBaseARM64 + 128
   267  			arng2 := (a.Offset >> 5) & 15
   268  			selreg := int((a.Offset>>9)&31) + RBaseARM64
   269  			idximm := (a.Offset >> 14) & 63
   270  
   271  			arngStr := ""
   272  			switch arng2 {
   273  			case 9:
   274  				arngStr = ".B"
   275  			case 10:
   276  				arngStr = ".H"
   277  			case 11:
   278  				arngStr = ".S"
   279  			case 12:
   280  				arngStr = ".D"
   281  			case 13:
   282  				arngStr = ".Q"
   283  			}
   284  
   285  			fmt.Fprintf(w, "[%s,$%d](%s%s)", Rconv(selreg), idximm, Rconv(preg), arngStr)
   286  			return
   287  		}
   288  
   289  		// TODO(rsc): This special case is for x86 instructions like
   290  		//	PINSRQ	CX,$1,X6
   291  		// where the $1 is included in the p->to Addr.
   292  		// Move into a new field.
   293  		if a.Offset != 0 && (a.Reg < RBaseARM64 || a.Reg >= RBaseMIPS) {
   294  			fmt.Fprintf(w, "$%d,%v", a.Offset, Rconv(int(a.Reg)))
   295  			return
   296  		}
   297  
   298  		if a.Name != NAME_NONE || a.Sym != nil {
   299  			a.WriteNameTo(w)
   300  			fmt.Fprintf(w, "(%v)(REG)", Rconv(int(a.Reg)))
   301  		} else {
   302  			io.WriteString(w, Rconv(int(a.Reg)))
   303  		}
   304  
   305  		if ((RBaseARM64+1<<10+1<<9) /* arm64.REG_ELEM */ <= a.Reg && a.Reg < (RBaseARM64+1<<11) /* arm64.REG_ZARNG */) ||
   306  			((RBaseARM64+1<<11+1<<9) /* arm64.REG_ZARNGELEM */ <= a.Reg && a.Reg < (RBaseARM64+1<<11+1<<10+1<<9) /* arm64.REG_PARNGZM */) {
   307  			fmt.Fprintf(w, "[%d]", a.Index)
   308  		}
   309  
   310  		if (RBaseLOONG64+(1<<10)+(1<<11)) /* loong64.REG_ELEM */ <= a.Reg &&
   311  			a.Reg < (RBaseLOONG64+(1<<10)+(2<<11)) /* loong64.REG_ELEM_END */ {
   312  			fmt.Fprintf(w, "[%d]", a.Index)
   313  		}
   314  
   315  	case TYPE_BRANCH:
   316  		if a.Sym != nil {
   317  			fmt.Fprintf(w, "%s%s(SB)", a.Sym.Name, abiDecorate(a, abiDetail))
   318  		} else if a.Target() != nil {
   319  			fmt.Fprint(w, a.Target().Pc)
   320  		} else {
   321  			fmt.Fprintf(w, "%d(PC)", a.Offset)
   322  		}
   323  
   324  	case TYPE_INDIR:
   325  		io.WriteString(w, "*")
   326  		a.writeNameTo(w, abiDetail)
   327  
   328  	case TYPE_MEM:
   329  		if buildcfg.GOARCH == "arm64" && (a.Scale < 0 || (a.Index != REG_NONE && (isZReg(int(a.Reg)) || isZReg(int(a.Index))))) {
   330  			// SVE extended addressing pattern
   331  			if a.Index == REG_NONE {
   332  				if a.Offset < 0 {
   333  					fmt.Fprintf(w, "(-VL*%d)(%v)", -a.Offset, Rconv(int(a.Reg)))
   334  				} else {
   335  					fmt.Fprintf(w, "(VL*%d)(%v)", a.Offset, Rconv(int(a.Reg)))
   336  				}
   337  			} else {
   338  				amount := 0
   339  				mod := 0
   340  				if a.Scale < 0 {
   341  					amount = int((a.Scale >> 12) & 0x7)
   342  					mod = int((a.Scale >> 9) & 0x7)
   343  				}
   344  				modStr := ""
   345  				switch mod {
   346  				case 1:
   347  					modStr = ".UXTW"
   348  				case 2:
   349  					modStr = ".SXTW"
   350  				}
   351  				amountStr := ""
   352  				if amount != 0 {
   353  					amountStr = fmt.Sprintf("<<%d", amount)
   354  				}
   355  				fmt.Fprintf(w, "(%v%s%s)(%v)", Rconv(int(a.Reg)), modStr, amountStr, Rconv(int(a.Index)))
   356  			}
   357  		} else {
   358  			a.WriteNameTo(w)
   359  			if a.Index != REG_NONE {
   360  				if a.Scale == 0 {
   361  					// arm64 shifted or extended register offset, scale = 0.
   362  					fmt.Fprintf(w, "(%v)", Rconv(int(a.Index)))
   363  				} else {
   364  					fmt.Fprintf(w, "(%v*%d)", Rconv(int(a.Index)), int(a.Scale))
   365  				}
   366  			}
   367  		}
   368  
   369  	case TYPE_CONST:
   370  		io.WriteString(w, "$")
   371  		a.WriteNameTo(w)
   372  		if a.Reg != 0 {
   373  			fmt.Fprintf(w, "(%v)", Rconv(int(a.Reg)))
   374  		}
   375  
   376  	case TYPE_TEXTSIZE:
   377  		if a.Val.(int32) == abi.ArgsSizeUnknown {
   378  			fmt.Fprintf(w, "$%d", a.Offset)
   379  		} else {
   380  			fmt.Fprintf(w, "$%d-%d", a.Offset, a.Val.(int32))
   381  		}
   382  
   383  	case TYPE_FCONST:
   384  		str := fmt.Sprintf("%.17g", a.Val.(float64))
   385  		// Make sure 1 prints as 1.0
   386  		if !strings.ContainsAny(str, ".e") {
   387  			str += ".0"
   388  		}
   389  		fmt.Fprintf(w, "$(%s)", str)
   390  
   391  	case TYPE_SCONST:
   392  		fmt.Fprintf(w, "$%q", a.Val.(string))
   393  
   394  	case TYPE_ADDR:
   395  		io.WriteString(w, "$")
   396  		a.writeNameTo(w, abiDetail)
   397  
   398  	case TYPE_SHIFT:
   399  		v := int(a.Offset)
   400  		ops := "<<>>->@>"
   401  		switch buildcfg.GOARCH {
   402  		case "arm":
   403  			op := ops[((v>>5)&3)<<1:]
   404  			if v&(1<<4) != 0 {
   405  				fmt.Fprintf(w, "R%d%c%cR%d", v&15, op[0], op[1], (v>>8)&15)
   406  			} else {
   407  				fmt.Fprintf(w, "R%d%c%c%d", v&15, op[0], op[1], (v>>7)&31)
   408  			}
   409  			if a.Reg != 0 {
   410  				fmt.Fprintf(w, "(%v)", Rconv(int(a.Reg)))
   411  			}
   412  		case "arm64":
   413  			op := ops[((v>>22)&3)<<1:]
   414  			r := (v >> 16) & 31
   415  			fmt.Fprintf(w, "%s%c%c%d", Rconv(r+RBaseARM64), op[0], op[1], (v>>10)&63)
   416  		default:
   417  			panic("TYPE_SHIFT is not supported on " + buildcfg.GOARCH)
   418  		}
   419  
   420  	case TYPE_REGREG:
   421  		fmt.Fprintf(w, "(%v, %v)", Rconv(int(a.Reg)), Rconv(int(a.Offset)))
   422  
   423  	case TYPE_REGREG2:
   424  		fmt.Fprintf(w, "%v, %v", Rconv(int(a.Offset)), Rconv(int(a.Reg)))
   425  
   426  	case TYPE_REGLIST:
   427  		io.WriteString(w, RLconv(a.Offset))
   428  
   429  	case TYPE_SPECIAL:
   430  		io.WriteString(w, SPCconv(a.Offset))
   431  	}
   432  }
   433  
   434  func (a *Addr) WriteNameTo(w io.Writer) {
   435  	a.writeNameTo(w, false)
   436  }
   437  
   438  func (a *Addr) writeNameTo(w io.Writer, abiDetail bool) {
   439  
   440  	switch a.Name {
   441  	default:
   442  		fmt.Fprintf(w, "name=%d", a.Name)
   443  
   444  	case NAME_NONE:
   445  		switch {
   446  		case a.Reg == REG_NONE:
   447  			fmt.Fprint(w, a.Offset)
   448  		case a.Offset == 0:
   449  			fmt.Fprintf(w, "(%v)", Rconv(int(a.Reg)))
   450  		case a.Offset != 0:
   451  			fmt.Fprintf(w, "%d(%v)", a.Offset, Rconv(int(a.Reg)))
   452  		}
   453  
   454  		// Note: a.Reg == REG_NONE encodes the default base register for the NAME_ type.
   455  	case NAME_EXTERN:
   456  		reg := "SB"
   457  		if a.Reg != REG_NONE {
   458  			reg = Rconv(int(a.Reg))
   459  		}
   460  		if a.Sym != nil {
   461  			fmt.Fprintf(w, "%s%s%s(%s)", a.Sym.Name, abiDecorate(a, abiDetail), offConv(a.Offset), reg)
   462  		} else {
   463  			fmt.Fprintf(w, "%s(%s)", offConv(a.Offset), reg)
   464  		}
   465  
   466  	case NAME_GOTREF:
   467  		reg := "SB"
   468  		if a.Reg != REG_NONE {
   469  			reg = Rconv(int(a.Reg))
   470  		}
   471  		if a.Sym != nil {
   472  			fmt.Fprintf(w, "%s%s@GOT(%s)", a.Sym.Name, offConv(a.Offset), reg)
   473  		} else {
   474  			fmt.Fprintf(w, "%s@GOT(%s)", offConv(a.Offset), reg)
   475  		}
   476  
   477  	case NAME_STATIC:
   478  		reg := "SB"
   479  		if a.Reg != REG_NONE {
   480  			reg = Rconv(int(a.Reg))
   481  		}
   482  		if a.Sym != nil {
   483  			fmt.Fprintf(w, "%s<>%s(%s)", a.Sym.Name, offConv(a.Offset), reg)
   484  		} else {
   485  			fmt.Fprintf(w, "<>%s(%s)", offConv(a.Offset), reg)
   486  		}
   487  
   488  	case NAME_AUTO:
   489  		reg := "SP"
   490  		if a.Reg != REG_NONE {
   491  			reg = Rconv(int(a.Reg))
   492  		}
   493  		if a.Sym != nil {
   494  			fmt.Fprintf(w, "%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg)
   495  		} else {
   496  			fmt.Fprintf(w, "%s(%s)", offConv(a.Offset), reg)
   497  		}
   498  
   499  	case NAME_PARAM:
   500  		reg := "FP"
   501  		if a.Reg != REG_NONE {
   502  			reg = Rconv(int(a.Reg))
   503  		}
   504  		if a.Sym != nil {
   505  			fmt.Fprintf(w, "%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg)
   506  		} else {
   507  			fmt.Fprintf(w, "%s(%s)", offConv(a.Offset), reg)
   508  		}
   509  	case NAME_TOCREF:
   510  		reg := "SB"
   511  		if a.Reg != REG_NONE {
   512  			reg = Rconv(int(a.Reg))
   513  		}
   514  		if a.Sym != nil {
   515  			fmt.Fprintf(w, "%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg)
   516  		} else {
   517  			fmt.Fprintf(w, "%s(%s)", offConv(a.Offset), reg)
   518  		}
   519  	}
   520  }
   521  
   522  func offConv(off int64) string {
   523  	if off == 0 {
   524  		return ""
   525  	}
   526  	return fmt.Sprintf("%+d", off)
   527  }
   528  
   529  // opSuffixSet is like regListSet, but for opcode suffixes.
   530  //
   531  // Unlike some other similar structures, uint8 space is not
   532  // divided by its own values set (because there are only 256 of them).
   533  // Instead, every arch may interpret/format all 8 bits as they like,
   534  // as long as they register proper cconv function for it.
   535  type opSuffixSet struct {
   536  	arch  string
   537  	cconv func(suffix uint8) string
   538  }
   539  
   540  var opSuffixSpace []opSuffixSet
   541  
   542  // RegisterOpSuffix assigns cconv function for formatting opcode suffixes
   543  // when compiling for GOARCH=arch.
   544  //
   545  // cconv is never called with 0 argument.
   546  func RegisterOpSuffix(arch string, cconv func(uint8) string) {
   547  	opSuffixSpace = append(opSuffixSpace, opSuffixSet{
   548  		arch:  arch,
   549  		cconv: cconv,
   550  	})
   551  }
   552  
   553  type regSet struct {
   554  	lo    int
   555  	hi    int
   556  	Rconv func(int) string
   557  }
   558  
   559  // Few enough architectures that a linear scan is fastest.
   560  // Not even worth sorting.
   561  var regSpace []regSet
   562  
   563  /*
   564  	Each architecture defines a register space as a unique
   565  	integer range.
   566  	Here is the list of architectures and the base of their register spaces.
   567  */
   568  
   569  const (
   570  	// Because of masking operations in the encodings, each register
   571  	// space should start at 0 modulo some power of 2.
   572  	RBase386     = 1 * 1024
   573  	RBaseAMD64   = 2 * 1024
   574  	RBaseARM     = 3 * 1024
   575  	RBasePPC64   = 4 * 1024  // range [4k, 8k)
   576  	RBaseARM64   = 8 * 1024  // range [8k, 18k)
   577  	RBaseMIPS    = 18 * 1024 // range [18k, 19k)
   578  	RBaseS390X   = 19 * 1024 // range [19k, 20k)
   579  	RBaseRISCV   = 20 * 1024 // range [20k, 21k)
   580  	RBaseWasm    = 21 * 1024
   581  	RBaseLOONG64 = 22 * 1024 // range [22K, 25k)
   582  )
   583  
   584  // RegisterRegister binds a pretty-printer (Rconv) for register
   585  // numbers to a given register number range. Lo is inclusive,
   586  // hi exclusive (valid registers are lo through hi-1).
   587  func RegisterRegister(lo, hi int, Rconv func(int) string) {
   588  	regSpace = append(regSpace, regSet{lo, hi, Rconv})
   589  }
   590  
   591  func Rconv(reg int) string {
   592  	if reg == REG_NONE {
   593  		return "NONE"
   594  	}
   595  	for i := range regSpace {
   596  		rs := &regSpace[i]
   597  		if rs.lo <= reg && reg < rs.hi {
   598  			return rs.Rconv(reg)
   599  		}
   600  	}
   601  	return fmt.Sprintf("R???%d", reg)
   602  }
   603  
   604  type regListSet struct {
   605  	lo     int64
   606  	hi     int64
   607  	RLconv func(int64) string
   608  }
   609  
   610  var regListSpace []regListSet
   611  
   612  // Each architecture is allotted a distinct subspace: [Lo, Hi) for declaring its
   613  // arch-specific register list numbers.
   614  const (
   615  	RegListARMLo = 0
   616  	RegListARMHi = 1 << 16
   617  
   618  	// arm64 uses the 60th bit to differentiate from other archs
   619  	RegListARM64Lo = 1 << 60
   620  	RegListARM64Hi = 1<<61 - 1
   621  
   622  	// x86 uses the 61th bit to differentiate from other archs
   623  	RegListX86Lo = 1 << 61
   624  	RegListX86Hi = 1<<62 - 1
   625  )
   626  
   627  // RegisterRegisterList binds a pretty-printer (RLconv) for register list
   628  // numbers to a given register list number range. Lo is inclusive,
   629  // hi exclusive (valid register list are lo through hi-1).
   630  func RegisterRegisterList(lo, hi int64, rlconv func(int64) string) {
   631  	regListSpace = append(regListSpace, regListSet{lo, hi, rlconv})
   632  }
   633  
   634  func RLconv(list int64) string {
   635  	for i := range regListSpace {
   636  		rls := &regListSpace[i]
   637  		if rls.lo <= list && list < rls.hi {
   638  			return rls.RLconv(list)
   639  		}
   640  	}
   641  	return fmt.Sprintf("RL???%d", list)
   642  }
   643  
   644  // Special operands
   645  type spcSet struct {
   646  	lo      int64
   647  	hi      int64
   648  	SPCconv func(int64) string
   649  }
   650  
   651  var spcSpace []spcSet
   652  
   653  // Each architecture is allotted a distinct subspace: [Lo, Hi) for declaring its
   654  // arch-specific special operands.
   655  const (
   656  	SpecialOperandARM64Base = 0 << 16
   657  	SpecialOperandRISCVBase = 1 << 16
   658  )
   659  
   660  // RegisterSpecialOperands binds a pretty-printer (SPCconv) for special
   661  // operand numbers to a given special operand number range. Lo is inclusive,
   662  // hi is exclusive (valid special operands are lo through hi-1).
   663  func RegisterSpecialOperands(lo, hi int64, rlconv func(int64) string) {
   664  	spcSpace = append(spcSpace, spcSet{lo, hi, rlconv})
   665  }
   666  
   667  // SPCconv returns the string representation of the special operand spc.
   668  func SPCconv(spc int64) string {
   669  	for i := range spcSpace {
   670  		spcs := &spcSpace[i]
   671  		if spcs.lo <= spc && spc < spcs.hi {
   672  			return spcs.SPCconv(spc)
   673  		}
   674  	}
   675  	return fmt.Sprintf("SPC???%d", spc)
   676  }
   677  
   678  type opSet struct {
   679  	lo    As
   680  	names []string
   681  }
   682  
   683  // Not even worth sorting
   684  var aSpace []opSet
   685  
   686  // RegisterOpcode binds a list of instruction names
   687  // to a given instruction number range.
   688  func RegisterOpcode(lo As, Anames []string) {
   689  	if len(Anames) > AllowedOpCodes {
   690  		panic(fmt.Sprintf("too many instructions, have %d max %d", len(Anames), AllowedOpCodes))
   691  	}
   692  	aSpace = append(aSpace, opSet{lo, Anames})
   693  }
   694  
   695  func (a As) String() string {
   696  	if 0 <= a && int(a) < len(Anames) {
   697  		return Anames[a]
   698  	}
   699  	for i := range aSpace {
   700  		as := &aSpace[i]
   701  		if as.lo <= a && int(a-as.lo) < len(as.names) {
   702  			return as.names[a-as.lo]
   703  		}
   704  	}
   705  	return fmt.Sprintf("A???%d", a)
   706  }
   707  
   708  var Anames = []string{
   709  	"XXX",
   710  	"CALL",
   711  	"DUFFCOPY",
   712  	"DUFFZERO",
   713  	"END",
   714  	"FUNCDATA",
   715  	"JMP",
   716  	"NOP",
   717  	"PCALIGN",
   718  	"PCALIGNMAX",
   719  	"PCDATA",
   720  	"RET",
   721  	"GETCALLERPC",
   722  	"TEXT",
   723  	"UNDEF",
   724  }
   725  
   726  func Bool2int(b bool) int {
   727  	// The compiler currently only optimizes this form.
   728  	// See issue 6011.
   729  	var i int
   730  	if b {
   731  		i = 1
   732  	} else {
   733  		i = 0
   734  	}
   735  	return i
   736  }
   737  
   738  func abiDecorate(a *Addr, abiDetail bool) string {
   739  	if !abiDetail || a.Sym == nil {
   740  		return ""
   741  	}
   742  	return fmt.Sprintf("<%s>", a.Sym.ABI())
   743  }
   744  
   745  // AlignmentPadding bytes to add to align code as requested.
   746  // Alignment is restricted to powers of 2 between 8 and 2048 inclusive.
   747  //
   748  // pc_: current offset in function, in bytes
   749  // p:  a PCALIGN or PCALIGNMAX prog
   750  // ctxt: the context, for current function
   751  // cursym: current function being assembled
   752  // returns number of bytes of padding needed,
   753  // updates minimum alignment for the function.
   754  func AlignmentPadding(pc int32, p *Prog, ctxt *Link, cursym *LSym) int {
   755  	v := AlignmentPaddingLength(pc, p, ctxt)
   756  	requireAlignment(p.From.Offset, ctxt, cursym)
   757  	return v
   758  }
   759  
   760  // AlignmentPaddingLength is the number of bytes to add to align code as requested.
   761  // Alignment is restricted to powers of 2 between 8 and 2048 inclusive.
   762  // This only computes the length and does not update the (missing parameter)
   763  // current function's own required alignment.
   764  //
   765  // pc: current offset in function, in bytes
   766  // p:  a PCALIGN or PCALIGNMAX prog
   767  // ctxt: the context, for current function
   768  // returns number of bytes of padding needed,
   769  func AlignmentPaddingLength(pc int32, p *Prog, ctxt *Link) int {
   770  	a := p.From.Offset
   771  	if !((a&(a-1) == 0) && 8 <= a && a <= 2048) {
   772  		ctxt.Diag("alignment value of an instruction must be a power of two and in the range [8, 2048], got %d\n", a)
   773  		return 0
   774  	}
   775  	pc64 := int64(pc)
   776  	lob := pc64 & (a - 1) // Low Order Bits -- if not zero, then not aligned
   777  	if p.As == APCALIGN {
   778  		if lob != 0 {
   779  			return int(a - lob)
   780  		}
   781  		return 0
   782  	}
   783  	// emit as many as s bytes of padding to obtain alignment
   784  	s := p.To.Offset
   785  	if s < 0 || s >= a {
   786  		ctxt.Diag("PCALIGNMAX 'amount' %d must be non-negative and smaller than the alignment %d\n", s, a)
   787  		return 0
   788  	}
   789  	if s >= a-lob {
   790  		return int(a - lob)
   791  	}
   792  	return 0
   793  }
   794  
   795  // requireAlignment ensures that the function is aligned enough to support
   796  // the required code alignment
   797  func requireAlignment(a int64, ctxt *Link, cursym *LSym) {
   798  	// TODO remove explicit knowledge about AIX.
   799  	if ctxt.Headtype != objabi.Haix && cursym.Align < int16(a) {
   800  		cursym.Align = int16(a)
   801  	}
   802  }
   803  

View as plain text