gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

util.go (13849B)


      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 }