machinst x64: lower and implement div/idiv; ADD TESTS

This commit is contained in:
Benjamin Bouvier
2020-06-26 15:36:49 +02:00
parent da30527368
commit f86ecdcb86
4 changed files with 341 additions and 4 deletions

View File

@@ -395,7 +395,7 @@ fn emit_simm(sink: &mut MachBuffer<Inst>, size: u8, simm32: u32) {
pub(crate) fn emit(
inst: &Inst,
sink: &mut MachBuffer<Inst>,
_flags: &settings::Flags,
flags: &settings::Flags,
state: &mut EmitState,
) {
match inst {
@@ -516,6 +516,128 @@ pub(crate) fn emit(
}
}
Inst::Div {
size,
signed,
divisor,
loc,
} => {
let (prefix, rex_flags) = match size {
2 => (LegacyPrefix::_66, RexFlags::clear_w()),
4 => (LegacyPrefix::None, RexFlags::clear_w()),
8 => (LegacyPrefix::None, RexFlags::set_w()),
_ => unreachable!(),
};
sink.add_trap(*loc, TrapCode::IntegerDivisionByZero);
let subopcode = if *signed { 7 } else { 6 };
match divisor {
RegMem::Reg { reg } => {
let src = int_reg_enc(*reg);
emit_std_enc_enc(sink, prefix, 0xF7, 1, subopcode, src, rex_flags)
}
RegMem::Mem { addr: src } => emit_std_enc_mem(
sink,
prefix,
0xF7,
1,
subopcode,
&src.finalize(state),
rex_flags,
),
}
}
Inst::SignExtendRaxRdx { size } => {
let (prefix, rex_flags) = match size {
2 => (LegacyPrefix::_66, RexFlags::clear_w()),
4 => (LegacyPrefix::None, RexFlags::clear_w()),
8 => (LegacyPrefix::None, RexFlags::set_w()),
_ => unreachable!(),
};
prefix.emit(sink);
rex_flags.emit_two_op(sink, 0, 0);
sink.put1(0x99);
}
Inst::SignedDivOrRem {
is_div,
size,
divisor,
loc,
} => {
debug_assert!(flags.avoid_div_traps());
// Check if the divisor is zero, first.
let inst = Inst::cmp_rmi_r(*size, RegMemImm::imm(0), *divisor);
inst.emit(sink, flags, state);
let inst = Inst::one_way_jmp(
CC::NZ,
BranchTarget::ResolvedOffset(Inst::size_of_trap() as isize),
);
inst.emit(sink, flags, state);
let inst = Inst::trap(*loc, TrapCode::IntegerDivisionByZero);
inst.emit(sink, flags, state);
// Now check if the divisor is -1.
let inst = Inst::cmp_rmi_r(*size, RegMemImm::imm(0xffffffff), *divisor);
inst.emit(sink, flags, state);
let do_op = sink.get_label();
// If not equal, jump to do-op.
let inst = Inst::one_way_jmp(CC::NZ, BranchTarget::Label(do_op));
inst.emit(sink, flags, state);
// Here, divisor == -1.
let done_label = if !*is_div {
// x % -1 = 0; put the result into the destination, $rdx.
let done_label = sink.get_label();
let inst = Inst::imm_r(*size == 8, 0, Writable::from_reg(regs::rdx()));
inst.emit(sink, flags, state);
let inst = Inst::jmp_known(BranchTarget::Label(done_label));
inst.emit(sink, flags, state);
Some(done_label)
} else {
// Check for integer overflow.
let inst = Inst::cmp_rmi_r(*size, RegMemImm::imm(0x80000000), regs::rax());
inst.emit(sink, flags, state);
// If not equal, jump over the trap.
let inst = Inst::one_way_jmp(
CC::NZ,
BranchTarget::ResolvedOffset(Inst::size_of_trap() as isize),
);
inst.emit(sink, flags, state);
let inst = Inst::trap(*loc, TrapCode::IntegerOverflow);
inst.emit(sink, flags, state);
None
};
sink.bind_label(do_op);
// Fill in the "high" parts: sign-extend the sign-bit of rax into rdx.
let inst = Inst::sign_extend_rax_to_rdx(*size);
inst.emit(sink, flags, state);
let inst = Inst::div(*size, true /*signed*/, RegMem::reg(*divisor), *loc);
inst.emit(sink, flags, state);
// The lowering takes care of moving the result back into the right register, see
// comment there.
if let Some(done) = done_label {
sink.bind_label(done);
}
}
Inst::Imm_R {
dst_is_64,
simm64,
@@ -1135,8 +1257,14 @@ pub(crate) fn emit(
Inst::Ud2 { trap_info } => {
sink.add_trap(trap_info.0, trap_info.1);
let cur_offset = sink.cur_offset();
sink.put1(0x0f);
sink.put1(0x0b);
assert_eq!(
sink.cur_offset() - cur_offset,
Inst::size_of_trap(),
"invalid trap size"
);
}
Inst::VirtualSPOffsetAdj { offset } => {