feat(asm): add vpcmp compare, full opmask set, legacy sse integers and bswap
Assisted-by: GLM 5.3
This commit is contained in:
+71
-27
@@ -48,15 +48,21 @@ func (e *enc) encodeMov(ops []Operand, size int) error {
|
||||
}
|
||||
src, dst := ops[0], ops[1]
|
||||
|
||||
// MOVQ with an XMM operand is the SSE2 packed-quadword move, NOT a
|
||||
// GPR move: mem→xmm and xmm↔xmm encode as F3 0F 7E (reg = dst),
|
||||
// xmm→mem as 66 0F D6 (rm = xmm). A GPR-move fallback would
|
||||
// silently emit REX.W 8B with the wrong operand meaning.
|
||||
// Integer scalar XMM moves: MOVQ with an XMM operand is the SSE2
|
||||
// packed-quadword move, NOT a GPR move: mem→xmm encodes as F3 0F 7E
|
||||
// (reg = dst, no REX.W — the Go assembler's form), xmm→mem as
|
||||
// 66 0F D6 (rm = xmm). MOVL is the packed-dword move instead:
|
||||
// 66 0F 6E load, 66 0F 7E store. A GPR-move fallback would silently
|
||||
// emit REX.W 8B with the wrong operand meaning.
|
||||
_, srcVec := vecReg(src)
|
||||
dstReg, dstVec := vecReg(dst)
|
||||
if srcVec || dstVec {
|
||||
if dstVec {
|
||||
i := &instr{prefix: 0xF3, opcode: []byte{0x0F, 0x7E}, modrm: -1, sib: -1}
|
||||
if size == 4 {
|
||||
i.prefix = 0x66
|
||||
i.opcode = []byte{0x0F, 0x6E}
|
||||
}
|
||||
if err := setRM(i, dstReg, src, 8); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -64,9 +70,12 @@ func (e *enc) encodeMov(ops []Operand, size int) error {
|
||||
}
|
||||
srcXMM, srcIsXMM := src.(Reg)
|
||||
if !srcIsXMM || !srcXMM.isVec() {
|
||||
return fmt.Errorf("MOVQ: store needs an XMM source")
|
||||
return fmt.Errorf("MOV: store needs an XMM source")
|
||||
}
|
||||
i := &instr{prefix: 0x66, opcode: []byte{0x0F, 0xD6}, modrm: -1, sib: -1}
|
||||
if size == 4 {
|
||||
i.opcode = []byte{0x0F, 0x7E}
|
||||
}
|
||||
if err := setRM(i, srcXMM, dst, 8); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -682,6 +691,21 @@ func (e *enc) encodeCount(base string, ops []Operand, size int) error {
|
||||
return e.emit(i)
|
||||
}
|
||||
|
||||
// encodeBswap encodes BSWAP: the single register operand is encoded in the
|
||||
// opcode byte (0F C8+r), with REX.B for R8-R15 and REX.W for the quad form.
|
||||
func (e *enc) encodeBswap(ops []Operand, size int) error {
|
||||
if len(ops) != 1 {
|
||||
return fmt.Errorf("BSWAP expects 1 operand, got %d", len(ops))
|
||||
}
|
||||
reg, ok := ops[0].(Reg)
|
||||
if !ok {
|
||||
return fmt.Errorf("BSWAP operand must be a register")
|
||||
}
|
||||
i := newInstr(size, []byte{0x0F, 0xC8 + byte(reg.idx&7)})
|
||||
i.rexB = reg.idx >= 8
|
||||
return e.emit(i)
|
||||
}
|
||||
|
||||
// --- mixed-width sign/zero-extending moves -----------------------------------
|
||||
|
||||
// movExtendOp maps Go's mixed-width move names to their opcode and destination
|
||||
@@ -784,33 +808,49 @@ func (e *enc) encodeSSEMove(m sseMove, ops []Operand) error {
|
||||
// --- legacy SSE packed binary and shuffles -----------------------------------
|
||||
|
||||
// sseBin describes a legacy (non-VEX) SSE packed/scalar binary op: an
|
||||
// optional mandatory prefix plus the 0F-prefixed opcode. Plan 9 asm
|
||||
// lists the source operand first, so MULPS X0, X1 computes X1 = X1 * X0.
|
||||
// optional mandatory prefix plus the 0F-prefixed opcode (0F38 for the
|
||||
// SSSE3 integer shuffles). Plan 9 asm lists the source operand first, so
|
||||
// MULPS X0, X1 computes X1 = X1 * X0.
|
||||
type sseBin struct {
|
||||
prefix byte // 0, 0x66, 0xF2 or 0xF3
|
||||
op byte
|
||||
map38 bool // opcode lives under 0F38 instead of 0F
|
||||
}
|
||||
|
||||
var sseBinTable = map[string]sseBin{
|
||||
"ADDPS": {0, 0x58}, "ADDPD": {0x66, 0x58},
|
||||
"MULPS": {0, 0x59}, "MULPD": {0x66, 0x59},
|
||||
"SUBPS": {0, 0x5C}, "SUBPD": {0x66, 0x5C},
|
||||
"DIVPS": {0, 0x5E}, "DIVPD": {0x66, 0x5E},
|
||||
"ANDPS": {0, 0x54}, "ANDPD": {0x66, 0x54},
|
||||
"ORPS": {0, 0x56}, "ORPD": {0x66, 0x56},
|
||||
"XORPS": {0, 0x57}, "XORPD": {0x66, 0x57},
|
||||
"MINPS": {0, 0x5D}, "MINPD": {0x66, 0x5D},
|
||||
"MAXPS": {0, 0x5F}, "MAXPD": {0x66, 0x5F},
|
||||
"ADDSS": {0xF3, 0x58}, "ADDSD": {0xF2, 0x58},
|
||||
"MULSS": {0xF3, 0x59}, "MULSD": {0xF2, 0x59},
|
||||
"SUBSS": {0xF3, 0x5C}, "SUBSD": {0xF2, 0x5C},
|
||||
"DIVSS": {0xF3, 0x5E}, "DIVSD": {0xF2, 0x5E},
|
||||
"MINSS": {0xF3, 0x5D}, "MINSD": {0xF2, 0x5D},
|
||||
"MAXSS": {0xF3, 0x5F}, "MAXSD": {0xF2, 0x5F},
|
||||
"UNPCKLPS": {0, 0x14}, "UNPCKHPS": {0, 0x15},
|
||||
"UNPCKLPD": {0x66, 0x14}, "UNPCKHPD": {0x66, 0x15},
|
||||
"CVTSS2SD": {0xF3, 0x5A}, "CVTSD2SS": {0xF2, 0x5A},
|
||||
"CVTPS2PD": {0, 0x5A}, "CVTPD2PS": {0x66, 0x5A},
|
||||
"ADDPS": {0, 0x58, false}, "ADDPD": {0x66, 0x58, false},
|
||||
"MULPS": {0, 0x59, false}, "MULPD": {0x66, 0x59, false},
|
||||
"SUBPS": {0, 0x5C, false}, "SUBPD": {0x66, 0x5C, false},
|
||||
"DIVPS": {0, 0x5E, false}, "DIVPD": {0x66, 0x5E, false},
|
||||
"ANDPS": {0, 0x54, false}, "ANDPD": {0x66, 0x54, false},
|
||||
"ORPS": {0, 0x56, false}, "ORPD": {0x66, 0x56, false},
|
||||
"XORPS": {0, 0x57, false}, "XORPD": {0x66, 0x57, false},
|
||||
"MINPS": {0, 0x5D, false}, "MINPD": {0x66, 0x5D, false},
|
||||
"MAXPS": {0, 0x5F, false}, "MAXPD": {0x66, 0x5F, false},
|
||||
"ADDSS": {0xF3, 0x58, false}, "ADDSD": {0xF2, 0x58, false},
|
||||
"MULSS": {0xF3, 0x59, false}, "MULSD": {0xF2, 0x59, false},
|
||||
"SUBSS": {0xF3, 0x5C, false}, "SUBSD": {0xF2, 0x5C, false},
|
||||
"DIVSS": {0xF3, 0x5E, false}, "DIVSD": {0xF2, 0x5E, false},
|
||||
"MINSS": {0xF3, 0x5D, false}, "MINSD": {0xF2, 0x5D, false},
|
||||
"MAXSS": {0xF3, 0x5F, false}, "MAXSD": {0xF2, 0x5F, false},
|
||||
"UNPCKLPS": {0, 0x14, false}, "UNPCKHPS": {0, 0x15, false},
|
||||
"UNPCKLPD": {0x66, 0x14, false}, "UNPCKHPD": {0x66, 0x15, false},
|
||||
"CVTSS2SD": {0xF3, 0x5A, false}, "CVTSD2SS": {0xF2, 0x5A, false},
|
||||
"CVTPS2PD": {0, 0x5A, false}, "CVTPD2PS": {0x66, 0x5A, false},
|
||||
// SSE2 packed integers (reg = reg op rm) and the SSSE3 byte shuffle.
|
||||
"PXOR": {0x66, 0xEF, false},
|
||||
"POR": {0x66, 0xEB, false},
|
||||
"PAND": {0x66, 0xDB, false},
|
||||
"PANDN": {0x66, 0xDF, false},
|
||||
"PADDB": {0x66, 0xFC, false}, "PADDW": {0x66, 0xFD, false},
|
||||
"PADDD": {0x66, 0xFE, false}, "PADDQ": {0x66, 0xD4, false},
|
||||
"PSUBB": {0x66, 0xF8, false}, "PSUBW": {0x66, 0xF9, false},
|
||||
"PSUBD": {0x66, 0xFA, false}, "PSUBQ": {0x66, 0xFB, false},
|
||||
"PCMPEQB": {0x66, 0x74, false}, "PCMPEQW": {0x66, 0x75, false},
|
||||
"PCMPEQD": {0x66, 0x76, false},
|
||||
"PCMPGTB": {0x66, 0x64, false}, "PCMPGTW": {0x66, 0x65, false},
|
||||
"PCMPGTD": {0x66, 0x66, false},
|
||||
"PSHUFB": {0x66, 0x00, true},
|
||||
}
|
||||
|
||||
// sseShuf describes a legacy SSE shuffle taking a trailing imm8
|
||||
@@ -835,7 +875,11 @@ func (e *enc) encodeSSEBin(m sseBin, ops []Operand) error {
|
||||
if !ok || !dstReg.isVec() {
|
||||
return fmt.Errorf("SSE binary destination must be a vector register")
|
||||
}
|
||||
i := &instr{prefix: m.prefix, opcode: []byte{0x0F, m.op}, modrm: -1, sib: -1}
|
||||
opcode := []byte{0x0F, m.op}
|
||||
if m.map38 {
|
||||
opcode = []byte{0x0F, 0x38, m.op}
|
||||
}
|
||||
i := &instr{prefix: m.prefix, opcode: opcode, modrm: -1, sib: -1}
|
||||
if err := setRM(i, dstReg, src, 8); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user