x64: Take SIGFPE signals for divide traps (#6026)
* x64: Take SIGFPE signals for divide traps Prior to this commit Wasmtime would configure `avoid_div_traps=true` unconditionally for Cranelift. This, for the division-based instructions, would change emitted code to explicitly trap on trap conditions instead of letting the `div` x86 instruction trap. There's no specific reason for Wasmtime, however, to specifically avoid traps in the `div` instruction. This means that the extra generated branches on x86 aren't necessary since the `div` and `idiv` instructions already trap for similar conditions as wasm requires. This commit instead disables the `avoid_div_traps` setting for Wasmtime's usage of Cranelift. Subsequently the codegen rules were updated slightly: * When `avoid_div_traps=true`, traps are no longer emitted for `div` instructions. * The `udiv`/`urem` instructions now list their trap as divide-by-zero instead of integer overflow. * The lowering for `sdiv` was updated to still explicitly check for zero but the integer overflow case is deferred to the instruction itself. * The lowering of `srem` no longer checks for zero and the listed trap for the `div` instruction is a divide-by-zero. This means that the codegen for `udiv` and `urem` no longer have any branches. The codegen for `sdiv` removes one branch but keeps the zero-check to differentiate the two kinds of traps. The codegen for `srem` removes one branch but keeps the -1 check since the semantics of `srem` mismatch with the semantics of `idiv` with a -1 divisor (specifically for INT_MIN). This is unlikely to have really all that much of a speedup but was something I noticed during #6008 which seemed like it'd be good to clean up. Plus Wasmtime's signal handling was already set up to catch `SIGFPE`, it was just never firing. * Remove the `avoid_div_traps` cranelift setting With no known users currently removing this should be possible and helps simplify the x64 backend. * x64: GC more support for avoid_div_traps Remove the `validate_sdiv_divisor*` pseudo-instructions and clean up some of the ISLE rules now that `div` is allowed to itself trap unconditionally. * x64: Store div trap code in instruction itself * Keep divisors in registers, not in memory Don't accidentally fold multiple traps together * Handle EXC_ARITHMETIC on macos * Update emit tests * Update winch and tests
This commit is contained in:
@@ -400,7 +400,18 @@ pub(crate) fn emit(
|
||||
emit_std_enc_enc(sink, prefix, opcode, 1, subopcode, enc_src, rex_flags)
|
||||
}
|
||||
|
||||
Inst::Div { sign, divisor, .. } | Inst::Div8 { sign, divisor, .. } => {
|
||||
Inst::Div {
|
||||
sign,
|
||||
trap,
|
||||
divisor,
|
||||
..
|
||||
}
|
||||
| Inst::Div8 {
|
||||
sign,
|
||||
trap,
|
||||
divisor,
|
||||
..
|
||||
} => {
|
||||
let divisor = divisor.clone().to_reg_mem().with_allocs(allocs);
|
||||
let size = match inst {
|
||||
Inst::Div {
|
||||
@@ -438,7 +449,7 @@ pub(crate) fn emit(
|
||||
OperandSize::Size64 => (0xF7, LegacyPrefixes::None),
|
||||
};
|
||||
|
||||
sink.add_trap(TrapCode::IntegerDivisionByZero);
|
||||
sink.add_trap(*trap);
|
||||
|
||||
let subopcode = match sign {
|
||||
DivSignedness::Signed => 7,
|
||||
@@ -613,6 +624,7 @@ pub(crate) fn emit(
|
||||
let inst = match size {
|
||||
OperandSize::Size8 => Inst::div8(
|
||||
DivSignedness::Signed,
|
||||
TrapCode::IntegerDivisionByZero,
|
||||
RegMem::reg(divisor),
|
||||
Gpr::new(regs::rax()).unwrap(),
|
||||
Writable::from_reg(Gpr::new(regs::rax()).unwrap()),
|
||||
@@ -620,6 +632,7 @@ pub(crate) fn emit(
|
||||
_ => Inst::div(
|
||||
size,
|
||||
DivSignedness::Signed,
|
||||
TrapCode::IntegerDivisionByZero,
|
||||
RegMem::reg(divisor),
|
||||
Gpr::new(regs::rax()).unwrap(),
|
||||
Gpr::new(regs::rdx()).unwrap(),
|
||||
@@ -632,55 +645,6 @@ pub(crate) fn emit(
|
||||
sink.bind_label(done_label);
|
||||
}
|
||||
|
||||
Inst::ValidateSdivDivisor {
|
||||
dividend, divisor, ..
|
||||
}
|
||||
| Inst::ValidateSdivDivisor64 {
|
||||
dividend, divisor, ..
|
||||
} => {
|
||||
let orig_inst = &inst;
|
||||
let divisor = allocs.next(divisor.to_reg());
|
||||
let dividend = allocs.next(dividend.to_reg());
|
||||
let size = match inst {
|
||||
Inst::ValidateSdivDivisor { size, .. } => *size,
|
||||
_ => OperandSize::Size64,
|
||||
};
|
||||
|
||||
// First trap if the divisor is zero
|
||||
let inst = Inst::cmp_rmi_r(size, RegMemImm::imm(0), divisor);
|
||||
inst.emit(&[], sink, info, state);
|
||||
let inst = Inst::trap_if(CC::Z, TrapCode::IntegerDivisionByZero);
|
||||
inst.emit(&[], sink, info, state);
|
||||
|
||||
// Now check if the divisor is -1. If it is then additionally
|
||||
// check if the dividend is INT_MIN. If it isn't then jump to the
|
||||
// end. If both conditions here are true then trap.
|
||||
let inst = Inst::cmp_rmi_r(size, RegMemImm::imm(0xffffffff), divisor);
|
||||
inst.emit(&[], sink, info, state);
|
||||
let done = sink.get_label();
|
||||
one_way_jmp(sink, CC::NZ, done);
|
||||
let int_min = match orig_inst {
|
||||
Inst::ValidateSdivDivisor64 { tmp, .. } => {
|
||||
let tmp = allocs.next(tmp.to_reg().to_reg());
|
||||
let inst = Inst::imm(size, i64::MIN as u64, Writable::from_reg(tmp));
|
||||
inst.emit(&[], sink, info, state);
|
||||
RegMemImm::reg(tmp)
|
||||
}
|
||||
_ => RegMemImm::imm(match size {
|
||||
OperandSize::Size8 => 0x80,
|
||||
OperandSize::Size16 => 0x8000,
|
||||
OperandSize::Size32 => 0x80000000,
|
||||
OperandSize::Size64 => unreachable!(),
|
||||
}),
|
||||
};
|
||||
let inst = Inst::cmp_rmi_r(size, int_min, dividend);
|
||||
inst.emit(&[], sink, info, state);
|
||||
let inst = Inst::trap_if(CC::Z, TrapCode::IntegerOverflow);
|
||||
inst.emit(&[], sink, info, state);
|
||||
|
||||
sink.bind_label(done);
|
||||
}
|
||||
|
||||
Inst::Imm {
|
||||
dst_size,
|
||||
simm64,
|
||||
|
||||
Reference in New Issue
Block a user