Documentation 6b54b78464
Loading...
Searching...
No Matches
vectorization.hpp
Go to the documentation of this file.
1/*!
2 * \file vectorization.hpp
3 * \brief Implementation of a portable SIMD type.
4 * \author P. Gomes
5 * \version 8.5.0 "Harrier"
6 *
7 * SU2 Project Website: https://su2code.github.io
8 *
9 * The SU2 Project is maintained by the SU2 Foundation
10 * (http://su2foundation.org)
11 *
12 * Copyright 2012-2026, SU2 Contributors (cf. AUTHORS.md)
13 *
14 * SU2 is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
18 *
19 * SU2 is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
23 *
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with SU2. If not, see <http://www.gnu.org/licenses/>.
26 */
27
28#pragma once
29
30#include "../linear_algebra/vector_expressions.hpp"
31#include "../parallelization/omp_structure.hpp"
32#include <initializer_list>
33#include <algorithm>
34#include <cmath>
35#ifdef __SSE2__
36#include "x86intrin.h"
37#endif
38#if defined(__ARM_NEON) || defined(__ARM_NEON__)
39#include <arm_neon.h>
40#endif
41
42namespace simd {
43/// \addtogroup SIMD
44/// @{
45
46using namespace VecExpr;
47
48/*--- Detect preferred SIMD size (bytes). This only covers x86 architectures. ---*/
49#if defined(__AVX512F__)
50constexpr size_t PREFERRED_SIZE = 64;
51#elif defined(__AVX__)
52constexpr size_t PREFERRED_SIZE = 32;
53#elif defined(__SSE2__) || defined(__ARM_NEON) || defined(__ARM_NEON__)
54constexpr size_t PREFERRED_SIZE = 16;
55#else
56constexpr size_t PREFERRED_SIZE = 8;
57#endif
58
59/*!
60 * \brief Convert the SIMD size (bytes) to a lenght (num elems).
61 */
62template <class T>
63constexpr size_t preferredLen() {
64 return PREFERRED_SIZE / sizeof(T);
65}
66
67template <>
68constexpr size_t preferredLen<su2double>() {
69#ifdef CODI_REVERSE_TYPE
70 /*--- Use a SIMD size of 1 for reverse AD, larger sizes increase
71 * the pre-accumulation time with no performance benefit. ---*/
72 return 1;
73#else
74 /*--- For forward AD there is a performance benefit. This covers
75 * forward AD and primal mode (su2double == passivedouble). ---*/
76 return PREFERRED_SIZE / sizeof(passivedouble);
77#endif
78}
79
80/*!
81 * \class Array
82 * \brief A simple SIMD type relying on implicit vectorization, i.e. done by
83 * the compiler, explicitly vectorized specializations are defined after.
84 * \note This class gets its math operator overloads from CVecExpr, the
85 * specializations do not use expression templates, IF YOU NEED A NEW FUNCTION,
86 * define it both in vector_expressions.hpp and in special_vectorization.hpp.
87 */
88template <class Scalar_t, size_t N = preferredLen<Scalar_t>()>
89class Array : public CVecExpr<Array<Scalar_t, N>, Scalar_t> {
90#define FOREACH for (size_t k = 0; k < N; ++k)
91 static_assert(N > 0, "Invalid SIMD size");
92
93 public:
94 using Scalar = Scalar_t;
95 enum : size_t { Size = N };
96 enum : size_t { Align = Size * alignof(Scalar) };
97 static constexpr bool StoreAsRef = true;
98
99 private:
100 alignas(Size * alignof(Scalar)) Scalar x_[N];
101
102 public:
103#define ARRAY_BOILERPLATE \
104 /*!--- Access elements ---*/ \
105 FORCEINLINE Scalar& operator[](size_t k) { return x_[k]; } \
106 FORCEINLINE const Scalar& operator[](size_t k) const { return x_[k]; } \
107 /*!--- Constructors ---*/ \
108 FORCEINLINE Array() = default; \
109 FORCEINLINE Array(Scalar x) { bcast(x); } \
110 FORCEINLINE Array(std::initializer_list<Scalar> vals) { \
111 auto it = vals.begin(); \
112 FOREACH { \
113 x_[k] = *it; \
114 ++it; \
115 } \
116 } \
117 FORCEINLINE Array(Scalar x0, Scalar dx) { FOREACH x_[k] = x0 + k * dx; } \
118 FORCEINLINE Array(const Scalar* ptr) { load(ptr); } \
119 template <class T> \
120 FORCEINLINE Array(const Scalar* beg, const T& off) { \
121 gather(beg, off); \
122 } \
123 /*!--- Reduction operations ---*/ \
124 FORCEINLINE Scalar sum() const { \
125 Scalar s(0); \
126 FOREACH { s += x_[k]; } \
127 return s; \
128 } \
129 FORCEINLINE Scalar dot(const Array& other) const { \
130 Scalar s(0); \
131 FOREACH { s += x_[k] * other[k]; } \
132 return s; \
133 }
134
135#if defined(CODI_REVERSE_TYPE) || defined(CODI_FORWARD_TYPE)
136 /*--- These are not very nice but without them it would not be
137 * possible to assign literals to Arrays of active types. ---*/
138 template <class U = Scalar, su2enable_if<std::is_same<U, su2double>::value> = 0>
140 bcast(x);
141 }
142 template <class U = Scalar, su2enable_if<std::is_same<U, su2double>::value> = 0>
144 bcast(x);
145 return *this;
146 }
147#endif
148
150
151 /*! \brief Copy construct from expression. */
152 template <class U>
154 FOREACH x_[k] = expr.derived()[k];
155 }
156
157 /*--- Implementation of the construction primitives. ---*/
158
159 FORCEINLINE void bcast(Scalar x) { FOREACH x_[k] = x; }
160 FORCEINLINE void load(const Scalar* ptr) { FOREACH x_[k] = ptr[k]; }
161 FORCEINLINE void loada(const Scalar* ptr) { load(ptr); }
162 FORCEINLINE void store(Scalar* ptr) const { FOREACH ptr[k] = x_[k]; }
163 FORCEINLINE void storea(Scalar* ptr) const { store(ptr); }
164 FORCEINLINE void stream(Scalar* ptr) const { store(ptr); }
165 template <class T>
166 FORCEINLINE void gather(const Scalar* begin, const T& offsets) {
167 FOREACH x_[k] = begin[offsets[k]];
168 }
169
170 /*--- Compound assignment operators. ---*/
171
172#define MAKE_COMPOUND(OP) \
173 FORCEINLINE Array& operator OP(Scalar x) { \
174 FOREACH { x_[k] OP x; } \
175 return *this; \
176 } \
177 template <class U> \
178 FORCEINLINE Array& operator OP(const CVecExpr<U, Scalar>& expr) { \
179 FOREACH { x_[k] OP expr.derived()[k]; } \
180 return *this; \
181 }
187#undef MAKE_COMPOUND
188
189#undef FOREACH
190};
191
192/*--- Explicit vectorization specializations, see e.g.
193 * https://software.intel.com/sites/landingpage/IntrinsicsGuide/
194 * for documentation on the "_mm*" functions. ---*/
195
196/*--- Size tags for overload resolution of some wrapper functions. ---*/
197namespace SizeTag {
198struct TWO {};
199struct FOUR {};
200struct EIGHT {};
201struct SIXTEEN {};
202} // namespace SizeTag
203
204/*--- Constants for bitwise implementations. ---*/
205/*--- abs forces the sign bit to 0 ("x" & 0b0111...). ---*/
206constexpr uint64_t abs_mask_d = 0x7FFFFFFFFFFFFFFFL;
207constexpr uint32_t abs_mask_s = 0x7FFFFFFFU;
208/*--- negation flips the sign bit ("x" ^ 0b1000...). ---*/
209constexpr uint64_t sign_mask_d = 0x8000000000000000L;
210constexpr uint32_t sign_mask_s = 0x80000000U;
211
212#ifdef __SSE2__
213/*!
214 * Create specialization for array of 2 doubles (this should be always available).
215 */
216#define ARRAY_T Array<double, 2>
217#define SCALAR_T double
218#define REGISTER_T __m128d
219#define SIZE_TAG SizeTag::TWO()
220
221static const __m128d abs_mask_2d = _mm_castsi128_pd(_mm_set1_epi64x(abs_mask_d));
222static const __m128d sign_mask_2d = _mm_castsi128_pd(_mm_set1_epi64x(sign_mask_d));
223static const __m128d ones_2d = _mm_set1_pd(1);
224
225FORCEINLINE __m128d set1_p(SizeTag::TWO, double p) { return _mm_set1_pd(p); }
226FORCEINLINE __m128d load_p(SizeTag::TWO, const double* p) { return _mm_load_pd(p); }
227FORCEINLINE __m128d loadu_p(SizeTag::TWO, const double* p) { return _mm_loadu_pd(p); }
228FORCEINLINE void store_p(double* p, __m128d x) { _mm_store_pd(p, x); }
229FORCEINLINE void storeu_p(double* p, __m128d x) { _mm_storeu_pd(p, x); }
230FORCEINLINE void stream_p(double* p, __m128d x) { _mm_stream_pd(p, x); }
231
232FORCEINLINE __m128d add_p(__m128d a, __m128d b) { return _mm_add_pd(a, b); }
233FORCEINLINE __m128d sub_p(__m128d a, __m128d b) { return _mm_sub_pd(a, b); }
234FORCEINLINE __m128d mul_p(__m128d a, __m128d b) { return _mm_mul_pd(a, b); }
235FORCEINLINE __m128d div_p(__m128d a, __m128d b) { return _mm_div_pd(a, b); }
236FORCEINLINE __m128d max_p(__m128d a, __m128d b) { return _mm_max_pd(a, b); }
237FORCEINLINE __m128d min_p(__m128d a, __m128d b) { return _mm_min_pd(a, b); }
238
239FORCEINLINE __m128d eq_p(__m128d a, __m128d b) { return _mm_and_pd(ones_2d, _mm_cmpeq_pd(a, b)); }
240FORCEINLINE __m128d lt_p(__m128d a, __m128d b) { return _mm_and_pd(ones_2d, _mm_cmplt_pd(a, b)); }
241FORCEINLINE __m128d le_p(__m128d a, __m128d b) { return _mm_and_pd(ones_2d, _mm_cmple_pd(a, b)); }
242FORCEINLINE __m128d ne_p(__m128d a, __m128d b) { return _mm_and_pd(ones_2d, _mm_cmpneq_pd(a, b)); }
243FORCEINLINE __m128d ge_p(__m128d a, __m128d b) { return _mm_and_pd(ones_2d, _mm_cmpge_pd(a, b)); }
244FORCEINLINE __m128d gt_p(__m128d a, __m128d b) { return _mm_and_pd(ones_2d, _mm_cmpgt_pd(a, b)); }
245
246FORCEINLINE __m128d sqrt_p(__m128d x) { return _mm_sqrt_pd(x); }
247FORCEINLINE __m128d abs_p(__m128d x) { return _mm_and_pd(x, abs_mask_2d); }
248FORCEINLINE __m128d neg_p(__m128d x) { return _mm_xor_pd(x, sign_mask_2d); }
249FORCEINLINE __m128d sign_p(__m128d x) { return _mm_or_pd(ones_2d, _mm_and_pd(x, sign_mask_2d)); }
250
251/*--- Generate specialization based on the defines
252 * and functions above by including the header. ---*/
253
255
256/*!
257 * Create specialization for array of 4 floats (this should be always available).
258 */
259#define ARRAY_T Array<float, 4>
260#define SCALAR_T float
261#define REGISTER_T __m128
262#define SIZE_TAG SizeTag::FOUR()
263
264static const __m128 abs_mask_4s = _mm_castsi128_ps(_mm_set1_epi32(abs_mask_s));
265static const __m128 sign_mask_4s = _mm_castsi128_ps(_mm_set1_epi32(sign_mask_s));
266static const __m128 ones_4s = _mm_set1_ps(1);
267
268FORCEINLINE __m128 set1_p(SizeTag::FOUR, float p) { return _mm_set1_ps(p); }
269FORCEINLINE __m128 load_p(SizeTag::FOUR, const float* p) { return _mm_load_ps(p); }
270FORCEINLINE __m128 loadu_p(SizeTag::FOUR, const float* p) { return _mm_loadu_ps(p); }
271FORCEINLINE void store_p(float* p, __m128 x) { _mm_store_ps(p, x); }
272FORCEINLINE void storeu_p(float* p, __m128 x) { _mm_storeu_ps(p, x); }
273FORCEINLINE void stream_p(float* p, __m128 x) { _mm_stream_ps(p, x); }
274
275FORCEINLINE __m128 add_p(__m128 a, __m128 b) { return _mm_add_ps(a, b); }
276FORCEINLINE __m128 sub_p(__m128 a, __m128 b) { return _mm_sub_ps(a, b); }
277FORCEINLINE __m128 mul_p(__m128 a, __m128 b) { return _mm_mul_ps(a, b); }
278FORCEINLINE __m128 div_p(__m128 a, __m128 b) { return _mm_div_ps(a, b); }
279FORCEINLINE __m128 max_p(__m128 a, __m128 b) { return _mm_max_ps(a, b); }
280FORCEINLINE __m128 min_p(__m128 a, __m128 b) { return _mm_min_ps(a, b); }
281
282FORCEINLINE __m128 eq_p(__m128 a, __m128 b) { return _mm_and_ps(ones_4s, _mm_cmpeq_ps(a, b)); }
283FORCEINLINE __m128 lt_p(__m128 a, __m128 b) { return _mm_and_ps(ones_4s, _mm_cmplt_ps(a, b)); }
284FORCEINLINE __m128 le_p(__m128 a, __m128 b) { return _mm_and_ps(ones_4s, _mm_cmple_ps(a, b)); }
285FORCEINLINE __m128 ne_p(__m128 a, __m128 b) { return _mm_and_ps(ones_4s, _mm_cmpneq_ps(a, b)); }
286FORCEINLINE __m128 ge_p(__m128 a, __m128 b) { return _mm_and_ps(ones_4s, _mm_cmpge_ps(a, b)); }
287FORCEINLINE __m128 gt_p(__m128 a, __m128 b) { return _mm_and_ps(ones_4s, _mm_cmpgt_ps(a, b)); }
288
289FORCEINLINE __m128 sqrt_p(__m128 x) { return _mm_sqrt_ps(x); }
290FORCEINLINE __m128 abs_p(__m128 x) { return _mm_and_ps(x, abs_mask_4s); }
291FORCEINLINE __m128 neg_p(__m128 x) { return _mm_xor_ps(x, sign_mask_4s); }
292FORCEINLINE __m128 sign_p(__m128 x) { return _mm_or_ps(ones_4s, _mm_and_ps(x, sign_mask_4s)); }
293
295
296#endif // __SSE2__
297
298#ifdef __AVX__
299/*!
300 * Create specialization for array of 4 doubles.
301 */
302#define ARRAY_T Array<double, 4>
303#define SCALAR_T double
304#define REGISTER_T __m256d
305#define SIZE_TAG SizeTag::FOUR()
306
307static const __m256d abs_mask_4d = _mm256_castsi256_pd(_mm256_set1_epi64x(abs_mask_d));
308static const __m256d sign_mask_4d = _mm256_castsi256_pd(_mm256_set1_epi64x(sign_mask_d));
309static const __m256d ones_4d = _mm256_set1_pd(1);
310
311FORCEINLINE __m256d set1_p(SizeTag::FOUR, double p) { return _mm256_set1_pd(p); }
312FORCEINLINE __m256d load_p(SizeTag::FOUR, const double* p) { return _mm256_load_pd(p); }
313FORCEINLINE __m256d loadu_p(SizeTag::FOUR, const double* p) { return _mm256_loadu_pd(p); }
314FORCEINLINE void store_p(double* p, __m256d x) { _mm256_store_pd(p, x); }
315FORCEINLINE void storeu_p(double* p, __m256d x) { _mm256_storeu_pd(p, x); }
316FORCEINLINE void stream_p(double* p, __m256d x) { _mm256_stream_pd(p, x); }
317
318FORCEINLINE __m256d add_p(__m256d a, __m256d b) { return _mm256_add_pd(a, b); }
319FORCEINLINE __m256d sub_p(__m256d a, __m256d b) { return _mm256_sub_pd(a, b); }
320FORCEINLINE __m256d mul_p(__m256d a, __m256d b) { return _mm256_mul_pd(a, b); }
321FORCEINLINE __m256d div_p(__m256d a, __m256d b) { return _mm256_div_pd(a, b); }
322FORCEINLINE __m256d max_p(__m256d a, __m256d b) { return _mm256_max_pd(a, b); }
323FORCEINLINE __m256d min_p(__m256d a, __m256d b) { return _mm256_min_pd(a, b); }
324
325FORCEINLINE __m256d eq_p(__m256d a, __m256d b) { return _mm256_and_pd(ones_4d, _mm256_cmp_pd(a, b, 0)); }
326FORCEINLINE __m256d lt_p(__m256d a, __m256d b) { return _mm256_and_pd(ones_4d, _mm256_cmp_pd(a, b, 1)); }
327FORCEINLINE __m256d le_p(__m256d a, __m256d b) { return _mm256_and_pd(ones_4d, _mm256_cmp_pd(a, b, 2)); }
328FORCEINLINE __m256d ne_p(__m256d a, __m256d b) { return _mm256_and_pd(ones_4d, _mm256_cmp_pd(a, b, 4)); }
329FORCEINLINE __m256d ge_p(__m256d a, __m256d b) { return _mm256_and_pd(ones_4d, _mm256_cmp_pd(a, b, 13)); }
330FORCEINLINE __m256d gt_p(__m256d a, __m256d b) { return _mm256_and_pd(ones_4d, _mm256_cmp_pd(a, b, 14)); }
331
332FORCEINLINE __m256d sqrt_p(__m256d x) { return _mm256_sqrt_pd(x); }
333FORCEINLINE __m256d abs_p(__m256d x) { return _mm256_and_pd(x, abs_mask_4d); }
334FORCEINLINE __m256d neg_p(__m256d x) { return _mm256_xor_pd(x, sign_mask_4d); }
335FORCEINLINE __m256d sign_p(__m256d x) { return _mm256_or_pd(ones_4d, _mm256_and_pd(x, sign_mask_4d)); }
336
338
339/*!
340 * Create specialization for array of 8 floats.
341 */
342#define ARRAY_T Array<float, 8>
343#define SCALAR_T float
344#define REGISTER_T __m256
345#define SIZE_TAG SizeTag::EIGHT()
346
347static const __m256 abs_mask_8s = _mm256_castsi256_ps(_mm256_set1_epi32(abs_mask_s));
348static const __m256 sign_mask_8s = _mm256_castsi256_ps(_mm256_set1_epi32(sign_mask_s));
349static const __m256 ones_8s = _mm256_set1_ps(1);
350
351FORCEINLINE __m256 set1_p(SizeTag::EIGHT, float p) { return _mm256_set1_ps(p); }
352FORCEINLINE __m256 load_p(SizeTag::EIGHT, const float* p) { return _mm256_load_ps(p); }
353FORCEINLINE __m256 loadu_p(SizeTag::EIGHT, const float* p) { return _mm256_loadu_ps(p); }
354FORCEINLINE void store_p(float* p, __m256 x) { _mm256_store_ps(p, x); }
355FORCEINLINE void storeu_p(float* p, __m256 x) { _mm256_storeu_ps(p, x); }
356FORCEINLINE void stream_p(float* p, __m256 x) { _mm256_stream_ps(p, x); }
357
358FORCEINLINE __m256 add_p(__m256 a, __m256 b) { return _mm256_add_ps(a, b); }
359FORCEINLINE __m256 sub_p(__m256 a, __m256 b) { return _mm256_sub_ps(a, b); }
360FORCEINLINE __m256 mul_p(__m256 a, __m256 b) { return _mm256_mul_ps(a, b); }
361FORCEINLINE __m256 div_p(__m256 a, __m256 b) { return _mm256_div_ps(a, b); }
362FORCEINLINE __m256 max_p(__m256 a, __m256 b) { return _mm256_max_ps(a, b); }
363FORCEINLINE __m256 min_p(__m256 a, __m256 b) { return _mm256_min_ps(a, b); }
364
365FORCEINLINE __m256 eq_p(__m256 a, __m256 b) { return _mm256_and_ps(ones_8s, _mm256_cmp_ps(a, b, 0)); }
366FORCEINLINE __m256 lt_p(__m256 a, __m256 b) { return _mm256_and_ps(ones_8s, _mm256_cmp_ps(a, b, 1)); }
367FORCEINLINE __m256 le_p(__m256 a, __m256 b) { return _mm256_and_ps(ones_8s, _mm256_cmp_ps(a, b, 2)); }
368FORCEINLINE __m256 ne_p(__m256 a, __m256 b) { return _mm256_and_ps(ones_8s, _mm256_cmp_ps(a, b, 4)); }
369FORCEINLINE __m256 ge_p(__m256 a, __m256 b) { return _mm256_and_ps(ones_8s, _mm256_cmp_ps(a, b, 13)); }
370FORCEINLINE __m256 gt_p(__m256 a, __m256 b) { return _mm256_and_ps(ones_8s, _mm256_cmp_ps(a, b, 14)); }
371
372FORCEINLINE __m256 sqrt_p(__m256 x) { return _mm256_sqrt_ps(x); }
373FORCEINLINE __m256 abs_p(__m256 x) { return _mm256_and_ps(x, abs_mask_8s); }
374FORCEINLINE __m256 neg_p(__m256 x) { return _mm256_xor_ps(x, sign_mask_8s); }
375FORCEINLINE __m256 sign_p(__m256 x) { return _mm256_or_ps(ones_8s, _mm256_and_ps(x, sign_mask_8s)); }
376
378
379#endif // __AVX__
380
381#ifdef __AVX512F__
382/*!
383 * Create specialization for array of 8 doubles.
384 */
385#define ARRAY_T Array<double, 8>
386#define SCALAR_T double
387#define REGISTER_T __m512d
388#define SIZE_TAG SizeTag::EIGHT()
389
390static const __m512d abs_mask_8d = _mm512_castsi512_pd(_mm512_set1_epi64(abs_mask_d));
391static const __m512d sign_mask_8d = _mm512_castsi512_pd(_mm512_set1_epi64(sign_mask_d));
392static const __m512d ones_8d = _mm512_set1_pd(1);
393
394FORCEINLINE __m512d set1_p(SizeTag::EIGHT, double p) { return _mm512_set1_pd(p); }
395FORCEINLINE __m512d load_p(SizeTag::EIGHT, const double* p) { return _mm512_load_pd(p); }
396FORCEINLINE __m512d loadu_p(SizeTag::EIGHT, const double* p) { return _mm512_loadu_pd(p); }
397FORCEINLINE void store_p(double* p, __m512d x) { _mm512_store_pd(p, x); }
398FORCEINLINE void storeu_p(double* p, __m512d x) { _mm512_storeu_pd(p, x); }
399FORCEINLINE void stream_p(double* p, __m512d x) { _mm512_stream_pd(p, x); }
400
401FORCEINLINE __m512d add_p(__m512d a, __m512d b) { return _mm512_add_pd(a, b); }
402FORCEINLINE __m512d sub_p(__m512d a, __m512d b) { return _mm512_sub_pd(a, b); }
403FORCEINLINE __m512d mul_p(__m512d a, __m512d b) { return _mm512_mul_pd(a, b); }
404FORCEINLINE __m512d div_p(__m512d a, __m512d b) { return _mm512_div_pd(a, b); }
405FORCEINLINE __m512d max_p(__m512d a, __m512d b) { return _mm512_max_pd(a, b); }
406FORCEINLINE __m512d min_p(__m512d a, __m512d b) { return _mm512_min_pd(a, b); }
407
408template <int opCode>
409FORCEINLINE __m512d cmp_p(__m512d a, __m512d b) {
410 return _mm512_mask_blend_pd(_mm512_cmp_pd_mask(a, b, opCode), _mm512_setzero_pd(), ones_8d);
411}
412FORCEINLINE __m512d eq_p(__m512d a, __m512d b) { return cmp_p<0>(a, b); }
413FORCEINLINE __m512d lt_p(__m512d a, __m512d b) { return cmp_p<1>(a, b); }
414FORCEINLINE __m512d le_p(__m512d a, __m512d b) { return cmp_p<2>(a, b); }
415FORCEINLINE __m512d ne_p(__m512d a, __m512d b) { return cmp_p<4>(a, b); }
416FORCEINLINE __m512d ge_p(__m512d a, __m512d b) { return cmp_p<13>(a, b); }
417FORCEINLINE __m512d gt_p(__m512d a, __m512d b) { return cmp_p<14>(a, b); }
418
419FORCEINLINE __m512d sqrt_p(__m512d x) { return _mm512_sqrt_pd(x); }
420FORCEINLINE __m512d abs_p(__m512d x) { return _mm512_and_pd(x, abs_mask_8d); }
421FORCEINLINE __m512d neg_p(__m512d x) { return _mm512_xor_pd(x, sign_mask_8d); }
422FORCEINLINE __m512d sign_p(__m512d x) { return _mm512_or_pd(ones_8d, _mm512_and_pd(x, sign_mask_8d)); }
423
425
426/*!
427 * Create specialization for array of 16 floats.
428 */
429#define ARRAY_T Array<float, 16>
430#define SCALAR_T float
431#define REGISTER_T __m512
432#define SIZE_TAG SizeTag::SIXTEEN()
433
434static const __m512 abs_mask_16s = _mm512_castsi512_ps(_mm512_set1_epi32(abs_mask_s));
435static const __m512 sign_mask_16s = _mm512_castsi512_ps(_mm512_set1_epi32(sign_mask_s));
436static const __m512 ones_16s = _mm512_set1_ps(1);
437
438FORCEINLINE __m512 set1_p(SizeTag::SIXTEEN, float p) { return _mm512_set1_ps(p); }
439FORCEINLINE __m512 load_p(SizeTag::SIXTEEN, const float* p) { return _mm512_load_ps(p); }
440FORCEINLINE __m512 loadu_p(SizeTag::SIXTEEN, const float* p) { return _mm512_loadu_ps(p); }
441FORCEINLINE void store_p(float* p, __m512 x) { _mm512_store_ps(p, x); }
442FORCEINLINE void storeu_p(float* p, __m512 x) { _mm512_storeu_ps(p, x); }
443FORCEINLINE void stream_p(float* p, __m512 x) { _mm512_stream_ps(p, x); }
444
445FORCEINLINE __m512 add_p(__m512 a, __m512 b) { return _mm512_add_ps(a, b); }
446FORCEINLINE __m512 sub_p(__m512 a, __m512 b) { return _mm512_sub_ps(a, b); }
447FORCEINLINE __m512 mul_p(__m512 a, __m512 b) { return _mm512_mul_ps(a, b); }
448FORCEINLINE __m512 div_p(__m512 a, __m512 b) { return _mm512_div_ps(a, b); }
449FORCEINLINE __m512 max_p(__m512 a, __m512 b) { return _mm512_max_ps(a, b); }
450FORCEINLINE __m512 min_p(__m512 a, __m512 b) { return _mm512_min_ps(a, b); }
451
452template <int opCode>
453FORCEINLINE __m512 cmp_p(__m512 a, __m512 b) {
454 return _mm512_mask_blend_ps(_mm512_cmp_ps_mask(a, b, opCode), _mm512_setzero_ps(), ones_16s);
455}
456FORCEINLINE __m512 eq_p(__m512 a, __m512 b) { return cmp_p<0>(a, b); }
457FORCEINLINE __m512 lt_p(__m512 a, __m512 b) { return cmp_p<1>(a, b); }
458FORCEINLINE __m512 le_p(__m512 a, __m512 b) { return cmp_p<2>(a, b); }
459FORCEINLINE __m512 ne_p(__m512 a, __m512 b) { return cmp_p<4>(a, b); }
460FORCEINLINE __m512 ge_p(__m512 a, __m512 b) { return cmp_p<13>(a, b); }
461FORCEINLINE __m512 gt_p(__m512 a, __m512 b) { return cmp_p<14>(a, b); }
462
463FORCEINLINE __m512 sqrt_p(__m512 x) { return _mm512_sqrt_ps(x); }
464FORCEINLINE __m512 abs_p(__m512 x) { return _mm512_and_ps(x, abs_mask_16s); }
465FORCEINLINE __m512 neg_p(__m512 x) { return _mm512_xor_ps(x, sign_mask_16s); }
466FORCEINLINE __m512 sign_p(__m512 x) { return _mm512_or_ps(ones_16s, _mm512_and_ps(x, sign_mask_16s)); }
467
469
470#endif // __AVX512F__
471
472#if defined(__ARM_NEON) || defined(__ARM_NEON__)
473/*!
474 * Create specialization for array of 2 doubles.
475 */
476#define ARRAY_T Array<double, 2>
477#define SCALAR_T double
478#define REGISTER_T float64x2_t
479#define SIZE_TAG SizeTag::TWO()
480
481static const uint64x2_t abs_mask_2d_u = vdupq_n_u64(abs_mask_d);
482static const uint64x2_t sign_mask_2d_u = vdupq_n_u64(sign_mask_d);
483static const uint64x2_t ones_2d_u = vreinterpretq_u64_f64(vdupq_n_f64(1.0));
484static const uint64x2_t ones_2u = vdupq_n_u64(~0ULL);
485
486FORCEINLINE float64x2_t set1_p(SizeTag::TWO, double p) { return vdupq_n_f64(p); }
487FORCEINLINE float64x2_t load_p(SizeTag::TWO, const double* p) { return vld1q_f64(p); }
488FORCEINLINE float64x2_t loadu_p(SizeTag::TWO, const double* p) { return vld1q_f64(p); }
489FORCEINLINE void store_p(double* p, float64x2_t x) { vst1q_f64(p, x); }
490FORCEINLINE void storeu_p(double* p, float64x2_t x) { vst1q_f64(p, x); }
491/*--- No direct NEON equivalent to streaming stores. ---*/
492FORCEINLINE void stream_p(double* p, float64x2_t x) { vst1q_f64(p, x); }
493
494FORCEINLINE float64x2_t add_p(float64x2_t a, float64x2_t b) { return vaddq_f64(a, b); }
495FORCEINLINE float64x2_t sub_p(float64x2_t a, float64x2_t b) { return vsubq_f64(a, b); }
496FORCEINLINE float64x2_t mul_p(float64x2_t a, float64x2_t b) { return vmulq_f64(a, b); }
497FORCEINLINE float64x2_t div_p(float64x2_t a, float64x2_t b) { return vdivq_f64(a, b); }
498FORCEINLINE float64x2_t max_p(float64x2_t a, float64x2_t b) { return vmaxq_f64(a, b); }
499FORCEINLINE float64x2_t min_p(float64x2_t a, float64x2_t b) { return vminq_f64(a, b); }
500
501/*--- Comparisons return uint64x2_t masks. Convert to 0.0 / 1.0. ---*/
502FORCEINLINE float64x2_t int2float(uint64x2_t a) { return vreinterpretq_f64_u64(a); }
503FORCEINLINE float64x2_t cmp2float(uint64x2_t cmp) { return int2float(vandq_u64(ones_2d_u, cmp)); }
504FORCEINLINE float64x2_t eq_p(float64x2_t a, float64x2_t b) { return cmp2float(vceqq_f64(a, b)); }
505FORCEINLINE float64x2_t lt_p(float64x2_t a, float64x2_t b) { return cmp2float(vcltq_f64(a, b)); }
506FORCEINLINE float64x2_t le_p(float64x2_t a, float64x2_t b) { return cmp2float(vcleq_f64(a, b)); }
507FORCEINLINE float64x2_t ne_p(float64x2_t a, float64x2_t b) { return cmp2float(veorq_u64(ones_2u, vceqq_f64(a, b))); }
508FORCEINLINE float64x2_t ge_p(float64x2_t a, float64x2_t b) { return cmp2float(vcgeq_f64(a, b)); }
509FORCEINLINE float64x2_t gt_p(float64x2_t a, float64x2_t b) { return cmp2float(vcgtq_f64(a, b)); }
510
511FORCEINLINE float64x2_t sqrt_p(float64x2_t x) { return vsqrtq_f64(x); }
512FORCEINLINE float64x2_t abs_p(float64x2_t x) { return int2float(vandq_u64(vreinterpretq_u64_f64(x), abs_mask_2d_u)); }
513FORCEINLINE float64x2_t neg_p(float64x2_t x) { return int2float(veorq_u64(vreinterpretq_u64_f64(x), sign_mask_2d_u)); }
514FORCEINLINE float64x2_t sign_p(float64x2_t x) {
515 return int2float(vorrq_u64(ones_2d_u, vandq_u64(vreinterpretq_u64_f64(x), sign_mask_2d_u)));
516}
517
519
520/*!
521 * Create specialization for array of 4 floats.
522 */
523#define ARRAY_T Array<float, 4>
524#define SCALAR_T float
525#define REGISTER_T float32x4_t
526#define SIZE_TAG SizeTag::FOUR()
527
528static const uint32x4_t abs_mask_4s_u = vdupq_n_u32(abs_mask_s);
529static const uint32x4_t sign_mask_4s_u = vdupq_n_u32(sign_mask_s);
530static const uint32x4_t ones_4s_u = vreinterpretq_u32_f32(vdupq_n_f32(1.0f));
531
532FORCEINLINE float32x4_t set1_p(SizeTag::FOUR, float p) { return vdupq_n_f32(p); }
533FORCEINLINE float32x4_t load_p(SizeTag::FOUR, const float* p) { return vld1q_f32(p); }
534FORCEINLINE float32x4_t loadu_p(SizeTag::FOUR, const float* p) { return vld1q_f32(p); }
535FORCEINLINE void store_p(float* p, float32x4_t x) { vst1q_f32(p, x); }
536FORCEINLINE void storeu_p(float* p, float32x4_t x) { vst1q_f32(p, x); }
537FORCEINLINE void stream_p(float* p, float32x4_t x) { vst1q_f32(p, x); }
538FORCEINLINE float32x4_t add_p(float32x4_t a, float32x4_t b) { return vaddq_f32(a, b); }
539FORCEINLINE float32x4_t sub_p(float32x4_t a, float32x4_t b) { return vsubq_f32(a, b); }
540FORCEINLINE float32x4_t mul_p(float32x4_t a, float32x4_t b) { return vmulq_f32(a, b); }
541FORCEINLINE float32x4_t div_p(float32x4_t a, float32x4_t b) { return vdivq_f32(a, b); }
542FORCEINLINE float32x4_t max_p(float32x4_t a, float32x4_t b) { return vmaxq_f32(a, b); }
543FORCEINLINE float32x4_t min_p(float32x4_t a, float32x4_t b) { return vminq_f32(a, b); }
544
545FORCEINLINE float32x4_t int2float(uint32x4_t a) { return vreinterpretq_f32_u32(a); }
546FORCEINLINE float32x4_t cmp2float(uint32x4_t cmp) { return int2float(vandq_u32(ones_4s_u, cmp)); }
547FORCEINLINE float32x4_t eq_p(float32x4_t a, float32x4_t b) { return cmp2float(vceqq_f32(a, b)); }
548FORCEINLINE float32x4_t lt_p(float32x4_t a, float32x4_t b) { return cmp2float(vcltq_f32(a, b)); }
549FORCEINLINE float32x4_t le_p(float32x4_t a, float32x4_t b) { return cmp2float(vcleq_f32(a, b)); }
550FORCEINLINE float32x4_t ne_p(float32x4_t a, float32x4_t b) { return cmp2float(vmvnq_u32(vceqq_f32(a, b))); }
551FORCEINLINE float32x4_t ge_p(float32x4_t a, float32x4_t b) { return cmp2float(vcgeq_f32(a, b)); }
552FORCEINLINE float32x4_t gt_p(float32x4_t a, float32x4_t b) { return cmp2float(vcgtq_f32(a, b)); }
553
554FORCEINLINE float32x4_t sqrt_p(float32x4_t x) { return vsqrtq_f32(x); }
555FORCEINLINE float32x4_t abs_p(float32x4_t x) { return int2float(vandq_u32(vreinterpretq_u32_f32(x), abs_mask_4s_u)); }
556FORCEINLINE float32x4_t neg_p(float32x4_t x) { return int2float(veorq_u32(vreinterpretq_u32_f32(x), sign_mask_4s_u)); }
557FORCEINLINE float32x4_t sign_p(float32x4_t x) {
558 return int2float(vorrq_u32(ones_4s_u, vandq_u32(vreinterpretq_u32_f32(x), sign_mask_4s_u)));
559}
560
562
563#endif // __ARM_NEON__
564
565#undef ARRAY_BOILERPLATE
566
567/// @}
568} // namespace simd
#define MAKE_COMPOUND(OP, ASSIGN_OP)
Compound assignement operations with scalars and expressions.
Definition CSysVector.hpp:488
Base vector expression class.
Definition vector_expressions.hpp:58
const Derived & derived() const
Cast the expression to Derived, usually to allow evaluation via operator[].
Definition vector_expressions.hpp:63
A simple SIMD type relying on implicit vectorization, i.e. done by the compiler, explicitly vectorize...
Definition vectorization.hpp:89
Array()=default
void gather(const Scalar *begin, const T &offsets)
Definition vectorization.hpp:166
Array & operator=(Scalar x)
Definition vectorization.hpp:182
Scalar_t Scalar
Definition vectorization.hpp:94
void bcast(Scalar x)
Definition vectorization.hpp:159
void storea(Scalar *ptr) const
Definition vectorization.hpp:163
Array(const CVecExpr< U, Scalar > &expr)
Copy construct from expression.
Definition vectorization.hpp:153
void load(const Scalar *ptr)
Definition vectorization.hpp:160
void loada(const Scalar *ptr)
Definition vectorization.hpp:161
static constexpr bool StoreAsRef
Definition vectorization.hpp:97
void store(Scalar *ptr) const
Definition vectorization.hpp:162
@ Align
Definition vectorization.hpp:96
@ Size
Definition vectorization.hpp:95
void stream(Scalar *ptr) const
Definition vectorization.hpp:164
double passivedouble
Definition code_config.hpp:129
#define FORCEINLINE
Definition code_config.hpp:44
__m128d div_p(__m128d a, __m128d b)
Definition vectorization.hpp:235
__m128d ne_p(__m128d a, __m128d b)
Definition vectorization.hpp:242
__m128d sqrt_p(__m128d x)
Definition vectorization.hpp:246
void stream_p(double *p, __m128d x)
Definition vectorization.hpp:230
__m128d set1_p(SizeTag::TWO, double p)
Definition vectorization.hpp:225
__m128d add_p(__m128d a, __m128d b)
Definition vectorization.hpp:232
__m128d max_p(__m128d a, __m128d b)
Definition vectorization.hpp:236
constexpr size_t preferredLen< su2double >()
Definition vectorization.hpp:68
__m128d eq_p(__m128d a, __m128d b)
Definition vectorization.hpp:239
__m128d min_p(__m128d a, __m128d b)
Definition vectorization.hpp:237
__m128d load_p(SizeTag::TWO, const double *p)
Definition vectorization.hpp:226
__m128d ge_p(__m128d a, __m128d b)
Definition vectorization.hpp:243
constexpr uint32_t sign_mask_s
Definition vectorization.hpp:210
void storeu_p(double *p, __m128d x)
Definition vectorization.hpp:229
#define FOREACH
Definition special_vectorization.hpp:47
__m128d loadu_p(SizeTag::TWO, const double *p)
Definition vectorization.hpp:227
__m128d neg_p(__m128d x)
Definition vectorization.hpp:248
constexpr size_t preferredLen()
Convert the SIMD size (bytes) to a lenght (num elems).
Definition vectorization.hpp:63
__m128d sub_p(__m128d a, __m128d b)
Definition vectorization.hpp:233
__m128d lt_p(__m128d a, __m128d b)
Definition vectorization.hpp:240
void store_p(double *p, __m128d x)
Definition vectorization.hpp:228
constexpr size_t PREFERRED_SIZE
Definition vectorization.hpp:54
__m128d mul_p(__m128d a, __m128d b)
Definition vectorization.hpp:234
constexpr uint64_t abs_mask_d
Definition vectorization.hpp:206
__m128d sign_p(__m128d x)
Definition vectorization.hpp:249
__m128d gt_p(__m128d a, __m128d b)
Definition vectorization.hpp:244
__m128d abs_p(__m128d x)
Definition vectorization.hpp:247
constexpr uint64_t sign_mask_d
Definition vectorization.hpp:209
constexpr uint32_t abs_mask_s
Definition vectorization.hpp:207
__m128d le_p(__m128d a, __m128d b)
Definition vectorization.hpp:241
Definition CSysVector.hpp:104
Definition vectorization.hpp:42
Code generator header to create specializations of simd::Array.
Definition vectorization.hpp:200
Definition vectorization.hpp:199
Definition vectorization.hpp:201
Definition vectorization.hpp:198
#define ARRAY_BOILERPLATE
Definition vectorization.hpp:103