Skip to main content

rt/
fx.rs

1// fx -- scalar SSE float operations with exactly the semantics the assembly uses
2// (cvttss2si, cvtss2si with round-half-even, sqrtss, minss, maxss), so float results
3// are bit-identical to ASMLIB.  Rust never fuses a multiply and an add into an FMA.
4
5use core::arch::x86_64::{_mm_cvtss_f32, _mm_cvtss_si32, _mm_cvttss_si32, _mm_set_ss, _mm_sqrt_ss};
6
7/// cvttss2si: truncate towards zero (0x80000000 for NaN / out of range)
8#[inline]
9pub fn cvtt(x: f32) -> i32 {
10    // SAFETY: SSE is part of the x86-64 baseline.
11    unsafe { _mm_cvttss_si32(_mm_set_ss(x)) }
12}
13
14/// cvtss2si: round to nearest, ties to even (the default MXCSR mode)
15#[inline]
16pub fn cvt(x: f32) -> i32 {
17    // SAFETY: SSE is part of the x86-64 baseline.
18    unsafe { _mm_cvtss_si32(_mm_set_ss(x)) }
19}
20
21#[inline]
22pub fn sqrt(x: f32) -> f32 {
23    // SAFETY: SSE is part of the x86-64 baseline.
24    unsafe { _mm_cvtss_f32(_mm_sqrt_ss(_mm_set_ss(x))) }
25}
26
27/// minss a, b: a < b ? a : b (b when unordered)
28#[inline]
29pub fn minss(a: f32, b: f32) -> f32 {
30    if a < b { a } else { b }
31}
32
33/// maxss a, b: a > b ? a : b (b when unordered)
34#[inline]
35pub fn maxss(a: f32, b: f32) -> f32 {
36    if a > b { a } else { b }
37}
38
39/// floor / ceil the way f_floor / f_ceil do it: truncate, then step if needed
40#[inline]
41pub fn floor_i(x: f32) -> i32 {
42    let t = cvtt(x);
43    if (t as f32) > x { t.wrapping_sub(1) } else { t }
44}
45
46#[inline]
47pub fn ceil_i(x: f32) -> i32 {
48    let t = cvtt(x);
49    if (t as f32) < x { t.wrapping_add(1) } else { t }
50}
51
52/// round(x / 255) for x in 0..=65025: ((x + 128) * 257) >> 16 (pmulhuw), equal to
53/// (x + 128 + ((x + 128) >> 8)) >> 8
54#[inline]
55pub fn div255(x: u32) -> u32 {
56    debug_assert!(x < 1 << 24, "div255 is for x <= 65025");
57    ((x + 128) * 257) >> 16
58}
59
60// ---- f64 (SSE2 sd / pd forms)
61
62/// sqrtsd
63#[inline]
64pub fn sqrtd(x: f64) -> f64 {
65    use core::arch::x86_64::{_mm_cvtsd_f64, _mm_set_sd, _mm_sqrt_pd};
66    // SAFETY: SSE2 is part of the x86-64 baseline.
67    unsafe { _mm_cvtsd_f64(_mm_sqrt_pd(_mm_set_sd(x))) }
68}
69
70/// maxsd a, b: a > b ? a : b (b when unordered)
71#[inline]
72pub fn maxsd(a: f64, b: f64) -> f64 {
73    if a > b { a } else { b }
74}
75
76/// minsd a, b: a < b ? a : b (b when unordered)
77#[inline]
78pub fn minsd(a: f64, b: f64) -> f64 {
79    if a < b { a } else { b }
80}
81
82/// cvttsd2si (32-bit): truncate towards zero (0x80000000 for NaN / out of range)
83#[inline]
84pub fn cvttd(x: f64) -> i32 {
85    use core::arch::x86_64::{_mm_cvttsd_si32, _mm_set_sd};
86    // SAFETY: SSE2 is part of the x86-64 baseline.
87    unsafe { _mm_cvttsd_si32(_mm_set_sd(x)) }
88}
89
90/// cvtsd2si (32-bit): round to nearest even (0x80000000 for NaN / out of range)
91#[inline]
92pub fn cvtd(x: f64) -> i32 {
93    use core::arch::x86_64::{_mm_cvtsd_si32, _mm_set_sd};
94    // SAFETY: SSE2 is part of the x86-64 baseline.
95    unsafe { _mm_cvtsd_si32(_mm_set_sd(x)) }
96}
97
98/// cvtsd2si (64-bit): round to nearest even (i64::MIN for NaN / out of range)
99#[inline]
100pub fn cvtd64(x: f64) -> i64 {
101    use core::arch::x86_64::{_mm_cvtsd_si64, _mm_set_sd};
102    // SAFETY: SSE2 is part of the x86-64 baseline.
103    unsafe { _mm_cvtsd_si64(_mm_set_sd(x)) }
104}
105
106/// cvttsd2si (64-bit): truncate (i64::MIN for NaN / out of range)
107#[inline]
108pub fn cvttd64(x: f64) -> i64 {
109    use core::arch::x86_64::{_mm_cvttsd_si64, _mm_set_sd};
110    // SAFETY: SSE2 is part of the x86-64 baseline.
111    unsafe { _mm_cvttsd_si64(_mm_set_sd(x)) }
112}