Eigen  5.0.1
 
Loading...
Searching...
No Matches
PacketMath.h
1// This file is part of Eigen, a lightweight C++ template library
2// for linear algebra.
3//
4// Copyright (C) 2020, Arm Limited and Contributors
5//
6// This Source Code Form is subject to the terms of the Mozilla
7// Public License v. 2.0. If a copy of the MPL was not distributed
8// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
9// SPDX-License-Identifier: MPL-2.0
10
11#ifndef EIGEN_PACKET_MATH_SVE_H
12#define EIGEN_PACKET_MATH_SVE_H
13
14// IWYU pragma: private
15#include "../../InternalHeaderCheck.h"
16
17namespace Eigen {
18namespace internal {
19#ifndef EIGEN_CACHEFRIENDLY_PRODUCT_THRESHOLD
20#define EIGEN_CACHEFRIENDLY_PRODUCT_THRESHOLD 8
21#endif
22
23#ifndef EIGEN_HAS_SINGLE_INSTRUCTION_MADD
24#define EIGEN_HAS_SINGLE_INSTRUCTION_MADD
25#endif
26
27#define EIGEN_ARCH_DEFAULT_NUMBER_OF_REGISTERS 32
28
29template <typename Scalar, int SVEVectorLength>
30struct sve_packet_size_selector {
31 enum { size = SVEVectorLength / (sizeof(Scalar) * CHAR_BIT) };
32};
33
34// A fixed-length SVE packet needs exactly its own size in alignment, no more --
35// hard-coding Aligned64 over-constrains every vector length below 512 and, since
36// the requirement then exceeds what a fixed-size object can offer, disables
37// vectorization for those types outright. The Alignment enum tops out at
38// Aligned128 (AlignedMask is 255), so clamp there for VL >= 2048.
39template <int SVEVectorLength>
40struct sve_packet_alignment_selector {
41 enum { alignment = plain_enum_min(SVEVectorLength / CHAR_BIT, Aligned128) };
42};
43
44/********************************* int32 **************************************/
45// Keep typedef: Doxygen 1.13.2 misparses an attributed using declaration.
46typedef svint32_t PacketXi __attribute__((arm_sve_vector_bits(EIGEN_ARM64_SVE_VL)));
47
48template <>
49struct packet_traits<numext::int32_t> : default_packet_traits {
50 typedef PacketXi type;
51 typedef PacketXi half; // Half not implemented yet
52 enum {
53 Vectorizable = 1,
54 AlignedOnScalar = 1,
55 size = sve_packet_size_selector<numext::int32_t, EIGEN_ARM64_SVE_VL>::size,
56
57 HasAdd = 1,
58 HasSub = 1,
59 HasShift = 1,
60 HasMul = 1,
61 HasNegate = 1,
62 HasAbs = 1,
63 HasArg = 0,
64 HasMin = 1,
65 HasMax = 1,
66 HasConj = 1,
67 // HasSetLinear and HasCmp stay 0 even though plset and pcmp_{eq,lt,le} are
68 // implemented below: both flags route work onto a packet path that measures
69 // far slower than the scalar loop GCC autovectorizes. On Neoverse V2 at
70 // VL=128, HasSetLinear costs LinSpaced 20.7 -> 60.6 us for float and
71 // 20.7 -> 121.6 us for double, and HasCmp costs (a < b).select(a, b)
72 // 6.2 -> 50.6 us. Neither is explained by pselect: these numbers are with
73 // the svsel specialization below in place, and it made the LinSpaced case
74 // worse rather than better. The cause is higher up, in how those evaluators
75 // drive the packet path, and should be found before either flag is set.
76 HasSetLinear = 0,
77 HasReduxp = 0 // Not implemented in SVE
78 };
79};
80
81template <>
82struct unpacket_traits<PacketXi> {
83 typedef numext::int32_t type;
84 typedef PacketXi half; // Half not yet implemented
85 enum {
86 size = sve_packet_size_selector<numext::int32_t, EIGEN_ARM64_SVE_VL>::size,
87 alignment = sve_packet_alignment_selector<EIGEN_ARM64_SVE_VL>::alignment,
88 vectorizable = true,
89 masked_load_available = false,
90 masked_store_available = false
91 };
92};
93
94template <>
95EIGEN_STRONG_INLINE void prefetch<numext::int32_t>(const numext::int32_t* addr) {
96 svprfw(svptrue_b32(), addr, SV_PLDL1KEEP);
97}
98
99template <>
100EIGEN_STRONG_INLINE PacketXi pset1<PacketXi>(const numext::int32_t& from) {
101 return svdup_n_s32(from);
102}
103
104template <>
105EIGEN_STRONG_INLINE PacketXi plset<PacketXi>(const numext::int32_t& a) {
106 return svindex_s32(a, 1);
107}
108
109template <>
110EIGEN_STRONG_INLINE PacketXi padd<PacketXi>(const PacketXi& a, const PacketXi& b) {
111 return svadd_s32_x(svptrue_b32(), a, b);
112}
113
114template <>
115EIGEN_STRONG_INLINE PacketXi psub<PacketXi>(const PacketXi& a, const PacketXi& b) {
116 return svsub_s32_x(svptrue_b32(), a, b);
117}
118
119template <>
120EIGEN_STRONG_INLINE PacketXi pnegate(const PacketXi& a) {
121 return svneg_s32_x(svptrue_b32(), a);
122}
123
124template <>
125EIGEN_STRONG_INLINE PacketXi pmul<PacketXi>(const PacketXi& a, const PacketXi& b) {
126 return svmul_s32_x(svptrue_b32(), a, b);
127}
128
129template <>
130EIGEN_STRONG_INLINE PacketXi pdiv<PacketXi>(const PacketXi& a, const PacketXi& b) {
131 return svdiv_s32_x(svptrue_b32(), a, b);
132}
133
134template <>
135EIGEN_STRONG_INLINE PacketXi pmadd(const PacketXi& a, const PacketXi& b, const PacketXi& c) {
136 return svmla_s32_x(svptrue_b32(), c, a, b);
137}
138
139template <>
140EIGEN_STRONG_INLINE PacketXi pmin<PacketXi>(const PacketXi& a, const PacketXi& b) {
141 return svmin_s32_x(svptrue_b32(), a, b);
142}
143
144template <>
145EIGEN_STRONG_INLINE PacketXi pmax<PacketXi>(const PacketXi& a, const PacketXi& b) {
146 return svmax_s32_x(svptrue_b32(), a, b);
147}
148
149template <>
150EIGEN_STRONG_INLINE PacketXi pcmp_le<PacketXi>(const PacketXi& a, const PacketXi& b) {
151 return svdup_n_s32_z(svcmple_s32(svptrue_b32(), a, b), 0xffffffffu);
152}
153
154template <>
155EIGEN_STRONG_INLINE PacketXi pcmp_lt<PacketXi>(const PacketXi& a, const PacketXi& b) {
156 return svdup_n_s32_z(svcmplt_s32(svptrue_b32(), a, b), 0xffffffffu);
157}
158
159template <>
160EIGEN_STRONG_INLINE PacketXi pcmp_eq<PacketXi>(const PacketXi& a, const PacketXi& b) {
161 return svdup_n_s32_z(svcmpeq_s32(svptrue_b32(), a, b), 0xffffffffu);
162}
163
164template <>
165EIGEN_STRONG_INLINE PacketXi ptrue<PacketXi>(const PacketXi& /*a*/) {
166 return svdup_n_s32_x(svptrue_b32(), 0xffffffffu);
167}
168
169template <>
170EIGEN_STRONG_INLINE PacketXi pzero<PacketXi>(const PacketXi& /*a*/) {
171 return svdup_n_s32_x(svptrue_b32(), 0);
172}
173
174template <>
175EIGEN_STRONG_INLINE PacketXi pand<PacketXi>(const PacketXi& a, const PacketXi& b) {
176 return svand_s32_x(svptrue_b32(), a, b);
177}
178
179template <>
180EIGEN_STRONG_INLINE PacketXi por<PacketXi>(const PacketXi& a, const PacketXi& b) {
181 return svorr_s32_x(svptrue_b32(), a, b);
182}
183
184template <>
185EIGEN_STRONG_INLINE PacketXi pxor<PacketXi>(const PacketXi& a, const PacketXi& b) {
186 return sveor_s32_x(svptrue_b32(), a, b);
187}
188
189template <>
190EIGEN_STRONG_INLINE PacketXi pandnot<PacketXi>(const PacketXi& a, const PacketXi& b) {
191 return svbic_s32_x(svptrue_b32(), a, b);
192}
193
194// SVE selects on a predicate, so turn Eigen's all-ones/all-zeros value mask back
195// into one and use svsel rather than the generic por(pand, pandnot). Measured on
196// Neoverse V2 at VL=128: latency 3.18 -> 2.55 ns, throughput 0.80 -> 0.64 ns/op.
197// SVE2's single-instruction svbsl is no faster than this, so it is not worth an
198// ISA-conditional path. The mask is compared as an integer: as a float it would
199// be a NaN bit pattern, and relying on NaN != 0 is needlessly subtle.
200template <>
201EIGEN_STRONG_INLINE PacketXi pselect<PacketXi>(const PacketXi& mask, const PacketXi& a, const PacketXi& b) {
202 return svsel_s32(svcmpne_n_s32(svptrue_b32(), mask, 0), a, b);
203}
204
205template <int N>
206EIGEN_STRONG_INLINE PacketXi parithmetic_shift_right(const PacketXi& a) {
207 // ASR, not ASRD: ASRD is the shift-for-divide form, which rounds toward zero,
208 // whereas a C++ arithmetic right shift rounds toward negative infinity.
209 return svasr_n_s32_x(svptrue_b32(), a, N);
210}
211
212template <int N>
213EIGEN_STRONG_INLINE PacketXi plogical_shift_right(const PacketXi& a) {
214 return svreinterpret_s32_u32(svlsr_n_u32_x(svptrue_b32(), svreinterpret_u32_s32(a), N));
215}
216
217template <int N>
218EIGEN_STRONG_INLINE PacketXi plogical_shift_left(const PacketXi& a) {
219 return svlsl_n_s32_x(svptrue_b32(), a, N);
220}
221
222template <>
223EIGEN_STRONG_INLINE PacketXi pload<PacketXi>(const numext::int32_t* from) {
224 EIGEN_DEBUG_ALIGNED_LOAD return svld1_s32(svptrue_b32(), from);
225}
226
227template <>
228EIGEN_STRONG_INLINE PacketXi ploadu<PacketXi>(const numext::int32_t* from) {
229 EIGEN_DEBUG_UNALIGNED_LOAD return svld1_s32(svptrue_b32(), from);
230}
231
232template <>
233EIGEN_STRONG_INLINE PacketXi ploaddup<PacketXi>(const numext::int32_t* from) {
234 // Load the size/2 values this reads into the low half and interleave them
235 // with themselves: svzip1 only consumes the low halves of its operands.
236 // The predicate is exact rather than svptrue -- ploaddup may only touch
237 // size/2 elements, and a wider one would read past the end of the input.
238 constexpr uint64_t kHalf = uint64_t(packet_traits<numext::int32_t>::size) / 2;
239 svint32_t lo = svld1_s32(svwhilelt_b32(uint64_t(0), kHalf), from);
240 return svzip1_s32(lo, lo);
241}
242
243template <>
244EIGEN_STRONG_INLINE PacketXi ploadquad<PacketXi>(const numext::int32_t* from) {
245 // As ploaddup, one zip further: size/4 values, each repeated four times.
246 // At the smallest vector length size/4 rounds to zero, where one element
247 // still has to be read.
248 constexpr uint64_t kQuarter = numext::maxi(uint64_t(packet_traits<numext::int32_t>::size) / 4, uint64_t(1));
249 svint32_t lo = svld1_s32(svwhilelt_b32(uint64_t(0), kQuarter), from);
250 lo = svzip1_s32(lo, lo);
251 return svzip1_s32(lo, lo);
252}
253
254template <>
255EIGEN_STRONG_INLINE void pstore<numext::int32_t>(numext::int32_t* to, const PacketXi& from) {
256 EIGEN_DEBUG_ALIGNED_STORE svst1_s32(svptrue_b32(), to, from);
257}
258
259template <>
260EIGEN_STRONG_INLINE void pstoreu<numext::int32_t>(numext::int32_t* to, const PacketXi& from) {
261 EIGEN_DEBUG_UNALIGNED_STORE svst1_s32(svptrue_b32(), to, from);
262}
263
264template <>
265EIGEN_DEVICE_FUNC inline PacketXi pgather<numext::int32_t, PacketXi>(const numext::int32_t* from, Index stride) {
266 // Index format: {base=0, base+stride, base+stride*2, base+stride*3, ...}
267 svint32_t indices = svindex_s32(0, stride);
268 return svld1_gather_s32index_s32(svptrue_b32(), from, indices);
269}
270
271template <>
272EIGEN_DEVICE_FUNC inline void pscatter<numext::int32_t, PacketXi>(numext::int32_t* to, const PacketXi& from,
273 Index stride) {
274 // Index format: {base=0, base+stride, base+stride*2, base+stride*3, ...}
275 svint32_t indices = svindex_s32(0, stride);
276 svst1_scatter_s32index_s32(svptrue_b32(), to, indices, from);
277}
278
279template <>
280EIGEN_STRONG_INLINE numext::int32_t pfirst<PacketXi>(const PacketXi& a) {
281 // svlasta returns the first element if all predicate bits are 0
282 return svlasta_s32(svpfalse_b(), a);
283}
284
285template <>
286EIGEN_STRONG_INLINE PacketXi preverse(const PacketXi& a) {
287 return svrev_s32(a);
288}
289
290template <>
291EIGEN_STRONG_INLINE PacketXi pabs(const PacketXi& a) {
292 return svabs_s32_x(svptrue_b32(), a);
293}
294
295template <>
296EIGEN_STRONG_INLINE numext::int32_t predux<PacketXi>(const PacketXi& a) {
297 return static_cast<numext::int32_t>(svaddv_s32(svptrue_b32(), a));
298}
299
300template <>
301EIGEN_STRONG_INLINE bool predux_any(const PacketXi& a) {
302 return svptest_any(svptrue_b32(), svcmpne_n_s32(svptrue_b32(), a, 0));
303}
304
305template <>
306EIGEN_STRONG_INLINE numext::int32_t predux_mul<PacketXi>(const PacketXi& a) {
307 // Multiply the vector by its reverse.
308 svint32_t prod = svmul_s32_x(svptrue_b32(), a, svrev_s32(a));
309
310 // Reduce with interleave-and-multiply.
311 // NOTE: Skip the final reduction since it is already handled by `rev` above.
312 for (int n = unpacket_traits<PacketXi>::size; n > 2; n >>= 1)
313 prod = svmul_s32_x(svptrue_b32(), svzip1_s32(prod, prod), svzip2_s32(prod, prod));
314
315 // The reduction is done to the first element.
316 return pfirst<PacketXi>(prod);
317}
318
319template <>
320EIGEN_STRONG_INLINE numext::int32_t predux_min<PacketXi>(const PacketXi& a) {
321 return svminv_s32(svptrue_b32(), a);
322}
323
324template <>
325EIGEN_STRONG_INLINE numext::int32_t predux_max<PacketXi>(const PacketXi& a) {
326 return svmaxv_s32(svptrue_b32(), a);
327}
328
329template <int N>
330EIGEN_DEVICE_FUNC inline void ptranspose(PacketBlock<PacketXi, N>& kernel) {
331 EIGEN_STATIC_ASSERT((N & (N - 1)) == 0, EIGEN_INTERNAL_ERROR_PLEASE_FILE_A_BUG_REPORT);
332 for (int stride = N / 2; stride > 0; stride >>= 1) {
333 for (int block = 0; block < N; block += 2 * stride) {
334 for (int k = 0; k < stride; ++k) {
335 PacketXi lo = svzip1_s32(kernel.packet[block + k], kernel.packet[block + k + stride]);
336 PacketXi hi = svzip2_s32(kernel.packet[block + k], kernel.packet[block + k + stride]);
337 kernel.packet[block + k] = lo;
338 kernel.packet[block + k + stride] = hi;
339 }
340 }
341 }
342}
343
344/********************************* int64 **************************************/
345// Keep typedef: Doxygen 1.13.2 misparses an attributed using declaration.
346typedef svint64_t PacketXl __attribute__((arm_sve_vector_bits(EIGEN_ARM64_SVE_VL)));
347
348template <>
349struct packet_traits<numext::int64_t> : default_packet_traits {
350 typedef PacketXl type;
351 typedef PacketXl half; // Half not implemented yet
352 enum {
353 Vectorizable = 1,
354 AlignedOnScalar = 1,
355 size = sve_packet_size_selector<numext::int64_t, EIGEN_ARM64_SVE_VL>::size,
356
357 HasAdd = 1,
358 HasSub = 1,
359 HasShift = 1,
360 HasMul = 1,
361 HasNegate = 1,
362 HasAbs = 1,
363 HasArg = 0,
364 HasMin = 1,
365 HasMax = 1,
366 HasConj = 1,
367 // See the int32 traits above for why HasSetLinear stays 0.
368 HasSetLinear = 0,
369 HasReduxp = 0 // Not implemented in SVE
370 };
371};
372
373template <>
374struct unpacket_traits<PacketXl> {
375 typedef numext::int64_t type;
376 typedef PacketXl half; // Half not yet implemented
377 enum {
378 size = sve_packet_size_selector<numext::int64_t, EIGEN_ARM64_SVE_VL>::size,
379 alignment = sve_packet_alignment_selector<EIGEN_ARM64_SVE_VL>::alignment,
380 vectorizable = true,
381 masked_load_available = false,
382 masked_store_available = false
383 };
384};
385
386template <>
387EIGEN_STRONG_INLINE void prefetch<numext::int64_t>(const numext::int64_t* addr) {
388 svprfd(svptrue_b64(), addr, SV_PLDL1KEEP);
389}
390
391template <>
392EIGEN_STRONG_INLINE PacketXl pset1<PacketXl>(const numext::int64_t& from) {
393 return svdup_n_s64(from);
394}
395
396template <>
397EIGEN_STRONG_INLINE PacketXl plset<PacketXl>(const numext::int64_t& a) {
398 return svindex_s64(a, 1);
399}
400
401template <>
402EIGEN_STRONG_INLINE PacketXl padd<PacketXl>(const PacketXl& a, const PacketXl& b) {
403 return svadd_s64_x(svptrue_b64(), a, b);
404}
405
406template <>
407EIGEN_STRONG_INLINE PacketXl psub<PacketXl>(const PacketXl& a, const PacketXl& b) {
408 return svsub_s64_x(svptrue_b64(), a, b);
409}
410
411template <>
412EIGEN_STRONG_INLINE PacketXl pnegate(const PacketXl& a) {
413 return svneg_s64_x(svptrue_b64(), a);
414}
415
416template <>
417EIGEN_STRONG_INLINE PacketXl pmul<PacketXl>(const PacketXl& a, const PacketXl& b) {
418 return svmul_s64_x(svptrue_b64(), a, b);
419}
420
421template <>
422EIGEN_STRONG_INLINE PacketXl pdiv<PacketXl>(const PacketXl& a, const PacketXl& b) {
423 return svdiv_s64_x(svptrue_b64(), a, b);
424}
425
426template <>
427EIGEN_STRONG_INLINE PacketXl pmadd(const PacketXl& a, const PacketXl& b, const PacketXl& c) {
428 return svmla_s64_x(svptrue_b64(), c, a, b);
429}
430
431template <>
432EIGEN_STRONG_INLINE PacketXl pmin<PacketXl>(const PacketXl& a, const PacketXl& b) {
433 return svmin_s64_x(svptrue_b64(), a, b);
434}
435
436template <>
437EIGEN_STRONG_INLINE PacketXl pmax<PacketXl>(const PacketXl& a, const PacketXl& b) {
438 return svmax_s64_x(svptrue_b64(), a, b);
439}
440
441template <>
442EIGEN_STRONG_INLINE PacketXl pcmp_le<PacketXl>(const PacketXl& a, const PacketXl& b) {
443 return svdup_n_s64_z(svcmple_s64(svptrue_b64(), a, b), numext::int64_t(-1));
444}
445
446template <>
447EIGEN_STRONG_INLINE PacketXl pcmp_lt<PacketXl>(const PacketXl& a, const PacketXl& b) {
448 return svdup_n_s64_z(svcmplt_s64(svptrue_b64(), a, b), numext::int64_t(-1));
449}
450
451template <>
452EIGEN_STRONG_INLINE PacketXl pcmp_eq<PacketXl>(const PacketXl& a, const PacketXl& b) {
453 return svdup_n_s64_z(svcmpeq_s64(svptrue_b64(), a, b), numext::int64_t(-1));
454}
455
456template <>
457EIGEN_STRONG_INLINE PacketXl ptrue<PacketXl>(const PacketXl& /*a*/) {
458 return svdup_n_s64_x(svptrue_b64(), numext::int64_t(-1));
459}
460
461template <>
462EIGEN_STRONG_INLINE PacketXl pzero<PacketXl>(const PacketXl& /*a*/) {
463 return svdup_n_s64_x(svptrue_b64(), 0);
464}
465
466template <>
467EIGEN_STRONG_INLINE PacketXl pand<PacketXl>(const PacketXl& a, const PacketXl& b) {
468 return svand_s64_x(svptrue_b64(), a, b);
469}
470
471template <>
472EIGEN_STRONG_INLINE PacketXl por<PacketXl>(const PacketXl& a, const PacketXl& b) {
473 return svorr_s64_x(svptrue_b64(), a, b);
474}
475
476template <>
477EIGEN_STRONG_INLINE PacketXl pxor<PacketXl>(const PacketXl& a, const PacketXl& b) {
478 return sveor_s64_x(svptrue_b64(), a, b);
479}
480
481template <>
482EIGEN_STRONG_INLINE PacketXl pandnot<PacketXl>(const PacketXl& a, const PacketXl& b) {
483 return svbic_s64_x(svptrue_b64(), a, b);
484}
485
486// See pselect<PacketXi>.
487template <>
488EIGEN_STRONG_INLINE PacketXl pselect<PacketXl>(const PacketXl& mask, const PacketXl& a, const PacketXl& b) {
489 return svsel_s64(svcmpne_n_s64(svptrue_b64(), mask, 0), a, b);
490}
491
492template <int N>
493EIGEN_STRONG_INLINE PacketXl parithmetic_shift_right(PacketXl a) {
494 // ASR, not ASRD: see parithmetic_shift_right<PacketXi>.
495 return svasr_n_s64_x(svptrue_b64(), a, N);
496}
497
498template <int N>
499EIGEN_STRONG_INLINE PacketXl plogical_shift_right(PacketXl a) {
500 return svreinterpret_s64_u64(svlsr_n_u64_x(svptrue_b64(), svreinterpret_u64_s64(a), N));
501}
502
503template <int N>
504EIGEN_STRONG_INLINE PacketXl plogical_shift_left(PacketXl a) {
505 return svlsl_n_s64_x(svptrue_b64(), a, N);
506}
507
508template <>
509EIGEN_STRONG_INLINE PacketXl pload<PacketXl>(const numext::int64_t* from) {
510 EIGEN_DEBUG_ALIGNED_LOAD return svld1_s64(svptrue_b64(), from);
511}
512
513template <>
514EIGEN_STRONG_INLINE PacketXl ploadu<PacketXl>(const numext::int64_t* from) {
515 EIGEN_DEBUG_UNALIGNED_LOAD return svld1_s64(svptrue_b64(), from);
516}
517
518template <>
519EIGEN_STRONG_INLINE PacketXl ploaddup<PacketXl>(const numext::int64_t* from) {
520 // Load the size/2 values this reads into the low half and interleave them
521 // with themselves: svzip1 only consumes the low halves of its operands.
522 // The predicate is exact rather than svptrue -- ploaddup may only touch
523 // size/2 elements, and a wider one would read past the end of the input.
524 constexpr uint64_t kHalf = uint64_t(packet_traits<numext::int64_t>::size) / 2;
525 svint64_t lo = svld1_s64(svwhilelt_b64(uint64_t(0), kHalf), from);
526 return svzip1_s64(lo, lo);
527}
528
529template <>
530EIGEN_STRONG_INLINE PacketXl ploadquad<PacketXl>(const numext::int64_t* from) {
531 // As ploaddup, one zip further: size/4 values, each repeated four times.
532 // At the smallest vector length size/4 rounds to zero, where one element
533 // still has to be read.
534 constexpr uint64_t kQuarter = numext::maxi(uint64_t(packet_traits<numext::int64_t>::size) / 4, uint64_t(1));
535 svint64_t lo = svld1_s64(svwhilelt_b64(uint64_t(0), kQuarter), from);
536 lo = svzip1_s64(lo, lo);
537 return svzip1_s64(lo, lo);
538}
539
540template <>
541EIGEN_STRONG_INLINE void pstore<numext::int64_t>(numext::int64_t* to, const PacketXl& from) {
542 EIGEN_DEBUG_ALIGNED_STORE svst1_s64(svptrue_b64(), to, from);
543}
544
545template <>
546EIGEN_STRONG_INLINE void pstoreu<numext::int64_t>(numext::int64_t* to, const PacketXl& from) {
547 EIGEN_DEBUG_UNALIGNED_STORE svst1_s64(svptrue_b64(), to, from);
548}
549
550template <>
551EIGEN_DEVICE_FUNC inline PacketXl pgather<numext::int64_t, PacketXl>(const numext::int64_t* from, Index stride) {
552 // Index format: {base=0, base+stride, base+stride*2, base+stride*3, ...}
553 svint64_t indices = svindex_s64(0, stride);
554 return svld1_gather_s64index_s64(svptrue_b64(), from, indices);
555}
556
557template <>
558EIGEN_DEVICE_FUNC inline void pscatter<numext::int64_t, PacketXl>(numext::int64_t* to, const PacketXl& from,
559 Index stride) {
560 // Index format: {base=0, base+stride, base+stride*2, base+stride*3, ...}
561 svint64_t indices = svindex_s64(0, stride);
562 svst1_scatter_s64index_s64(svptrue_b64(), to, indices, from);
563}
564
565template <>
566EIGEN_STRONG_INLINE numext::int64_t pfirst<PacketXl>(const PacketXl& a) {
567 // svlasta returns the first element if all predicate bits are 0
568 return svlasta_s64(svpfalse_b(), a);
569}
570
571template <>
572EIGEN_STRONG_INLINE PacketXl preverse(const PacketXl& a) {
573 return svrev_s64(a);
574}
575
576template <>
577EIGEN_STRONG_INLINE PacketXl pabs(const PacketXl& a) {
578 return svabs_s64_x(svptrue_b64(), a);
579}
580
581template <>
582EIGEN_STRONG_INLINE numext::int64_t predux<PacketXl>(const PacketXl& a) {
583 return static_cast<numext::int64_t>(svaddv_s64(svptrue_b64(), a));
584}
585
586template <>
587EIGEN_STRONG_INLINE numext::int64_t predux_mul<PacketXl>(const PacketXl& a) {
588 // Multiplying by the reverse pairs lane i with lane n-1-i, so every product of
589 // a pair appears in both halves; interleaving the halves then folds them
590 // together, halving the live span each round. At VL = 128 there are two lanes
591 // and the first multiply has already combined them.
592 PacketXl prod = svmul_s64_x(svptrue_b64(), a, svrev_s64(a));
593 EIGEN_UNROLL_LOOP
594 for (int n = unpacket_traits<PacketXl>::size; n > 2; n >>= 1) {
595 prod = svmul_s64_x(svptrue_b64(), svzip1_s64(prod, prod), svzip2_s64(prod, prod));
596 }
597 return pfirst<PacketXl>(prod);
598}
599
600template <>
601EIGEN_STRONG_INLINE numext::int64_t predux_min<PacketXl>(const PacketXl& a) {
602 return svminv_s64(svptrue_b64(), a);
603}
604
605template <>
606EIGEN_STRONG_INLINE numext::int64_t predux_max<PacketXl>(const PacketXl& a) {
607 return svmaxv_s64(svptrue_b64(), a);
608}
609
610template <int N>
611EIGEN_DEVICE_FUNC inline void ptranspose(PacketBlock<PacketXl, N>& kernel) {
612 EIGEN_STATIC_ASSERT((N & (N - 1)) == 0, EIGEN_INTERNAL_ERROR_PLEASE_FILE_A_BUG_REPORT);
613 for (int stride = N / 2; stride > 0; stride >>= 1) {
614 for (int block = 0; block < N; block += 2 * stride) {
615 for (int k = 0; k < stride; ++k) {
616 PacketXl lo = svzip1_s64(kernel.packet[block + k], kernel.packet[block + k + stride]);
617 PacketXl hi = svzip2_s64(kernel.packet[block + k], kernel.packet[block + k + stride]);
618 kernel.packet[block + k] = lo;
619 kernel.packet[block + k + stride] = hi;
620 }
621 }
622 }
623}
624
625/********************************* float32 ************************************/
626
627// Keep typedef: Doxygen 1.13.2 misparses an attributed using declaration.
628typedef svfloat32_t PacketXf __attribute__((arm_sve_vector_bits(EIGEN_ARM64_SVE_VL)));
629
630template <>
631struct packet_traits<float> : default_packet_traits {
632 typedef PacketXf type;
633 typedef PacketXf half;
634
635 enum {
636 Vectorizable = 1,
637 AlignedOnScalar = 1,
638 size = sve_packet_size_selector<float, EIGEN_ARM64_SVE_VL>::size,
639
640 HasAdd = 1,
641 HasSub = 1,
642 HasShift = 1,
643 HasMul = 1,
644 HasNegate = 1,
645 HasAbs = 1,
646 HasArg = 0,
647 HasMin = 1,
648 HasMax = 1,
649 HasConj = 1,
650 // See the int32 traits above for why HasSetLinear stays 0.
651 HasSetLinear = 0,
652 HasReduxp = 0, // Not implemented in SVE
653
654 HasDiv = 1,
655
656 HasCmp = 1,
657 HasSin = EIGEN_FAST_MATH,
658 HasCos = EIGEN_FAST_MATH,
659 HasTan = EIGEN_FAST_MATH,
660 HasACos = 1,
661 HasASin = 1,
662 HasATan = 1,
663 HasATanh = 1,
664 HasLog = 1,
665 HasLog1p = 1,
666 HasExpm1 = 1,
667 HasExp = 1,
668 HasPow = 1,
669 HasSqrt = 1,
670 HasRsqrt = 1,
671 HasCbrt = 1,
672 HasTanh = EIGEN_FAST_MATH,
673 HasErf = EIGEN_FAST_MATH,
674 HasErfc = EIGEN_FAST_MATH
675 };
676};
677
678template <>
679struct unpacket_traits<PacketXf> {
680 typedef float type;
681 typedef PacketXf half; // Half not yet implemented
682 typedef PacketXi integer_packet;
683
684 enum {
685 size = sve_packet_size_selector<float, EIGEN_ARM64_SVE_VL>::size,
686 alignment = sve_packet_alignment_selector<EIGEN_ARM64_SVE_VL>::alignment,
687 vectorizable = true,
688 masked_load_available = false,
689 masked_store_available = false
690 };
691};
692
693template <>
694EIGEN_STRONG_INLINE PacketXf pset1<PacketXf>(const float& from) {
695 return svdup_n_f32(from);
696}
697
698template <>
699EIGEN_STRONG_INLINE PacketXf pset1frombits<PacketXf>(numext::uint32_t from) {
700 return svreinterpret_f32_u32(svdup_n_u32_x(svptrue_b32(), from));
701}
702
703template <>
704EIGEN_STRONG_INLINE PacketXf plset<PacketXf>(const float& a) {
705 // As the double overload: svindex + a convert, no constant array.
706 return svadd_f32_x(svptrue_b32(), pset1<PacketXf>(a), svcvt_f32_s32_x(svptrue_b32(), svindex_s32(0, 1)));
707}
708
709template <>
710EIGEN_STRONG_INLINE PacketXf padd<PacketXf>(const PacketXf& a, const PacketXf& b) {
711 return svadd_f32_x(svptrue_b32(), a, b);
712}
713
714template <>
715EIGEN_STRONG_INLINE PacketXf psub<PacketXf>(const PacketXf& a, const PacketXf& b) {
716 return svsub_f32_x(svptrue_b32(), a, b);
717}
718
719template <>
720EIGEN_STRONG_INLINE PacketXf pnegate(const PacketXf& a) {
721 return svneg_f32_x(svptrue_b32(), a);
722}
723
724template <>
725EIGEN_STRONG_INLINE PacketXf pmul<PacketXf>(const PacketXf& a, const PacketXf& b) {
726 return svmul_f32_x(svptrue_b32(), a, b);
727}
728
729template <>
730EIGEN_STRONG_INLINE PacketXf pdiv<PacketXf>(const PacketXf& a, const PacketXf& b) {
731 return svdiv_f32_x(svptrue_b32(), a, b);
732}
733
734template <>
735EIGEN_STRONG_INLINE PacketXf pmadd(const PacketXf& a, const PacketXf& b, const PacketXf& c) {
736 return svmla_f32_x(svptrue_b32(), c, a, b);
737}
738
739template <>
740EIGEN_STRONG_INLINE PacketXf pmsub(const PacketXf& a, const PacketXf& b, const PacketXf& c) {
741 return svmla_f32_x(svptrue_b32(), svneg_f32_x(svptrue_b32(), c), a, b);
742}
743
744template <>
745struct pminmax_propagates_nan<PacketXf> : bool_constant<true> {};
746
747template <>
748EIGEN_STRONG_INLINE PacketXf pmin<PacketXf>(const PacketXf& a, const PacketXf& b) {
749 return svmin_f32_x(svptrue_b32(), a, b);
750}
751
752template <>
753EIGEN_STRONG_INLINE PacketXf pmin<PropagateNumbers, PacketXf>(const PacketXf& a, const PacketXf& b) {
754 return svminnm_f32_x(svptrue_b32(), a, b);
755}
756
757template <>
758EIGEN_STRONG_INLINE PacketXf pmax<PacketXf>(const PacketXf& a, const PacketXf& b) {
759 return svmax_f32_x(svptrue_b32(), a, b);
760}
761
762template <>
763EIGEN_STRONG_INLINE PacketXf pmax<PropagateNumbers, PacketXf>(const PacketXf& a, const PacketXf& b) {
764 return svmaxnm_f32_x(svptrue_b32(), a, b);
765}
766
767// Float comparisons in SVE return svbool (predicate). Use svdup to set active
768// lanes to 1 (0xffffffffu) and inactive lanes to 0.
769template <>
770EIGEN_STRONG_INLINE PacketXf pcmp_le<PacketXf>(const PacketXf& a, const PacketXf& b) {
771 return svreinterpret_f32_u32(svdup_n_u32_z(svcmple_f32(svptrue_b32(), a, b), 0xffffffffu));
772}
773
774template <>
775EIGEN_STRONG_INLINE PacketXf pcmp_lt<PacketXf>(const PacketXf& a, const PacketXf& b) {
776 return svreinterpret_f32_u32(svdup_n_u32_z(svcmplt_f32(svptrue_b32(), a, b), 0xffffffffu));
777}
778
779template <>
780EIGEN_STRONG_INLINE PacketXf pcmp_eq<PacketXf>(const PacketXf& a, const PacketXf& b) {
781 return svreinterpret_f32_u32(svdup_n_u32_z(svcmpeq_f32(svptrue_b32(), a, b), 0xffffffffu));
782}
783
784// Do a predicate inverse (svnot_b_z) on the predicate resulted from the
785// greater/equal comparison (svcmpge_f32). Then fill a float vector with the
786// active elements.
787template <>
788EIGEN_STRONG_INLINE PacketXf pcmp_lt_or_nan<PacketXf>(const PacketXf& a, const PacketXf& b) {
789 return svreinterpret_f32_u32(svdup_n_u32_z(svnot_b_z(svptrue_b32(), svcmpge_f32(svptrue_b32(), a, b)), 0xffffffffu));
790}
791
792template <>
793EIGEN_STRONG_INLINE PacketXf pfloor<PacketXf>(const PacketXf& a) {
794 return svrintm_f32_x(svptrue_b32(), a);
795}
796template <>
797EIGEN_STRONG_INLINE PacketXf pceil<PacketXf>(const PacketXf& a) {
798 return svrintp_f32_x(svptrue_b32(), a);
799}
800template <>
801EIGEN_STRONG_INLINE PacketXf print<PacketXf>(const PacketXf& a) {
802 return svrintn_f32_x(svptrue_b32(), a);
803}
804template <>
805EIGEN_STRONG_INLINE PacketXf ptrunc<PacketXf>(const PacketXf& a) {
806 return svrintz_f32_x(svptrue_b32(), a);
807}
808template <>
809EIGEN_STRONG_INLINE PacketXf pround<PacketXf>(const PacketXf& a) {
810 return svrinta_f32_x(svptrue_b32(), a);
811}
812
813template <>
814EIGEN_STRONG_INLINE PacketXf ptrue<PacketXf>(const PacketXf& /*a*/) {
815 PacketXf r = svreinterpret_f32_u32(svdup_n_u32_x(svptrue_b32(), 0xffffffffu));
816 EIGEN_FAST_MATH_CONSTANT_BARRIER(r);
817 return r;
818}
819
820// Logical Operations are not supported for float, so reinterpret casts
821template <>
822EIGEN_STRONG_INLINE PacketXf pand<PacketXf>(const PacketXf& a, const PacketXf& b) {
823 return svreinterpret_f32_u32(svand_u32_x(svptrue_b32(), svreinterpret_u32_f32(a), svreinterpret_u32_f32(b)));
824}
825
826template <>
827EIGEN_STRONG_INLINE PacketXf por<PacketXf>(const PacketXf& a, const PacketXf& b) {
828 return svreinterpret_f32_u32(svorr_u32_x(svptrue_b32(), svreinterpret_u32_f32(a), svreinterpret_u32_f32(b)));
829}
830
831template <>
832EIGEN_STRONG_INLINE PacketXf pxor<PacketXf>(const PacketXf& a, const PacketXf& b) {
833 return svreinterpret_f32_u32(sveor_u32_x(svptrue_b32(), svreinterpret_u32_f32(a), svreinterpret_u32_f32(b)));
834}
835
836template <>
837EIGEN_STRONG_INLINE PacketXf pandnot<PacketXf>(const PacketXf& a, const PacketXf& b) {
838 return svreinterpret_f32_u32(svbic_u32_x(svptrue_b32(), svreinterpret_u32_f32(a), svreinterpret_u32_f32(b)));
839}
840
841// See pselect<PacketXi>.
842template <>
843EIGEN_STRONG_INLINE PacketXf pselect<PacketXf>(const PacketXf& mask, const PacketXf& a, const PacketXf& b) {
844 return svsel_f32(svcmpne_n_s32(svptrue_b32(), svreinterpret_s32_f32(mask), 0), a, b);
845}
846
847template <>
848EIGEN_STRONG_INLINE PacketXf pload<PacketXf>(const float* from) {
849 EIGEN_DEBUG_ALIGNED_LOAD return svld1_f32(svptrue_b32(), from);
850}
851
852template <>
853EIGEN_STRONG_INLINE PacketXf ploadu<PacketXf>(const float* from) {
854 EIGEN_DEBUG_UNALIGNED_LOAD return svld1_f32(svptrue_b32(), from);
855}
856
857template <>
858EIGEN_STRONG_INLINE PacketXf ploaddup<PacketXf>(const float* from) {
859 // Load the size/2 values this reads into the low half and interleave them
860 // with themselves: svzip1 only consumes the low halves of its operands.
861 // The predicate is exact rather than svptrue -- ploaddup may only touch
862 // size/2 elements, and a wider one would read past the end of the input.
863 constexpr uint64_t kHalf = uint64_t(packet_traits<float>::size) / 2;
864 svfloat32_t lo = svld1_f32(svwhilelt_b32(uint64_t(0), kHalf), from);
865 return svzip1_f32(lo, lo);
866}
867
868template <>
869EIGEN_STRONG_INLINE PacketXf ploadquad<PacketXf>(const float* from) {
870 // As ploaddup, one zip further: size/4 values, each repeated four times.
871 // At the smallest vector length size/4 rounds to zero, where one element
872 // still has to be read.
873 constexpr uint64_t kQuarter = numext::maxi(uint64_t(packet_traits<float>::size) / 4, uint64_t(1));
874 svfloat32_t lo = svld1_f32(svwhilelt_b32(uint64_t(0), kQuarter), from);
875 lo = svzip1_f32(lo, lo);
876 return svzip1_f32(lo, lo);
877}
878
879template <>
880EIGEN_STRONG_INLINE void pstore<float>(float* to, const PacketXf& from) {
881 EIGEN_DEBUG_ALIGNED_STORE svst1_f32(svptrue_b32(), to, from);
882}
883
884template <>
885EIGEN_STRONG_INLINE void pstoreu<float>(float* to, const PacketXf& from) {
886 EIGEN_DEBUG_UNALIGNED_STORE svst1_f32(svptrue_b32(), to, from);
887}
888
889template <>
890EIGEN_DEVICE_FUNC inline PacketXf pgather<float, PacketXf>(const float* from, Index stride) {
891 // Index format: {base=0, base+stride, base+stride*2, base+stride*3, ...}
892 svint32_t indices = svindex_s32(0, stride);
893 return svld1_gather_s32index_f32(svptrue_b32(), from, indices);
894}
895
896template <>
897EIGEN_DEVICE_FUNC inline void pscatter<float, PacketXf>(float* to, const PacketXf& from, Index stride) {
898 // Index format: {base=0, base+stride, base+stride*2, base+stride*3, ...}
899 svint32_t indices = svindex_s32(0, stride);
900 svst1_scatter_s32index_f32(svptrue_b32(), to, indices, from);
901}
902
903template <>
904EIGEN_STRONG_INLINE float pfirst<PacketXf>(const PacketXf& a) {
905 // svlasta returns the first element if all predicate bits are 0
906 return svlasta_f32(svpfalse_b(), a);
907}
908
909template <>
910EIGEN_STRONG_INLINE PacketXf preverse(const PacketXf& a) {
911 return svrev_f32(a);
912}
913
914template <>
915EIGEN_STRONG_INLINE PacketXf pabs(const PacketXf& a) {
916 return svabs_f32_x(svptrue_b32(), a);
917}
918
919// TODO(tellenbach): Should this go into MathFunctions.h? If so, change for
920// all vector extensions and the generic version.
921template <>
922EIGEN_STRONG_INLINE PacketXf pfrexp<PacketXf>(const PacketXf& a, PacketXf& exponent) {
923 return pfrexp_generic(a, exponent);
924}
925
926template <>
927EIGEN_STRONG_INLINE float predux<PacketXf>(const PacketXf& a) {
928 return svaddv_f32(svptrue_b32(), a);
929}
930
931template <>
932EIGEN_STRONG_INLINE bool predux_any(const PacketXf& a) {
933 const svuint32_t bits = svreinterpret_u32_f32(a);
934 return svptest_any(svptrue_b32(), svcmpne_n_u32(svptrue_b32(), bits, 0));
935}
936
937template <>
938EIGEN_STRONG_INLINE bool predux_all(const PacketXf& a) {
939 const svbool_t all = svptrue_b32();
940 return !svptest_any(all, svcmpeq_n_f32(all, a, 0.0f));
941}
942
943template <>
944EIGEN_STRONG_INLINE Index predux_count(const PacketXf& a) {
945 const svbool_t all = svptrue_b32();
946 return static_cast<Index>(svcntp_b32(all, svcmpne_n_f32(all, a, 0.0f)));
947}
948
949// Other reduction functions:
950// mul
951template <>
952EIGEN_STRONG_INLINE float predux_mul<PacketXf>(const PacketXf& a) {
953 // Multiply the vector by its reverse.
954 svfloat32_t prod = svmul_f32_x(svptrue_b32(), a, svrev_f32(a));
955
956 // Reduce with interleave-and-multiply.
957 // NOTE: Skip the final reduction since it is already handled by `rev` above.
958 for (int n = unpacket_traits<PacketXf>::size; n > 2; n >>= 1)
959 prod = svmul_f32_x(svptrue_b32(), svzip1_f32(prod, prod), svzip2_f32(prod, prod));
960
961 // The reduction is done to the first element.
962 return pfirst<PacketXf>(prod);
963}
964
965template <>
966EIGEN_STRONG_INLINE float predux_min<PacketXf>(const PacketXf& a) {
967 return svminv_f32(svptrue_b32(), a);
968}
969
970template <>
971EIGEN_STRONG_INLINE float predux_max<PacketXf>(const PacketXf& a) {
972 return svmaxv_f32(svptrue_b32(), a);
973}
974
975template <int N>
976EIGEN_DEVICE_FUNC inline void ptranspose(PacketBlock<PacketXf, N>& kernel) {
977 EIGEN_STATIC_ASSERT((N & (N - 1)) == 0, EIGEN_INTERNAL_ERROR_PLEASE_FILE_A_BUG_REPORT);
978 for (int stride = N / 2; stride > 0; stride >>= 1) {
979 for (int block = 0; block < N; block += 2 * stride) {
980 for (int k = 0; k < stride; ++k) {
981 PacketXf lo = svzip1_f32(kernel.packet[block + k], kernel.packet[block + k + stride]);
982 PacketXf hi = svzip2_f32(kernel.packet[block + k], kernel.packet[block + k + stride]);
983 kernel.packet[block + k] = lo;
984 kernel.packet[block + k + stride] = hi;
985 }
986 }
987 }
988}
989
990template <>
991EIGEN_STRONG_INLINE PacketXf pldexp<PacketXf>(const PacketXf& a, const PacketXf& exponent) {
992 // fscale is a * 2^n rounded once; the conversion saturates, and a saturated n over- or underflows every finite a.
993 return svscale_f32_x(svptrue_b32(), a, svcvt_s32_f32_x(svptrue_b32(), exponent));
994}
995
996template <>
997EIGEN_STRONG_INLINE PacketXf psqrt<PacketXf>(const PacketXf& a) {
998 return svsqrt_f32_x(svptrue_b32(), a);
999}
1000
1001/********************************* float64 ************************************/
1002// Double was missing from this backend: packet_traits<double> fell through to
1003// default_packet_traits, so every double operation under EIGEN_ARM64_USE_SVE was
1004// scalar.
1005// Keep typedef: Doxygen 1.13.2 misparses an attributed using declaration.
1006typedef svfloat64_t PacketXd __attribute__((arm_sve_vector_bits(EIGEN_ARM64_SVE_VL)));
1007
1008template <>
1009struct packet_traits<double> : default_packet_traits {
1010 typedef PacketXd type;
1011 typedef PacketXd half; // Half not implemented yet
1012
1013 enum {
1014 Vectorizable = 1,
1015 AlignedOnScalar = 1,
1016 size = sve_packet_size_selector<double, EIGEN_ARM64_SVE_VL>::size,
1017
1018 HasAdd = 1,
1019 HasSub = 1,
1020 HasShift = 1,
1021 HasMul = 1,
1022 HasNegate = 1,
1023 HasAbs = 1,
1024 HasArg = 0,
1025 HasMin = 1,
1026 HasMax = 1,
1027 HasConj = 1,
1028 // See the int32 traits above for why HasSetLinear stays 0.
1029 HasSetLinear = 0,
1030 HasReduxp = 0, // Not implemented in SVE
1031
1032 HasDiv = 1,
1033 HasCmp = 1,
1034 HasSqrt = 1,
1035 HasRsqrt = 1,
1036 // asin and acos are absent from EIGEN_INSTANTIATE_GENERIC_MATH_FUNCS_DOUBLE,
1037 // so they stay scalar.
1038 HasSin = EIGEN_FAST_MATH,
1039 HasCos = EIGEN_FAST_MATH,
1040 HasTan = EIGEN_FAST_MATH,
1041 HasATan = 1,
1042 HasATanh = 1,
1043 HasLog = 1,
1044 HasLog1p = 1,
1045 HasExpm1 = 1,
1046 HasExp = 1,
1047 HasPow = 1,
1048 HasCbrt = 1,
1049 HasTanh = EIGEN_FAST_MATH
1050 };
1051};
1052
1053template <>
1054struct unpacket_traits<PacketXd> {
1055 typedef double type;
1056 typedef PacketXd half; // Half not yet implemented
1057 typedef PacketXl integer_packet;
1058
1059 enum {
1060 size = sve_packet_size_selector<double, EIGEN_ARM64_SVE_VL>::size,
1061 alignment = sve_packet_alignment_selector<EIGEN_ARM64_SVE_VL>::alignment,
1062 vectorizable = true,
1063 masked_load_available = false,
1064 masked_store_available = false
1065 };
1066};
1067
1068template <>
1069EIGEN_STRONG_INLINE void prefetch<double>(const double* addr) {
1070 svprfd(svptrue_b64(), addr, SV_PLDL1KEEP);
1071}
1072
1073template <>
1074EIGEN_STRONG_INLINE PacketXd pset1<PacketXd>(const double& from) {
1075 return svdup_n_f64(from);
1076}
1077
1078template <>
1079EIGEN_STRONG_INLINE PacketXd pset1frombits<PacketXd>(numext::uint64_t from) {
1080 return svreinterpret_f64_u64(svdup_n_u64_x(svptrue_b64(), from));
1081}
1082
1083template <>
1084EIGEN_STRONG_INLINE PacketXd plset<PacketXd>(const double& a) {
1085 // The ramp comes from svindex + a widening convert rather than a constant
1086 // array: no static storage, no load, and no alignment question.
1087 return svadd_f64_x(svptrue_b64(), pset1<PacketXd>(a), svcvt_f64_s64_x(svptrue_b64(), svindex_s64(0, 1)));
1088}
1089
1090template <>
1091EIGEN_STRONG_INLINE PacketXd padd<PacketXd>(const PacketXd& a, const PacketXd& b) {
1092 return svadd_f64_x(svptrue_b64(), a, b);
1093}
1094
1095template <>
1096EIGEN_STRONG_INLINE PacketXd psub<PacketXd>(const PacketXd& a, const PacketXd& b) {
1097 return svsub_f64_x(svptrue_b64(), a, b);
1098}
1099
1100template <>
1101EIGEN_STRONG_INLINE PacketXd pnegate(const PacketXd& a) {
1102 return svneg_f64_x(svptrue_b64(), a);
1103}
1104
1105template <>
1106EIGEN_STRONG_INLINE PacketXd pmul<PacketXd>(const PacketXd& a, const PacketXd& b) {
1107 return svmul_f64_x(svptrue_b64(), a, b);
1108}
1109
1110template <>
1111EIGEN_STRONG_INLINE PacketXd pdiv<PacketXd>(const PacketXd& a, const PacketXd& b) {
1112 return svdiv_f64_x(svptrue_b64(), a, b);
1113}
1114
1115template <>
1116EIGEN_STRONG_INLINE PacketXd pmadd(const PacketXd& a, const PacketXd& b, const PacketXd& c) {
1117 return svmla_f64_x(svptrue_b64(), c, a, b);
1118}
1119
1120template <>
1121EIGEN_STRONG_INLINE PacketXd pmsub(const PacketXd& a, const PacketXd& b, const PacketXd& c) {
1122 return svmla_f64_x(svptrue_b64(), svneg_f64_x(svptrue_b64(), c), a, b);
1123}
1124
1125template <>
1126struct pminmax_propagates_nan<PacketXd> : bool_constant<true> {};
1127
1128template <>
1129EIGEN_STRONG_INLINE PacketXd pmin<PacketXd>(const PacketXd& a, const PacketXd& b) {
1130 return svmin_f64_x(svptrue_b64(), a, b);
1131}
1132
1133template <>
1134EIGEN_STRONG_INLINE PacketXd pmin<PropagateNumbers, PacketXd>(const PacketXd& a, const PacketXd& b) {
1135 return svminnm_f64_x(svptrue_b64(), a, b);
1136}
1137
1138template <>
1139EIGEN_STRONG_INLINE PacketXd pmax<PacketXd>(const PacketXd& a, const PacketXd& b) {
1140 return svmax_f64_x(svptrue_b64(), a, b);
1141}
1142
1143template <>
1144EIGEN_STRONG_INLINE PacketXd pmax<PropagateNumbers, PacketXd>(const PacketXd& a, const PacketXd& b) {
1145 return svmaxnm_f64_x(svptrue_b64(), a, b);
1146}
1147
1148// Comparisons in SVE return a predicate. Use svdup to set active lanes to all
1149// ones and inactive lanes to zero, matching the float path above.
1150template <>
1151EIGEN_STRONG_INLINE PacketXd pcmp_le<PacketXd>(const PacketXd& a, const PacketXd& b) {
1152 return svreinterpret_f64_u64(svdup_n_u64_z(svcmple_f64(svptrue_b64(), a, b), 0xffffffffffffffffull));
1153}
1154
1155template <>
1156EIGEN_STRONG_INLINE PacketXd pcmp_lt<PacketXd>(const PacketXd& a, const PacketXd& b) {
1157 return svreinterpret_f64_u64(svdup_n_u64_z(svcmplt_f64(svptrue_b64(), a, b), 0xffffffffffffffffull));
1158}
1159
1160template <>
1161EIGEN_STRONG_INLINE PacketXd pcmp_eq<PacketXd>(const PacketXd& a, const PacketXd& b) {
1162 return svreinterpret_f64_u64(svdup_n_u64_z(svcmpeq_f64(svptrue_b64(), a, b), 0xffffffffffffffffull));
1163}
1164
1165template <>
1166EIGEN_STRONG_INLINE PacketXd pcmp_lt_or_nan<PacketXd>(const PacketXd& a, const PacketXd& b) {
1167 return svreinterpret_f64_u64(
1168 svdup_n_u64_z(svnot_b_z(svptrue_b64(), svcmpge_f64(svptrue_b64(), a, b)), 0xffffffffffffffffull));
1169}
1170
1171template <>
1172EIGEN_STRONG_INLINE PacketXd pfloor<PacketXd>(const PacketXd& a) {
1173 return svrintm_f64_x(svptrue_b64(), a);
1174}
1175template <>
1176EIGEN_STRONG_INLINE PacketXd pceil<PacketXd>(const PacketXd& a) {
1177 return svrintp_f64_x(svptrue_b64(), a);
1178}
1179template <>
1180EIGEN_STRONG_INLINE PacketXd print<PacketXd>(const PacketXd& a) {
1181 return svrintn_f64_x(svptrue_b64(), a);
1182}
1183template <>
1184EIGEN_STRONG_INLINE PacketXd ptrunc<PacketXd>(const PacketXd& a) {
1185 return svrintz_f64_x(svptrue_b64(), a);
1186}
1187template <>
1188EIGEN_STRONG_INLINE PacketXd pround<PacketXd>(const PacketXd& a) {
1189 return svrinta_f64_x(svptrue_b64(), a);
1190}
1191
1192template <>
1193EIGEN_STRONG_INLINE PacketXd ptrue<PacketXd>(const PacketXd& /*a*/) {
1194 PacketXd r = svreinterpret_f64_u64(svdup_n_u64_x(svptrue_b64(), 0xffffffffffffffffull));
1195 EIGEN_FAST_MATH_CONSTANT_BARRIER(r);
1196 return r;
1197}
1198
1199// Logical operations are not supported for float64, so reinterpret casts.
1200template <>
1201EIGEN_STRONG_INLINE PacketXd pand<PacketXd>(const PacketXd& a, const PacketXd& b) {
1202 return svreinterpret_f64_u64(svand_u64_x(svptrue_b64(), svreinterpret_u64_f64(a), svreinterpret_u64_f64(b)));
1203}
1204
1205template <>
1206EIGEN_STRONG_INLINE PacketXd por<PacketXd>(const PacketXd& a, const PacketXd& b) {
1207 return svreinterpret_f64_u64(svorr_u64_x(svptrue_b64(), svreinterpret_u64_f64(a), svreinterpret_u64_f64(b)));
1208}
1209
1210template <>
1211EIGEN_STRONG_INLINE PacketXd pxor<PacketXd>(const PacketXd& a, const PacketXd& b) {
1212 return svreinterpret_f64_u64(sveor_u64_x(svptrue_b64(), svreinterpret_u64_f64(a), svreinterpret_u64_f64(b)));
1213}
1214
1215template <>
1216EIGEN_STRONG_INLINE PacketXd pandnot<PacketXd>(const PacketXd& a, const PacketXd& b) {
1217 return svreinterpret_f64_u64(svbic_u64_x(svptrue_b64(), svreinterpret_u64_f64(a), svreinterpret_u64_f64(b)));
1218}
1219
1220// See pselect<PacketXi>.
1221template <>
1222EIGEN_STRONG_INLINE PacketXd pselect<PacketXd>(const PacketXd& mask, const PacketXd& a, const PacketXd& b) {
1223 return svsel_f64(svcmpne_n_s64(svptrue_b64(), svreinterpret_s64_f64(mask), 0), a, b);
1224}
1225
1226template <>
1227EIGEN_STRONG_INLINE PacketXd pload<PacketXd>(const double* from) {
1228 EIGEN_DEBUG_ALIGNED_LOAD return svld1_f64(svptrue_b64(), from);
1229}
1230
1231template <>
1232EIGEN_STRONG_INLINE PacketXd ploadu<PacketXd>(const double* from) {
1233 EIGEN_DEBUG_UNALIGNED_LOAD return svld1_f64(svptrue_b64(), from);
1234}
1235
1236template <>
1237EIGEN_STRONG_INLINE PacketXd ploaddup<PacketXd>(const double* from) {
1238 // Load the size/2 values this reads into the low half and interleave them
1239 // with themselves: svzip1 only consumes the low halves of its operands.
1240 // The predicate is exact rather than svptrue -- ploaddup may only touch
1241 // size/2 elements, and a wider one would read past the end of the input.
1242 constexpr uint64_t kHalf = uint64_t(packet_traits<double>::size) / 2;
1243 svfloat64_t lo = svld1_f64(svwhilelt_b64(uint64_t(0), kHalf), from);
1244 return svzip1_f64(lo, lo);
1245}
1246
1247template <>
1248EIGEN_STRONG_INLINE PacketXd ploadquad<PacketXd>(const double* from) {
1249 // As ploaddup, one zip further: size/4 values, each repeated four times.
1250 // At the smallest vector length size/4 rounds to zero, where one element
1251 // still has to be read.
1252 constexpr uint64_t kQuarter = numext::maxi(uint64_t(packet_traits<double>::size) / 4, uint64_t(1));
1253 svfloat64_t lo = svld1_f64(svwhilelt_b64(uint64_t(0), kQuarter), from);
1254 lo = svzip1_f64(lo, lo);
1255 return svzip1_f64(lo, lo);
1256}
1257
1258template <>
1259EIGEN_STRONG_INLINE void pstore<double>(double* to, const PacketXd& from) {
1260 EIGEN_DEBUG_ALIGNED_STORE svst1_f64(svptrue_b64(), to, from);
1261}
1262
1263template <>
1264EIGEN_STRONG_INLINE void pstoreu<double>(double* to, const PacketXd& from) {
1265 EIGEN_DEBUG_UNALIGNED_STORE svst1_f64(svptrue_b64(), to, from);
1266}
1267
1268template <>
1269EIGEN_DEVICE_FUNC inline PacketXd pgather<double, PacketXd>(const double* from, Index stride) {
1270 // Index format: {base=0, base+stride, base+stride*2, base+stride*3, ...}
1271 svint64_t indices = svindex_s64(0, stride);
1272 return svld1_gather_s64index_f64(svptrue_b64(), from, indices);
1273}
1274
1275template <>
1276EIGEN_DEVICE_FUNC inline void pscatter<double, PacketXd>(double* to, const PacketXd& from, Index stride) {
1277 // Index format: {base=0, base+stride, base+stride*2, base+stride*3, ...}
1278 svint64_t indices = svindex_s64(0, stride);
1279 svst1_scatter_s64index_f64(svptrue_b64(), to, indices, from);
1280}
1281
1282template <>
1283EIGEN_STRONG_INLINE double pfirst<PacketXd>(const PacketXd& a) {
1284 // svlasta returns the first element if all predicate bits are 0
1285 return svlasta_f64(svpfalse_b(), a);
1286}
1287
1288template <>
1289EIGEN_STRONG_INLINE PacketXd preverse(const PacketXd& a) {
1290 return svrev_f64(a);
1291}
1292
1293template <>
1294EIGEN_STRONG_INLINE PacketXd pabs(const PacketXd& a) {
1295 return svabs_f64_x(svptrue_b64(), a);
1296}
1297
1298template <>
1299EIGEN_STRONG_INLINE double predux<PacketXd>(const PacketXd& a) {
1300 return svaddv_f64(svptrue_b64(), a);
1301}
1302
1303template <>
1304EIGEN_STRONG_INLINE bool predux_any(const PacketXd& a) {
1305 const svuint64_t bits = svreinterpret_u64_f64(a);
1306 return svptest_any(svptrue_b64(), svcmpne_n_u64(svptrue_b64(), bits, 0));
1307}
1308
1309template <>
1310EIGEN_STRONG_INLINE bool predux_all(const PacketXd& a) {
1311 const svbool_t all = svptrue_b64();
1312 return !svptest_any(all, svcmpeq_n_f64(all, a, 0.0));
1313}
1314
1315template <>
1316EIGEN_STRONG_INLINE Index predux_count(const PacketXd& a) {
1317 const svbool_t all = svptrue_b64();
1318 return static_cast<Index>(svcntp_b64(all, svcmpne_n_f64(all, a, 0.0)));
1319}
1320
1321template <>
1322EIGEN_STRONG_INLINE double predux_mul<PacketXd>(const PacketXd& a) {
1323 // Multiply the vector by its reverse.
1324 svfloat64_t prod = svmul_f64_x(svptrue_b64(), a, svrev_f64(a));
1325
1326 // Reduce with interleave-and-multiply.
1327 // NOTE: Skip the final reduction since it is already handled by `rev` above.
1328 for (int n = unpacket_traits<PacketXd>::size; n > 2; n >>= 1)
1329 prod = svmul_f64_x(svptrue_b64(), svzip1_f64(prod, prod), svzip2_f64(prod, prod));
1330
1331 // The reduction is done to the first element.
1332 return pfirst<PacketXd>(prod);
1333}
1334
1335template <>
1336EIGEN_STRONG_INLINE double predux_min<PacketXd>(const PacketXd& a) {
1337 return svminv_f64(svptrue_b64(), a);
1338}
1339
1340template <>
1341EIGEN_STRONG_INLINE double predux_max<PacketXd>(const PacketXd& a) {
1342 return svmaxv_f64(svptrue_b64(), a);
1343}
1344
1345template <int N>
1346EIGEN_DEVICE_FUNC inline void ptranspose(PacketBlock<PacketXd, N>& kernel) {
1347 EIGEN_STATIC_ASSERT((N & (N - 1)) == 0, EIGEN_INTERNAL_ERROR_PLEASE_FILE_A_BUG_REPORT);
1348 for (int stride = N / 2; stride > 0; stride >>= 1) {
1349 for (int block = 0; block < N; block += 2 * stride) {
1350 for (int k = 0; k < stride; ++k) {
1351 PacketXd lo = svzip1_f64(kernel.packet[block + k], kernel.packet[block + k + stride]);
1352 PacketXd hi = svzip2_f64(kernel.packet[block + k], kernel.packet[block + k + stride]);
1353 kernel.packet[block + k] = lo;
1354 kernel.packet[block + k + stride] = hi;
1355 }
1356 }
1357 }
1358}
1359
1360template <>
1361EIGEN_STRONG_INLINE PacketXd pfrexp<PacketXd>(const PacketXd& a, PacketXd& exponent) {
1362 return pfrexp_generic(a, exponent);
1363}
1364
1365template <>
1366EIGEN_STRONG_INLINE PacketXd pldexp<PacketXd>(const PacketXd& a, const PacketXd& exponent) {
1367 return svscale_f64_x(svptrue_b64(), a, svcvt_s64_f64_x(svptrue_b64(), exponent)); // see pldexp<PacketXf>
1368}
1369
1370template <>
1371EIGEN_STRONG_INLINE PacketXd psqrt<PacketXd>(const PacketXd& a) {
1372 return svsqrt_f64_x(svptrue_b64(), a);
1373}
1374
1375template <>
1376EIGEN_STRONG_INLINE PacketXd prsqrt<PacketXd>(const PacketXd& a) {
1377 // Newton off the FRSQRTE seed, as NEON's Packet2d does. The generic
1378 // preciprocal(psqrt(x)) form is correct but pays a double-precision FDIV,
1379 // which is slow enough here to lose to the scalar loop.
1380 return generic_rsqrt_newton_step<PacketXd, /*Steps=*/3>::run(a, svrsqrte_f64(a));
1381}
1382
1383/*---------------- load/store segment support ----------------*/
1384
1385// Predicates with lanes [begin, begin + count) active. begin is a literal 0 at the assignment
1386// call sites, so that branch folds and the common case is a single whilelt.
1387EIGEN_STRONG_INLINE svbool_t sve_segment_predicate_b32(Index begin, Index count) {
1388 eigen_assert(begin >= 0 && count >= 0);
1389 if (begin == 0) return svwhilelt_b32(uint64_t(0), uint64_t(count));
1390 return svnot_b_z(svwhilelt_b32(uint64_t(0), uint64_t(begin + count)), svwhilelt_b32(uint64_t(0), uint64_t(begin)));
1391}
1392
1393EIGEN_STRONG_INLINE svbool_t sve_segment_predicate_b64(Index begin, Index count) {
1394 eigen_assert(begin >= 0 && count >= 0);
1395 if (begin == 0) return svwhilelt_b64(uint64_t(0), uint64_t(count));
1396 return svnot_b_z(svwhilelt_b64(uint64_t(0), uint64_t(begin + count)), svwhilelt_b64(uint64_t(0), uint64_t(begin)));
1397}
1398
1399/*---------------- int32 ----------------*/
1400
1401template <>
1402struct has_packet_segment<PacketXi> : std::true_type {};
1403
1404template <>
1405inline PacketXi ploaduSegment<PacketXi>(const numext::int32_t* from, Index begin, Index count) {
1406 return svld1_s32(sve_segment_predicate_b32(begin, count), from);
1407}
1408
1409template <>
1410inline void pstoreuSegment<numext::int32_t, PacketXi>(numext::int32_t* to, const PacketXi& from, Index begin,
1411 Index count) {
1412 svst1_s32(sve_segment_predicate_b32(begin, count), to, from);
1413}
1414
1415/*---------------- int64 ----------------*/
1416
1417template <>
1418struct has_packet_segment<PacketXl> : std::true_type {};
1419
1420template <>
1421inline PacketXl ploaduSegment<PacketXl>(const numext::int64_t* from, Index begin, Index count) {
1422 return svld1_s64(sve_segment_predicate_b64(begin, count), from);
1423}
1424
1425template <>
1426inline void pstoreuSegment<numext::int64_t, PacketXl>(numext::int64_t* to, const PacketXl& from, Index begin,
1427 Index count) {
1428 svst1_s64(sve_segment_predicate_b64(begin, count), to, from);
1429}
1430
1431/*---------------- float ----------------*/
1432
1433template <>
1434struct has_packet_segment<PacketXf> : std::true_type {};
1435
1436template <>
1437inline PacketXf ploaduSegment<PacketXf>(const float* from, Index begin, Index count) {
1438 return svld1_f32(sve_segment_predicate_b32(begin, count), from);
1439}
1440
1441template <>
1442inline void pstoreuSegment<float, PacketXf>(float* to, const PacketXf& from, Index begin, Index count) {
1443 svst1_f32(sve_segment_predicate_b32(begin, count), to, from);
1444}
1445
1446/*---------------- double ----------------*/
1447
1448template <>
1449struct has_packet_segment<PacketXd> : std::true_type {};
1450
1451template <>
1452inline PacketXd ploaduSegment<PacketXd>(const double* from, Index begin, Index count) {
1453 return svld1_f64(sve_segment_predicate_b64(begin, count), from);
1454}
1455
1456template <>
1457inline void pstoreuSegment<double, PacketXd>(double* to, const PacketXd& from, Index begin, Index count) {
1458 svst1_f64(sve_segment_predicate_b64(begin, count), to, from);
1459}
1460
1461/*---------------- end load/store segment support ----------------*/
1462
1463} // namespace internal
1464} // namespace Eigen
1465
1466#endif // EIGEN_PACKET_MATH_SVE_H
@ Aligned128
Definition Constants.h:241