x64: Fix udiv and sdiv for 8bit integers

This commit is contained in:
bjorn3
2020-08-22 10:44:12 +02:00
committed by Benjamin Bouvier
parent 74642b166f
commit ce033f2a0c
4 changed files with 59 additions and 19 deletions

View File

@@ -662,11 +662,12 @@ pub(crate) fn emit(
divisor, divisor,
loc, loc,
} => { } => {
let (prefix, rex_flags) = match size { let (opcode, prefix, rex_flags) = match size {
2 => (LegacyPrefixes::_66, RexFlags::clear_w()), 1 => (0xF6, LegacyPrefixes::None, RexFlags::clear_w()),
4 => (LegacyPrefixes::None, RexFlags::clear_w()), 2 => (0xF7, LegacyPrefixes::_66, RexFlags::clear_w()),
8 => (LegacyPrefixes::None, RexFlags::set_w()), 4 => (0xF7, LegacyPrefixes::None, RexFlags::clear_w()),
_ => unreachable!(), 8 => (0xF7, LegacyPrefixes::None, RexFlags::set_w()),
_ => unreachable!("{}", size),
}; };
sink.add_trap(*loc, TrapCode::IntegerDivisionByZero); sink.add_trap(*loc, TrapCode::IntegerDivisionByZero);
@@ -675,12 +676,12 @@ pub(crate) fn emit(
match divisor { match divisor {
RegMem::Reg { reg } => { RegMem::Reg { reg } => {
let src = int_reg_enc(*reg); let src = int_reg_enc(*reg);
emit_std_enc_enc(sink, prefix, 0xF7, 1, subopcode, src, rex_flags) emit_std_enc_enc(sink, prefix, opcode, 1, subopcode, src, rex_flags)
} }
RegMem::Mem { addr: src } => emit_std_enc_mem( RegMem::Mem { addr: src } => emit_std_enc_mem(
sink, sink,
prefix, prefix,
0xF7, opcode,
1, 1,
subopcode, subopcode,
&src.finalize(state), &src.finalize(state),
@@ -715,6 +716,11 @@ pub(crate) fn emit(
} }
} }
Inst::SignExtendAlAh => {
sink.put1(0x66);
sink.put1(0x98);
}
Inst::SignExtendRaxRdx { size } => { Inst::SignExtendRaxRdx { size } => {
match size { match size {
2 => sink.put1(0x66), 2 => sink.put1(0x66),

View File

@@ -1354,6 +1354,10 @@ fn test_x64_emit() {
"mul %rdi", "mul %rdi",
)); ));
// ========================================================
// cbw
insns.push((Inst::sign_extend_al_to_ah(), "6698", "cbw"));
// ======================================================== // ========================================================
// cdq family: SignExtendRaxRdx // cdq family: SignExtendRaxRdx
insns.push((Inst::sign_extend_rax_to_rdx(2), "6699", "cwd")); insns.push((Inst::sign_extend_rax_to_rdx(2), "6699", "cwd"));

View File

@@ -100,6 +100,9 @@ pub enum Inst {
loc: SourceLoc, loc: SourceLoc,
}, },
/// Do a sign-extend based on the sign of the value in al into ah: (cbw)
SignExtendAlAh,
/// Do a sign-extend based on the sign of the value in rax into rdx: (cwd cdq cqo) /// Do a sign-extend based on the sign of the value in rax into rdx: (cwd cdq cqo)
SignExtendRaxRdx { SignExtendRaxRdx {
size: u8, // 1, 2, 4 or 8 size: u8, // 1, 2, 4 or 8
@@ -574,6 +577,10 @@ impl Inst {
} }
} }
pub(crate) fn sign_extend_al_to_ah() -> Inst {
Inst::SignExtendAlAh
}
pub(crate) fn sign_extend_rax_to_rdx(size: u8) -> Inst { pub(crate) fn sign_extend_rax_to_rdx(size: u8) -> Inst {
debug_assert!(size == 8 || size == 4 || size == 2); debug_assert!(size == 8 || size == 4 || size == 2);
Inst::SignExtendRaxRdx { size } Inst::SignExtendRaxRdx { size }
@@ -1259,6 +1266,8 @@ impl ShowWithRRU for Inst {
show_ireg_sized(divisor.to_reg(), mb_rru, *size), show_ireg_sized(divisor.to_reg(), mb_rru, *size),
), ),
Inst::SignExtendAlAh => "cbw".into(),
Inst::SignExtendRaxRdx { size } => match size { Inst::SignExtendRaxRdx { size } => match size {
2 => "cwd", 2 => "cwd",
4 => "cdq", 4 => "cdq",
@@ -1687,9 +1696,13 @@ fn x64_get_regs(inst: &Inst, collector: &mut RegUsageCollector) {
Inst::Neg { src, .. } => { Inst::Neg { src, .. } => {
collector.add_mod(*src); collector.add_mod(*src);
} }
Inst::Div { divisor, .. } => { Inst::Div { size, divisor, .. } => {
collector.add_mod(Writable::from_reg(regs::rax())); collector.add_mod(Writable::from_reg(regs::rax()));
collector.add_mod(Writable::from_reg(regs::rdx())); if *size == 1 {
collector.add_def(Writable::from_reg(regs::rdx()));
} else {
collector.add_mod(Writable::from_reg(regs::rdx()));
}
divisor.get_regs_as_uses(collector); divisor.get_regs_as_uses(collector);
} }
Inst::MulHi { rhs, .. } => { Inst::MulHi { rhs, .. } => {
@@ -1708,6 +1721,9 @@ fn x64_get_regs(inst: &Inst, collector: &mut RegUsageCollector) {
collector.add_def(*tmp); collector.add_def(*tmp);
} }
} }
Inst::SignExtendAlAh => {
collector.add_mod(Writable::from_reg(regs::rax()));
}
Inst::SignExtendRaxRdx { .. } => { Inst::SignExtendRaxRdx { .. } => {
collector.add_use(regs::rax()); collector.add_use(regs::rax());
collector.add_def(Writable::from_reg(regs::rdx())); collector.add_def(Writable::from_reg(regs::rdx()));
@@ -2012,7 +2028,7 @@ fn x64_map_regs<RUM: RegUsageMapper>(inst: &mut Inst, mapper: &RUM) {
map_def(mapper, tmp) map_def(mapper, tmp)
} }
} }
Inst::SignExtendRaxRdx { .. } => {} Inst::SignExtendAlAh | Inst::SignExtendRaxRdx { .. } => {}
Inst::XmmUnaryRmR { Inst::XmmUnaryRmR {
ref mut src, ref mut src,
ref mut dst, ref mut dst,

View File

@@ -2381,16 +2381,30 @@ fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
let divisor = input_to_reg_mem(ctx, inputs[1]); let divisor = input_to_reg_mem(ctx, inputs[1]);
// Fill in the high parts: // Fill in the high parts:
if kind.is_signed() { if input_ty == types::I8 {
// sign-extend the sign-bit of rax into rdx, for signed opcodes. if kind.is_signed() {
ctx.emit(Inst::sign_extend_rax_to_rdx(size)); // sign-extend the sign-bit of al into ah, for signed opcodes.
ctx.emit(Inst::sign_extend_al_to_ah());
} else {
ctx.emit(Inst::movzx_rm_r(
ExtMode::BL,
RegMem::reg(regs::rax()),
Writable::from_reg(regs::rax()),
/* infallible */ None,
));
}
} else { } else {
// zero for unsigned opcodes. if kind.is_signed() {
ctx.emit(Inst::imm_r( // sign-extend the sign-bit of rax into rdx, for signed opcodes.
true, /* is_64 */ ctx.emit(Inst::sign_extend_rax_to_rdx(size));
0, } else {
Writable::from_reg(regs::rdx()), // zero for unsigned opcodes.
)); ctx.emit(Inst::imm_r(
true, /* is_64 */
0,
Writable::from_reg(regs::rdx()),
));
}
} }
// Emit the actual idiv. // Emit the actual idiv.