1663 lines
36 KiB
Go
1663 lines
36 KiB
Go
package amd64
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/tetratelabs/wazero/internal/engine/wazevo/backend"
|
|
"github.com/tetratelabs/wazero/internal/engine/wazevo/backend/regalloc"
|
|
"github.com/tetratelabs/wazero/internal/engine/wazevo/ssa"
|
|
)
|
|
|
|
type instruction struct {
|
|
prev, next *instruction
|
|
abi *backend.FunctionABI
|
|
op1, op2 operand
|
|
u1, u2 uint64
|
|
b1 bool
|
|
addedBeforeRegAlloc bool
|
|
kind instructionKind
|
|
}
|
|
|
|
// Next implements regalloc.Instr.
|
|
func (i *instruction) Next() regalloc.Instr {
|
|
return i.next
|
|
}
|
|
|
|
// Prev implements regalloc.Instr.
|
|
func (i *instruction) Prev() regalloc.Instr {
|
|
return i.prev
|
|
}
|
|
|
|
// IsCall implements regalloc.Instr.
|
|
func (i *instruction) IsCall() bool { return i.kind == call }
|
|
|
|
// IsIndirectCall implements regalloc.Instr.
|
|
func (i *instruction) IsIndirectCall() bool { return i.kind == callIndirect }
|
|
|
|
// IsReturn implements regalloc.Instr.
|
|
func (i *instruction) IsReturn() bool { return i.kind == ret }
|
|
|
|
// AddedBeforeRegAlloc implements regalloc.Instr.
|
|
func (i *instruction) AddedBeforeRegAlloc() bool { return i.addedBeforeRegAlloc }
|
|
|
|
// String implements regalloc.Instr.
|
|
func (i *instruction) String() string {
|
|
switch i.kind {
|
|
case nop0:
|
|
return "nop"
|
|
case ret:
|
|
return "ret"
|
|
case imm:
|
|
if i.b1 {
|
|
return fmt.Sprintf("movabsq $%d, %s", int64(i.u1), i.op2.format(true))
|
|
} else {
|
|
return fmt.Sprintf("movl $%d, %s", int32(i.u1), i.op2.format(false))
|
|
}
|
|
case aluRmiR:
|
|
return fmt.Sprintf("%s %s, %s", aluRmiROpcode(i.u1), i.op1.format(i.b1), i.op2.format(i.b1))
|
|
case movRR:
|
|
if i.b1 {
|
|
return fmt.Sprintf("movq %s, %s", i.op1.format(true), i.op2.format(true))
|
|
} else {
|
|
return fmt.Sprintf("movl %s, %s", i.op1.format(false), i.op2.format(false))
|
|
}
|
|
case xmmRmR:
|
|
return fmt.Sprintf("%s %s, %s", sseOpcode(i.u1), i.op1.format(false), i.op2.format(false))
|
|
case gprToXmm:
|
|
return fmt.Sprintf("%s %s, %s", sseOpcode(i.u1), i.op1.format(i.b1), i.op2.format(i.b1))
|
|
case xmmUnaryRmR:
|
|
return fmt.Sprintf("%s %s, %s", sseOpcode(i.u1), i.op1.format(false), i.op2.format(false))
|
|
case unaryRmR:
|
|
var suffix string
|
|
if i.b1 {
|
|
suffix = "q"
|
|
} else {
|
|
suffix = "l"
|
|
}
|
|
return fmt.Sprintf("%s%s %s, %s", unaryRmROpcode(i.u1), suffix, i.op1.format(i.b1), i.op2.format(i.b1))
|
|
case not:
|
|
var op string
|
|
if i.b1 {
|
|
op = "notq"
|
|
} else {
|
|
op = "notl"
|
|
}
|
|
return fmt.Sprintf("%s %s", op, i.op1.format(i.b1))
|
|
case neg:
|
|
var op string
|
|
if i.b1 {
|
|
op = "negq"
|
|
} else {
|
|
op = "negl"
|
|
}
|
|
return fmt.Sprintf("%s %s", op, i.op1.format(i.b1))
|
|
case div:
|
|
panic("TODO")
|
|
case mulHi:
|
|
signed, _64 := i.u1 != 0, i.b1
|
|
var op string
|
|
switch {
|
|
case signed && _64:
|
|
op = "imulq"
|
|
case !signed && _64:
|
|
op = "mulq"
|
|
case signed && !_64:
|
|
op = "imull"
|
|
case !signed && !_64:
|
|
op = "mull"
|
|
}
|
|
return fmt.Sprintf("%s %s", op, i.op1.format(i.b1))
|
|
case checkedDivOrRemSeq:
|
|
panic("TODO")
|
|
case signExtendData:
|
|
panic("TODO")
|
|
case movzxRmR:
|
|
return fmt.Sprintf("movzx.%s %s, %s", extMode(i.u1), i.op1.format(true), i.op2.format(true))
|
|
case mov64MR:
|
|
return fmt.Sprintf("movq %s, %s", i.op1.format(true), i.op2.format(true))
|
|
case lea:
|
|
return fmt.Sprintf("lea %s, %s", i.op1.format(true), i.op2.format(true))
|
|
case movsxRmR:
|
|
return fmt.Sprintf("movsx.%s %s, %s", extMode(i.u1), i.op1.format(true), i.op2.format(true))
|
|
case movRM:
|
|
var suffix string
|
|
switch i.u1 {
|
|
case 1:
|
|
suffix = "b"
|
|
case 2:
|
|
suffix = "w"
|
|
case 4:
|
|
suffix = "l"
|
|
case 8:
|
|
suffix = "q"
|
|
}
|
|
return fmt.Sprintf("mov.%s %s, %s", suffix, i.op1.format(true), i.op2.format(true))
|
|
case shiftR:
|
|
var suffix string
|
|
if i.b1 {
|
|
suffix = "q"
|
|
} else {
|
|
suffix = "l"
|
|
}
|
|
return fmt.Sprintf("%s%s %s, %s", shiftROp(i.u1), suffix, i.op1.format(false), i.op2.format(i.b1))
|
|
case xmmRmiReg:
|
|
return fmt.Sprintf("%s %s, %s", sseOpcode(i.u1), i.op1.format(true), i.op2.format(true))
|
|
case cmpRmiR:
|
|
var op, suffix string
|
|
if i.u1 != 0 {
|
|
op = "cmp"
|
|
} else {
|
|
op = "test"
|
|
}
|
|
if i.b1 {
|
|
suffix = "q"
|
|
} else {
|
|
suffix = "l"
|
|
}
|
|
if op == "test" && i.op1.kind == operandKindMem {
|
|
// Print consistently with AT&T syntax.
|
|
return fmt.Sprintf("%s%s %s, %s", op, suffix, i.op2.format(i.b1), i.op1.format(i.b1))
|
|
}
|
|
return fmt.Sprintf("%s%s %s, %s", op, suffix, i.op1.format(i.b1), i.op2.format(i.b1))
|
|
case setcc:
|
|
return fmt.Sprintf("set%s %s", cond(i.u1), i.op1.format(true))
|
|
case cmove:
|
|
var suffix string
|
|
if i.b1 {
|
|
suffix = "q"
|
|
} else {
|
|
suffix = "l"
|
|
}
|
|
return fmt.Sprintf("cmov%s%s %s, %s", cond(i.u1), suffix, i.op1.format(i.b1), i.op2.format(i.b1))
|
|
case push64:
|
|
return fmt.Sprintf("pushq %s", i.op1.format(true))
|
|
case pop64:
|
|
return fmt.Sprintf("popq %s", i.op1.format(true))
|
|
case xmmMovRM:
|
|
return fmt.Sprintf("%s %s, %s", sseOpcode(i.u1), i.op1.format(true), i.op2.format(true))
|
|
case xmmLoadConst:
|
|
panic("TODO")
|
|
case xmmToGpr:
|
|
panic("TODO")
|
|
case cvtUint64ToFloatSeq:
|
|
panic("TODO")
|
|
case cvtFloatToSintSeq:
|
|
panic("TODO")
|
|
case cvtFloatToUintSeq:
|
|
panic("TODO")
|
|
case xmmMinMaxSeq:
|
|
panic("TODO")
|
|
case xmmCmove:
|
|
panic("TODO")
|
|
case xmmCmpRmR:
|
|
panic("TODO")
|
|
case xmmRmRImm:
|
|
panic("TODO")
|
|
case jmp:
|
|
return fmt.Sprintf("jmp %s", i.op1.format(true))
|
|
case jmpIf:
|
|
return fmt.Sprintf("j%s %s", cond(i.u1), i.op1.format(true))
|
|
case jmpTableSequence:
|
|
panic("TODO")
|
|
case exitSequence:
|
|
return fmt.Sprintf("exit_sequence %s", i.op1.format(true))
|
|
case ud2:
|
|
return "ud2"
|
|
case call:
|
|
if i.u2 > 0 {
|
|
return fmt.Sprintf("call $%d", int32(i.u2))
|
|
} else {
|
|
return fmt.Sprintf("call %s", ssa.FuncRef(i.u1))
|
|
}
|
|
case callIndirect:
|
|
return fmt.Sprintf("callq *%s", i.op1.format(true))
|
|
case v128ConstIsland:
|
|
return fmt.Sprintf("v128ConstIsland (%#x, %#x)", i.u1, i.u2)
|
|
default:
|
|
panic(fmt.Sprintf("BUG: %d", int(i.kind)))
|
|
}
|
|
}
|
|
|
|
// Defs implements regalloc.Instr.
|
|
func (i *instruction) Defs(regs *[]regalloc.VReg) []regalloc.VReg {
|
|
*regs = (*regs)[:0]
|
|
switch dk := defKinds[i.kind]; dk {
|
|
case defKindNone:
|
|
case defKindOp2:
|
|
if !i.op2.r.Valid() {
|
|
panic("BUG" + i.String())
|
|
}
|
|
*regs = append(*regs, i.op2.r)
|
|
case defKindCall:
|
|
*regs = append(*regs, i.abi.RetRealRegs...)
|
|
default:
|
|
panic(fmt.Sprintf("BUG: invalid defKind \"%s\" for %s", dk, i))
|
|
}
|
|
return *regs
|
|
}
|
|
|
|
// Uses implements regalloc.Instr.
|
|
func (i *instruction) Uses(regs *[]regalloc.VReg) []regalloc.VReg {
|
|
*regs = (*regs)[:0]
|
|
switch uk := useKinds[i.kind]; uk {
|
|
case useKindNone:
|
|
case useKindOp1Op2Reg, useKindOp1RegOp2:
|
|
opAny, opReg := &i.op1, &i.op2
|
|
if uk == useKindOp1RegOp2 {
|
|
opAny, opReg = opReg, opAny
|
|
}
|
|
// The destination operand (op2) can be only reg,
|
|
// the source operand (op1) can be imm32, reg or mem.
|
|
switch opAny.kind {
|
|
case operandKindReg:
|
|
*regs = append(*regs, opAny.r)
|
|
case operandKindMem:
|
|
opAny.amode.uses(regs)
|
|
case operandKindImm32:
|
|
default:
|
|
panic(fmt.Sprintf("BUG: invalid operand: %s", i))
|
|
}
|
|
if opReg.kind != operandKindReg {
|
|
panic(fmt.Sprintf("BUG: invalid operand: %s", i))
|
|
}
|
|
*regs = append(*regs, opReg.r)
|
|
case useKindOp1:
|
|
op := i.op1
|
|
switch op.kind {
|
|
case operandKindReg:
|
|
*regs = append(*regs, op.r)
|
|
case operandKindMem:
|
|
op.amode.uses(regs)
|
|
case operandKindImm32, operandKindLabel:
|
|
default:
|
|
panic(fmt.Sprintf("BUG: invalid operand: %s", i))
|
|
}
|
|
case useKindCallInd:
|
|
op := i.op1
|
|
switch op.kind {
|
|
case operandKindReg:
|
|
*regs = append(*regs, op.r)
|
|
case operandKindMem:
|
|
op.amode.uses(regs)
|
|
default:
|
|
panic(fmt.Sprintf("BUG: invalid operand: %s", i))
|
|
}
|
|
*regs = append(*regs, i.abi.ArgRealRegs...)
|
|
case useKindCall:
|
|
*regs = append(*regs, i.abi.ArgRealRegs...)
|
|
default:
|
|
panic(fmt.Sprintf("BUG: invalid useKind %s for %s", uk, i))
|
|
}
|
|
return *regs
|
|
}
|
|
|
|
// AssignUse implements regalloc.Instr.
|
|
func (i *instruction) AssignUse(index int, v regalloc.VReg) {
|
|
switch uk := useKinds[i.kind]; uk {
|
|
case useKindNone:
|
|
case useKindCallInd:
|
|
if index != 0 {
|
|
panic("BUG")
|
|
}
|
|
op := &i.op1
|
|
switch op.kind {
|
|
case operandKindReg:
|
|
op.r = v
|
|
case operandKindMem:
|
|
op.amode.assignUses(index, v)
|
|
default:
|
|
panic("BUG")
|
|
}
|
|
case useKindOp1Op2Reg, useKindOp1RegOp2:
|
|
op, opMustBeReg := &i.op1, &i.op2
|
|
if uk == useKindOp1RegOp2 {
|
|
op, opMustBeReg = opMustBeReg, op
|
|
}
|
|
switch op.kind {
|
|
case operandKindReg:
|
|
if index == 0 {
|
|
if op.r.IsRealReg() {
|
|
panic("BUG already assigned: " + i.String())
|
|
}
|
|
op.r = v
|
|
} else if index == 1 {
|
|
if opMustBeReg.r.IsRealReg() {
|
|
panic("BUG already assigned: " + i.String())
|
|
}
|
|
opMustBeReg.r = v
|
|
} else {
|
|
panic("BUG")
|
|
}
|
|
case operandKindMem:
|
|
nregs := op.amode.nregs()
|
|
if index < nregs {
|
|
op.amode.assignUses(index, v)
|
|
} else if index == nregs {
|
|
if opMustBeReg.r.IsRealReg() {
|
|
panic("BUG already assigned: " + i.String())
|
|
}
|
|
opMustBeReg.r = v
|
|
} else {
|
|
panic("BUG")
|
|
}
|
|
case operandKindImm32:
|
|
if index == 0 {
|
|
if opMustBeReg.r.IsRealReg() {
|
|
panic("BUG already assigned: " + i.String())
|
|
}
|
|
opMustBeReg.r = v
|
|
} else {
|
|
panic("BUG")
|
|
}
|
|
default:
|
|
panic(fmt.Sprintf("BUG: invalid operand pair: %s", i))
|
|
}
|
|
case useKindOp1:
|
|
op := &i.op1
|
|
switch op.kind {
|
|
case operandKindReg:
|
|
if index != 0 {
|
|
panic("BUG")
|
|
}
|
|
if op.r.IsRealReg() {
|
|
panic("BUG already assigned: " + i.String())
|
|
}
|
|
op.r = v
|
|
case operandKindMem:
|
|
op.amode.assignUses(index, v)
|
|
default:
|
|
panic(fmt.Sprintf("BUG: invalid operand: %s", i))
|
|
}
|
|
default:
|
|
panic(fmt.Sprintf("BUG: invalid useKind %s for %s", uk, i))
|
|
}
|
|
}
|
|
|
|
// AssignDef implements regalloc.Instr.
|
|
func (i *instruction) AssignDef(reg regalloc.VReg) {
|
|
switch dk := defKinds[i.kind]; dk {
|
|
case defKindNone:
|
|
case defKindOp2:
|
|
if !i.op2.r.Valid() {
|
|
panic("BUG already assigned" + i.String())
|
|
}
|
|
i.op2.r = reg
|
|
default:
|
|
panic(fmt.Sprintf("BUG: invalid defKind \"%s\" for %s", dk, i))
|
|
}
|
|
}
|
|
|
|
// IsCopy implements regalloc.Instr.
|
|
func (i *instruction) IsCopy() bool {
|
|
k := i.kind
|
|
return k == movRR || (k == xmmUnaryRmR && i.op1.kind == operandKindReg)
|
|
}
|
|
|
|
func resetInstruction(i *instruction) {
|
|
*i = instruction{}
|
|
}
|
|
|
|
func setNext(i *instruction, next *instruction) {
|
|
i.next = next
|
|
}
|
|
|
|
func setPrev(i *instruction, prev *instruction) {
|
|
i.prev = prev
|
|
}
|
|
|
|
func asNop(i *instruction) {
|
|
i.kind = nop0
|
|
}
|
|
|
|
func (i *instruction) asNop0WithLabel(label backend.Label) *instruction { //nolint
|
|
i.kind = nop0
|
|
i.u1 = uint64(label)
|
|
return i
|
|
}
|
|
|
|
func (i *instruction) nop0Label() backend.Label {
|
|
return backend.Label(i.u1)
|
|
}
|
|
|
|
type instructionKind int
|
|
|
|
const (
|
|
nop0 instructionKind = iota + 1
|
|
|
|
// Integer arithmetic/bit-twiddling: (add sub and or xor mul, etc.) (32 64) (reg addr imm) reg
|
|
aluRmiR
|
|
|
|
// Instructions on GPR that only read src and defines dst (dst is not modified): bsr, etc.
|
|
unaryRmR
|
|
|
|
// Bitwise not
|
|
not
|
|
|
|
// Integer negation
|
|
neg
|
|
|
|
// Integer quotient and remainder: (div idiv) $rax $rdx (reg addr)
|
|
div
|
|
|
|
// The high bits (RDX) of a (un)signed multiply: RDX:RAX := RAX * rhs.
|
|
mulHi
|
|
|
|
// A synthetic sequence to implement the right inline checks for remainder and division,
|
|
// assuming the dividend is in %rax.
|
|
// Puts the result back into %rax if is_div, %rdx if !is_div, to mimic what the div
|
|
// instruction does.
|
|
// The generated code sequence is described in the emit's function match arm for this
|
|
// instruction.
|
|
///
|
|
// Note: %rdx is marked as modified by this instruction, to avoid an early clobber problem
|
|
// with the temporary and divisor registers. Make sure to zero %rdx right before this
|
|
// instruction, or you might run into regalloc failures where %rdx is live before its first
|
|
// def!
|
|
checkedDivOrRemSeq
|
|
|
|
// Do a sign-extend based on the sign of the value in rax into rdx: (cwd cdq cqo)
|
|
// or al into ah: (cbw)
|
|
signExtendData
|
|
|
|
// Constant materialization: (imm32 imm64) reg.
|
|
// Either: movl $imm32, %reg32 or movabsq $imm64, %reg64.
|
|
imm
|
|
|
|
// GPR to GPR move: mov (64 32) reg reg.
|
|
movRR
|
|
|
|
// movzxRmR is zero-extended loads or move (R to R), except for 64 bits: movz (bl bq wl wq lq) addr reg.
|
|
// Note that the lq variant doesn't really exist since the default zero-extend rule makes it
|
|
// unnecessary. For that case we emit the equivalent "movl AM, reg32".
|
|
movzxRmR
|
|
|
|
// mov64MR is a plain 64-bit integer load, since movzxRmR can't represent that.
|
|
mov64MR
|
|
|
|
// Loads the memory address of addr into dst.
|
|
lea
|
|
|
|
// Sign-extended loads and moves: movs (bl bq wl wq lq) addr reg.
|
|
movsxRmR
|
|
|
|
// Integer stores: mov (b w l q) reg addr.
|
|
movRM
|
|
|
|
// Arithmetic shifts: (shl shr sar) (b w l q) imm reg.
|
|
shiftR
|
|
|
|
// Arithmetic SIMD shifts.
|
|
xmmRmiReg
|
|
|
|
// Integer comparisons/tests: cmp or test (b w l q) (reg addr imm) reg.
|
|
cmpRmiR
|
|
|
|
// Materializes the requested condition code in the destination reg.
|
|
setcc
|
|
|
|
// Integer conditional move.
|
|
// Overwrites the destination register.
|
|
cmove
|
|
|
|
// pushq (reg addr imm)
|
|
push64
|
|
|
|
// popq reg
|
|
pop64
|
|
|
|
// XMM (scalar or vector) binary op: (add sub and or xor mul adc? sbb?) (32 64) (reg addr) reg
|
|
xmmRmR
|
|
|
|
// XMM (scalar or vector) unary op: mov between XMM registers (32 64) (reg addr) reg.
|
|
//
|
|
// This differs from xmmRmR in that the dst register of xmmUnaryRmR is not used in the
|
|
// computation of the instruction dst value and so does not have to be a previously valid
|
|
// value. This is characteristic of mov instructions.
|
|
xmmUnaryRmR
|
|
|
|
// XMM (scalar or vector) unary op (from xmm to mem): stores, movd, movq
|
|
xmmMovRM
|
|
|
|
// XMM (vector) unary op (to move a constant value into an xmm register): movups
|
|
xmmLoadConst
|
|
|
|
// XMM (scalar) unary op (from xmm to integer reg): movd, movq, cvtts{s,d}2si
|
|
xmmToGpr
|
|
|
|
// XMM (scalar) unary op (from integer to float reg): movd, movq, cvtsi2s{s,d}
|
|
gprToXmm
|
|
|
|
// Converts an unsigned int64 to a float32/float64.
|
|
cvtUint64ToFloatSeq
|
|
|
|
// Converts a scalar xmm to a signed int32/int64.
|
|
cvtFloatToSintSeq
|
|
|
|
// Converts a scalar xmm to an unsigned int32/int64.
|
|
cvtFloatToUintSeq
|
|
|
|
// A sequence to compute min/max with the proper NaN semantics for xmm registers.
|
|
xmmMinMaxSeq
|
|
|
|
// XMM (scalar) conditional move.
|
|
// Overwrites the destination register if cc is set.
|
|
xmmCmove
|
|
|
|
// Float comparisons/tests: cmp (b w l q) (reg addr imm) reg.
|
|
xmmCmpRmR
|
|
|
|
// A binary XMM instruction with an 8-bit immediate: e.g. cmp (ps pd) imm (reg addr) reg
|
|
xmmRmRImm
|
|
|
|
// Direct call: call simm32.
|
|
// Note that the offset is the relative to the *current RIP*, which points to the first byte of the next instruction.
|
|
call
|
|
|
|
// Indirect call: callq (reg mem).
|
|
callIndirect
|
|
|
|
// Return.
|
|
ret
|
|
|
|
// Jump: jmp (reg, mem, imm32 or label)
|
|
jmp
|
|
|
|
// Jump conditionally: jcond cond label.
|
|
jmpIf
|
|
|
|
// Jump-table sequence, as one compound instruction (see note in lower.rs for rationale).
|
|
// The generated code sequence is described in the emit's function match arm for this
|
|
// instruction.
|
|
// See comment in lowering about the temporaries signedness.
|
|
jmpTableSequence
|
|
|
|
// exitSequence exits the execution and go back to the Go world.
|
|
exitSequence
|
|
|
|
// An instruction that will always trigger the illegal instruction exception.
|
|
ud2
|
|
|
|
// v128ConstIsland is 16 bytes (128-bit) constant that will be loaded into an XMM.
|
|
v128ConstIsland
|
|
|
|
instrMax
|
|
)
|
|
|
|
func (k instructionKind) String() string {
|
|
switch k {
|
|
case nop0:
|
|
return "nop"
|
|
case ret:
|
|
return "ret"
|
|
case imm:
|
|
return "imm"
|
|
case aluRmiR:
|
|
return "aluRmiR"
|
|
case movRR:
|
|
return "movRR"
|
|
case xmmRmR:
|
|
return "xmmRmR"
|
|
case gprToXmm:
|
|
return "gprToXmm"
|
|
case xmmUnaryRmR:
|
|
return "xmmUnaryRmR"
|
|
case unaryRmR:
|
|
return "unaryRmR"
|
|
case not:
|
|
return "not"
|
|
case neg:
|
|
return "neg"
|
|
case div:
|
|
return "div"
|
|
case mulHi:
|
|
return "mulHi"
|
|
case checkedDivOrRemSeq:
|
|
return "checkedDivOrRemSeq"
|
|
case signExtendData:
|
|
return "signExtendData"
|
|
case movzxRmR:
|
|
return "movzxRmR"
|
|
case mov64MR:
|
|
return "mov64MR"
|
|
case lea:
|
|
return "lea"
|
|
case movsxRmR:
|
|
return "movsxRmR"
|
|
case movRM:
|
|
return "movRM"
|
|
case shiftR:
|
|
return "shiftR"
|
|
case xmmRmiReg:
|
|
return "xmmRmiReg"
|
|
case cmpRmiR:
|
|
return "cmpRmiR"
|
|
case setcc:
|
|
return "setcc"
|
|
case cmove:
|
|
return "cmove"
|
|
case push64:
|
|
return "push64"
|
|
case pop64:
|
|
return "pop64"
|
|
case xmmMovRM:
|
|
return "xmmMovRM"
|
|
case xmmLoadConst:
|
|
return "xmmLoadConst"
|
|
case xmmToGpr:
|
|
return "xmmToGpr"
|
|
case cvtUint64ToFloatSeq:
|
|
return "cvtUint64ToFloatSeq"
|
|
case cvtFloatToSintSeq:
|
|
return "cvtFloatToSintSeq"
|
|
case cvtFloatToUintSeq:
|
|
return "cvtFloatToUintSeq"
|
|
case xmmMinMaxSeq:
|
|
return "xmmMinMaxSeq"
|
|
case xmmCmove:
|
|
return "xmmCmove"
|
|
case xmmCmpRmR:
|
|
return "xmmCmpRmR"
|
|
case xmmRmRImm:
|
|
return "xmmRmRImm"
|
|
case jmpIf:
|
|
return "jmpIf"
|
|
case jmp:
|
|
return "jmp"
|
|
case jmpTableSequence:
|
|
return "jmpTableSeq"
|
|
case exitSequence:
|
|
return "exit_sequence"
|
|
case v128ConstIsland:
|
|
return "v128ConstIsland"
|
|
case ud2:
|
|
return "ud2"
|
|
default:
|
|
panic("BUG")
|
|
}
|
|
}
|
|
|
|
type aluRmiROpcode byte
|
|
|
|
const (
|
|
aluRmiROpcodeAdd aluRmiROpcode = iota + 1
|
|
aluRmiROpcodeSub
|
|
aluRmiROpcodeAnd
|
|
aluRmiROpcodeOr
|
|
aluRmiROpcodeXor
|
|
aluRmiROpcodeMul
|
|
)
|
|
|
|
func (a aluRmiROpcode) String() string {
|
|
switch a {
|
|
case aluRmiROpcodeAdd:
|
|
return "add"
|
|
case aluRmiROpcodeSub:
|
|
return "sub"
|
|
case aluRmiROpcodeAnd:
|
|
return "and"
|
|
case aluRmiROpcodeOr:
|
|
return "or"
|
|
case aluRmiROpcodeXor:
|
|
return "xor"
|
|
case aluRmiROpcodeMul:
|
|
return "imul"
|
|
default:
|
|
panic("BUG")
|
|
}
|
|
}
|
|
|
|
func (i *instruction) asJmpIf(cond cond, target operand) *instruction {
|
|
i.kind = jmpIf
|
|
i.u1 = uint64(cond)
|
|
i.op1 = target
|
|
return i
|
|
}
|
|
|
|
func (i *instruction) asJmp(target operand) *instruction {
|
|
i.kind = jmp
|
|
i.op1 = target
|
|
return i
|
|
}
|
|
|
|
func (i *instruction) jmpLabel() backend.Label {
|
|
switch i.kind {
|
|
case jmp, jmpIf, lea:
|
|
return i.op1.label()
|
|
default:
|
|
panic("BUG")
|
|
}
|
|
}
|
|
|
|
func (i *instruction) asLEA(a amode, rd regalloc.VReg) *instruction {
|
|
i.kind = lea
|
|
i.op1 = newOperandMem(a)
|
|
i.op2 = newOperandReg(rd)
|
|
return i
|
|
}
|
|
|
|
func (i *instruction) asCall(ref ssa.FuncRef, abi *backend.FunctionABI) *instruction {
|
|
i.kind = call
|
|
i.abi = abi
|
|
i.u1 = uint64(ref)
|
|
return i
|
|
}
|
|
|
|
func (i *instruction) asCallIndirect(ptr operand, abi *backend.FunctionABI) *instruction {
|
|
if ptr.kind != operandKindReg && ptr.kind != operandKindMem {
|
|
panic("BUG")
|
|
}
|
|
i.kind = callIndirect
|
|
i.abi = abi
|
|
i.op1 = ptr
|
|
return i
|
|
}
|
|
|
|
func (i *instruction) asRet(abi *backend.FunctionABI) *instruction {
|
|
i.kind = ret
|
|
i.abi = abi
|
|
return i
|
|
}
|
|
|
|
func (i *instruction) asImm(dst regalloc.VReg, value uint64, _64 bool) *instruction {
|
|
i.kind = imm
|
|
i.op2 = newOperandReg(dst)
|
|
i.u1 = value
|
|
i.b1 = _64
|
|
return i
|
|
}
|
|
|
|
func (i *instruction) asAluRmiR(op aluRmiROpcode, rm operand, rd regalloc.VReg, _64 bool) *instruction {
|
|
if rm.kind != operandKindReg && rm.kind != operandKindMem && rm.kind != operandKindImm32 {
|
|
panic("BUG")
|
|
}
|
|
i.kind = aluRmiR
|
|
i.op1 = rm
|
|
i.op2 = newOperandReg(rd)
|
|
i.u1 = uint64(op)
|
|
i.b1 = _64
|
|
return i
|
|
}
|
|
|
|
func (i *instruction) asXmmRmR(op sseOpcode, rm operand, rd regalloc.VReg) *instruction {
|
|
if rm.kind != operandKindReg && rm.kind != operandKindMem {
|
|
panic("BUG")
|
|
}
|
|
i.kind = xmmRmR
|
|
i.op1 = rm
|
|
i.op2 = newOperandReg(rd)
|
|
i.u1 = uint64(op)
|
|
return i
|
|
}
|
|
|
|
func (i *instruction) asGprToXmm(op sseOpcode, rm operand, rd regalloc.VReg, _64 bool) *instruction {
|
|
if rm.kind != operandKindReg && rm.kind != operandKindMem {
|
|
panic("BUG")
|
|
}
|
|
i.kind = gprToXmm
|
|
i.op1 = rm
|
|
i.op2 = newOperandReg(rd)
|
|
i.u1 = uint64(op)
|
|
i.b1 = _64
|
|
return i
|
|
}
|
|
|
|
func (i *instruction) asMovRM(rm regalloc.VReg, rd operand, size byte) *instruction {
|
|
if rd.kind != operandKindMem {
|
|
panic("BUG")
|
|
}
|
|
i.kind = movRM
|
|
i.op1 = newOperandReg(rm)
|
|
i.op2 = rd
|
|
i.u1 = uint64(size)
|
|
return i
|
|
}
|
|
|
|
func (i *instruction) asMovsxRmR(ext extMode, src operand, rd regalloc.VReg) *instruction {
|
|
if src.kind != operandKindReg && src.kind != operandKindMem {
|
|
panic("BUG")
|
|
}
|
|
i.kind = movsxRmR
|
|
i.op1 = src
|
|
i.op2 = newOperandReg(rd)
|
|
i.u1 = uint64(ext)
|
|
return i
|
|
}
|
|
|
|
func (i *instruction) asMovzxRmR(ext extMode, src operand, rd regalloc.VReg) *instruction {
|
|
if src.kind != operandKindReg && src.kind != operandKindMem {
|
|
panic("BUG")
|
|
}
|
|
i.kind = movzxRmR
|
|
i.op1 = src
|
|
i.op2 = newOperandReg(rd)
|
|
i.u1 = uint64(ext)
|
|
return i
|
|
}
|
|
|
|
func (i *instruction) asUD2() *instruction {
|
|
i.kind = ud2
|
|
return i
|
|
}
|
|
|
|
func (i *instruction) asMov64MR(rm operand, rd regalloc.VReg) *instruction {
|
|
if rm.kind != operandKindMem {
|
|
panic("BUG")
|
|
}
|
|
i.kind = mov64MR
|
|
i.op1 = rm
|
|
i.op2 = newOperandReg(rd)
|
|
return i
|
|
}
|
|
|
|
func (i *instruction) asMovRR(rm, rd regalloc.VReg, _64 bool) *instruction {
|
|
i.kind = movRR
|
|
i.op1 = newOperandReg(rm)
|
|
i.op2 = newOperandReg(rd)
|
|
i.b1 = _64
|
|
return i
|
|
}
|
|
|
|
func (i *instruction) asNot(rm operand, _64 bool) *instruction {
|
|
if rm.kind != operandKindReg && rm.kind != operandKindMem {
|
|
panic("BUG")
|
|
}
|
|
i.kind = not
|
|
i.op1 = rm
|
|
i.b1 = _64
|
|
return i
|
|
}
|
|
|
|
func (i *instruction) asNeg(rm operand, _64 bool) *instruction {
|
|
if rm.kind != operandKindReg && rm.kind != operandKindMem {
|
|
panic("BUG")
|
|
}
|
|
i.kind = neg
|
|
i.op1 = rm
|
|
i.b1 = _64
|
|
return i
|
|
}
|
|
|
|
func (i *instruction) asMulHi(rm operand, signed, _64 bool) *instruction {
|
|
if rm.kind != operandKindReg && (rm.kind != operandKindMem) {
|
|
panic("BUG")
|
|
}
|
|
i.kind = mulHi
|
|
i.op1 = rm
|
|
i.b1 = _64
|
|
if signed {
|
|
i.u1 = 1
|
|
}
|
|
return i
|
|
}
|
|
|
|
func (i *instruction) asUnaryRmR(op unaryRmROpcode, rm operand, rd regalloc.VReg, _64 bool) *instruction {
|
|
if rm.kind != operandKindReg && rm.kind != operandKindMem {
|
|
panic("BUG")
|
|
}
|
|
i.kind = unaryRmR
|
|
i.op1 = rm
|
|
i.op2 = newOperandReg(rd)
|
|
i.u1 = uint64(op)
|
|
i.b1 = _64
|
|
return i
|
|
}
|
|
|
|
func (i *instruction) asShiftR(op shiftROp, amount operand, rd regalloc.VReg, _64 bool) *instruction {
|
|
if amount.kind != operandKindReg && amount.kind != operandKindImm32 {
|
|
panic("BUG")
|
|
}
|
|
i.kind = shiftR
|
|
i.op1 = amount
|
|
i.op2 = newOperandReg(rd)
|
|
i.u1 = uint64(op)
|
|
i.b1 = _64
|
|
return i
|
|
}
|
|
|
|
func (i *instruction) asXmmRmiReg(op sseOpcode, rm operand, rd regalloc.VReg) *instruction {
|
|
if rm.kind != operandKindReg && rm.kind != operandKindImm32 && rm.kind != operandKindMem {
|
|
panic("BUG")
|
|
}
|
|
i.kind = xmmRmiReg
|
|
i.op1 = rm
|
|
i.op2 = newOperandReg(rd)
|
|
i.u1 = uint64(op)
|
|
return i
|
|
}
|
|
|
|
func (i *instruction) asCmpRmiR(cmp bool, rm operand, rd regalloc.VReg, _64 bool) *instruction {
|
|
if rm.kind != operandKindReg && rm.kind != operandKindImm32 && rm.kind != operandKindMem {
|
|
panic("BUG")
|
|
}
|
|
i.kind = cmpRmiR
|
|
i.op1 = rm
|
|
i.op2 = newOperandReg(rd)
|
|
if cmp {
|
|
i.u1 = 1
|
|
}
|
|
i.b1 = _64
|
|
return i
|
|
}
|
|
|
|
func (i *instruction) asSetcc(c cond, rd regalloc.VReg) *instruction {
|
|
i.kind = setcc
|
|
i.op1 = newOperandReg(rd)
|
|
i.u1 = uint64(c)
|
|
return i
|
|
}
|
|
|
|
func (i *instruction) asCmove(c cond, rm operand, rd regalloc.VReg, _64 bool) *instruction {
|
|
i.kind = cmove
|
|
i.op1 = rm
|
|
i.op2 = newOperandReg(rd)
|
|
i.u1 = uint64(c)
|
|
i.b1 = _64
|
|
return i
|
|
}
|
|
|
|
func (i *instruction) asExitSeq(execCtx regalloc.VReg) *instruction {
|
|
i.kind = exitSequence
|
|
i.op1 = newOperandReg(execCtx)
|
|
return i
|
|
}
|
|
|
|
func (i *instruction) asXmmUnaryRmR(op sseOpcode, rm operand, rd regalloc.VReg) *instruction {
|
|
if rm.kind != operandKindReg && rm.kind != operandKindMem {
|
|
panic("BUG")
|
|
}
|
|
i.kind = xmmUnaryRmR
|
|
i.op1 = rm
|
|
i.op2 = newOperandReg(rd)
|
|
i.u1 = uint64(op)
|
|
return i
|
|
}
|
|
|
|
func (i *instruction) asV128ConstIsland(lo, hi uint64) *instruction {
|
|
i.kind = v128ConstIsland
|
|
i.u1 = lo
|
|
i.u2 = hi
|
|
return i
|
|
}
|
|
|
|
func (i *instruction) asXmmMovRM(op sseOpcode, rm regalloc.VReg, rd operand) *instruction {
|
|
if rd.kind != operandKindMem {
|
|
panic("BUG")
|
|
}
|
|
i.kind = xmmMovRM
|
|
i.op1 = newOperandReg(rm)
|
|
i.op2 = rd
|
|
i.u1 = uint64(op)
|
|
return i
|
|
}
|
|
|
|
func (i *instruction) asPop64(rm regalloc.VReg) *instruction {
|
|
i.kind = pop64
|
|
i.op1 = newOperandReg(rm)
|
|
return i
|
|
}
|
|
|
|
func (i *instruction) asPush64(op operand) *instruction {
|
|
if op.kind != operandKindReg && op.kind != operandKindMem && op.kind != operandKindImm32 {
|
|
panic("BUG")
|
|
}
|
|
i.kind = push64
|
|
i.op1 = op
|
|
return i
|
|
}
|
|
|
|
type unaryRmROpcode byte
|
|
|
|
const (
|
|
unaryRmROpcodeBsr unaryRmROpcode = iota
|
|
unaryRmROpcodeBsf
|
|
unaryRmROpcodeLzcnt
|
|
unaryRmROpcodeTzcnt
|
|
unaryRmROpcodePopcnt
|
|
)
|
|
|
|
func (u unaryRmROpcode) String() string {
|
|
switch u {
|
|
case unaryRmROpcodeBsr:
|
|
return "bsr"
|
|
case unaryRmROpcodeBsf:
|
|
return "bsf"
|
|
case unaryRmROpcodeLzcnt:
|
|
return "lzcnt"
|
|
case unaryRmROpcodeTzcnt:
|
|
return "tzcnt"
|
|
case unaryRmROpcodePopcnt:
|
|
return "popcnt"
|
|
default:
|
|
panic("BUG")
|
|
}
|
|
}
|
|
|
|
type shiftROp byte
|
|
|
|
const (
|
|
shiftROpRotateLeft shiftROp = 0
|
|
shiftROpRotateRight shiftROp = 1
|
|
shiftROpShiftLeft shiftROp = 4
|
|
shiftROpShiftRightLogical shiftROp = 5
|
|
shiftROpShiftRightArithmetic shiftROp = 7
|
|
)
|
|
|
|
func (s shiftROp) String() string {
|
|
switch s {
|
|
case shiftROpRotateLeft:
|
|
return "rol"
|
|
case shiftROpRotateRight:
|
|
return "ror"
|
|
case shiftROpShiftLeft:
|
|
return "shl"
|
|
case shiftROpShiftRightLogical:
|
|
return "shr"
|
|
case shiftROpShiftRightArithmetic:
|
|
return "sar"
|
|
default:
|
|
panic("BUG")
|
|
}
|
|
}
|
|
|
|
type sseOpcode byte
|
|
|
|
const (
|
|
sseOpcodeAddps sseOpcode = iota + 1
|
|
sseOpcodeAddpd
|
|
sseOpcodeAddss
|
|
sseOpcodeAddsd
|
|
sseOpcodeAndps
|
|
sseOpcodeAndpd
|
|
sseOpcodeAndnps
|
|
sseOpcodeAndnpd
|
|
sseOpcodeComiss
|
|
sseOpcodeComisd
|
|
sseOpcodeCmpps
|
|
sseOpcodeCmppd
|
|
sseOpcodeCmpss
|
|
sseOpcodeCmpsd
|
|
sseOpcodeCvtdq2ps
|
|
sseOpcodeCvtsd2ss
|
|
sseOpcodeCvtsd2si
|
|
sseOpcodeCvtsi2ss
|
|
sseOpcodeCvtsi2sd
|
|
sseOpcodeCvtss2si
|
|
sseOpcodeCvtss2sd
|
|
sseOpcodeCvttps2dq
|
|
sseOpcodeCvttss2si
|
|
sseOpcodeCvttsd2si
|
|
sseOpcodeDivps
|
|
sseOpcodeDivpd
|
|
sseOpcodeDivss
|
|
sseOpcodeDivsd
|
|
sseOpcodeInsertps
|
|
sseOpcodeMaxps
|
|
sseOpcodeMaxpd
|
|
sseOpcodeMaxss
|
|
sseOpcodeMaxsd
|
|
sseOpcodeMinps
|
|
sseOpcodeMinpd
|
|
sseOpcodeMinss
|
|
sseOpcodeMinsd
|
|
sseOpcodeMovaps
|
|
sseOpcodeMovapd
|
|
sseOpcodeMovd
|
|
sseOpcodeMovdqa
|
|
sseOpcodeMovdqu
|
|
sseOpcodeMovlhps
|
|
sseOpcodeMovmskps
|
|
sseOpcodeMovmskpd
|
|
sseOpcodeMovq
|
|
sseOpcodeMovss
|
|
sseOpcodeMovsd
|
|
sseOpcodeMovups
|
|
sseOpcodeMovupd
|
|
sseOpcodeMulps
|
|
sseOpcodeMulpd
|
|
sseOpcodeMulss
|
|
sseOpcodeMulsd
|
|
sseOpcodeOrps
|
|
sseOpcodeOrpd
|
|
sseOpcodePabsb
|
|
sseOpcodePabsw
|
|
sseOpcodePabsd
|
|
sseOpcodePackssdw
|
|
sseOpcodePacksswb
|
|
sseOpcodePackusdw
|
|
sseOpcodePackuswb
|
|
sseOpcodePaddb
|
|
sseOpcodePaddd
|
|
sseOpcodePaddq
|
|
sseOpcodePaddw
|
|
sseOpcodePaddsb
|
|
sseOpcodePaddsw
|
|
sseOpcodePaddusb
|
|
sseOpcodePaddusw
|
|
sseOpcodePalignr
|
|
sseOpcodePand
|
|
sseOpcodePandn
|
|
sseOpcodePavgb
|
|
sseOpcodePavgw
|
|
sseOpcodePcmpeqb
|
|
sseOpcodePcmpeqw
|
|
sseOpcodePcmpeqd
|
|
sseOpcodePcmpeqq
|
|
sseOpcodePcmpgtb
|
|
sseOpcodePcmpgtw
|
|
sseOpcodePcmpgtd
|
|
sseOpcodePcmpgtq
|
|
sseOpcodePextrb
|
|
sseOpcodePextrw
|
|
sseOpcodePextrd
|
|
sseOpcodePinsrb
|
|
sseOpcodePinsrw
|
|
sseOpcodePinsrd
|
|
sseOpcodePmaddwd
|
|
sseOpcodePmaxsb
|
|
sseOpcodePmaxsw
|
|
sseOpcodePmaxsd
|
|
sseOpcodePmaxub
|
|
sseOpcodePmaxuw
|
|
sseOpcodePmaxud
|
|
sseOpcodePminsb
|
|
sseOpcodePminsw
|
|
sseOpcodePminsd
|
|
sseOpcodePminub
|
|
sseOpcodePminuw
|
|
sseOpcodePminud
|
|
sseOpcodePmovmskb
|
|
sseOpcodePmovsxbd
|
|
sseOpcodePmovsxbw
|
|
sseOpcodePmovsxbq
|
|
sseOpcodePmovsxwd
|
|
sseOpcodePmovsxwq
|
|
sseOpcodePmovsxdq
|
|
sseOpcodePmovzxbd
|
|
sseOpcodePmovzxbw
|
|
sseOpcodePmovzxbq
|
|
sseOpcodePmovzxwd
|
|
sseOpcodePmovzxwq
|
|
sseOpcodePmovzxdq
|
|
sseOpcodePmulld
|
|
sseOpcodePmullw
|
|
sseOpcodePmuludq
|
|
sseOpcodePor
|
|
sseOpcodePshufb
|
|
sseOpcodePshufd
|
|
sseOpcodePsllw
|
|
sseOpcodePslld
|
|
sseOpcodePsllq
|
|
sseOpcodePsraw
|
|
sseOpcodePsrad
|
|
sseOpcodePsrlw
|
|
sseOpcodePsrld
|
|
sseOpcodePsrlq
|
|
sseOpcodePsubb
|
|
sseOpcodePsubd
|
|
sseOpcodePsubq
|
|
sseOpcodePsubw
|
|
sseOpcodePsubsb
|
|
sseOpcodePsubsw
|
|
sseOpcodePsubusb
|
|
sseOpcodePsubusw
|
|
sseOpcodePtest
|
|
sseOpcodePunpckhbw
|
|
sseOpcodePunpcklbw
|
|
sseOpcodePxor
|
|
sseOpcodeRcpss
|
|
sseOpcodeRoundps
|
|
sseOpcodeRoundpd
|
|
sseOpcodeRoundss
|
|
sseOpcodeRoundsd
|
|
sseOpcodeRsqrtss
|
|
sseOpcodeSqrtps
|
|
sseOpcodeSqrtpd
|
|
sseOpcodeSqrtss
|
|
sseOpcodeSqrtsd
|
|
sseOpcodeSubps
|
|
sseOpcodeSubpd
|
|
sseOpcodeSubss
|
|
sseOpcodeSubsd
|
|
sseOpcodeUcomiss
|
|
sseOpcodeUcomisd
|
|
sseOpcodeXorps
|
|
sseOpcodeXorpd
|
|
)
|
|
|
|
func (s sseOpcode) String() string {
|
|
switch s {
|
|
case sseOpcodeAddps:
|
|
return "addps"
|
|
case sseOpcodeAddpd:
|
|
return "addpd"
|
|
case sseOpcodeAddss:
|
|
return "addss"
|
|
case sseOpcodeAddsd:
|
|
return "addsd"
|
|
case sseOpcodeAndps:
|
|
return "andps"
|
|
case sseOpcodeAndpd:
|
|
return "andpd"
|
|
case sseOpcodeAndnps:
|
|
return "andnps"
|
|
case sseOpcodeAndnpd:
|
|
return "andnpd"
|
|
case sseOpcodeComiss:
|
|
return "comiss"
|
|
case sseOpcodeComisd:
|
|
return "comisd"
|
|
case sseOpcodeCmpps:
|
|
return "cmpps"
|
|
case sseOpcodeCmppd:
|
|
return "cmppd"
|
|
case sseOpcodeCmpss:
|
|
return "cmpss"
|
|
case sseOpcodeCmpsd:
|
|
return "cmpsd"
|
|
case sseOpcodeCvtdq2ps:
|
|
return "cvtdq2ps"
|
|
case sseOpcodeCvtsd2ss:
|
|
return "cvtsd2ss"
|
|
case sseOpcodeCvtsd2si:
|
|
return "cvtsd2si"
|
|
case sseOpcodeCvtsi2ss:
|
|
return "cvtsi2ss"
|
|
case sseOpcodeCvtsi2sd:
|
|
return "cvtsi2sd"
|
|
case sseOpcodeCvtss2si:
|
|
return "cvtss2si"
|
|
case sseOpcodeCvtss2sd:
|
|
return "cvtss2sd"
|
|
case sseOpcodeCvttps2dq:
|
|
return "cvttps2dq"
|
|
case sseOpcodeCvttss2si:
|
|
return "cvttss2si"
|
|
case sseOpcodeCvttsd2si:
|
|
return "cvttsd2si"
|
|
case sseOpcodeDivps:
|
|
return "divps"
|
|
case sseOpcodeDivpd:
|
|
return "divpd"
|
|
case sseOpcodeDivss:
|
|
return "divss"
|
|
case sseOpcodeDivsd:
|
|
return "divsd"
|
|
case sseOpcodeInsertps:
|
|
return "insertps"
|
|
case sseOpcodeMaxps:
|
|
return "maxps"
|
|
case sseOpcodeMaxpd:
|
|
return "maxpd"
|
|
case sseOpcodeMaxss:
|
|
return "maxss"
|
|
case sseOpcodeMaxsd:
|
|
return "maxsd"
|
|
case sseOpcodeMinps:
|
|
return "minps"
|
|
case sseOpcodeMinpd:
|
|
return "minpd"
|
|
case sseOpcodeMinss:
|
|
return "minss"
|
|
case sseOpcodeMinsd:
|
|
return "minsd"
|
|
case sseOpcodeMovaps:
|
|
return "movaps"
|
|
case sseOpcodeMovapd:
|
|
return "movapd"
|
|
case sseOpcodeMovd:
|
|
return "movd"
|
|
case sseOpcodeMovdqa:
|
|
return "movdqa"
|
|
case sseOpcodeMovdqu:
|
|
return "movdqu"
|
|
case sseOpcodeMovlhps:
|
|
return "movlhps"
|
|
case sseOpcodeMovmskps:
|
|
return "movmskps"
|
|
case sseOpcodeMovmskpd:
|
|
return "movmskpd"
|
|
case sseOpcodeMovq:
|
|
return "movq"
|
|
case sseOpcodeMovss:
|
|
return "movss"
|
|
case sseOpcodeMovsd:
|
|
return "movsd"
|
|
case sseOpcodeMovups:
|
|
return "movups"
|
|
case sseOpcodeMovupd:
|
|
return "movupd"
|
|
case sseOpcodeMulps:
|
|
return "mulps"
|
|
case sseOpcodeMulpd:
|
|
return "mulpd"
|
|
case sseOpcodeMulss:
|
|
return "mulss"
|
|
case sseOpcodeMulsd:
|
|
return "mulsd"
|
|
case sseOpcodeOrps:
|
|
return "orps"
|
|
case sseOpcodeOrpd:
|
|
return "orpd"
|
|
case sseOpcodePabsb:
|
|
return "pabsb"
|
|
case sseOpcodePabsw:
|
|
return "pabsw"
|
|
case sseOpcodePabsd:
|
|
return "pabsd"
|
|
case sseOpcodePackssdw:
|
|
return "packssdw"
|
|
case sseOpcodePacksswb:
|
|
return "packsswb"
|
|
case sseOpcodePackusdw:
|
|
return "packusdw"
|
|
case sseOpcodePackuswb:
|
|
return "packuswb"
|
|
case sseOpcodePaddb:
|
|
return "paddb"
|
|
case sseOpcodePaddd:
|
|
return "paddd"
|
|
case sseOpcodePaddq:
|
|
return "paddq"
|
|
case sseOpcodePaddw:
|
|
return "paddw"
|
|
case sseOpcodePaddsb:
|
|
return "paddsb"
|
|
case sseOpcodePaddsw:
|
|
return "paddsw"
|
|
case sseOpcodePaddusb:
|
|
return "paddusb"
|
|
case sseOpcodePaddusw:
|
|
return "paddusw"
|
|
case sseOpcodePalignr:
|
|
return "palignr"
|
|
case sseOpcodePand:
|
|
return "pand"
|
|
case sseOpcodePandn:
|
|
return "pandn"
|
|
case sseOpcodePavgb:
|
|
return "pavgb"
|
|
case sseOpcodePavgw:
|
|
return "pavgw"
|
|
case sseOpcodePcmpeqb:
|
|
return "pcmpeqb"
|
|
case sseOpcodePcmpeqw:
|
|
return "pcmpeqw"
|
|
case sseOpcodePcmpeqd:
|
|
return "pcmpeqd"
|
|
case sseOpcodePcmpeqq:
|
|
return "pcmpeqq"
|
|
case sseOpcodePcmpgtb:
|
|
return "pcmpgtb"
|
|
case sseOpcodePcmpgtw:
|
|
return "pcmpgtw"
|
|
case sseOpcodePcmpgtd:
|
|
return "pcmpgtd"
|
|
case sseOpcodePcmpgtq:
|
|
return "pcmpgtq"
|
|
case sseOpcodePextrb:
|
|
return "pextrb"
|
|
case sseOpcodePextrw:
|
|
return "pextrw"
|
|
case sseOpcodePextrd:
|
|
return "pextrd"
|
|
case sseOpcodePinsrb:
|
|
return "pinsrb"
|
|
case sseOpcodePinsrw:
|
|
return "pinsrw"
|
|
case sseOpcodePinsrd:
|
|
return "pinsrd"
|
|
case sseOpcodePmaddwd:
|
|
return "pmaddwd"
|
|
case sseOpcodePmaxsb:
|
|
return "pmaxsb"
|
|
case sseOpcodePmaxsw:
|
|
return "pmaxsw"
|
|
case sseOpcodePmaxsd:
|
|
return "pmaxsd"
|
|
case sseOpcodePmaxub:
|
|
return "pmaxub"
|
|
case sseOpcodePmaxuw:
|
|
return "pmaxuw"
|
|
case sseOpcodePmaxud:
|
|
return "pmaxud"
|
|
case sseOpcodePminsb:
|
|
return "pminsb"
|
|
case sseOpcodePminsw:
|
|
return "pminsw"
|
|
case sseOpcodePminsd:
|
|
return "pminsd"
|
|
case sseOpcodePminub:
|
|
return "pminub"
|
|
case sseOpcodePminuw:
|
|
return "pminuw"
|
|
case sseOpcodePminud:
|
|
return "pminud"
|
|
case sseOpcodePmovmskb:
|
|
return "pmovmskb"
|
|
case sseOpcodePmovsxbd:
|
|
return "pmovsxbd"
|
|
case sseOpcodePmovsxbw:
|
|
return "pmovsxbw"
|
|
case sseOpcodePmovsxbq:
|
|
return "pmovsxbq"
|
|
case sseOpcodePmovsxwd:
|
|
return "pmovsxwd"
|
|
case sseOpcodePmovsxwq:
|
|
return "pmovsxwq"
|
|
case sseOpcodePmovsxdq:
|
|
return "pmovsxdq"
|
|
case sseOpcodePmovzxbd:
|
|
return "pmovzxbd"
|
|
case sseOpcodePmovzxbw:
|
|
return "pmovzxbw"
|
|
case sseOpcodePmovzxbq:
|
|
return "pmovzxbq"
|
|
case sseOpcodePmovzxwd:
|
|
return "pmovzxwd"
|
|
case sseOpcodePmovzxwq:
|
|
return "pmovzxwq"
|
|
case sseOpcodePmovzxdq:
|
|
return "pmovzxdq"
|
|
case sseOpcodePmulld:
|
|
return "pmulld"
|
|
case sseOpcodePmullw:
|
|
return "pmullw"
|
|
case sseOpcodePmuludq:
|
|
return "pmuludq"
|
|
case sseOpcodePor:
|
|
return "por"
|
|
case sseOpcodePshufb:
|
|
return "pshufb"
|
|
case sseOpcodePshufd:
|
|
return "pshufd"
|
|
case sseOpcodePsllw:
|
|
return "psllw"
|
|
case sseOpcodePslld:
|
|
return "pslld"
|
|
case sseOpcodePsllq:
|
|
return "psllq"
|
|
case sseOpcodePsraw:
|
|
return "psraw"
|
|
case sseOpcodePsrad:
|
|
return "psrad"
|
|
case sseOpcodePsrlw:
|
|
return "psrlw"
|
|
case sseOpcodePsrld:
|
|
return "psrld"
|
|
case sseOpcodePsrlq:
|
|
return "psrlq"
|
|
case sseOpcodePsubb:
|
|
return "psubb"
|
|
case sseOpcodePsubd:
|
|
return "psubd"
|
|
case sseOpcodePsubq:
|
|
return "psubq"
|
|
case sseOpcodePsubw:
|
|
return "psubw"
|
|
case sseOpcodePsubsb:
|
|
return "psubsb"
|
|
case sseOpcodePsubsw:
|
|
return "psubsw"
|
|
case sseOpcodePsubusb:
|
|
return "psubusb"
|
|
case sseOpcodePsubusw:
|
|
return "psubusw"
|
|
case sseOpcodePtest:
|
|
return "ptest"
|
|
case sseOpcodePunpckhbw:
|
|
return "punpckhbw"
|
|
case sseOpcodePunpcklbw:
|
|
return "punpcklbw"
|
|
case sseOpcodePxor:
|
|
return "pxor"
|
|
case sseOpcodeRcpss:
|
|
return "rcpss"
|
|
case sseOpcodeRoundps:
|
|
return "roundps"
|
|
case sseOpcodeRoundpd:
|
|
return "roundpd"
|
|
case sseOpcodeRoundss:
|
|
return "roundss"
|
|
case sseOpcodeRoundsd:
|
|
return "roundsd"
|
|
case sseOpcodeRsqrtss:
|
|
return "rsqrtss"
|
|
case sseOpcodeSqrtps:
|
|
return "sqrtps"
|
|
case sseOpcodeSqrtpd:
|
|
return "sqrtpd"
|
|
case sseOpcodeSqrtss:
|
|
return "sqrtss"
|
|
case sseOpcodeSqrtsd:
|
|
return "sqrtsd"
|
|
case sseOpcodeSubps:
|
|
return "subps"
|
|
case sseOpcodeSubpd:
|
|
return "subpd"
|
|
case sseOpcodeSubss:
|
|
return "subss"
|
|
case sseOpcodeSubsd:
|
|
return "subsd"
|
|
case sseOpcodeUcomiss:
|
|
return "ucomiss"
|
|
case sseOpcodeUcomisd:
|
|
return "ucomisd"
|
|
case sseOpcodeXorps:
|
|
return "xorps"
|
|
case sseOpcodeXorpd:
|
|
return "xorpd"
|
|
default:
|
|
panic("BUG")
|
|
}
|
|
}
|
|
|
|
func linkInstr(prev, next *instruction) *instruction {
|
|
prev.next = next
|
|
next.prev = prev
|
|
return next
|
|
}
|
|
|
|
type defKind byte
|
|
|
|
const (
|
|
defKindNone defKind = iota + 1
|
|
defKindOp2
|
|
defKindCall
|
|
)
|
|
|
|
var defKinds = [instrMax]defKind{
|
|
nop0: defKindNone,
|
|
ret: defKindNone,
|
|
movRR: defKindOp2,
|
|
movRM: defKindNone,
|
|
xmmMovRM: defKindNone,
|
|
aluRmiR: defKindNone,
|
|
shiftR: defKindNone,
|
|
imm: defKindOp2,
|
|
unaryRmR: defKindOp2,
|
|
xmmUnaryRmR: defKindOp2,
|
|
mov64MR: defKindOp2,
|
|
movsxRmR: defKindOp2,
|
|
movzxRmR: defKindOp2,
|
|
gprToXmm: defKindOp2,
|
|
call: defKindCall,
|
|
callIndirect: defKindCall,
|
|
ud2: defKindNone,
|
|
jmp: defKindNone,
|
|
jmpIf: defKindNone,
|
|
cmpRmiR: defKindNone,
|
|
exitSequence: defKindNone,
|
|
lea: defKindOp2,
|
|
v128ConstIsland: defKindNone,
|
|
}
|
|
|
|
// String implements fmt.Stringer.
|
|
func (d defKind) String() string {
|
|
switch d {
|
|
case defKindNone:
|
|
return "none"
|
|
case defKindOp2:
|
|
return "op2"
|
|
case defKindCall:
|
|
return "call"
|
|
default:
|
|
return "invalid"
|
|
}
|
|
}
|
|
|
|
type useKind byte
|
|
|
|
const (
|
|
useKindNone useKind = iota + 1
|
|
useKindOp1
|
|
// useKindOp1Op2Reg is Op1 can be any operand, Op2 must be a register.
|
|
useKindOp1Op2Reg
|
|
// useKindOp1RegOp2 is Op1 must be a register, Op2 can be any operand.
|
|
useKindOp1RegOp2
|
|
useKindCall
|
|
useKindCallInd
|
|
)
|
|
|
|
var useKinds = [instrMax]useKind{
|
|
nop0: useKindNone,
|
|
ret: useKindNone,
|
|
movRR: useKindOp1,
|
|
movRM: useKindOp1RegOp2,
|
|
xmmMovRM: useKindOp1RegOp2,
|
|
aluRmiR: useKindOp1Op2Reg,
|
|
shiftR: useKindOp1Op2Reg,
|
|
imm: useKindNone,
|
|
unaryRmR: useKindOp1,
|
|
xmmUnaryRmR: useKindOp1,
|
|
mov64MR: useKindOp1,
|
|
movzxRmR: useKindOp1,
|
|
movsxRmR: useKindOp1,
|
|
gprToXmm: useKindOp1,
|
|
call: useKindCall,
|
|
callIndirect: useKindCallInd,
|
|
ud2: useKindNone,
|
|
jmpIf: useKindOp1,
|
|
jmp: useKindOp1,
|
|
cmpRmiR: useKindOp1Op2Reg,
|
|
exitSequence: useKindOp1,
|
|
lea: useKindOp1,
|
|
v128ConstIsland: useKindNone,
|
|
}
|
|
|
|
func (u useKind) String() string {
|
|
switch u {
|
|
case useKindNone:
|
|
return "none"
|
|
case useKindOp1:
|
|
return "op1"
|
|
case useKindOp1Op2Reg:
|
|
return "op1op2Reg"
|
|
case useKindOp1RegOp2:
|
|
return "op1RegOp2"
|
|
case useKindCall:
|
|
return "call"
|
|
default:
|
|
return "invalid"
|
|
}
|
|
}
|