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: ::Int = F::Int::ONE;
104
105 let sign_bit: ::Int = F::SIGN_MASK as F::Int;
106 let abs_mask: ::Int = sign_bit - one;
107 let exponent_mask: ::Int = F::EXP_MASK;
108 let inf_rep: ::Int = exponent_mask;
109
110 let a_rep: ::Int = a.to_bits();
111 let b_rep: ::Int = b.to_bits();
112 let a_abs: ::Int = a_rep & abs_mask;
113 let b_abs: ::Int = b_rep & abs_mask;
114
115 a_abs > inf_rep || b_abs > inf_rep
116}
117
118#[cfg(f16_enabled)]
119intrinsics! {
120 pub extern "C" fn __lehf2(a: f16, b: f16) -> crate::float::cmp::CmpResult {
121 cmp(a, b).to_le_abi()
122 }
123
124 pub extern "C" fn __gehf2(a: f16, b: f16) -> crate::float::cmp::CmpResult {
125 cmp(a, b).to_ge_abi()
126 }
127
128 pub extern "C" fn __unordhf2(a: f16, b: f16) -> crate::float::cmp::CmpResult {
129 unord(a, b) as crate::float::cmp::CmpResult
130 }
131
132 pub extern "C" fn __eqhf2(a: f16, b: f16) -> crate::float::cmp::CmpResult {
133 cmp(a, b).to_le_abi()
134 }
135
136 pub extern "C" fn __lthf2(a: f16, b: f16) -> crate::float::cmp::CmpResult {
137 cmp(a, b).to_le_abi()
138 }
139
140 pub extern "C" fn __nehf2(a: f16, b: f16) -> crate::float::cmp::CmpResult {
141 cmp(a, b).to_le_abi()
142 }
143
144 pub extern "C" fn __gthf2(a: f16, b: f16) -> crate::float::cmp::CmpResult {
145 cmp(a, b).to_ge_abi()
146 }
147}
148
149intrinsics! {
150 pub extern "C" fn __lesf2(a: f32, b: f32) -> crate::float::cmp::CmpResult {
151 cmp(a, b).to_le_abi()
152 }
153
154 pub extern "C" fn __gesf2(a: f32, b: f32) -> crate::float::cmp::CmpResult {
155 cmp(a, b).to_ge_abi()
156 }
157
158 #[arm_aeabi_alias = __aeabi_fcmpun]
159 pub extern "C" fn __unordsf2(a: f32, b: f32) -> crate::float::cmp::CmpResult {
160 unord(a, b) as crate::float::cmp::CmpResult
161 }
162
163 pub extern "C" fn __eqsf2(a: f32, b: f32) -> crate::float::cmp::CmpResult {
164 cmp(a, b).to_le_abi()
165 }
166
167 pub extern "C" fn __ltsf2(a: f32, b: f32) -> crate::float::cmp::CmpResult {
168 cmp(a, b).to_le_abi()
169 }
170
171 pub extern "C" fn __nesf2(a: f32, b: f32) -> crate::float::cmp::CmpResult {
172 cmp(a, b).to_le_abi()
173 }
174
175 pub extern "C" fn __gtsf2(a: f32, b: f32) -> crate::float::cmp::CmpResult {
176 cmp(a, b).to_ge_abi()
177 }
178
179 pub extern "C" fn __ledf2(a: f64, b: f64) -> crate::float::cmp::CmpResult {
180 cmp(a, b).to_le_abi()
181 }
182
183 pub extern "C" fn __gedf2(a: f64, b: f64) -> crate::float::cmp::CmpResult {
184 cmp(a, b).to_ge_abi()
185 }
186
187 #[arm_aeabi_alias = __aeabi_dcmpun]
188 pub extern "C" fn __unorddf2(a: f64, b: f64) -> crate::float::cmp::CmpResult {
189 unord(a, b) as crate::float::cmp::CmpResult
190 }
191
192 pub extern "C" fn __eqdf2(a: f64, b: f64) -> crate::float::cmp::CmpResult {
193 cmp(a, b).to_le_abi()
194 }
195
196 pub extern "C" fn __ltdf2(a: f64, b: f64) -> crate::float::cmp::CmpResult {
197 cmp(a, b).to_le_abi()
198 }
199
200 pub extern "C" fn __nedf2(a: f64, b: f64) -> crate::float::cmp::CmpResult {
201 cmp(a, b).to_le_abi()
202 }
203
204 pub extern "C" fn __gtdf2(a: f64, b: f64) -> crate::float::cmp::CmpResult {
205 cmp(a, b).to_ge_abi()
206 }
207}
208
209#[cfg(f128_enabled)]
210intrinsics! {
211 #[ppc_alias = __lekf2]
212 pub extern "C" fn __letf2(a: f128, b: f128) -> crate::float::cmp::CmpResult {
213 cmp(a, b).to_le_abi()
214 }
215
216 #[ppc_alias = __gekf2]
217 pub extern "C" fn __getf2(a: f128, b: f128) -> crate::float::cmp::CmpResult {
218 cmp(a, b).to_ge_abi()
219 }
220
221 #[ppc_alias = __unordkf2]
222 pub extern "C" fn __unordtf2(a: f128, b: f128) -> crate::float::cmp::CmpResult {
223 unord(a, b) as crate::float::cmp::CmpResult
224 }
225
226 #[ppc_alias = __eqkf2]
227 pub extern "C" fn __eqtf2(a: f128, b: f128) -> crate::float::cmp::CmpResult {
228 cmp(a, b).to_le_abi()
229 }
230
231 #[ppc_alias = __ltkf2]
232 pub extern "C" fn __lttf2(a: f128, b: f128) -> crate::float::cmp::CmpResult {
233 cmp(a, b).to_le_abi()
234 }
235
236 #[ppc_alias = __nekf2]
237 pub extern "C" fn __netf2(a: f128, b: f128) -> crate::float::cmp::CmpResult {
238 cmp(a, b).to_le_abi()
239 }
240
241 #[ppc_alias = __gtkf2]
242 pub extern "C" fn __gttf2(a: f128, b: f128) -> crate::float::cmp::CmpResult {
243 cmp(a, b).to_ge_abi()
244 }
245}
246
247#[cfg(target_arch = "arm")]
248intrinsics! {
249 pub extern "aapcs" fn __aeabi_fcmple(a: f32, b: f32) -> i32 {
250 (__lesf2(a, b) <= 0) as i32
251 }
252
253 pub extern "aapcs" fn __aeabi_fcmpge(a: f32, b: f32) -> i32 {
254 (__gesf2(a, b) >= 0) as i32
255 }
256
257 pub extern "aapcs" fn __aeabi_fcmpeq(a: f32, b: f32) -> i32 {
258 (__eqsf2(a, b) == 0) as i32
259 }
260
261 pub extern "aapcs" fn __aeabi_fcmplt(a: f32, b: f32) -> i32 {
262 (__ltsf2(a, b) < 0) as i32
263 }
264
265 pub extern "aapcs" fn __aeabi_fcmpgt(a: f32, b: f32) -> i32 {
266 (__gtsf2(a, b) > 0) as i32
267 }
268
269 pub extern "aapcs" fn __aeabi_dcmple(a: f64, b: f64) -> i32 {
270 (__ledf2(a, b) <= 0) as i32
271 }
272
273 pub extern "aapcs" fn __aeabi_dcmpge(a: f64, b: f64) -> i32 {
274 (__gedf2(a, b) >= 0) as i32
275 }
276
277 pub extern "aapcs" fn __aeabi_dcmpeq(a: f64, b: f64) -> i32 {
278 (__eqdf2(a, b) == 0) as i32
279 }
280
281 pub extern "aapcs" fn __aeabi_dcmplt(a: f64, b: f64) -> i32 {
282 (__ltdf2(a, b) < 0) as i32
283 }
284
285 pub extern "aapcs" fn __aeabi_dcmpgt(a: f64, b: f64) -> i32 {
286 (__gtdf2(a, b) > 0) as i32
287 }
288}
289