x64: Migrate {s,u}{div,rem} to ISLE (#6008)
* x64: Add precise-output tests for div traps
This adds a suite of `*.clif` files which are intended to test the
`avoid_div_traps=true` compilation of the `{s,u}{div,rem}` instructions.
* x64: Remove conditional regalloc in `Div` instruction
Move the 8-bit `Div` logic into a dedicated `Div8` instruction to avoid
having conditionally-used registers with respect to regalloc.
* x64: Migrate non-trapping, `udiv`/`urem` to ISLE
* x64: Port checked `udiv` to ISLE
* x64: Migrate urem entirely to ISLE
* x64: Use `test` instead of `cmp` to compare-to-zero
* x64: Port `sdiv` lowering to ISLE
* x64: Port `srem` lowering to ISLE
* Tidy up regalloc behavior and fix tests
* Update docs and winch
* Review comments
* Reword again
* More refactoring test fixes
* More test fixes
This commit is contained in:
@@ -58,14 +58,23 @@
|
||||
(dst WritableGpr))
|
||||
|
||||
;; Integer quotient and remainder: (div idiv) $rax $rdx (reg addr)
|
||||
(Div (size OperandSize) ;; 1, 2, 4, or 8
|
||||
(signed bool)
|
||||
;;
|
||||
;; Note that this isn't used for 8-bit division which has its own `Div8`
|
||||
;; instruction.
|
||||
(Div (size OperandSize) ;; 2, 4, or 8
|
||||
(sign DivSignedness)
|
||||
(divisor GprMem)
|
||||
(dividend_lo Gpr)
|
||||
(dividend_hi Gpr)
|
||||
(dst_quotient WritableGpr)
|
||||
(dst_remainder WritableGpr))
|
||||
|
||||
;; Same as `Div`, but for 8-bits where the regalloc behavior is different
|
||||
(Div8 (sign DivSignedness)
|
||||
(divisor GprMem)
|
||||
(dividend Gpr)
|
||||
(dst WritableGpr))
|
||||
|
||||
;; The high (and low) bits of a (un)signed multiply: `RDX:RAX := RAX *
|
||||
;; rhs`.
|
||||
(MulHi (size OperandSize)
|
||||
@@ -75,19 +84,47 @@
|
||||
(dst_lo WritableGpr)
|
||||
(dst_hi WritableGpr))
|
||||
|
||||
;; A synthetic sequence to implement the right inline checks for
|
||||
;; remainder and division, assuming the dividend is in %rax.
|
||||
;; A synthetic instruction sequence used as part of the lowering of the
|
||||
;; `srem` instruction which returns 0 if the divisor is -1 and
|
||||
;; otherwise executes an `idiv` instruction.
|
||||
;;
|
||||
;; The generated code sequence is described in the emit's function match
|
||||
;; arm for this instruction.
|
||||
(CheckedDivOrRemSeq (kind DivOrRemKind)
|
||||
(size OperandSize)
|
||||
(dividend_lo Gpr)
|
||||
(dividend_hi Gpr)
|
||||
(divisor Gpr)
|
||||
(dst_quotient WritableGpr)
|
||||
(dst_remainder WritableGpr)
|
||||
(tmp OptionWritableGpr))
|
||||
;; Note that this does not check for 0 as that's expected to be done
|
||||
;; separately. Also note that 8-bit types don't use this and use
|
||||
;; `CheckedSRemSeq8` instead.
|
||||
(CheckedSRemSeq (size OperandSize)
|
||||
(dividend_lo Gpr)
|
||||
(dividend_hi Gpr)
|
||||
(divisor Gpr)
|
||||
(dst_quotient WritableGpr)
|
||||
(dst_remainder WritableGpr))
|
||||
|
||||
;; Same as above but for 8-bit types.
|
||||
(CheckedSRemSeq8 (dividend Gpr)
|
||||
(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)
|
||||
@@ -628,6 +665,10 @@
|
||||
Size32
|
||||
Size64))
|
||||
|
||||
(type DivSignedness
|
||||
(enum Signed
|
||||
Unsigned))
|
||||
|
||||
(type FenceKind extern
|
||||
(enum MFence
|
||||
LFence
|
||||
@@ -690,12 +731,6 @@
|
||||
Tzcnt
|
||||
Popcnt))
|
||||
|
||||
(type DivOrRemKind extern
|
||||
(enum SignedDiv
|
||||
UnsignedDiv
|
||||
SignedRem
|
||||
UnsignedRem))
|
||||
|
||||
(type SseOpcode extern
|
||||
(enum Addps
|
||||
Addpd
|
||||
@@ -4521,15 +4556,70 @@
|
||||
|
||||
;;;; Division/Remainders ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(decl emit_div_or_rem (DivOrRemKind Type WritableGpr Gpr Gpr) Unit)
|
||||
(extern constructor emit_div_or_rem emit_div_or_rem)
|
||||
;; Helper for creating `CheckedSRemSeq` instructions.
|
||||
(decl x64_checked_srem_seq (OperandSize Gpr Gpr Gpr) ValueRegs)
|
||||
(rule (x64_checked_srem_seq size dividend_lo dividend_hi divisor)
|
||||
(let ((dst_quotient WritableGpr (temp_writable_gpr))
|
||||
(dst_remainder WritableGpr (temp_writable_gpr))
|
||||
(_ Unit (emit (MInst.CheckedSRemSeq size dividend_lo dividend_hi divisor dst_quotient dst_remainder))))
|
||||
(value_regs dst_quotient dst_remainder)))
|
||||
|
||||
(decl div_or_rem (DivOrRemKind Value Value) Gpr)
|
||||
(rule (div_or_rem kind a @ (value_type ty) b)
|
||||
(decl x64_checked_srem_seq8 (Gpr Gpr) Gpr)
|
||||
(rule (x64_checked_srem_seq8 dividend divisor)
|
||||
(let ((dst WritableGpr (temp_writable_gpr))
|
||||
(_ Unit (emit_div_or_rem kind ty dst a b)))
|
||||
(_ Unit (emit (MInst.CheckedSRemSeq8 dividend divisor dst))))
|
||||
dst))
|
||||
|
||||
;; Helper for creating `Div8` instructions
|
||||
(decl x64_div8 (Gpr GprMem DivSignedness) Gpr)
|
||||
(rule (x64_div8 dividend divisor sign)
|
||||
(let ((dst WritableGpr (temp_writable_gpr))
|
||||
(_ Unit (emit (MInst.Div8 sign 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)
|
||||
(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))))
|
||||
(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))
|
||||
|
||||
;; 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))
|
||||
|
||||
;; Helper for creating `SignExtendData` instructions
|
||||
(decl x64_sign_extend_data (Gpr OperandSize) Gpr)
|
||||
(rule (x64_sign_extend_data src size)
|
||||
(let ((dst WritableGpr (temp_writable_gpr))
|
||||
(_ 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)
|
||||
|
||||
Reference in New Issue
Block a user