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:
Alex Crichton
2023-03-15 19:18:45 -05:00
committed by GitHub
parent 5ff2824ebb
commit 5ae8575296
72 changed files with 505 additions and 2624 deletions

View File

@@ -1446,10 +1446,6 @@
(decl vxrs_ext2_disabled () Type)
(extern extractor vxrs_ext2_disabled vxrs_ext2_disabled)
(decl allow_div_traps () Type)
(extern extractor allow_div_traps allow_div_traps)
;; Helpers for SIMD lane number operations ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; There are two ways to map vector types onto the SIMD vector registers

View File

@@ -536,7 +536,6 @@
(rule (lower (has_type (fits_in_64 ty) (udiv x y)))
(let (;; Look at the divisor to determine whether we need to generate
;; an explicit division-by zero check.
(DZcheck bool (zero_divisor_check_needed y))
;; Load up the dividend, by loading the input (possibly zero-
;; extended) input into the low half of the register pair,
;; and setting the high half to zero.
@@ -545,10 +544,6 @@
;; Load up the divisor, zero-extended if necessary.
(ext_y Reg (put_in_reg_zext32 y))
(ext_ty Type (ty_ext32 ty))
;; Now actually perform the division-by zero check if necessary.
;; This cannot be done earlier than here, because the check
;; requires an already extended divisor value.
(_ Reg (maybe_trap_if_zero_divisor DZcheck ext_ty ext_y))
;; Emit the actual divide instruction.
(pair RegPair (udivmod ext_ty ext_x ext_y)))
;; The quotient can be found in the low half of the result.
@@ -557,38 +552,13 @@
;; Implement `urem`. Same as `udiv`, but finds the remainder in
;; the high half of the result register pair instead.
(rule (lower (has_type (fits_in_64 ty) (urem x y)))
(let ((DZcheck bool (zero_divisor_check_needed y))
(ext_x RegPair (regpair (imm (ty_ext32 ty) 0)
(let ((ext_x RegPair (regpair (imm (ty_ext32 ty) 0)
(put_in_reg_zext32 x)))
(ext_y Reg (put_in_reg_zext32 y))
(ext_ty Type (ty_ext32 ty))
(_ Reg (maybe_trap_if_zero_divisor DZcheck ext_ty ext_y))
(pair RegPair (udivmod ext_ty ext_x ext_y)))
(copy_reg ty (regpair_hi pair))))
;; Determine whether we need to perform a divide-by-zero-check.
;;
;; If the `avoid_div_traps` flag is false, we never need to perform
;; that check; we can rely on the divide instruction itself to trap.
;;
;; If the `avoid_div_traps` flag is true, we perform the check explicitly.
;; This still can be omittted if the divisor is a non-zero immediate.
(decl zero_divisor_check_needed (Value) bool)
(rule 2 (zero_divisor_check_needed (i64_from_value x))
(if (i64_nonzero x))
$false)
(rule 1 (zero_divisor_check_needed (value_type (allow_div_traps))) $false)
(rule 0 (zero_divisor_check_needed _) $true)
;; Perform the divide-by-zero check if required.
;; This is simply a compare-and-trap of the (extended) divisor against 0.
(decl maybe_trap_if_zero_divisor (bool Type Reg) Reg)
(rule (maybe_trap_if_zero_divisor $false _ _) (invalid_reg))
(rule (maybe_trap_if_zero_divisor $true ext_ty reg)
(icmps_simm16_and_trap ext_ty reg 0
(intcc_as_cond (IntCC.Equal))
(trap_code_division_by_zero)))
;;;; Rules for `sdiv` and `srem` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -610,15 +580,12 @@
(rule (lower (has_type (fits_in_64 ty) (sdiv x y)))
(let (;; Look at the divisor to determine whether we need to generate
;; explicit division-by-zero and/or integer-overflow checks.
(DZcheck bool (zero_divisor_check_needed y))
(OFcheck bool (div_overflow_check_needed y))
;; Load up the dividend (sign-extended to 64-bit)
(ext_x Reg (put_in_reg_sext64 x))
;; Load up the divisor (sign-extended if necessary).
(ext_y Reg (put_in_reg_sext32 y))
(ext_ty Type (ty_ext32 ty))
;; Perform division-by-zero check (same as for `udiv`).
(_ Reg (maybe_trap_if_zero_divisor DZcheck ext_ty ext_y))
;; Perform integer-overflow check if necessary.
(_ Reg (maybe_trap_if_sdiv_overflow OFcheck ext_ty ty ext_x ext_y))
;; Emit the actual divide instruction.
@@ -630,12 +597,10 @@
;; the high half of the result register pair instead. Also, handle
;; the integer overflow case differently, see below.
(rule (lower (has_type (fits_in_64 ty) (srem x y)))
(let ((DZcheck bool (zero_divisor_check_needed y))
(OFcheck bool (div_overflow_check_needed y))
(let ((OFcheck bool (div_overflow_check_needed y))
(ext_x Reg (put_in_reg_sext64 x))
(ext_y Reg (put_in_reg_sext32 y))
(ext_ty Type (ty_ext32 ty))
(_ Reg (maybe_trap_if_zero_divisor DZcheck ext_ty ext_y))
(checked_x Reg (maybe_avoid_srem_overflow OFcheck ext_ty ext_x ext_y))
(pair RegPair (sdivmod ext_ty checked_x ext_y)))
(copy_reg ty (regpair_hi pair))))

View File

@@ -291,15 +291,6 @@ impl generated_code::Context for IsleContext<'_, '_, MInst, S390xBackend> {
Box::new(symbol_reloc.clone())
}
#[inline]
fn allow_div_traps(&mut self, _: Type) -> Option<()> {
if !self.backend.flags.avoid_div_traps() {
Some(())
} else {
None
}
}
#[inline]
fn mie2_enabled(&mut self, _: Type) -> Option<()> {
if self.backend.isa_flags.has_mie2() {