From 6f2dca8c70ff9489e995035e82f1499ae49e1dac Mon Sep 17 00:00:00 2001 From: CrazyboyQCD <53971641+CrazyboyQCD@users.noreply.github.com> Date: Fri, 13 Dec 2024 12:19:35 +0800 Subject: [PATCH] Add `fjcvtzs` instruction for `ARMv8.3` target (#4084) * feature: add fjcvtzs instruction for `ARMv8.3` target * Add a test for various edge cases. --------- Co-authored-by: Hans Larsen --- .../engine/src/builtins/number/conversions.rs | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/core/engine/src/builtins/number/conversions.rs b/core/engine/src/builtins/number/conversions.rs index b81a8539d6a..9457febdf86 100644 --- a/core/engine/src/builtins/number/conversions.rs +++ b/core/engine/src/builtins/number/conversions.rs @@ -2,6 +2,7 @@ /// /// [ToInt32]: https://tc39.es/ecma262/#sec-toint32 #[allow(clippy::float_cmp)] +#[cfg(not(all(target_arch = "aarch64", target_feature = "jsconv")))] pub(crate) fn f64_to_int32(number: f64) -> i32 { const SIGN_MASK: u64 = 0x8000_0000_0000_0000; const EXPONENT_MASK: u64 = 0x7FF0_0000_0000_0000; @@ -72,9 +73,54 @@ pub(crate) fn f64_to_int32(number: f64) -> i32 { (sign(number) * (bits as i64)) as i32 } +/// Converts a 64-bit floating point number to an `i32` using [`FJCVTZS`][FJCVTZS] instruction on `ARMv8.3`. +/// +/// [FJCVTZS]: https://developer.arm.com/documentation/dui0801/h/A64-Floating-point-Instructions/FJCVTZS +#[cfg(all(target_arch = "aarch64", target_feature = "jsconv"))] +pub(crate) fn f64_to_int32(number: f64) -> i32 { + if number.is_nan() { + return 0; + } + let ret: i32; + // SAFETY: Number is not nan so no floating-point exception should throw. + unsafe { + std::arch::asm!( + "fjcvtzs {dst:w}, {src:d}", + src = in(vreg) number, + dst = out(reg) ret, + ) + } + ret +} + +/// Converts a 64-bit floating point number to an `i32` using [`FJCVTZS`][FJCVTZS] instruction on `ARMv8.3`. +/// +/// [FJCVTZS]: https://developer.arm.com/documentation/dui0801/h/A64-Floating-point-Instructions/FJCVTZS +#[cfg(all(target_arch = "aarch64", target_feature = "jsconv"))] +pub(crate) fn f64_to_uint32(number: f64) -> u32 { + f64_to_int32(number) as u32 +} + /// Converts a 64-bit floating point number to an `u32` according to the [`ToUint32`][ToUint32] algorithm. /// /// [ToUint32]: https://tc39.es/ecma262/#sec-touint32 +#[cfg(not(all(target_arch = "aarch64", target_feature = "jsconv")))] pub(crate) fn f64_to_uint32(number: f64) -> u32 { f64_to_int32(number) as u32 } + +#[test] +fn f64_to_int32_conversion() { + use crate::builtins::Number; + + assert_eq!(f64_to_int32(0.0), 0); + assert_eq!(f64_to_int32(-0.0), 0); + assert_eq!(f64_to_int32(f64::NAN), 0); + assert_eq!(f64_to_int32(f64::INFINITY), 0); + assert_eq!(f64_to_int32(f64::NEG_INFINITY), 0); + assert_eq!(f64_to_int32((i64::from(i32::MAX) + 1) as f64), i32::MIN); + assert_eq!(f64_to_int32((i64::from(i32::MIN) - 1) as f64), i32::MAX); + + assert_eq!(f64_to_int32(Number::MAX_SAFE_INTEGER + 1.0), 0); + assert_eq!(f64_to_int32(Number::MIN_SAFE_INTEGER - 1.0), 0); +}