Skip to content

Commit c10d4de

Browse files
committed
core: Use __jcvt() intrinsic on AArch64 for f64→i32 conversion
In ruffle-rs#21780, an optimisation has been added to use the fjcvtzs ARMv8.3 instruction when available, to convert a f64 into an i32. This made me wonder why core::arch::aarch64 didn’t have an intrinsic for this instruction, so I implemented it in stdarch[1], which got pulled in Rust yesterday[2] (see the tracking issue[3]). This PR makes use of this new intrinsic to remove the unsafe asm!() block, and simplify the code. [1] rust-lang/stdarch#1938 [2] rust-lang/rust#148402 [3] rust-lang/rust#147555
1 parent 8d9fddc commit c10d4de

File tree

2 files changed

+7
-28
lines changed

2 files changed

+7
-28
lines changed

core/src/ecma_conversions.rs

Lines changed: 4 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,10 @@ pub fn f64_to_wrapping_i32(n: f64) -> i32 {
4444
#[cfg(target_arch = "aarch64")]
4545
{
4646
if std::arch::is_aarch64_feature_detected!("jsconv") {
47-
// SAFETY: `jsconv` feature is checked in both compile time and runtime to be existed, so it's safe to call.
48-
unsafe { f64_to_wrapping_int32_aarch64(n) }
47+
// Converts an `f64` to an `i32` with ECMAScript `ToInt32` wrapping behavior.
48+
// The value will be wrapped in the range [-2^31, 2^31).
49+
// Optimized for aarch64 cpu with the fjcvtzs instruction.
50+
std::arch::aarch64::__jcvt(n)
4951
} else {
5052
f64_to_wrapping_i32_generic(n)
5153
}
@@ -59,32 +61,6 @@ fn f64_to_wrapping_i32_generic(n: f64) -> i32 {
5961
f64_to_wrapping_u32(n) as i32
6062
}
6163

62-
/// Converts an `f64` to an `i32` with ECMAScript `ToInt32` wrapping behavior.
63-
/// The value will be wrapped in the range [-2^31, 2^31).
64-
/// Optimized for aarch64 cpu with the fjcvtzs instruction.
65-
///
66-
/// # Safety
67-
///
68-
/// The caller must ensure either:
69-
/// - The target platform is aarch64 with `jsconv` feature enabled, or
70-
/// - Runtime feature detection has been performed to verify `jsconv` support
71-
#[allow(unused)]
72-
#[cfg(target_arch = "aarch64")]
73-
#[target_feature(enable = "jsconv")]
74-
unsafe fn f64_to_wrapping_int32_aarch64(number: f64) -> i32 {
75-
let ret: i32;
76-
// SAFETY: fjcvtzs instruction is available under jsconv feature.
77-
unsafe {
78-
std::arch::asm!(
79-
"fjcvtzs {dst:w}, {src:d}",
80-
src = in(vreg) number,
81-
dst = out(reg) ret,
82-
options(nostack, nomem, pure)
83-
);
84-
}
85-
ret
86-
}
87-
8864
/// Implements the IEEE-754 "Round to nearest, ties to even" rounding rule.
8965
/// (e.g., both 1.5 and 2.5 will round to 2).
9066
/// This also clamps out-of-range values and NaN to `i32::MIN`.

core/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
// This lint is good in theory, but in AVMs we often need to do `let x = args.get(0); let y = args.get(1);` etc.
55
// It'd make those much less readable and consistent.
66
#![allow(clippy::get_first)]
7+
// This enables the core::arch::aarch64::__jcvt() intrinsic, which is used to convert a f64 into an
8+
// i32 the JavaScript way on ARMv8.3+.
9+
#![feature(stdarch_aarch64_jscvt)]
710

811
#[macro_use]
912
mod display_object;

0 commit comments

Comments
 (0)