From ac4d28f4dd4d618bad244c9530e37318fba1cb39 Mon Sep 17 00:00:00 2001 From: Jamey Sharp Date: Wed, 1 Feb 2023 13:55:36 -0800 Subject: [PATCH] Constant-fold icmp instructions (#5666) We found examples of icmp instructions with both operands constant in spidermonkey.wasm. --- cranelift/codegen/src/isle_prelude.rs | 22 +++ cranelift/codegen/src/opts/cprop.isle | 8 ++ cranelift/codegen/src/prelude.isle | 3 + .../filetests/filetests/egraph/algebraic.clif | 18 +++ .../filetests/filetests/egraph/cprop.clif | 128 +++++++++++++++--- 5 files changed, 161 insertions(+), 18 deletions(-) diff --git a/cranelift/codegen/src/isle_prelude.rs b/cranelift/codegen/src/isle_prelude.rs index 81f10d9fde..2b6f729f7f 100644 --- a/cranelift/codegen/src/isle_prelude.rs +++ b/cranelift/codegen/src/isle_prelude.rs @@ -134,6 +134,28 @@ macro_rules! isle_common_prelude_methods { x & 0xffff_ffff } + #[inline] + fn imm64_icmp(&mut self, ty: Type, cc: &IntCC, x: Imm64, y: Imm64) -> Imm64 { + let shift = u32::checked_sub(64, ty.bits()).unwrap_or(0); + let mask = u64::MAX >> shift; + let x = (x.bits() as u64) & mask; + let y = (y.bits() as u64) & mask; + let sext = |v| ((v << shift) as i64) >> shift; + let result = match cc { + IntCC::Equal => x == y, + IntCC::NotEqual => x != y, + IntCC::UnsignedGreaterThanOrEqual => x >= y, + IntCC::UnsignedGreaterThan => x > y, + IntCC::UnsignedLessThanOrEqual => x <= y, + IntCC::UnsignedLessThan => x < y, + IntCC::SignedGreaterThanOrEqual => sext(x) >= sext(y), + IntCC::SignedGreaterThan => sext(x) > sext(y), + IntCC::SignedLessThanOrEqual => sext(x) <= sext(y), + IntCC::SignedLessThan => sext(x) < sext(y), + }; + Imm64::new(result.into()) + } + #[inline] fn ty_bits(&mut self, ty: Type) -> u8 { use std::convert::TryInto; diff --git a/cranelift/codegen/src/opts/cprop.isle b/cranelift/codegen/src/opts/cprop.isle index 0f6efff0ec..38ea51400b 100644 --- a/cranelift/codegen/src/opts/cprop.isle +++ b/cranelift/codegen/src/opts/cprop.isle @@ -70,6 +70,14 @@ (iconst ty k2))) (subsume (iconst ty (imm64_sshr ty k1 k2)))) +(rule (simplify + (icmp result_ty + cc + (iconst ty k1) + (iconst ty k2))) + (subsume (iconst result_ty (imm64_icmp ty cc k1 k2)))) + + ;; Canonicalize via commutativity: push immediates to the right. ;; ;; (op k x) --> (op x k) diff --git a/cranelift/codegen/src/prelude.isle b/cranelift/codegen/src/prelude.isle index 0aec07a862..0a0cab5808 100644 --- a/cranelift/codegen/src/prelude.isle +++ b/cranelift/codegen/src/prelude.isle @@ -150,6 +150,9 @@ (decl pure u64_uextend_u32 (u64) u64) (extern constructor u64_uextend_u32 u64_uextend_u32) +(decl pure imm64_icmp (Type IntCC Imm64 Imm64) Imm64) +(extern constructor imm64_icmp imm64_icmp) + (decl u64_is_zero (bool) u64) (extern extractor infallible u64_is_zero u64_is_zero) diff --git a/cranelift/filetests/filetests/egraph/algebraic.clif b/cranelift/filetests/filetests/egraph/algebraic.clif index fd0f495974..5b23600478 100644 --- a/cranelift/filetests/filetests/egraph/algebraic.clif +++ b/cranelift/filetests/filetests/egraph/algebraic.clif @@ -192,3 +192,21 @@ block0(v0: i64): return v4 ; check: return v4 } + +function %f2(i8) -> i8 { +block0(v1: i8): + v2 = icmp eq v1, v1 + return v2 +} + +; check: v3 = iconst.i8 1 +; check: return v3 + +function %f3(i8) -> i8 { +block0(v1: i8): + v2 = icmp ne v1, v1 + return v2 +} + +; check: v3 = iconst.i8 0 +; check: return v3 diff --git a/cranelift/filetests/filetests/egraph/cprop.clif b/cranelift/filetests/filetests/egraph/cprop.clif index 03882877aa..afecd7fc13 100644 --- a/cranelift/filetests/filetests/egraph/cprop.clif +++ b/cranelift/filetests/filetests/egraph/cprop.clif @@ -23,24 +23,6 @@ block0: ; check: v3 = iconst.i16 0xfffe ; nextln: return v3 -function %f2(i8) -> i8 { -block0(v1: i8): - v2 = icmp eq v1, v1 - return v2 -} - -; check: v3 = iconst.i8 1 -; check: return v3 - -function %f3(i8) -> i8 { -block0(v1: i8): - v2 = icmp ne v1, v1 - return v2 -} - -; check: v3 = iconst.i8 0 -; check: return v3 - function %ishl() -> i8 { block0: v0 = iconst.i8 1 @@ -73,3 +55,113 @@ block0: ; check: v3 = iconst.i8 -4 ; check: return v3 + +function %icmp_eq_i32() -> i8 { +block0: + v0 = iconst.i32 1 + v1 = iconst.i32 2 + v2 = icmp eq v0, v1 + return v2 +} + +; check: v3 = iconst.i8 0 +; nextln: return v3 + +function %icmp_ne_i32() -> i8 { +block0: + v0 = iconst.i32 1 + v1 = iconst.i32 2 + v2 = icmp ne v0, v1 + return v2 +} + +; check: v3 = iconst.i8 1 +; nextln: return v3 + +function %icmp_ult_i32() -> i8 { +block0: + v0 = iconst.i32 1 + v1 = iconst.i32 2 + v2 = icmp ult v0, v1 + return v2 +} + +; check: v3 = iconst.i8 1 +; nextln: return v3 + +function %icmp_ule_i32() -> i8 { +block0: + v0 = iconst.i32 1 + v1 = iconst.i32 2 + v2 = icmp ule v0, v1 + return v2 +} + +; check: v3 = iconst.i8 1 +; nextln: return v3 + +function %icmp_uge_i32() -> i8 { +block0: + v0 = iconst.i32 1 + v1 = iconst.i32 2 + v2 = icmp uge v0, v1 + return v2 +} + +; check: v3 = iconst.i8 0 +; nextln: return v3 + +function %icmp_ugt_i32() -> i8 { +block0: + v0 = iconst.i32 1 + v1 = iconst.i32 2 + v2 = icmp ugt v0, v1 + return v2 +} + +; check: v3 = iconst.i8 0 +; nextln: return v3 + +function %icmp_slt_i32() -> i8 { +block0: + v0 = iconst.i32 -1 + v1 = iconst.i32 2 + v2 = icmp slt v0, v1 + return v2 +} + +; check: v3 = iconst.i8 1 +; nextln: return v3 + +function %icmp_sle_i32() -> i8 { +block0: + v0 = iconst.i32 -1 + v1 = iconst.i32 2 + v2 = icmp sle v0, v1 + return v2 +} + +; check: v3 = iconst.i8 1 +; nextln: return v3 + +function %icmp_sge_i32() -> i8 { +block0: + v0 = iconst.i32 -1 + v1 = iconst.i32 2 + v2 = icmp sge v0, v1 + return v2 +} + +; check: v3 = iconst.i8 0 +; nextln: return v3 + +function %icmp_sgt_i32() -> i8 { +block0: + v0 = iconst.i32 -1 + v1 = iconst.i32 2 + v2 = icmp sgt v0, v1 + return v2 +} + +; check: v3 = iconst.i8 0 +; nextln: return v3