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:
@@ -282,34 +282,13 @@ impl Assembler {
|
||||
/// caller has correctly allocated the dividend as `(rdx:rax)` and
|
||||
/// accounted for the quotient to be stored in `rax`.
|
||||
pub fn div(&mut self, divisor: Reg, dst: (Reg, Reg), kind: DivKind, size: OperandSize) {
|
||||
match kind {
|
||||
// Signed division goes through a pseudo-instruction to validate
|
||||
// the divisor followed by a sign extension to initialize `rdx`.
|
||||
let trap = match kind {
|
||||
// Signed division has two trapping conditions, integer overflow and
|
||||
// divide-by-zero. Check for divide-by-zero explicitly and let the
|
||||
// hardware detect overflow.
|
||||
//
|
||||
// The dividend is sign extended to initialize `rdx`.
|
||||
DivKind::Signed => {
|
||||
if size == OperandSize::S64 {
|
||||
self.emit(Inst::ValidateSdivDivisor64 {
|
||||
dividend: dst.0.into(),
|
||||
divisor: divisor.into(),
|
||||
tmp: regs::scratch().into(),
|
||||
});
|
||||
} else {
|
||||
self.emit(Inst::ValidateSdivDivisor {
|
||||
dividend: dst.0.into(),
|
||||
divisor: divisor.into(),
|
||||
size: size.into(),
|
||||
});
|
||||
}
|
||||
self.emit(Inst::SignExtendData {
|
||||
size: size.into(),
|
||||
src: dst.0.into(),
|
||||
dst: dst.1.into(),
|
||||
});
|
||||
}
|
||||
|
||||
// Unsigned division only needs to check for 0 and then the `rdx`
|
||||
// divisor_hi is initialized with zero through an xor-against-itself
|
||||
// op.
|
||||
DivKind::Unsigned => {
|
||||
self.emit(Inst::CmpRmiR {
|
||||
size: size.into(),
|
||||
src: GprMemImm::new(RegMemImm::imm(0)).unwrap(),
|
||||
@@ -320,6 +299,20 @@ impl Assembler {
|
||||
cc: CC::Z,
|
||||
trap_code: TrapCode::IntegerDivisionByZero,
|
||||
});
|
||||
self.emit(Inst::SignExtendData {
|
||||
size: size.into(),
|
||||
src: dst.0.into(),
|
||||
dst: dst.1.into(),
|
||||
});
|
||||
TrapCode::IntegerOverflow
|
||||
}
|
||||
|
||||
// Unsigned division only traps in one case, on divide-by-zero, so
|
||||
// defer that to the trap opcode.
|
||||
//
|
||||
// The divisor_hi reg is initialized with zero through an
|
||||
// xor-against-itself op.
|
||||
DivKind::Unsigned => {
|
||||
self.emit(Inst::AluRmiR {
|
||||
size: size.into(),
|
||||
op: AluRmiROpcode::Xor,
|
||||
@@ -327,11 +320,13 @@ impl Assembler {
|
||||
src2: dst.1.into(),
|
||||
dst: dst.1.into(),
|
||||
});
|
||||
TrapCode::IntegerDivisionByZero
|
||||
}
|
||||
}
|
||||
};
|
||||
self.emit(Inst::Div {
|
||||
sign: kind.into(),
|
||||
size: size.into(),
|
||||
trap,
|
||||
divisor: GprMem::new(RegMem::reg(divisor.into())).unwrap(),
|
||||
dividend_lo: dst.0.into(),
|
||||
dividend_hi: dst.1.into(),
|
||||
@@ -348,17 +343,6 @@ impl Assembler {
|
||||
/// caller has correctly allocated the dividend as `(rdx:rax)` and
|
||||
/// accounted for the remainder to be stored in `rdx`.
|
||||
pub fn rem(&mut self, divisor: Reg, dst: (Reg, Reg), kind: RemKind, size: OperandSize) {
|
||||
// First check for zero and explicitly trap.
|
||||
self.emit(Inst::CmpRmiR {
|
||||
size: size.into(),
|
||||
src: GprMemImm::new(RegMemImm::imm(0)).unwrap(),
|
||||
dst: divisor.into(),
|
||||
opcode: CmpOpcode::Cmp,
|
||||
});
|
||||
self.emit(Inst::TrapIf {
|
||||
cc: CC::Z,
|
||||
trap_code: TrapCode::IntegerDivisionByZero,
|
||||
});
|
||||
match kind {
|
||||
// Signed remainder goes through a pseudo-instruction which has
|
||||
// some internal branching. The `dividend_hi`, or `rdx`, is
|
||||
@@ -391,6 +375,7 @@ impl Assembler {
|
||||
});
|
||||
self.emit(Inst::Div {
|
||||
sign: DivSignedness::Unsigned,
|
||||
trap: TrapCode::IntegerDivisionByZero,
|
||||
size: size.into(),
|
||||
divisor: GprMem::new(RegMem::reg(divisor.into())).unwrap(),
|
||||
dividend_lo: dst.0.into(),
|
||||
|
||||
Reference in New Issue
Block a user