mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-11-04 07:02:24 -06:00 
			
		
		
		
	
		
			
				
	
	
		
			598 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			598 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// Copyright 2015 The Go Authors. All rights reserved.
 | 
						|
// Use of this source code is governed by a BSD-style
 | 
						|
// license that can be found in the LICENSE file.
 | 
						|
 | 
						|
package obj
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
	"github.com/twitchyliquid64/golang-asm/objabi"
 | 
						|
	"fmt"
 | 
						|
	"io"
 | 
						|
	"strings"
 | 
						|
)
 | 
						|
 | 
						|
const REG_NONE = 0
 | 
						|
 | 
						|
// Line returns a string containing the filename and line number for p
 | 
						|
func (p *Prog) Line() string {
 | 
						|
	return p.Ctxt.OutermostPos(p.Pos).Format(false, true)
 | 
						|
}
 | 
						|
func (p *Prog) InnermostLine(w io.Writer) {
 | 
						|
	p.Ctxt.InnermostPos(p.Pos).WriteTo(w, false, true)
 | 
						|
}
 | 
						|
 | 
						|
// InnermostLineNumber returns a string containing the line number for the
 | 
						|
// innermost inlined function (if any inlining) at p's position
 | 
						|
func (p *Prog) InnermostLineNumber() string {
 | 
						|
	return p.Ctxt.InnermostPos(p.Pos).LineNumber()
 | 
						|
}
 | 
						|
 | 
						|
// InnermostLineNumberHTML returns a string containing the line number for the
 | 
						|
// innermost inlined function (if any inlining) at p's position
 | 
						|
func (p *Prog) InnermostLineNumberHTML() string {
 | 
						|
	return p.Ctxt.InnermostPos(p.Pos).LineNumberHTML()
 | 
						|
}
 | 
						|
 | 
						|
// InnermostFilename returns a string containing the innermost
 | 
						|
// (in inlining) filename at p's position
 | 
						|
func (p *Prog) InnermostFilename() string {
 | 
						|
	// TODO For now, this is only used for debugging output, and if we need more/better information, it might change.
 | 
						|
	// 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.
 | 
						|
	pos := p.Ctxt.InnermostPos(p.Pos)
 | 
						|
	if !pos.IsKnown() {
 | 
						|
		return "<unknown file name>"
 | 
						|
	}
 | 
						|
	return pos.Filename()
 | 
						|
}
 | 
						|
 | 
						|
var armCondCode = []string{
 | 
						|
	".EQ",
 | 
						|
	".NE",
 | 
						|
	".CS",
 | 
						|
	".CC",
 | 
						|
	".MI",
 | 
						|
	".PL",
 | 
						|
	".VS",
 | 
						|
	".VC",
 | 
						|
	".HI",
 | 
						|
	".LS",
 | 
						|
	".GE",
 | 
						|
	".LT",
 | 
						|
	".GT",
 | 
						|
	".LE",
 | 
						|
	"",
 | 
						|
	".NV",
 | 
						|
}
 | 
						|
 | 
						|
/* ARM scond byte */
 | 
						|
const (
 | 
						|
	C_SCOND     = (1 << 4) - 1
 | 
						|
	C_SBIT      = 1 << 4
 | 
						|
	C_PBIT      = 1 << 5
 | 
						|
	C_WBIT      = 1 << 6
 | 
						|
	C_FBIT      = 1 << 7
 | 
						|
	C_UBIT      = 1 << 7
 | 
						|
	C_SCOND_XOR = 14
 | 
						|
)
 | 
						|
 | 
						|
// CConv formats opcode suffix bits (Prog.Scond).
 | 
						|
func CConv(s uint8) string {
 | 
						|
	if s == 0 {
 | 
						|
		return ""
 | 
						|
	}
 | 
						|
	for i := range opSuffixSpace {
 | 
						|
		sset := &opSuffixSpace[i]
 | 
						|
		if sset.arch == objabi.GOARCH {
 | 
						|
			return sset.cconv(s)
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return fmt.Sprintf("SC???%d", s)
 | 
						|
}
 | 
						|
 | 
						|
// CConvARM formats ARM opcode suffix bits (mostly condition codes).
 | 
						|
func CConvARM(s uint8) string {
 | 
						|
	// TODO: could be great to move suffix-related things into
 | 
						|
	// ARM asm backends some day.
 | 
						|
	// obj/x86 can be used as an example.
 | 
						|
 | 
						|
	sc := armCondCode[(s&C_SCOND)^C_SCOND_XOR]
 | 
						|
	if s&C_SBIT != 0 {
 | 
						|
		sc += ".S"
 | 
						|
	}
 | 
						|
	if s&C_PBIT != 0 {
 | 
						|
		sc += ".P"
 | 
						|
	}
 | 
						|
	if s&C_WBIT != 0 {
 | 
						|
		sc += ".W"
 | 
						|
	}
 | 
						|
	if s&C_UBIT != 0 { /* ambiguous with FBIT */
 | 
						|
		sc += ".U"
 | 
						|
	}
 | 
						|
	return sc
 | 
						|
}
 | 
						|
 | 
						|
func (p *Prog) String() string {
 | 
						|
	if p == nil {
 | 
						|
		return "<nil Prog>"
 | 
						|
	}
 | 
						|
	if p.Ctxt == nil {
 | 
						|
		return "<Prog without ctxt>"
 | 
						|
	}
 | 
						|
	return fmt.Sprintf("%.5d (%v)\t%s", p.Pc, p.Line(), p.InstructionString())
 | 
						|
}
 | 
						|
 | 
						|
func (p *Prog) InnermostString(w io.Writer) {
 | 
						|
	if p == nil {
 | 
						|
		io.WriteString(w, "<nil Prog>")
 | 
						|
		return
 | 
						|
	}
 | 
						|
	if p.Ctxt == nil {
 | 
						|
		io.WriteString(w, "<Prog without ctxt>")
 | 
						|
		return
 | 
						|
	}
 | 
						|
	fmt.Fprintf(w, "%.5d (", p.Pc)
 | 
						|
	p.InnermostLine(w)
 | 
						|
	io.WriteString(w, ")\t")
 | 
						|
	p.WriteInstructionString(w)
 | 
						|
}
 | 
						|
 | 
						|
// InstructionString returns a string representation of the instruction without preceding
 | 
						|
// program counter or file and line number.
 | 
						|
func (p *Prog) InstructionString() string {
 | 
						|
	buf := new(bytes.Buffer)
 | 
						|
	p.WriteInstructionString(buf)
 | 
						|
	return buf.String()
 | 
						|
}
 | 
						|
 | 
						|
// WriteInstructionString writes a string representation of the instruction without preceding
 | 
						|
// program counter or file and line number.
 | 
						|
func (p *Prog) WriteInstructionString(w io.Writer) {
 | 
						|
	if p == nil {
 | 
						|
		io.WriteString(w, "<nil Prog>")
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	if p.Ctxt == nil {
 | 
						|
		io.WriteString(w, "<Prog without ctxt>")
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	sc := CConv(p.Scond)
 | 
						|
 | 
						|
	io.WriteString(w, p.As.String())
 | 
						|
	io.WriteString(w, sc)
 | 
						|
	sep := "\t"
 | 
						|
 | 
						|
	if p.From.Type != TYPE_NONE {
 | 
						|
		io.WriteString(w, sep)
 | 
						|
		WriteDconv(w, p, &p.From)
 | 
						|
		sep = ", "
 | 
						|
	}
 | 
						|
	if p.Reg != REG_NONE {
 | 
						|
		// Should not happen but might as well show it if it does.
 | 
						|
		fmt.Fprintf(w, "%s%v", sep, Rconv(int(p.Reg)))
 | 
						|
		sep = ", "
 | 
						|
	}
 | 
						|
	for i := range p.RestArgs {
 | 
						|
		io.WriteString(w, sep)
 | 
						|
		WriteDconv(w, p, &p.RestArgs[i])
 | 
						|
		sep = ", "
 | 
						|
	}
 | 
						|
 | 
						|
	if p.As == ATEXT {
 | 
						|
		// If there are attributes, print them. Otherwise, skip the comma.
 | 
						|
		// In short, print one of these two:
 | 
						|
		// TEXT	foo(SB), DUPOK|NOSPLIT, $0
 | 
						|
		// TEXT	foo(SB), $0
 | 
						|
		s := p.From.Sym.Attribute.TextAttrString()
 | 
						|
		if s != "" {
 | 
						|
			fmt.Fprintf(w, "%s%s", sep, s)
 | 
						|
			sep = ", "
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if p.To.Type != TYPE_NONE {
 | 
						|
		io.WriteString(w, sep)
 | 
						|
		WriteDconv(w, p, &p.To)
 | 
						|
	}
 | 
						|
	if p.RegTo2 != REG_NONE {
 | 
						|
		fmt.Fprintf(w, "%s%v", sep, Rconv(int(p.RegTo2)))
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (ctxt *Link) NewProg() *Prog {
 | 
						|
	p := new(Prog)
 | 
						|
	p.Ctxt = ctxt
 | 
						|
	return p
 | 
						|
}
 | 
						|
 | 
						|
func (ctxt *Link) CanReuseProgs() bool {
 | 
						|
	return ctxt.Debugasm == 0
 | 
						|
}
 | 
						|
 | 
						|
func Dconv(p *Prog, a *Addr) string {
 | 
						|
	buf := new(bytes.Buffer)
 | 
						|
	WriteDconv(buf, p, a)
 | 
						|
	return buf.String()
 | 
						|
}
 | 
						|
 | 
						|
func WriteDconv(w io.Writer, p *Prog, a *Addr) {
 | 
						|
	switch a.Type {
 | 
						|
	default:
 | 
						|
		fmt.Fprintf(w, "type=%d", a.Type)
 | 
						|
 | 
						|
	case TYPE_NONE:
 | 
						|
		if a.Name != NAME_NONE || a.Reg != 0 || a.Sym != nil {
 | 
						|
			a.WriteNameTo(w)
 | 
						|
			fmt.Fprintf(w, "(%v)(NONE)", Rconv(int(a.Reg)))
 | 
						|
		}
 | 
						|
 | 
						|
	case TYPE_REG:
 | 
						|
		// TODO(rsc): This special case is for x86 instructions like
 | 
						|
		//	PINSRQ	CX,$1,X6
 | 
						|
		// where the $1 is included in the p->to Addr.
 | 
						|
		// Move into a new field.
 | 
						|
		if a.Offset != 0 && (a.Reg < RBaseARM64 || a.Reg >= RBaseMIPS) {
 | 
						|
			fmt.Fprintf(w, "$%d,%v", a.Offset, Rconv(int(a.Reg)))
 | 
						|
			return
 | 
						|
		}
 | 
						|
 | 
						|
		if a.Name != NAME_NONE || a.Sym != nil {
 | 
						|
			a.WriteNameTo(w)
 | 
						|
			fmt.Fprintf(w, "(%v)(REG)", Rconv(int(a.Reg)))
 | 
						|
		} else {
 | 
						|
			io.WriteString(w, Rconv(int(a.Reg)))
 | 
						|
		}
 | 
						|
		if (RBaseARM64+1<<10+1<<9) /* arm64.REG_ELEM */ <= a.Reg &&
 | 
						|
			a.Reg < (RBaseARM64+1<<11) /* arm64.REG_ELEM_END */ {
 | 
						|
			fmt.Fprintf(w, "[%d]", a.Index)
 | 
						|
		}
 | 
						|
 | 
						|
	case TYPE_BRANCH:
 | 
						|
		if a.Sym != nil {
 | 
						|
			fmt.Fprintf(w, "%s(SB)", a.Sym.Name)
 | 
						|
		} else if a.Target() != nil {
 | 
						|
			fmt.Fprint(w, a.Target().Pc)
 | 
						|
		} else {
 | 
						|
			fmt.Fprintf(w, "%d(PC)", a.Offset)
 | 
						|
		}
 | 
						|
 | 
						|
	case TYPE_INDIR:
 | 
						|
		io.WriteString(w, "*")
 | 
						|
		a.WriteNameTo(w)
 | 
						|
 | 
						|
	case TYPE_MEM:
 | 
						|
		a.WriteNameTo(w)
 | 
						|
		if a.Index != REG_NONE {
 | 
						|
			if a.Scale == 0 {
 | 
						|
				// arm64 shifted or extended register offset, scale = 0.
 | 
						|
				fmt.Fprintf(w, "(%v)", Rconv(int(a.Index)))
 | 
						|
			} else {
 | 
						|
				fmt.Fprintf(w, "(%v*%d)", Rconv(int(a.Index)), int(a.Scale))
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
	case TYPE_CONST:
 | 
						|
		io.WriteString(w, "$")
 | 
						|
		a.WriteNameTo(w)
 | 
						|
		if a.Reg != 0 {
 | 
						|
			fmt.Fprintf(w, "(%v)", Rconv(int(a.Reg)))
 | 
						|
		}
 | 
						|
 | 
						|
	case TYPE_TEXTSIZE:
 | 
						|
		if a.Val.(int32) == objabi.ArgsSizeUnknown {
 | 
						|
			fmt.Fprintf(w, "$%d", a.Offset)
 | 
						|
		} else {
 | 
						|
			fmt.Fprintf(w, "$%d-%d", a.Offset, a.Val.(int32))
 | 
						|
		}
 | 
						|
 | 
						|
	case TYPE_FCONST:
 | 
						|
		str := fmt.Sprintf("%.17g", a.Val.(float64))
 | 
						|
		// Make sure 1 prints as 1.0
 | 
						|
		if !strings.ContainsAny(str, ".e") {
 | 
						|
			str += ".0"
 | 
						|
		}
 | 
						|
		fmt.Fprintf(w, "$(%s)", str)
 | 
						|
 | 
						|
	case TYPE_SCONST:
 | 
						|
		fmt.Fprintf(w, "$%q", a.Val.(string))
 | 
						|
 | 
						|
	case TYPE_ADDR:
 | 
						|
		io.WriteString(w, "$")
 | 
						|
		a.WriteNameTo(w)
 | 
						|
 | 
						|
	case TYPE_SHIFT:
 | 
						|
		v := int(a.Offset)
 | 
						|
		ops := "<<>>->@>"
 | 
						|
		switch objabi.GOARCH {
 | 
						|
		case "arm":
 | 
						|
			op := ops[((v>>5)&3)<<1:]
 | 
						|
			if v&(1<<4) != 0 {
 | 
						|
				fmt.Fprintf(w, "R%d%c%cR%d", v&15, op[0], op[1], (v>>8)&15)
 | 
						|
			} else {
 | 
						|
				fmt.Fprintf(w, "R%d%c%c%d", v&15, op[0], op[1], (v>>7)&31)
 | 
						|
			}
 | 
						|
			if a.Reg != 0 {
 | 
						|
				fmt.Fprintf(w, "(%v)", Rconv(int(a.Reg)))
 | 
						|
			}
 | 
						|
		case "arm64":
 | 
						|
			op := ops[((v>>22)&3)<<1:]
 | 
						|
			r := (v >> 16) & 31
 | 
						|
			fmt.Fprintf(w, "%s%c%c%d", Rconv(r+RBaseARM64), op[0], op[1], (v>>10)&63)
 | 
						|
		default:
 | 
						|
			panic("TYPE_SHIFT is not supported on " + objabi.GOARCH)
 | 
						|
		}
 | 
						|
 | 
						|
	case TYPE_REGREG:
 | 
						|
		fmt.Fprintf(w, "(%v, %v)", Rconv(int(a.Reg)), Rconv(int(a.Offset)))
 | 
						|
 | 
						|
	case TYPE_REGREG2:
 | 
						|
		fmt.Fprintf(w, "%v, %v", Rconv(int(a.Offset)), Rconv(int(a.Reg)))
 | 
						|
 | 
						|
	case TYPE_REGLIST:
 | 
						|
		io.WriteString(w, RLconv(a.Offset))
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (a *Addr) WriteNameTo(w io.Writer) {
 | 
						|
	switch a.Name {
 | 
						|
	default:
 | 
						|
		fmt.Fprintf(w, "name=%d", a.Name)
 | 
						|
 | 
						|
	case NAME_NONE:
 | 
						|
		switch {
 | 
						|
		case a.Reg == REG_NONE:
 | 
						|
			fmt.Fprint(w, a.Offset)
 | 
						|
		case a.Offset == 0:
 | 
						|
			fmt.Fprintf(w, "(%v)", Rconv(int(a.Reg)))
 | 
						|
		case a.Offset != 0:
 | 
						|
			fmt.Fprintf(w, "%d(%v)", a.Offset, Rconv(int(a.Reg)))
 | 
						|
		}
 | 
						|
 | 
						|
		// Note: a.Reg == REG_NONE encodes the default base register for the NAME_ type.
 | 
						|
	case NAME_EXTERN:
 | 
						|
		reg := "SB"
 | 
						|
		if a.Reg != REG_NONE {
 | 
						|
			reg = Rconv(int(a.Reg))
 | 
						|
		}
 | 
						|
		if a.Sym != nil {
 | 
						|
			fmt.Fprintf(w, "%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg)
 | 
						|
		} else {
 | 
						|
			fmt.Fprintf(w, "%s(%s)", offConv(a.Offset), reg)
 | 
						|
		}
 | 
						|
 | 
						|
	case NAME_GOTREF:
 | 
						|
		reg := "SB"
 | 
						|
		if a.Reg != REG_NONE {
 | 
						|
			reg = Rconv(int(a.Reg))
 | 
						|
		}
 | 
						|
		if a.Sym != nil {
 | 
						|
			fmt.Fprintf(w, "%s%s@GOT(%s)", a.Sym.Name, offConv(a.Offset), reg)
 | 
						|
		} else {
 | 
						|
			fmt.Fprintf(w, "%s@GOT(%s)", offConv(a.Offset), reg)
 | 
						|
		}
 | 
						|
 | 
						|
	case NAME_STATIC:
 | 
						|
		reg := "SB"
 | 
						|
		if a.Reg != REG_NONE {
 | 
						|
			reg = Rconv(int(a.Reg))
 | 
						|
		}
 | 
						|
		if a.Sym != nil {
 | 
						|
			fmt.Fprintf(w, "%s<>%s(%s)", a.Sym.Name, offConv(a.Offset), reg)
 | 
						|
		} else {
 | 
						|
			fmt.Fprintf(w, "<>%s(%s)", offConv(a.Offset), reg)
 | 
						|
		}
 | 
						|
 | 
						|
	case NAME_AUTO:
 | 
						|
		reg := "SP"
 | 
						|
		if a.Reg != REG_NONE {
 | 
						|
			reg = Rconv(int(a.Reg))
 | 
						|
		}
 | 
						|
		if a.Sym != nil {
 | 
						|
			fmt.Fprintf(w, "%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg)
 | 
						|
		} else {
 | 
						|
			fmt.Fprintf(w, "%s(%s)", offConv(a.Offset), reg)
 | 
						|
		}
 | 
						|
 | 
						|
	case NAME_PARAM:
 | 
						|
		reg := "FP"
 | 
						|
		if a.Reg != REG_NONE {
 | 
						|
			reg = Rconv(int(a.Reg))
 | 
						|
		}
 | 
						|
		if a.Sym != nil {
 | 
						|
			fmt.Fprintf(w, "%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg)
 | 
						|
		} else {
 | 
						|
			fmt.Fprintf(w, "%s(%s)", offConv(a.Offset), reg)
 | 
						|
		}
 | 
						|
	case NAME_TOCREF:
 | 
						|
		reg := "SB"
 | 
						|
		if a.Reg != REG_NONE {
 | 
						|
			reg = Rconv(int(a.Reg))
 | 
						|
		}
 | 
						|
		if a.Sym != nil {
 | 
						|
			fmt.Fprintf(w, "%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg)
 | 
						|
		} else {
 | 
						|
			fmt.Fprintf(w, "%s(%s)", offConv(a.Offset), reg)
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func offConv(off int64) string {
 | 
						|
	if off == 0 {
 | 
						|
		return ""
 | 
						|
	}
 | 
						|
	return fmt.Sprintf("%+d", off)
 | 
						|
}
 | 
						|
 | 
						|
// opSuffixSet is like regListSet, but for opcode suffixes.
 | 
						|
//
 | 
						|
// Unlike some other similar structures, uint8 space is not
 | 
						|
// divided by its own values set (because there are only 256 of them).
 | 
						|
// Instead, every arch may interpret/format all 8 bits as they like,
 | 
						|
// as long as they register proper cconv function for it.
 | 
						|
type opSuffixSet struct {
 | 
						|
	arch  string
 | 
						|
	cconv func(suffix uint8) string
 | 
						|
}
 | 
						|
 | 
						|
var opSuffixSpace []opSuffixSet
 | 
						|
 | 
						|
// RegisterOpSuffix assigns cconv function for formatting opcode suffixes
 | 
						|
// when compiling for GOARCH=arch.
 | 
						|
//
 | 
						|
// cconv is never called with 0 argument.
 | 
						|
func RegisterOpSuffix(arch string, cconv func(uint8) string) {
 | 
						|
	opSuffixSpace = append(opSuffixSpace, opSuffixSet{
 | 
						|
		arch:  arch,
 | 
						|
		cconv: cconv,
 | 
						|
	})
 | 
						|
}
 | 
						|
 | 
						|
type regSet struct {
 | 
						|
	lo    int
 | 
						|
	hi    int
 | 
						|
	Rconv func(int) string
 | 
						|
}
 | 
						|
 | 
						|
// Few enough architectures that a linear scan is fastest.
 | 
						|
// Not even worth sorting.
 | 
						|
var regSpace []regSet
 | 
						|
 | 
						|
/*
 | 
						|
	Each architecture defines a register space as a unique
 | 
						|
	integer range.
 | 
						|
	Here is the list of architectures and the base of their register spaces.
 | 
						|
*/
 | 
						|
 | 
						|
const (
 | 
						|
	// Because of masking operations in the encodings, each register
 | 
						|
	// space should start at 0 modulo some power of 2.
 | 
						|
	RBase386   = 1 * 1024
 | 
						|
	RBaseAMD64 = 2 * 1024
 | 
						|
	RBaseARM   = 3 * 1024
 | 
						|
	RBasePPC64 = 4 * 1024  // range [4k, 8k)
 | 
						|
	RBaseARM64 = 8 * 1024  // range [8k, 13k)
 | 
						|
	RBaseMIPS  = 13 * 1024 // range [13k, 14k)
 | 
						|
	RBaseS390X = 14 * 1024 // range [14k, 15k)
 | 
						|
	RBaseRISCV = 15 * 1024 // range [15k, 16k)
 | 
						|
	RBaseWasm  = 16 * 1024
 | 
						|
)
 | 
						|
 | 
						|
// RegisterRegister binds a pretty-printer (Rconv) for register
 | 
						|
// numbers to a given register number range. Lo is inclusive,
 | 
						|
// hi exclusive (valid registers are lo through hi-1).
 | 
						|
func RegisterRegister(lo, hi int, Rconv func(int) string) {
 | 
						|
	regSpace = append(regSpace, regSet{lo, hi, Rconv})
 | 
						|
}
 | 
						|
 | 
						|
func Rconv(reg int) string {
 | 
						|
	if reg == REG_NONE {
 | 
						|
		return "NONE"
 | 
						|
	}
 | 
						|
	for i := range regSpace {
 | 
						|
		rs := ®Space[i]
 | 
						|
		if rs.lo <= reg && reg < rs.hi {
 | 
						|
			return rs.Rconv(reg)
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return fmt.Sprintf("R???%d", reg)
 | 
						|
}
 | 
						|
 | 
						|
type regListSet struct {
 | 
						|
	lo     int64
 | 
						|
	hi     int64
 | 
						|
	RLconv func(int64) string
 | 
						|
}
 | 
						|
 | 
						|
var regListSpace []regListSet
 | 
						|
 | 
						|
// Each architecture is allotted a distinct subspace: [Lo, Hi) for declaring its
 | 
						|
// arch-specific register list numbers.
 | 
						|
const (
 | 
						|
	RegListARMLo = 0
 | 
						|
	RegListARMHi = 1 << 16
 | 
						|
 | 
						|
	// arm64 uses the 60th bit to differentiate from other archs
 | 
						|
	RegListARM64Lo = 1 << 60
 | 
						|
	RegListARM64Hi = 1<<61 - 1
 | 
						|
 | 
						|
	// x86 uses the 61th bit to differentiate from other archs
 | 
						|
	RegListX86Lo = 1 << 61
 | 
						|
	RegListX86Hi = 1<<62 - 1
 | 
						|
)
 | 
						|
 | 
						|
// RegisterRegisterList binds a pretty-printer (RLconv) for register list
 | 
						|
// numbers to a given register list number range. Lo is inclusive,
 | 
						|
// hi exclusive (valid register list are lo through hi-1).
 | 
						|
func RegisterRegisterList(lo, hi int64, rlconv func(int64) string) {
 | 
						|
	regListSpace = append(regListSpace, regListSet{lo, hi, rlconv})
 | 
						|
}
 | 
						|
 | 
						|
func RLconv(list int64) string {
 | 
						|
	for i := range regListSpace {
 | 
						|
		rls := ®ListSpace[i]
 | 
						|
		if rls.lo <= list && list < rls.hi {
 | 
						|
			return rls.RLconv(list)
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return fmt.Sprintf("RL???%d", list)
 | 
						|
}
 | 
						|
 | 
						|
type opSet struct {
 | 
						|
	lo    As
 | 
						|
	names []string
 | 
						|
}
 | 
						|
 | 
						|
// Not even worth sorting
 | 
						|
var aSpace []opSet
 | 
						|
 | 
						|
// RegisterOpcode binds a list of instruction names
 | 
						|
// to a given instruction number range.
 | 
						|
func RegisterOpcode(lo As, Anames []string) {
 | 
						|
	if len(Anames) > AllowedOpCodes {
 | 
						|
		panic(fmt.Sprintf("too many instructions, have %d max %d", len(Anames), AllowedOpCodes))
 | 
						|
	}
 | 
						|
	aSpace = append(aSpace, opSet{lo, Anames})
 | 
						|
}
 | 
						|
 | 
						|
func (a As) String() string {
 | 
						|
	if 0 <= a && int(a) < len(Anames) {
 | 
						|
		return Anames[a]
 | 
						|
	}
 | 
						|
	for i := range aSpace {
 | 
						|
		as := &aSpace[i]
 | 
						|
		if as.lo <= a && int(a-as.lo) < len(as.names) {
 | 
						|
			return as.names[a-as.lo]
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return fmt.Sprintf("A???%d", a)
 | 
						|
}
 | 
						|
 | 
						|
var Anames = []string{
 | 
						|
	"XXX",
 | 
						|
	"CALL",
 | 
						|
	"DUFFCOPY",
 | 
						|
	"DUFFZERO",
 | 
						|
	"END",
 | 
						|
	"FUNCDATA",
 | 
						|
	"JMP",
 | 
						|
	"NOP",
 | 
						|
	"PCALIGN",
 | 
						|
	"PCDATA",
 | 
						|
	"RET",
 | 
						|
	"GETCALLERPC",
 | 
						|
	"TEXT",
 | 
						|
	"UNDEF",
 | 
						|
}
 | 
						|
 | 
						|
func Bool2int(b bool) int {
 | 
						|
	// The compiler currently only optimizes this form.
 | 
						|
	// See issue 6011.
 | 
						|
	var i int
 | 
						|
	if b {
 | 
						|
		i = 1
 | 
						|
	} else {
 | 
						|
		i = 0
 | 
						|
	}
 | 
						|
	return i
 | 
						|
}
 |