1use super::*;
2
3pick! {
4 if #[cfg(target_feature="sse2")] {
5 #[derive(Default, Clone, Copy, PartialEq, Eq)]
12 #[repr(C, align(16))]
13 pub struct i64x2 { pub(crate) sse: m128i }
14 } else if #[cfg(target_feature="simd128")] {
15 use core::arch::wasm32::*;
16
17 #[derive(Clone, Copy)]
24 #[repr(transparent)]
25 pub struct i64x2 { pub(crate) simd: v128 }
26
27 impl Default for i64x2 {
28 fn default() -> Self {
29 Self::splat(0)
30 }
31 }
32
33 impl PartialEq for i64x2 {
34 fn eq(&self, other: &Self) -> bool {
35 u64x2_all_true(i64x2_eq(self.simd, other.simd))
36 }
37 }
38
39 impl Eq for i64x2 { }
40 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
41 use core::arch::aarch64::*;
42
43 #[repr(C)]
50 #[derive(Copy, Clone)]
51 pub struct i64x2 { pub(crate) neon : int64x2_t }
52
53 impl Default for i64x2 {
54 #[inline]
55 fn default() -> Self {
56 unsafe { Self { neon: vdupq_n_s64(0)} }
57 }
58 }
59
60 impl PartialEq for i64x2 {
61 #[inline]
62 fn eq(&self, other: &Self) -> bool {
63 unsafe {
64 vgetq_lane_s64(self.neon,0) == vgetq_lane_s64(other.neon,0) && vgetq_lane_s64(self.neon,1) == vgetq_lane_s64(other.neon,1)
65 }
66 }
67 }
68
69 impl Eq for i64x2 { }
70 } else {
71 #[derive(Default, Clone, Copy, PartialEq, Eq)]
78 #[repr(C, align(16))]
79 pub struct i64x2 { arr: [i64;2] }
80 }
81}
82
83impl_simd_int! {
84 unsafe {
85 T = i64,
86 N = 2,
87 Simd = i64x2,
88 UintSimd = u64x2,
89 T_BITS = 64,
90 T_BITS_MUL_2 = 128,
91 BitmaskType = u32,
92 [0, 1],
93 optional_type_x86_inner { X86Inner = __m128i },
94 optional_type_arm_inner { ArmInner = int64x2_t },
95 optional_type_wasm_inner { WasmInner = v128 },
96 }
97
98 #[inline]
99 fn simd_lt(self, rhs: Self) -> Self::Output {
100 pick! {
101 if #[cfg(target_feature="sse4.2")] {
102 Self { sse: cmp_gt_mask_i64_m128i( rhs.sse, self.sse) }
104 } else if #[cfg(target_feature="simd128")] {
105 Self { simd: i64x2_lt(self.simd, rhs.simd) }
106 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
107 unsafe {Self { neon: vreinterpretq_s64_u64(vcltq_s64(self.neon, rhs.neon)) }}
108 } else {
109 let s: [i64;2] = cast(self);
110 let r: [i64;2] = cast(rhs);
111 cast([
112 if s[0] < r[0] { -1_i64 } else { 0 },
113 if s[1] < r[1] { -1_i64 } else { 0 },
114 ])
115 }
116 }
117 }
118
119 #[inline]
120 fn simd_gt(self, rhs: Self) -> Self::Output {
121 pick! {
122 if #[cfg(target_feature="sse4.2")] {
123 Self { sse: cmp_gt_mask_i64_m128i(self.sse, rhs.sse) }
124 } else if #[cfg(target_feature="simd128")] {
125 Self { simd: i64x2_gt(self.simd, rhs.simd) }
126 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
127 unsafe {Self { neon: vreinterpretq_s64_u64(vcgtq_s64(self.neon, rhs.neon)) }}
128 } else {
129 let s: [i64;2] = cast(self);
130 let r: [i64;2] = cast(rhs);
131 cast([
132 if s[0] > r[0] { -1_i64 } else { 0 },
133 if s[1] > r[1] { -1_i64 } else { 0 },
134 ])
135 }
136 }
137 }
138
139 #[inline]
140 fn simd_le(self, rhs: Self) -> Self::Output {
141 pick! {
142 if #[cfg(target_feature="sse4.1")] {
143 !self.simd_gt(rhs)
144 } else if #[cfg(target_feature="simd128")] {
145 Self { simd: i64x2_le(self.simd, rhs.simd) }
146 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
147 !self.simd_gt(rhs)
148 } else {
149 let s: [i64;2] = cast(self);
150 let r: [i64;2] = cast(rhs);
151 cast([
152 if s[0] <= r[0] { -1_i64 } else { 0 },
153 if s[1] <= r[1] { -1_i64 } else { 0 },
154 ])
155 }
156 }
157 }
158
159 #[inline]
160 fn simd_ge(self, rhs: Self) -> Self::Output {
161 pick! {
162 if #[cfg(target_feature="sse4.1")] {
163 !self.simd_lt(rhs)
164 } else if #[cfg(target_feature="simd128")] {
165 Self { simd: i64x2_ge(self.simd, rhs.simd) }
166 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
167 !self.simd_lt(rhs)
168 } else {
169 let s: [i64;2] = cast(self);
170 let r: [i64;2] = cast(rhs);
171 cast([
172 if s[0] >= r[0] { -1_i64 } else { 0 },
173 if s[1] >= r[1] { -1_i64 } else { 0 },
174 ])
175 }
176 }
177 }
178
179 #[inline]
180 fn shr(self, rhs: u64x2) -> Self::Output {
181 pick! {
182 if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
183 unsafe {
184 let shift_by = vnegq_s64(vreinterpretq_s64_u64(vandq_u64(rhs.neon, vmovq_n_u64(63))));
187 Self { neon: vshlq_s64(self.neon, shift_by) }
188 }
189 } else {
190 let arr: [i64; 2] = cast(self);
191 let rhs: [u64; 2] = cast(rhs);
192 cast([
193 arr[0].wrapping_shr(rhs[0] as u32),
194 arr[1].wrapping_shr(rhs[1] as u32),
195 ])
196 }
197 }
198 }
199
200 #[inline]
201 fn shr(self, rhs: u32) -> Self::Output {
202 pick! {
203 if #[cfg(target_feature="simd128")] {
204 Self { simd: i64x2_shr(self.simd, rhs) }
205 } else {
206 let arr: [i64; 2] = cast(self);
207 cast([
208 arr[0].wrapping_shr(rhs),
209 arr[1].wrapping_shr(rhs),
210 ])
211 }
212 }
213 }
214
215 #[inline]
216 pub fn max(self, rhs: Self) -> Self {
217 self.simd_gt(rhs).select(self, rhs)
218 }
219
220 #[inline]
221 pub fn min(self, rhs: Self) -> Self {
222 self.simd_lt(rhs).select(self, rhs)
223 }
224
225 #[inline]
226 pub fn reduce_max(self) -> i64 {
227 pick! {
228 if #[cfg(any(target_feature="sse2", target_feature="simd128"))] {
229 let array: [i64; 2] = cast(self);
230 array[0].max(array[1])
231 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
232 unsafe { vgetq_lane_s64(self.neon, 0).max(vgetq_lane_s64(self.neon, 1)) }
233 } else {
234 self.arr[0].max(self.arr[1])
235 }
236 }
237 }
238
239 #[inline]
240 pub fn reduce_min(self) -> i64 {
241 pick! {
242 if #[cfg(any(target_feature="sse2", target_feature="simd128"))] {
243 let array: [i64; 2] = cast(self);
244 array[0].min(array[1])
245 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
246 unsafe { vgetq_lane_s64(self.neon, 0).min(vgetq_lane_s64(self.neon, 1)) }
247 } else {
248 self.arr[0].min(self.arr[1])
249 }
250 }
251 }
252
253 #[inline]
254 pub fn unbounded_shr(self, rhs: u64x2) -> Self {
255 pick! {
256 if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
257 unsafe {
258 Self { neon: vshlq_s64(self.neon, vnegq_s64(vreinterpretq_s64_u64(rhs.min(u64x2::splat(64)).neon))) }
261 }
262 } else {
263 cast::<u64x2, i64x2>(rhs.simd_lt(64)).select(
266 self >> cast::<u64x2, i64x2>(rhs),
267 self.is_negative(),
268 )
269 }
270 }
271 }
272
273 #[inline]
274 pub fn unbounded_shr_scalar(self, rhs: u32) -> Self {
275 pick! {
276 if #[cfg(target_feature="simd128")] {
277 if rhs < 64 { Self { simd: i64x2_shr(self.simd, rhs) } } else { self.is_negative() }
278 } else {
279 let self_array = self.to_array();
280
281 Self::new([
282 self_array[0].unbounded_shr(rhs),
283 self_array[1].unbounded_shr(rhs),
284 ])
285 }
286 }
287 }
288
289 #[inline]
290 pub fn saturating_add(self, rhs: Self) -> Self {
291 pick! {
292 if #[cfg(any(target_feature="sse2", target_feature="simd128"))] {
293 let result = self + rhs;
294 let overflow = (!(self ^ rhs) & (self ^ result)).is_negative();
295 let negative = self.is_negative();
296
297 overflow.select(Self::MAX ^ negative, result)
299 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
300 unsafe { Self { neon: vqaddq_s64(self.neon, rhs.neon) } }
301 } else {
302 Self {
303 arr: [
304 self.arr[0].saturating_add(rhs.arr[0]),
305 self.arr[1].saturating_add(rhs.arr[1]),
306 ],
307 }
308 }
309 }
310 }
311
312 #[inline]
313 pub fn saturating_sub(self, rhs: Self) -> Self {
314 pick! {
315 if #[cfg(any(target_feature="sse2", target_feature="simd128"))] {
316 let result = self - rhs;
317 let overflow = ((self ^ rhs) & (self ^ result)).is_negative();
318 let negative = self.is_negative();
319
320 overflow.select(Self::MAX ^ negative, result)
322 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
323 unsafe { Self { neon: vqsubq_s64(self.neon, rhs.neon) } }
324 } else {
325 Self {
326 arr: [
327 self.arr[0].saturating_sub(rhs.arr[0]),
328 self.arr[1].saturating_sub(rhs.arr[1]),
329 ],
330 }
331 }
332 }
333 }
334
335 #[inline]
336 pub fn overflowing_mul(self, rhs: Self) -> (Self, Self) {
337 let self_array = self.to_array();
342 let rhs_array = rhs.to_array();
343
344 let result = [
345 self_array[0].overflowing_mul(rhs_array[0]),
346 self_array[1].overflowing_mul(rhs_array[1]),
347 ];
348 (
349 Self::new([result[0].0, result[1].0]),
350 Self::new([-(result[0].1 as i64), -(result[1].1 as i64)]),
351 )
352 }
353
354 optional_fn_widening_mul {
355 }
357
358 #[inline]
359 pub fn mul_keep_low_high(self, rhs: Self) -> (u64x2, i64x2) {
360 let self_array = self.to_array();
364 let rhs_array = rhs.to_array();
365
366 let widening_mul = [
367 (self_array[0] as i128).wrapping_mul(rhs_array[0] as i128),
368 (self_array[1] as i128).wrapping_mul(rhs_array[1] as i128),
369 ];
370
371 (
372 u64x2::new([
373 widening_mul[0] as u64,
374 widening_mul[1] as u64,
375 ]),
376 i64x2::new([
377 (widening_mul[0] >> 64) as i64,
378 (widening_mul[1] >> 64) as i64,
379 ]),
380 )
381 }
382
383 #[inline]
384 pub fn mul_keep_high(self, rhs: Self) -> Self {
385 let self_array = self.to_array();
386 let rhs_array = rhs.to_array();
387
388 Self::new([
389 ((self_array[0] as i128).wrapping_mul(rhs_array[0] as i128) >> 64) as i64,
390 ((self_array[1] as i128).wrapping_mul(rhs_array[1] as i128) >> 64) as i64,
391 ])
392 }
393
394 #[inline]
395 pub fn abs(self) -> Self {
396 pick! {
397 if #[cfg(target_feature="simd128")] {
399 Self { simd: i64x2_abs(self.simd) }
400 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
401 unsafe {Self { neon: vabsq_s64(self.neon) }}
402 } else {
403 let arr: [i64; 2] = cast(self);
404 cast(
405 [
406 arr[0].wrapping_abs(),
407 arr[1].wrapping_abs(),
408 ])
409 }
410 }
411 }
412
413 #[inline]
414 pub fn is_positive(self) -> Self {
415 pick! {
416 if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
417 Self { neon: unsafe { vreinterpretq_s64_u64(vcgtzq_s64(self.neon)) } }
418 } else {
419 self.simd_gt(Self::ZERO)
420 }
421 }
422 }
423
424 #[inline]
425 pub fn is_negative(self) -> Self {
426 pick! {
427 if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
428 Self { neon: unsafe { vreinterpretq_s64_u64(vcltzq_s64(self.neon)) } }
429 } else {
430 self.simd_lt(Self::ZERO)
431 }
432 }
433 }
434
435 optional_fn_deserialize {}
436}
437
438impl i64x2 {
441 #[inline]
443 #[must_use]
444 pub fn round_float(self) -> f64x2 {
445 let arr: [i64; 2] = cast(self);
446 cast([arr[0] as f64, arr[1] as f64])
447 }
448
449 #[must_use]
451 #[inline]
452 #[allow(dead_code)]
453 pub(crate) fn unpack_lo(self, b: Self) -> Self {
454 pick! {
455 if #[cfg(target_feature="sse2")] {
456 Self { sse: unpack_low_i64_m128i(self.sse, b.sse) }
457 } else if #[cfg(target_feature="simd128")] {
458 Self { simd: i64x2_shuffle::<0, 2>(self.simd, b.simd) }
459 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))] {
460 Self { neon: unsafe { vzip1q_s64(self.neon, b.neon) } }
461 } else {
462 Self::new([self.as_array()[0], b.as_array()[0]])
463 }
464 }
465 }
466
467 #[must_use]
469 #[inline]
470 #[allow(dead_code)]
471 pub(crate) fn unpack_hi(self, b: Self) -> Self {
472 pick! {
473 if #[cfg(target_feature="sse2")] {
474 Self { sse: unpack_high_i64_m128i(self.sse, b.sse) }
475 } else if #[cfg(target_feature="simd128")] {
476 Self { simd: i64x2_shuffle::<1, 3>(self.simd, b.simd) }
477 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))] {
478 Self { neon: unsafe { vzip2q_s64(self.neon, b.neon) } }
479 } else {
480 Self::new([self.as_array()[1], b.as_array()[1]])
481 }
482 }
483 }
484}