util.go raw

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