fix(asm): encode BSF, BSR and POPCNT
Assisted-by: GLM 5.3
This commit is contained in:
+1
-1
@@ -95,7 +95,7 @@ func (e *enc) encode(mnem string, ops []Operand) error {
|
|||||||
return e.encodePushPop(ops, true)
|
return e.encodePushPop(ops, true)
|
||||||
case "POP":
|
case "POP":
|
||||||
return e.encodePushPop(ops, false)
|
return e.encodePushPop(ops, false)
|
||||||
case "LZCNT", "TZCNT":
|
case "BSF", "BSR", "LZCNT", "TZCNT", "POPCNT":
|
||||||
return e.encodeCount(base, ops, size)
|
return e.encodeCount(base, ops, size)
|
||||||
case "MOVBLZX", "MOVBQZX", "MOVWLZX", "MOVWQZX", "MOVWLSX", "MOVLQSX":
|
case "MOVBLZX", "MOVBQZX", "MOVWLZX", "MOVWQZX", "MOVWLSX", "MOVLQSX":
|
||||||
return e.encodeMovExtend(base, ops)
|
return e.encodeMovExtend(base, ops)
|
||||||
|
|||||||
@@ -203,6 +203,14 @@ func TestScalarGroundTruth(t *testing.T) {
|
|||||||
{"LZCNTQ R8,R9", "LZCNTQ", []Operand{r8, r9}, "f34d0fbdc8", "LZCNT"},
|
{"LZCNTQ R8,R9", "LZCNTQ", []Operand{r8, r9}, "f34d0fbdc8", "LZCNT"},
|
||||||
{"LZCNTW AX,CX", "LZCNTW", []Operand{AX, CX}, "66f30fbdc8", "LZCNT"},
|
{"LZCNTW AX,CX", "LZCNTW", []Operand{AX, CX}, "66f30fbdc8", "LZCNT"},
|
||||||
{"TZCNTL AX,CX", "TZCNTL", []Operand{AX, CX}, "f30fbcc8", "TZCNT"},
|
{"TZCNTL AX,CX", "TZCNTL", []Operand{AX, CX}, "f30fbcc8", "TZCNT"},
|
||||||
|
// Bit scan: BSF/BSR are the unprefixed forms of TZCNT/LZCNT's map.
|
||||||
|
{"BSFL AX,CX", "BSFL", []Operand{AX, CX}, "0fbcc8", "BSF"},
|
||||||
|
{"BSFQ R8,R9", "BSFQ", []Operand{r8, r9}, "4d0fbcc8", "BSF"},
|
||||||
|
{"BSFW AX,CX", "BSFW", []Operand{AX, CX}, "660fbcc8", "BSF"},
|
||||||
|
{"BSRL AX,CX", "BSRL", []Operand{AX, CX}, "0fbdc8", "BSR"},
|
||||||
|
{"BSRQ AX,CX", "BSRQ", []Operand{AX, CX}, "480fbdc8", "BSR"},
|
||||||
|
{"POPCNTL AX,CX", "POPCNTL", []Operand{AX, CX}, "f30fb8c8", "POPCNT"},
|
||||||
|
{"POPCNTQ R8,R9", "POPCNTQ", []Operand{r8, r9}, "f34d0fb8c8", "POPCNT"},
|
||||||
{"CMOVLGT CX,AX", "CMOVLGT", []Operand{CX, AX}, "0f4fc1", "CMOVG"},
|
{"CMOVLGT CX,AX", "CMOVLGT", []Operand{CX, AX}, "0f4fc1", "CMOVG"},
|
||||||
{"CMOVLEQ CX,AX", "CMOVLEQ", []Operand{CX, AX}, "0f44c1", "CMOVE"},
|
{"CMOVLEQ CX,AX", "CMOVLEQ", []Operand{CX, AX}, "0f44c1", "CMOVE"},
|
||||||
{"CMOVQGT R9,R8", "CMOVQGT", []Operand{r9, r8}, "4d0f4fc1", "CMOVG"},
|
{"CMOVQGT R9,R8", "CMOVQGT", []Operand{r9, r8}, "4d0f4fc1", "CMOVG"},
|
||||||
|
|||||||
+24
-10
@@ -597,25 +597,39 @@ func (e *enc) encodeSet(upper string, ops []Operand) error {
|
|||||||
return e.emit(i)
|
return e.emit(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- LZCNT / TZCNT ----------------------------------------------------------
|
// --- bit scan / bit count ----------------------------------------------------
|
||||||
|
|
||||||
// encodeCount encodes LZCNT/TZCNT (leading / trailing zero count): F3 0F BD
|
// countOp maps the bit-scan and bit-count mnemonics to their opcode byte and
|
||||||
// or F3 0F BC, with reg = dst and rm = src. The size suffix selects the
|
// mandatory prefix. TZCNT/LZCNT/POPCNT are the F3-prefixed forms of the
|
||||||
// operand width (LZCNTW/LZCNTL/LZCNTQ).
|
// same map as BSF/BSR's 0F BC/BD; POPCNT is F3 0F B8.
|
||||||
|
var countOp = map[string]struct {
|
||||||
|
op byte
|
||||||
|
prefix byte
|
||||||
|
}{
|
||||||
|
"BSF": {0xBC, 0},
|
||||||
|
"BSR": {0xBD, 0},
|
||||||
|
"TZCNT": {0xBC, 0xF3},
|
||||||
|
"LZCNT": {0xBD, 0xF3},
|
||||||
|
"POPCNT": {0xB8, 0xF3},
|
||||||
|
}
|
||||||
|
|
||||||
|
// encodeCount encodes the bit-scan and bit-count family — BSF (0F BC),
|
||||||
|
// BSR (0F BD), TZCNT (F3 0F BC), LZCNT (F3 0F BD) and POPCNT (F3 0F B8) —
|
||||||
|
// with reg = dst and rm = src. The size suffix selects the operand width
|
||||||
|
// (BSFQ, TZCNTL, …). Note BSF/BSR leave the destination undefined when the
|
||||||
|
// source is zero (unlike their F3-prefixed counterparts); callers must
|
||||||
|
// guard non-zero inputs themselves.
|
||||||
func (e *enc) encodeCount(base string, ops []Operand, size int) error {
|
func (e *enc) encodeCount(base string, ops []Operand, size int) error {
|
||||||
if len(ops) != 2 {
|
if len(ops) != 2 {
|
||||||
return fmt.Errorf("%s expects 2 operands, got %d", base, len(ops))
|
return fmt.Errorf("%s expects 2 operands, got %d", base, len(ops))
|
||||||
}
|
}
|
||||||
op := byte(0xBD)
|
spec := countOp[base]
|
||||||
if base == "TZCNT" {
|
|
||||||
op = 0xBC
|
|
||||||
}
|
|
||||||
dstReg, ok := ops[1].(Reg)
|
dstReg, ok := ops[1].(Reg)
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("%s destination must be a register", base)
|
return fmt.Errorf("%s destination must be a register", base)
|
||||||
}
|
}
|
||||||
i := newInstr(size, []byte{0x0F, op})
|
i := newInstr(size, []byte{0x0F, spec.op})
|
||||||
i.prefix = 0xF3
|
i.prefix = spec.prefix
|
||||||
if err := setRM(i, dstReg, ops[0], size); err != nil {
|
if err := setRM(i, dstReg, ops[0], size); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user