feat(amd64): assemble the double-shift and static-SB operand shapes
Assisted-by: GLM 5.3 Flash
This commit is contained in:
+64
-5
@@ -498,13 +498,34 @@ func (e *enc) encodeUnary(op struct {
|
||||
|
||||
// --- SHL/SHR/SAR ------------------------------------------------------------
|
||||
|
||||
func (e *enc) encodeShift(digit int, ops []Operand, size int) error {
|
||||
// doubleShiftOp maps the two mnemonics whose three-operand form go tool asm
|
||||
// accepts to the SHLD/SHRD opcode pair (imm8 form, CL form). SAR, SAL and
|
||||
// the rotates have no such form: the oracle rejects SARQ/ROLQ with three
|
||||
// operands, and so do we.
|
||||
var doubleShiftOp = map[string][2]byte{
|
||||
"SHL": {0xA4, 0xA5}, // SHLD
|
||||
"SHR": {0xAC, 0xAD}, // SHRD
|
||||
}
|
||||
|
||||
// isShiftCountCL reports whether a count operand is the CL register or its
|
||||
// CX spelling: go tool asm accepts both (CX names the same low byte) and
|
||||
// rejects ECX/RCX.
|
||||
func isShiftCountCL(o Operand) bool {
|
||||
reg, ok := o.(Reg)
|
||||
return ok && reg.idx == 1 && (reg.size == 1 || reg.size == 2)
|
||||
}
|
||||
|
||||
func (e *enc) encodeShift(base string, ops []Operand, size int) error {
|
||||
digit := shiftOp[base]
|
||||
if len(ops) == 3 {
|
||||
return e.encodeDoubleShift(base, ops, size)
|
||||
}
|
||||
if len(ops) != 2 {
|
||||
return fmt.Errorf("shift expects 2 operands, got %d", len(ops))
|
||||
}
|
||||
count, dst := ops[0], ops[1]
|
||||
// Count is $1, %CL, or an imm8.
|
||||
if reg, ok := count.(Reg); ok && reg.idx == 1 && reg.size <= 1 {
|
||||
// Count is $1, CL (or its CX spelling), or an imm8.
|
||||
if isShiftCountCL(count) {
|
||||
// CL: 0xD2 (8-bit) / 0xD3.
|
||||
op := byte(0xD3)
|
||||
if size == 1 {
|
||||
@@ -551,6 +572,44 @@ func (e *enc) encodeShift(digit int, ops []Operand, size int) error {
|
||||
return e.emit(i)
|
||||
}
|
||||
|
||||
// encodeDoubleShift emits the three-operand SHL/SHR form, which the Go
|
||||
// assembler spells as a shift but encodes as SHLD/SHRD (0F A4/A5, 0F AC/AD):
|
||||
// the first operand is the count ($imm or CL), the second feeds the vacated
|
||||
// bits (the reg field) and the third is the shifted value (the r/m field),
|
||||
// matching go tool asm byte for byte. The W/L/Q widths exist; the oracle
|
||||
// rejects the three-operand B form and every SAR/rotate one.
|
||||
func (e *enc) encodeDoubleShift(base string, ops []Operand, size int) error {
|
||||
opc, ok := doubleShiftOp[base]
|
||||
if !ok || size == 1 {
|
||||
return fmt.Errorf("%s: shift expects 2 operands, got %d", base, len(ops))
|
||||
}
|
||||
count, src, dst := ops[0], ops[1], ops[2]
|
||||
srcReg, ok := src.(Reg)
|
||||
if !ok {
|
||||
return fmt.Errorf("%s: middle operand must be a register, like go tool asm", base)
|
||||
}
|
||||
i := newInstr(size, []byte{0x0F, opc[0]})
|
||||
if isShiftCountCL(count) {
|
||||
// CL (or CX) form: 0F A5/AD.
|
||||
i.opcode[1] = opc[1]
|
||||
} else {
|
||||
imm, ok := count.(Imm)
|
||||
if !ok {
|
||||
return fmt.Errorf("shift count must be $1, CL or an immediate")
|
||||
}
|
||||
// The count is an unsigned imm8: the same range convention as the
|
||||
// two-operand shift above.
|
||||
if imm < 0 || imm > 255 {
|
||||
return fmt.Errorf("shift count $%d is out of the 0..255 range", int64(imm))
|
||||
}
|
||||
i.imm = []byte{byte(imm)}
|
||||
}
|
||||
if err := setRMReg(i, srcReg.idx, srcReg.idx >= 8, false, dst, size); err != nil {
|
||||
return err
|
||||
}
|
||||
return e.emit(i)
|
||||
}
|
||||
|
||||
// --- IMUL -------------------------------------------------------------------
|
||||
|
||||
func (e *enc) encodeImul(ops []Operand, size int) error {
|
||||
@@ -986,12 +1045,12 @@ func (e *enc) encodeSSEMove(m sseMove, ops []Operand) error {
|
||||
op = m.load
|
||||
reg, rm = dstReg, src
|
||||
case srcVec:
|
||||
if _, ok := dst.(Mem); !ok {
|
||||
if !isX86Mem(dst) {
|
||||
return fmt.Errorf("SSE move: invalid destination operand")
|
||||
}
|
||||
reg, rm = srcReg, dst
|
||||
case dstVec:
|
||||
if _, ok := src.(Mem); !ok {
|
||||
if !isX86Mem(src) {
|
||||
return fmt.Errorf("SSE move: invalid source operand")
|
||||
}
|
||||
op = m.load
|
||||
|
||||
Reference in New Issue
Block a user