diff --git a/cranelift/codegen/src/opts.rs b/cranelift/codegen/src/opts.rs index c24e6190eeea..e897b2dc4437 100644 --- a/cranelift/codegen/src/opts.rs +++ b/cranelift/codegen/src/opts.rs @@ -262,4 +262,23 @@ impl<'a, 'b, 'c> generated_code::Context for IsleContext<'a, 'b, 'c> { fn uextend_maybe_etor(&mut self, value: Value, returns: &mut Self::uextend_maybe_etor_returns) { *returns = MaybeUnaryEtorIter::new(Opcode::Uextend, value); } + + // NB: Cranelift's defined semantics for `fcvt_from_{s,u}int` match Rust's + // own semantics for converting an integer to a float, so these are all + // implemented with `as` conversions in Rust. + fn f32_from_uint(&mut self, n: u64) -> Ieee32 { + Ieee32::with_float(n as f32) + } + + fn f64_from_uint(&mut self, n: u64) -> Ieee64 { + Ieee64::with_float(n as f64) + } + + fn f32_from_sint(&mut self, n: i64) -> Ieee32 { + Ieee32::with_float(n as f32) + } + + fn f64_from_sint(&mut self, n: i64) -> Ieee64 { + Ieee64::with_float(n as f64) + } } diff --git a/cranelift/codegen/src/opts/cprop.isle b/cranelift/codegen/src/opts/cprop.isle index f84b300f62bd..77b76a508232 100644 --- a/cranelift/codegen/src/opts/cprop.isle +++ b/cranelift/codegen/src/opts/cprop.isle @@ -244,3 +244,23 @@ (bxor ty a b@(iconst _ _)) (bxor ty c d@(iconst _ _)))) (bxor ty (bxor ty a c) (bxor ty b d))) + + +;; Constant fold int-to-float conversions. +(rule (simplify (fcvt_from_uint $F32 (iconst_u _ n))) + (f32const $F32 (f32_from_uint n))) +(rule (simplify (fcvt_from_uint $F64 (iconst_u _ n))) + (f64const $F64 (f64_from_uint n))) +(rule (simplify (fcvt_from_sint $F32 (iconst_s _ n))) + (f32const $F32 (f32_from_sint n))) +(rule (simplify (fcvt_from_sint $F64 (iconst_s _ n))) + (f64const $F64 (f64_from_sint n))) + +(decl f32_from_uint (u64) Ieee32) +(extern constructor f32_from_uint f32_from_uint) +(decl f64_from_uint (u64) Ieee64) +(extern constructor f64_from_uint f64_from_uint) +(decl f32_from_sint (i64) Ieee32) +(extern constructor f32_from_sint f32_from_sint) +(decl f64_from_sint (i64) Ieee64) +(extern constructor f64_from_sint f64_from_sint) diff --git a/cranelift/codegen/src/opts/vector.isle b/cranelift/codegen/src/opts/vector.isle index fd47fe69c41b..5ad57d8dc604 100644 --- a/cranelift/codegen/src/opts/vector.isle +++ b/cranelift/codegen/src/opts/vector.isle @@ -1,8 +1,9 @@ ;; For various ops lift a splat outside of the op to try to open up ;; optimization opportunities with scalars. -;; + ;; NB: for int-to-float conversion op this simplification is also -;; required for correctness at this time due to #6562 +;; required for the x64 backend because it doesn't fully implement int-to-float +;; conversions for 64x2 vectors, for more information see #6562 (rule (simplify (fcvt_from_uint float_vector_ty (splat _ x))) (splat float_vector_ty (fcvt_from_uint (lane_type float_vector_ty) x))) (rule (simplify (fcvt_from_sint float_vector_ty (splat _ x))) diff --git a/cranelift/filetests/filetests/egraph/fcvt-from-int.clif b/cranelift/filetests/filetests/egraph/fcvt-from-int.clif new file mode 100644 index 000000000000..9d98b01412b0 --- /dev/null +++ b/cranelift/filetests/filetests/egraph/fcvt-from-int.clif @@ -0,0 +1,222 @@ +test optimize +set opt_level=speed +target x86_64 +target aarch64 +target s390x +target riscv64 + +function %i32_0_to_f32() -> f32 { +block0: + v0 = iconst.i32 0 + v1 = fcvt_from_sint.f32 v0 + return v1 + ; check: v2 = f32const 0.0 + ; check: return v2 +} + +function %i32_neg1_to_f32() -> f32 { +block0: + v0 = iconst.i32 -1 + v1 = fcvt_from_sint.f32 v0 + return v1 + ; check: v2 = f32const -0x1.000000p0 + ; check: return v2 +} + +function %i32_1_to_f32() -> f32 { +block0: + v0 = iconst.i32 1 + v1 = fcvt_from_sint.f32 v0 + return v1 + ; check: v2 = f32const 0x1.000000p0 + ; check: return v2 +} + +function %u32_0_to_f32() -> f32 { +block0: + v0 = iconst.i32 0 + v1 = fcvt_from_uint.f32 v0 + return v1 + ; check: v2 = f32const 0.0 + ; check: return v2 +} + +function %u32_neg1_to_f32() -> f32 { +block0: + v0 = iconst.i32 -1 + v1 = fcvt_from_uint.f32 v0 + return v1 + ; check: v2 = f32const 0x1.000000p32 + ; check: return v2 +} + +function %u32_1_to_f32() -> f32 { +block0: + v0 = iconst.i32 1 + v1 = fcvt_from_uint.f32 v0 + return v1 + ; check: v2 = f32const 0x1.000000p0 + ; check: return v2 +} + +function %i32_0_to_f64() -> f64 { +block0: + v0 = iconst.i32 0 + v1 = fcvt_from_sint.f64 v0 + return v1 + ; check: v2 = f64const 0.0 + ; check: return v2 +} + +function %i32_neg1_to_f64() -> f64 { +block0: + v0 = iconst.i32 -1 + v1 = fcvt_from_sint.f64 v0 + return v1 + ; check: v2 = f64const -0x1.0000000000000p0 + ; check: return v2 +} + +function %i32_1_to_f64() -> f64 { +block0: + v0 = iconst.i32 1 + v1 = fcvt_from_sint.f64 v0 + return v1 + ; check: v2 = f64const 0x1.0000000000000p0 + ; check: return v2 +} + +function %u32_0_to_f64() -> f64 { +block0: + v0 = iconst.i32 0 + v1 = fcvt_from_uint.f64 v0 + return v1 + ; check: v2 = f64const 0.0 + ; check: return v2 +} + +function %u32_neg1_to_f64() -> f64 { +block0: + v0 = iconst.i32 -1 + v1 = fcvt_from_uint.f64 v0 + return v1 + ; check: v2 = f64const 0x1.fffffffe00000p31 + ; check: return v2 +} + +function %u32_1_to_f64() -> f64 { +block0: + v0 = iconst.i32 1 + v1 = fcvt_from_uint.f64 v0 + return v1 + ; check: v2 = f64const 0x1.0000000000000p0 + ; check: return v2 +} + +function %i64_0_to_f32() -> f32 { +block0: + v0 = iconst.i64 0 + v1 = fcvt_from_sint.f32 v0 + return v1 + ; check: v2 = f32const 0.0 + ; check: return v2 +} + +function %i64_neg1_to_f32() -> f32 { +block0: + v0 = iconst.i64 -1 + v1 = fcvt_from_sint.f32 v0 + return v1 + ; check: v2 = f32const -0x1.000000p0 + ; check: return v2 +} + +function %i64_1_to_f32() -> f32 { +block0: + v0 = iconst.i64 1 + v1 = fcvt_from_sint.f32 v0 + return v1 + ; check: v2 = f32const 0x1.000000p0 + ; check: return v2 +} + +function %u64_0_to_f32() -> f32 { +block0: + v0 = iconst.i64 0 + v1 = fcvt_from_uint.f32 v0 + return v1 + ; check: v2 = f32const 0.0 + ; check: return v2 +} + +function %u64_neg1_to_f32() -> f32 { +block0: + v0 = iconst.i64 -1 + v1 = fcvt_from_uint.f32 v0 + return v1 + ; check: v2 = f32const 0x1.000000p64 + ; check: return v2 +} + +function %u64_1_to_f32() -> f32 { +block0: + v0 = iconst.i64 1 + v1 = fcvt_from_uint.f32 v0 + return v1 + ; check: v2 = f32const 0x1.000000p0 + ; check: return v2 +} + +function %i64_0_to_f64() -> f64 { +block0: + v0 = iconst.i64 0 + v1 = fcvt_from_sint.f64 v0 + return v1 + ; check: v2 = f64const 0.0 + ; check: return v2 +} + +function %i64_neg1_to_f64() -> f64 { +block0: + v0 = iconst.i64 -1 + v1 = fcvt_from_sint.f64 v0 + return v1 + ; check: v2 = f64const -0x1.0000000000000p0 + ; check: return v2 +} + +function %i64_1_to_f64() -> f64 { +block0: + v0 = iconst.i64 1 + v1 = fcvt_from_sint.f64 v0 + return v1 + ; check: v2 = f64const 0x1.0000000000000p0 + ; check: return v2 +} + +function %u64_0_to_f64() -> f64 { +block0: + v0 = iconst.i64 0 + v1 = fcvt_from_uint.f64 v0 + return v1 + ; check: v2 = f64const 0.0 + ; check: return v2 +} + +function %u64_neg1_to_f64() -> f64 { +block0: + v0 = iconst.i64 -1 + v1 = fcvt_from_uint.f64 v0 + return v1 + ; check: v2 = f64const 0x1.0000000000000p64 + ; check: return v2 +} + +function %u64_1_to_f64() -> f64 { +block0: + v0 = iconst.i64 1 + v1 = fcvt_from_uint.f64 v0 + return v1 + ; check: v2 = f64const 0x1.0000000000000p0 + ; check: return v2 +} diff --git a/tests/misc_testsuite/int-to-float-splat.wast b/tests/misc_testsuite/int-to-float-splat.wast new file mode 100644 index 000000000000..2a6110200293 --- /dev/null +++ b/tests/misc_testsuite/int-to-float-splat.wast @@ -0,0 +1,15 @@ +(module + (func (param i32) (result v128) + local.get 0 + i32x4.splat + f64x2.convert_low_i32x4_u + ) +) + +(module + (func (result v128) + i32.const 0 + i32x4.splat + f64x2.convert_low_i32x4_u + ) +)