cranelift: simplify icmp against UMAX/SMIN/SMAX (#6037)
* cranelift: simplify `icmp` against UMAX/SMIN/SMAX * Add tests for icmp against numeric limits
This commit is contained in:
@@ -217,6 +217,36 @@ macro_rules! isle_common_prelude_methods {
|
||||
u64::MAX >> shift
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn ty_umin(&mut self, _ty: Type) -> u64 {
|
||||
0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn ty_umax(&mut self, ty: Type) -> u64 {
|
||||
self.ty_mask(ty)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn ty_smin(&mut self, ty: Type) -> u64 {
|
||||
let ty_bits = ty.bits();
|
||||
debug_assert_ne!(ty_bits, 0);
|
||||
let shift = 64_u64
|
||||
.checked_sub(ty_bits.into())
|
||||
.expect("unimplemented for > 64 bits");
|
||||
(i64::MIN as u64) >> shift
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn ty_smax(&mut self, ty: Type) -> u64 {
|
||||
let ty_bits = ty.bits();
|
||||
debug_assert_ne!(ty_bits, 0);
|
||||
let shift = 64_u64
|
||||
.checked_sub(ty_bits.into())
|
||||
.expect("unimplemented for > 64 bits");
|
||||
(i64::MAX as u64) >> shift
|
||||
}
|
||||
|
||||
fn fits_in_16(&mut self, ty: Type) -> Option<Type> {
|
||||
if ty.bits() <= 16 && !ty.is_dynamic_vector() {
|
||||
Some(ty)
|
||||
|
||||
@@ -377,20 +377,97 @@
|
||||
(iconst _ (u64_from_imm64 1))))
|
||||
extend)
|
||||
|
||||
;; `x < 0` is always false for unsigned integers, and `x >= 0` is always true
|
||||
;; for unsigned integers, along with their reversals.
|
||||
;; ult(x, 0) == false.
|
||||
(rule (simplify
|
||||
(icmp (fits_in_64 (ty_int ty))
|
||||
(IntCC.UnsignedLessThan)
|
||||
_
|
||||
(iconst _ (u64_from_imm64 0))))
|
||||
(iconst ty (imm64 0)))
|
||||
(icmp (fits_in_64 (ty_int bty)) (IntCC.UnsignedLessThan) x zero @ (iconst _ (u64_from_imm64 0))))
|
||||
(subsume (iconst bty (imm64 0))))
|
||||
|
||||
;; ule(x, 0) == eq(x, 0)
|
||||
(rule (simplify
|
||||
(icmp (fits_in_64 (ty_int ty))
|
||||
(IntCC.UnsignedGreaterThanOrEqual)
|
||||
_
|
||||
(iconst _ (u64_from_imm64 0))))
|
||||
(iconst ty (imm64 1)))
|
||||
(icmp (fits_in_64 (ty_int bty)) (IntCC.UnsignedLessThanOrEqual) x zero @ (iconst _ (u64_from_imm64 0))))
|
||||
(icmp bty (IntCC.Equal) x zero))
|
||||
|
||||
;; ugt(x, 0) == ne(x, 0).
|
||||
(rule (simplify
|
||||
(icmp (fits_in_64 (ty_int bty)) (IntCC.UnsignedGreaterThan) x zero @ (iconst _ (u64_from_imm64 0))))
|
||||
(icmp bty (IntCC.NotEqual) x zero))
|
||||
|
||||
;; uge(x, 0) == true.
|
||||
(rule (simplify
|
||||
(icmp (fits_in_64 (ty_int bty)) (IntCC.UnsignedGreaterThanOrEqual) x zero @ (iconst _ (u64_from_imm64 0))))
|
||||
(subsume (iconst bty (imm64 1))))
|
||||
|
||||
;; ult(x, UMAX) == ne(x, UMAX).
|
||||
(rule (simplify
|
||||
(icmp (fits_in_64 (ty_int bty)) (IntCC.UnsignedLessThan) x umax @ (iconst cty (u64_from_imm64 y))))
|
||||
(if-let $true (u64_eq y (ty_umax cty)))
|
||||
(icmp bty (IntCC.NotEqual) x umax))
|
||||
|
||||
;; ule(x, UMAX) == true.
|
||||
(rule (simplify
|
||||
(icmp (fits_in_64 (ty_int bty)) (IntCC.UnsignedLessThanOrEqual) x umax @ (iconst cty (u64_from_imm64 y))))
|
||||
(if-let $true (u64_eq y (ty_umax cty)))
|
||||
(subsume (iconst bty (imm64 1))))
|
||||
|
||||
;; ugt(x, UMAX) == false.
|
||||
(rule (simplify
|
||||
(icmp (fits_in_64 (ty_int bty)) (IntCC.UnsignedGreaterThan) x umax @ (iconst cty (u64_from_imm64 y))))
|
||||
(if-let $true (u64_eq y (ty_umax cty)))
|
||||
(subsume (iconst bty (imm64 0))))
|
||||
|
||||
;; uge(x, UMAX) == eq(x, UMAX).
|
||||
(rule (simplify
|
||||
(icmp (fits_in_64 (ty_int bty)) (IntCC.UnsignedGreaterThanOrEqual) x umax @ (iconst cty (u64_from_imm64 y))))
|
||||
(if-let $true (u64_eq y (ty_umax cty)))
|
||||
(icmp bty (IntCC.Equal) x umax))
|
||||
|
||||
;; slt(x, SMIN) == false.
|
||||
(rule (simplify
|
||||
(icmp (fits_in_64 (ty_int bty)) (IntCC.SignedLessThan) x smin @ (iconst cty (u64_from_imm64 y))))
|
||||
(if-let $true (u64_eq y (ty_smin cty)))
|
||||
(subsume (iconst bty (imm64 0))))
|
||||
|
||||
;; sle(x, SMIN) == eq(x, SMIN).
|
||||
(rule (simplify
|
||||
(icmp (fits_in_64 (ty_int bty)) (IntCC.SignedLessThanOrEqual) x smin @ (iconst cty (u64_from_imm64 y))))
|
||||
(if-let $true (u64_eq y (ty_smin cty)))
|
||||
(icmp bty (IntCC.Equal) x smin))
|
||||
|
||||
;; sgt(x, SMIN) == ne(x, SMIN).
|
||||
(rule (simplify
|
||||
(icmp (fits_in_64 (ty_int bty)) (IntCC.SignedGreaterThan) x smin @ (iconst cty (u64_from_imm64 y))))
|
||||
(if-let $true (u64_eq y (ty_smin cty)))
|
||||
(icmp bty (IntCC.NotEqual) x smin))
|
||||
|
||||
;; sge(x, SMIN) == true.
|
||||
(rule (simplify
|
||||
(icmp (fits_in_64 (ty_int bty)) (IntCC.SignedGreaterThanOrEqual) x smin @ (iconst cty (u64_from_imm64 y))))
|
||||
(if-let $true (u64_eq y (ty_smin cty)))
|
||||
(subsume (iconst bty (imm64 1))))
|
||||
|
||||
;; slt(x, SMAX) == ne(x, SMAX).
|
||||
(rule (simplify
|
||||
(icmp (fits_in_64 (ty_int bty)) (IntCC.SignedLessThan) x smax @ (iconst cty (u64_from_imm64 y))))
|
||||
(if-let $true (u64_eq y (ty_smax cty)))
|
||||
(icmp bty (IntCC.NotEqual) x smax))
|
||||
|
||||
;; sle(x, SMAX) == true.
|
||||
(rule (simplify
|
||||
(icmp (fits_in_64 (ty_int bty)) (IntCC.SignedLessThanOrEqual) x smax @ (iconst cty (u64_from_imm64 y))))
|
||||
(if-let $true (u64_eq y (ty_smax cty)))
|
||||
(subsume (iconst bty (imm64 1))))
|
||||
|
||||
;; sgt(x, SMAX) == false.
|
||||
(rule (simplify
|
||||
(icmp (fits_in_64 (ty_int bty)) (IntCC.SignedGreaterThan) x smax @ (iconst cty (u64_from_imm64 y))))
|
||||
(if-let $true (u64_eq y (ty_smax cty)))
|
||||
(subsume (iconst bty (imm64 0))))
|
||||
|
||||
;; sge(x, SMAX) == eq(x, SMAX).
|
||||
(rule (simplify
|
||||
(icmp (fits_in_64 (ty_int bty)) (IntCC.SignedGreaterThanOrEqual) x smax @ (iconst cty (u64_from_imm64 y))))
|
||||
(if-let $true (u64_eq y (ty_smax cty)))
|
||||
(icmp bty (IntCC.Equal) x smax))
|
||||
|
||||
;; 32-bit integers zero-extended to 64-bit integers are never negative
|
||||
(rule (simplify
|
||||
|
||||
@@ -208,6 +208,23 @@
|
||||
|
||||
(extern const $I32X4XN Type)
|
||||
|
||||
;; Get the unsigned minimum value for a given type.
|
||||
;; This always zero, but is included for completeness.
|
||||
(decl pure ty_umin (Type) u64)
|
||||
(extern constructor ty_umin ty_umin)
|
||||
|
||||
;; Get the unsigned maximum value for a given type.
|
||||
(decl pure ty_umax (Type) u64)
|
||||
(extern constructor ty_umax ty_umax)
|
||||
|
||||
;; Get the signed minimum value for a given type.
|
||||
(decl pure ty_smin (Type) u64)
|
||||
(extern constructor ty_smin ty_smin)
|
||||
|
||||
;; Get the signed maximum value for a given type.
|
||||
(decl pure ty_smax (Type) u64)
|
||||
(extern constructor ty_smax ty_smax)
|
||||
|
||||
;; Get the bit width of a given type.
|
||||
(decl pure ty_bits (Type) u8)
|
||||
(extern constructor ty_bits ty_bits)
|
||||
|
||||
@@ -418,45 +418,149 @@ block0(v1: i64, v2: i64):
|
||||
; check: v4 = uextend.i64 v3
|
||||
; check: return v4
|
||||
|
||||
function %ult_zero_always_false(i64) -> i8 {
|
||||
block0(v1: i64):
|
||||
v2 = iconst.i64 0
|
||||
v3 = icmp ult v1, v2
|
||||
return v3
|
||||
function %icmp_ult_0(i32) -> i8 {
|
||||
block0(v0: i32):
|
||||
v1 = iconst.i32 0
|
||||
v2 = icmp ult v0, v1
|
||||
return v2
|
||||
; check: v3 = iconst.i8 0
|
||||
; check: return v3
|
||||
}
|
||||
|
||||
; check: v4 = iconst.i8 0
|
||||
; check: return v4
|
||||
|
||||
function %ugt_zero_always_false(i64) -> i8 {
|
||||
block0(v1: i64):
|
||||
v2 = iconst.i64 0
|
||||
v3 = icmp ugt v2, v1
|
||||
return v3
|
||||
function %icmp_ule_0(i32) -> i8 {
|
||||
block0(v0: i32):
|
||||
v1 = iconst.i32 0
|
||||
v2 = icmp ule v0, v1
|
||||
return v2
|
||||
; check: v3 = icmp eq v0, v1
|
||||
; check: return v3
|
||||
}
|
||||
|
||||
; check: v5 = iconst.i8 0
|
||||
; check: return v5
|
||||
|
||||
function %uge_zero_always_false(i64) -> i8 {
|
||||
block0(v1: i64):
|
||||
v2 = iconst.i64 0
|
||||
v3 = icmp uge v1, v2
|
||||
return v3
|
||||
function %icmp_ugt_0(i32) -> i8 {
|
||||
block0(v0: i32):
|
||||
v1 = iconst.i32 0
|
||||
v2 = icmp ugt v0, v1
|
||||
return v2
|
||||
; check: v3 = icmp ne v0, v1
|
||||
; check: return v3
|
||||
}
|
||||
|
||||
; check: v4 = iconst.i8 1
|
||||
; check: return v4
|
||||
|
||||
function %ule_zero_always_false(i64) -> i8 {
|
||||
block0(v1: i64):
|
||||
v2 = iconst.i64 0
|
||||
v3 = icmp ule v2, v1
|
||||
return v3
|
||||
function %icmp_uge_0(i32) -> i8 {
|
||||
block0(v0: i32):
|
||||
v1 = iconst.i32 0
|
||||
v2 = icmp uge v0, v1
|
||||
return v2
|
||||
; check: v3 = iconst.i8 1
|
||||
; check: return v3
|
||||
}
|
||||
|
||||
; check: v5 = iconst.i8 1
|
||||
; check: return v5
|
||||
function %icmp_ult_umax(i32) -> i8 {
|
||||
block0(v0: i32):
|
||||
v1 = iconst.i32 0xffff_ffff
|
||||
v2 = icmp ult v0, v1
|
||||
return v2
|
||||
; check: v3 = icmp ne v0, v1
|
||||
; check: return v3
|
||||
}
|
||||
|
||||
function %icmp_ule_umax(i32) -> i8 {
|
||||
block0(v0: i32):
|
||||
v1 = iconst.i32 0xffff_ffff
|
||||
v2 = icmp ule v0, v1
|
||||
return v2
|
||||
; check: v3 = iconst.i8 1
|
||||
; check: return v3
|
||||
}
|
||||
|
||||
function %icmp_ugt_umax(i32) -> i8 {
|
||||
block0(v0: i32):
|
||||
v1 = iconst.i32 0xffff_ffff
|
||||
v2 = icmp ugt v0, v1
|
||||
return v2
|
||||
; check: v3 = iconst.i8 0
|
||||
; check: return v3
|
||||
}
|
||||
|
||||
function %icmp_uge_umax(i32) -> i8 {
|
||||
block0(v0: i32):
|
||||
v1 = iconst.i32 0xffff_ffff
|
||||
v2 = icmp uge v0, v1
|
||||
return v2
|
||||
; check: v3 = icmp eq v0, v1
|
||||
; check: return v3
|
||||
}
|
||||
|
||||
function %icmp_slt_smin(i32) -> i8 {
|
||||
block0(v0: i32):
|
||||
v1 = iconst.i32 0x8000_0000
|
||||
v2 = icmp slt v0, v1
|
||||
return v2
|
||||
; check: v3 = iconst.i8 0
|
||||
; check: return v3
|
||||
}
|
||||
|
||||
function %icmp_sle_smin(i32) -> i8 {
|
||||
block0(v0: i32):
|
||||
v1 = iconst.i32 0x8000_0000
|
||||
v2 = icmp sle v0, v1
|
||||
return v2
|
||||
; check: v3 = icmp eq v0, v1
|
||||
; check: return v3
|
||||
}
|
||||
|
||||
function %icmp_sgt_smin(i32) -> i8 {
|
||||
block0(v0: i32):
|
||||
v1 = iconst.i32 0x8000_0000
|
||||
v2 = icmp sgt v0, v1
|
||||
return v2
|
||||
; check: v3 = icmp ne v0, v1
|
||||
; check: return v3
|
||||
}
|
||||
|
||||
function %icmp_sge_smin(i32) -> i8 {
|
||||
block0(v0: i32):
|
||||
v1 = iconst.i32 0x8000_0000
|
||||
v2 = icmp sge v0, v1
|
||||
return v2
|
||||
; check: v3 = iconst.i8 1
|
||||
; check: return v3
|
||||
}
|
||||
|
||||
function %icmp_slt_smax(i32) -> i8 {
|
||||
block0(v0: i32):
|
||||
v1 = iconst.i32 0x7FFF_FFFF
|
||||
v2 = icmp slt v0, v1
|
||||
return v2
|
||||
; check: v3 = icmp ne v0, v1
|
||||
; check: return v3
|
||||
}
|
||||
|
||||
function %icmp_sle_smax(i32) -> i8 {
|
||||
block0(v0: i32):
|
||||
v1 = iconst.i32 0x7FFF_FFFF
|
||||
v2 = icmp sle v0, v1
|
||||
return v2
|
||||
; check: v3 = iconst.i8 1
|
||||
; check: return v3
|
||||
}
|
||||
|
||||
function %icmp_sgt_smax(i32) -> i8 {
|
||||
block0(v0: i32):
|
||||
v1 = iconst.i32 0x7FFF_FFFF
|
||||
v2 = icmp sgt v0, v1
|
||||
return v2
|
||||
; check: v3 = iconst.i8 0
|
||||
; check: return v3
|
||||
}
|
||||
|
||||
function %icmp_sge_smax(i32) -> i8 {
|
||||
block0(v0: i32):
|
||||
v1 = iconst.i32 0x7FFF_FFFF
|
||||
v2 = icmp sge v0, v1
|
||||
return v2
|
||||
; check: v3 = icmp eq v0, v1
|
||||
; check: return v3
|
||||
}
|
||||
|
||||
function %extend_always_above_zero(i32) -> i8 {
|
||||
block0(v1: i32):
|
||||
|
||||
Reference in New Issue
Block a user