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

@@ -63,6 +63,7 @@
;; instruction.
(Div (size OperandSize) ;; 2, 4, or 8
(sign DivSignedness)
(trap TrapCode)
(divisor GprMem)
(dividend_lo Gpr)
(dividend_hi Gpr)
@@ -71,6 +72,7 @@
;; Same as `Div`, but for 8-bits where the regalloc behavior is different
(Div8 (sign DivSignedness)
(trap TrapCode)
(divisor GprMem)
(dividend Gpr)
(dst WritableGpr))
@@ -103,29 +105,6 @@
(divisor Gpr)
(dst WritableGpr))
;; Validates that the `divisor` can be safely divided into the
;; `dividend`.
;;
;; This is a separate pseudo-instruction because it has some jumps in
;; ways that can't be modeled otherwise with instructions right now. This
;; will trap if the `divisor` is zero or if it's -1 and `dividend` is
;; INT_MIN for the associated type.
;;
;; Note that 64-bit types must use `ValidateSdivDivisor64`.
(ValidateSdivDivisor (size OperandSize)
(dividend Gpr)
(divisor Gpr))
;; Same as `ValidateSdivDivisor` but for 64-bit types.
;;
;; This is a distinct instruction because the emission in `emit.rs`
;; requires a temporary register to load an immediate into, hence the
;; `tmp` field in this instruction not present in the non-64-bit one.
(ValidateSdivDivisor64 (dividend Gpr)
(divisor Gpr)
(tmp WritableGpr))
;; Do a sign-extend based on the sign of the value in rax into rdx: (cwd
;; cdq cqo) or al into ah: (cbw)
(SignExtendData (size OperandSize) ;; 1, 2, 4, or 8
@@ -4506,32 +4485,32 @@
dst))
;; Helper for creating `Div8` instructions
(decl x64_div8 (Gpr GprMem DivSignedness) Gpr)
(rule (x64_div8 dividend divisor sign)
(decl x64_div8 (Gpr GprMem DivSignedness TrapCode) Gpr)
(rule (x64_div8 dividend divisor sign trap)
(let ((dst WritableGpr (temp_writable_gpr))
(_ Unit (emit (MInst.Div8 sign divisor dividend dst))))
(_ Unit (emit (MInst.Div8 sign trap divisor dividend dst))))
dst))
;; Helper for creating `Div` instructions
;;
;; Two registers are returned through `ValueRegs` where the first is the
;; quotient and the second is the remainder.
(decl x64_div (Gpr Gpr GprMem OperandSize DivSignedness) ValueRegs)
(rule (x64_div dividend_lo dividend_hi divisor size sign)
(decl x64_div (Gpr Gpr GprMem OperandSize DivSignedness TrapCode) ValueRegs)
(rule (x64_div dividend_lo dividend_hi divisor size sign trap)
(let ((dst_quotient WritableGpr (temp_writable_gpr))
(dst_remainder WritableGpr (temp_writable_gpr))
(_ Unit (emit (MInst.Div size sign divisor dividend_lo dividend_hi dst_quotient dst_remainder))))
(_ Unit (emit (MInst.Div size sign trap divisor dividend_lo dividend_hi dst_quotient dst_remainder))))
(value_regs dst_quotient dst_remainder)))
;; Helper for `Div`, returning the quotient and discarding the remainder.
(decl x64_div_quotient (Gpr Gpr GprMem OperandSize DivSignedness) ValueRegs)
(rule (x64_div_quotient dividend_lo dividend_hi divisor size sign)
(value_regs_get (x64_div dividend_lo dividend_hi divisor size sign) 0))
(decl x64_div_quotient (Gpr Gpr GprMem OperandSize DivSignedness TrapCode) ValueRegs)
(rule (x64_div_quotient dividend_lo dividend_hi divisor size sign trap)
(value_regs_get (x64_div dividend_lo dividend_hi divisor size sign trap) 0))
;; Helper for `Div`, returning the remainder and discarding the quotient.
(decl x64_div_remainder (Gpr Gpr GprMem OperandSize DivSignedness) ValueRegs)
(rule (x64_div_remainder dividend_lo dividend_hi divisor size sign)
(value_regs_get (x64_div dividend_lo dividend_hi divisor size sign) 1))
(decl x64_div_remainder (Gpr Gpr GprMem OperandSize DivSignedness TrapCode) ValueRegs)
(rule (x64_div_remainder dividend_lo dividend_hi divisor size sign trap)
(value_regs_get (x64_div dividend_lo dividend_hi divisor size sign trap) 1))
;; Helper for creating `SignExtendData` instructions
(decl x64_sign_extend_data (Gpr OperandSize) Gpr)
@@ -4540,21 +4519,6 @@
(_ Unit (emit (MInst.SignExtendData size src dst))))
dst))
;; Helper for creating `ValidateSdivDivisor` instructions.
(decl validate_sdiv_divisor (OperandSize Gpr Gpr) Gpr)
(rule (validate_sdiv_divisor size dividend divisor)
(let ((_ Unit (emit (MInst.ValidateSdivDivisor size dividend divisor))))
divisor))
;; Helper for creating `ValidateSdivDivisor64` instructions.
(decl validate_sdiv_divisor64 (Gpr Gpr) Gpr)
(rule (validate_sdiv_divisor64 dividend divisor)
(let (
(tmp WritableGpr (temp_writable_gpr))
(_ Unit (emit (MInst.ValidateSdivDivisor64 dividend divisor tmp)))
)
divisor))
;;;; Pinned Register ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(decl read_pinned_gpr () Gpr)