compiler_builtins/float/
cmp.rs

1#![allow(unreachable_code)]
2
3use crate::float::Float;
4use crate::int::MinInt;
5use crate::support::cfg_if;
6
7// Taken from LLVM config:
8// https://github.com/llvm/llvm-project/blob/0cf3c437c18ed27d9663d87804a9a15ff6874af2/compiler-rt/lib/builtins/fp_compare_impl.inc#L11-L27
9cfg_if! {
10    if #[cfg(any(target_arch = "aarch64", target_arch = "arm64ec"))] {
11        // Aarch64 uses `int` rather than a pointer-sized value.
12        pub type CmpResult = i32;
13    } else if #[cfg(target_arch = "avr")] {
14        // AVR uses a single byte.
15        pub type CmpResult = i8;
16    } else {
17        // In compiler-rt, LLP64 ABIs use `long long` and everything else uses `long`. In effect,
18        // this means the return value is always pointer-sized.
19        pub type CmpResult = isize;
20    }
21}
22
23#[derive(Clone, Copy)]
24enum Result {
25    Less,
26    Equal,
27    Greater,
28    Unordered,
29}
30
31impl Result {
32    fn to_le_abi(self) -> CmpResult {
33        match self {
34            Result::Less => -1,
35            Result::Equal => 0,
36            Result::Greater => 1,
37            Result::Unordered => 1,
38        }
39    }
40
41    fn to_ge_abi(self) -> CmpResult {
42        match self {
43            Result::Less => -1,
44            Result::Equal => 0,
45            Result::Greater => 1,
46            Result::Unordered => -1,
47        }
48    }
49}
50
51fn cmp<F: Float>(a: F, b: F) -> Result {
52    let one = F::Int::ONE;
53    let zero = F::Int::ZERO;
54    let szero = F::SignedInt::ZERO;
55
56    let sign_bit = F::SIGN_MASK as F::Int;
57    let abs_mask = sign_bit - one;
58    let exponent_mask = F::EXP_MASK;
59    let inf_rep = exponent_mask;
60
61    let a_rep = a.to_bits();
62    let b_rep = b.to_bits();
63    let a_abs = a_rep & abs_mask;
64    let b_abs = b_rep & abs_mask;
65
66    // If either a or b is NaN, they are unordered.
67    if a_abs > inf_rep || b_abs > inf_rep {
68        return Result::Unordered;
69    }
70
71    // If a and b are both zeros, they are equal.
72    if a_abs | b_abs == zero {
73        return Result::Equal;
74    }
75
76    let a_srep = a.to_bits_signed();
77    let b_srep = b.to_bits_signed();
78
79    // If at least one of a and b is positive, we get the same result comparing
80    // a and b as signed integers as we would with a fp_ting-point compare.
81    if a_srep & b_srep >= szero {
82        if a_srep < b_srep {
83            Result::Less
84        } else if a_srep == b_srep {
85            Result::Equal
86        } else {
87            Result::Greater
88        }
89    // Otherwise, both are negative, so we need to flip the sense of the
90    // comparison to get the correct result.  (This assumes a twos- or ones-
91    // complement integer representation; if integers are represented in a
92    // sign-magnitude representation, then this flip is incorrect).
93    } else if a_srep > b_srep {
94        Result::Less
95    } else if a_srep == b_srep {
96        Result::Equal
97    } else {
98        Result::Greater
99    }
100}
101
102fn unord<F: Float>(a: F, b: F) -> bool {
103    let one = F::Int::ONE;
104
105    let sign_bit = F::SIGN_MASK as F::Int;
106    let abs_mask = sign_bit - one;
107    let exponent_mask = F::EXP_MASK;
108    let inf_rep = exponent_mask;
109
110    let a_rep = a.to_bits();
111    let b_rep = b.to_bits();
112    let a_abs = a_rep & abs_mask;
113    let b_abs = b_rep & abs_mask;
114
115    a_abs > inf_rep || b_abs > inf_rep
116}
117
118intrinsics! {
119    pub extern "C" fn __lesf2(a: f32, b: f32) -> crate::float::cmp::CmpResult {
120        cmp(a, b).to_le_abi()
121    }
122
123    pub extern "C" fn __gesf2(a: f32, b: f32) -> crate::float::cmp::CmpResult {
124        cmp(a, b).to_ge_abi()
125    }
126
127    #[arm_aeabi_alias = __aeabi_fcmpun]
128    pub extern "C" fn __unordsf2(a: f32, b: f32) -> crate::float::cmp::CmpResult {
129        unord(a, b) as crate::float::cmp::CmpResult
130    }
131
132    pub extern "C" fn __eqsf2(a: f32, b: f32) -> crate::float::cmp::CmpResult {
133        cmp(a, b).to_le_abi()
134    }
135
136    pub extern "C" fn __ltsf2(a: f32, b: f32) -> crate::float::cmp::CmpResult {
137        cmp(a, b).to_le_abi()
138    }
139
140    pub extern "C" fn __nesf2(a: f32, b: f32) -> crate::float::cmp::CmpResult {
141        cmp(a, b).to_le_abi()
142    }
143
144    pub extern "C" fn __gtsf2(a: f32, b: f32) -> crate::float::cmp::CmpResult {
145        cmp(a, b).to_ge_abi()
146    }
147
148    pub extern "C" fn __ledf2(a: f64, b: f64) -> crate::float::cmp::CmpResult {
149        cmp(a, b).to_le_abi()
150    }
151
152    pub extern "C" fn __gedf2(a: f64, b: f64) -> crate::float::cmp::CmpResult {
153        cmp(a, b).to_ge_abi()
154    }
155
156    #[arm_aeabi_alias = __aeabi_dcmpun]
157    pub extern "C" fn __unorddf2(a: f64, b: f64) -> crate::float::cmp::CmpResult {
158        unord(a, b) as crate::float::cmp::CmpResult
159    }
160
161    pub extern "C" fn __eqdf2(a: f64, b: f64) -> crate::float::cmp::CmpResult {
162        cmp(a, b).to_le_abi()
163    }
164
165    pub extern "C" fn __ltdf2(a: f64, b: f64) -> crate::float::cmp::CmpResult {
166        cmp(a, b).to_le_abi()
167    }
168
169    pub extern "C" fn __nedf2(a: f64, b: f64) -> crate::float::cmp::CmpResult {
170        cmp(a, b).to_le_abi()
171    }
172
173    pub extern "C" fn __gtdf2(a: f64, b: f64) -> crate::float::cmp::CmpResult {
174        cmp(a, b).to_ge_abi()
175    }
176}
177
178#[cfg(f128_enabled)]
179intrinsics! {
180    #[ppc_alias = __lekf2]
181    pub extern "C" fn __letf2(a: f128, b: f128) -> crate::float::cmp::CmpResult {
182        cmp(a, b).to_le_abi()
183    }
184
185    #[ppc_alias = __gekf2]
186    pub extern "C" fn __getf2(a: f128, b: f128) -> crate::float::cmp::CmpResult {
187        cmp(a, b).to_ge_abi()
188    }
189
190    #[ppc_alias = __unordkf2]
191    pub extern "C" fn __unordtf2(a: f128, b: f128) -> crate::float::cmp::CmpResult {
192        unord(a, b) as crate::float::cmp::CmpResult
193    }
194
195    #[ppc_alias = __eqkf2]
196    pub extern "C" fn __eqtf2(a: f128, b: f128) -> crate::float::cmp::CmpResult {
197        cmp(a, b).to_le_abi()
198    }
199
200    #[ppc_alias = __ltkf2]
201    pub extern "C" fn __lttf2(a: f128, b: f128) -> crate::float::cmp::CmpResult {
202        cmp(a, b).to_le_abi()
203    }
204
205    #[ppc_alias = __nekf2]
206    pub extern "C" fn __netf2(a: f128, b: f128) -> crate::float::cmp::CmpResult {
207        cmp(a, b).to_le_abi()
208    }
209
210    #[ppc_alias = __gtkf2]
211    pub extern "C" fn __gttf2(a: f128, b: f128) -> crate::float::cmp::CmpResult {
212        cmp(a, b).to_ge_abi()
213    }
214}
215
216#[cfg(target_arch = "arm")]
217intrinsics! {
218    pub extern "aapcs" fn __aeabi_fcmple(a: f32, b: f32) -> i32 {
219        (__lesf2(a, b) <= 0) as i32
220    }
221
222    pub extern "aapcs" fn __aeabi_fcmpge(a: f32, b: f32) -> i32 {
223        (__gesf2(a, b) >= 0) as i32
224    }
225
226    pub extern "aapcs" fn __aeabi_fcmpeq(a: f32, b: f32) -> i32 {
227        (__eqsf2(a, b) == 0) as i32
228    }
229
230    pub extern "aapcs" fn __aeabi_fcmplt(a: f32, b: f32) -> i32 {
231        (__ltsf2(a, b) < 0) as i32
232    }
233
234    pub extern "aapcs" fn __aeabi_fcmpgt(a: f32, b: f32) -> i32 {
235        (__gtsf2(a, b) > 0) as i32
236    }
237
238    pub extern "aapcs" fn __aeabi_dcmple(a: f64, b: f64) -> i32 {
239        (__ledf2(a, b) <= 0) as i32
240    }
241
242    pub extern "aapcs" fn __aeabi_dcmpge(a: f64, b: f64) -> i32 {
243        (__gedf2(a, b) >= 0) as i32
244    }
245
246    pub extern "aapcs" fn __aeabi_dcmpeq(a: f64, b: f64) -> i32 {
247        (__eqdf2(a, b) == 0) as i32
248    }
249
250    pub extern "aapcs" fn __aeabi_dcmplt(a: f64, b: f64) -> i32 {
251        (__ltdf2(a, b) < 0) as i32
252    }
253
254    pub extern "aapcs" fn __aeabi_dcmpgt(a: f64, b: f64) -> i32 {
255        (__gtdf2(a, b) > 0) as i32
256    }
257}