Skip to main content

rt/aarch64/
fx.rs

1// fx -- rt/fx.rs on ARM64: the scalar operations with the instructions of the CPU and the results
2// of SSE (apps/portabletest compares them with portable/fx.rs).  They round the same way; what
3// differs:
4//   fcvtzs / fcvtns  saturate and give 0 for NaN, SSE gives the lowest value.  Too negative saturates
5//                    to that anyway, so one comparison is enough.  (`as` is fcvtzs here.)
6//   fsqrt            of a negative number gives ARM's default NaN (positive), SSE its "indefinite"
7//                    (negative)
8// No preserves_flags in the asm!: these instructions may set the flags of the FPSR, which nobody reads.
9
10use core::arch::asm;
11
12// the first values too high for the integer; ROUNDS_ABOVE_I32 ties to 2^31 (an even number)
13const ABOVE_I32F: f32 = 2147483648.0;
14const ABOVE_I32: f64 = 2147483648.0;
15const ROUNDS_ABOVE_I32: f64 = 2147483647.5;
16const ABOVE_I64: f64 = 9223372036854775808.0;
17
18/// cvttss2si: truncate towards zero (0x80000000 for NaN / out of range)
19#[inline]
20pub fn cvtt(x: f32) -> i32 {
21    if x < ABOVE_I32F { x as i32 } else { i32::MIN }
22}
23
24/// cvtss2si: round to nearest, ties to even
25#[inline]
26pub fn cvt(x: f32) -> i32 {
27    let r: i32;
28    // SAFETY: the instruction only computes one register from another.
29    unsafe { asm!("fcvtns {0:w}, {1:s}", out(reg) r, in(vreg) x, options(pure, nomem, nostack)) };
30    if x < ABOVE_I32F { r } else { i32::MIN }
31}
32
33/// sqrtss
34#[inline]
35pub fn sqrt(x: f32) -> f32 {
36    let r: f32;
37    // SAFETY: the instruction only computes one register from another.
38    unsafe { asm!("fsqrt {0:s}, {1:s}", out(vreg) r, in(vreg) x, options(pure, nomem, nostack)) };
39    if r.is_nan() { crate::portable::fx::arithmetic32(x, x, r) } else { r }
40}
41
42/// minss a, b: a < b ? a : b (b when unordered)
43#[inline]
44pub fn minss(a: f32, b: f32) -> f32 {
45    if a < b { a } else { b }
46}
47
48/// maxss a, b: a > b ? a : b (b when unordered)
49#[inline]
50pub fn maxss(a: f32, b: f32) -> f32 {
51    if a > b { a } else { b }
52}
53
54/// floor / ceil the way f_floor / f_ceil do it: truncate, then step if needed
55#[inline]
56pub fn floor_i(x: f32) -> i32 {
57    let t = cvtt(x);
58    if (t as f32) > x { t.wrapping_sub(1) } else { t }
59}
60
61#[inline]
62pub fn ceil_i(x: f32) -> i32 {
63    let t = cvtt(x);
64    if (t as f32) < x { t.wrapping_add(1) } else { t }
65}
66
67/// round(x / 255) for x in 0..=65025: ((x + 128) * 257) >> 16 (pmulhuw), equal to
68/// (x + 128 + ((x + 128) >> 8)) >> 8
69#[inline]
70pub fn div255(x: u32) -> u32 {
71    debug_assert!(x < 1 << 24, "div255 is for x <= 65025");
72    ((x + 128) * 257) >> 16
73}
74
75// ---- f64
76
77/// sqrtsd
78#[inline]
79pub fn sqrtd(x: f64) -> f64 {
80    let r: f64;
81    // SAFETY: the instruction only computes one register from another.
82    unsafe { asm!("fsqrt {0:d}, {1:d}", out(vreg) r, in(vreg) x, options(pure, nomem, nostack)) };
83    if r.is_nan() { crate::portable::fx::arithmetic64(x, x, r) } else { r }
84}
85
86/// maxsd a, b: a > b ? a : b (b when unordered)
87#[inline]
88pub fn maxsd(a: f64, b: f64) -> f64 {
89    if a > b { a } else { b }
90}
91
92/// minsd a, b: a < b ? a : b (b when unordered)
93#[inline]
94pub fn minsd(a: f64, b: f64) -> f64 {
95    if a < b { a } else { b }
96}
97
98/// cvttsd2si (32-bit): truncate towards zero (0x80000000 for NaN / out of range)
99#[inline]
100pub fn cvttd(x: f64) -> i32 {
101    if x < ABOVE_I32 { x as i32 } else { i32::MIN }
102}
103
104/// cvtsd2si (32-bit): round to nearest even (0x80000000 for NaN / out of range)
105#[inline]
106pub fn cvtd(x: f64) -> i32 {
107    let r: i32;
108    // SAFETY: the instruction only computes one register from another.
109    unsafe { asm!("fcvtns {0:w}, {1:d}", out(reg) r, in(vreg) x, options(pure, nomem, nostack)) };
110    if x < ROUNDS_ABOVE_I32 { r } else { i32::MIN }
111}
112
113/// cvtsd2si (64-bit): round to nearest even (i64::MIN for NaN / out of range)
114#[inline]
115pub fn cvtd64(x: f64) -> i64 {
116    let r: i64;
117    // SAFETY: the instruction only computes one register from another.
118    unsafe { asm!("fcvtns {0:x}, {1:d}", out(reg) r, in(vreg) x, options(pure, nomem, nostack)) };
119    if x < ABOVE_I64 { r } else { i64::MIN }
120}
121
122/// cvttsd2si (64-bit): truncate (i64::MIN for NaN / out of range)
123#[inline]
124pub fn cvttd64(x: f64) -> i64 {
125    if x < ABOVE_I64 { x as i64 } else { i64::MIN }
126}