vector.h 19.9 KB
Newer Older
Pedro Gonnet's avatar
Pedro Gonnet committed
1
2
/*******************************************************************************
 * This file is part of SWIFT.
3
 * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk)
4
 *               2015 Matthieu Schaller (matthieu.schaller@durham.ac.uk)
5
 *
Pedro Gonnet's avatar
Pedro Gonnet committed
6
7
8
9
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
10
 *
Pedro Gonnet's avatar
Pedro Gonnet committed
11
12
13
14
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
15
 *
Pedro Gonnet's avatar
Pedro Gonnet committed
16
17
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
 *
Pedro Gonnet's avatar
Pedro Gonnet committed
19
 ******************************************************************************/
20
21
#ifndef SWIFT_VECTOR_H
#define SWIFT_VECTOR_H
Pedro Gonnet's avatar
Pedro Gonnet committed
22

23
/* Config parameters. */
24
25
#include "../config.h"

26
27
28
/* Local headers */
#include "inline.h"

29
30
#ifdef WITH_VECTORIZATION

31
32
33
/* Need to check whether compiler supports this (IBM does not)
   This will prevent the macros to be defined and switch off
   explicit vectorization if the compiled does not support it */
34
#ifdef HAVE_IMMINTRIN_H
35
/* Include the header file with the intrinsics for Intel architecture. */
36
#include <immintrin.h>
37
#endif
Pedro Gonnet's avatar
Pedro Gonnet committed
38

39
40
41
/* Define the vector macro. */
#define VEC_MACRO(elcount, type) \
  __attribute__((vector_size((elcount) * sizeof(type)))) type
Pedro Gonnet's avatar
Pedro Gonnet committed
42

43
/* So what will the vector size be? */
James Willis's avatar
James Willis committed
44
45

/* AVX-512 intrinsics*/
James Willis's avatar
James Willis committed
46
#ifdef HAVE_AVX512_F
47
48
49
50
51
#define VEC_HAVE_GATHER
#define VEC_SIZE 16
#define VEC_FLOAT __m512
#define VEC_DBL __m512d
#define VEC_INT __m512i
52
#define KNL_MASK_16 __mmask16
53
#define vec_load(a) _mm512_load_ps(a)
James Willis's avatar
James Willis committed
54
#define vec_store(a, addr) _mm512_store_ps(addr, a)
55
56
#define vec_setzero() _mm512_setzero_ps()
#define vec_setintzero() _mm512_setzero_epi32()
57
#define vec_set1(a) _mm512_set1_ps(a)
58
#define vec_setint1(a) _mm512_set1_epi32(a)
59
60
61
62
#define vec_set(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) \
  _mm512_set_ps(p, o, n, m, l, k, j, i, h, g, f, e, d, c, b, a)
#define vec_dbl_set(a, b, c, d, e, f, g, h) \
  _mm512_set_pd(h, g, f, e, d, c, b, a)
63
#define vec_add(a, b) _mm512_add_ps(a, b)
64
#define vec_mask_add(a, b, mask) _mm512_mask_add_ps(a, mask, b, a)
65
#define vec_sub(a, b) _mm512_sub_ps(a, b)
66
#define vec_mask_sub(a, b, mask) _mm512_mask_sub_ps(a, mask, a, b)
67
#define vec_mul(a, b) _mm512_mul_ps(a, b)
68
#define vec_div(a, b) _mm512_div_ps(a, b)
69
#define vec_fma(a, b, c) _mm512_fmadd_ps(a, b, c)
70
#define vec_fnma(a, b, c) _mm512_fnmadd_ps(a, b, c)
71
#define vec_sqrt(a) _mm512_sqrt_ps(a)
72
73
#define vec_rcp(a) _mm512_rcp14_ps(a)
#define vec_rsqrt(a) _mm512_rsqrt14_ps(a)
74
75
76
77
#define vec_ftoi(a) _mm512_cvttps_epi32(a)
#define vec_fmin(a, b) _mm512_min_ps(a, b)
#define vec_fmax(a, b) _mm512_max_ps(a, b)
#define vec_fabs(a) _mm512_andnot_ps(_mm512_set1_ps(-0.f), a)
78
79
#define vec_floor(a) _mm512_floor_ps(a)
#define vec_cmp_gt(a, b) _mm512_cmp_ps_mask(a, b, _CMP_GT_OQ)
James Willis's avatar
James Willis committed
80
81
#define vec_cmp_lt(a, b) _mm512_cmp_ps_mask(a, b, _CMP_LT_OQ)
#define vec_cmp_lte(a, b) _mm512_cmp_ps_mask(a, b, _CMP_LE_OQ)
82
#define vec_cmp_gte(a, b) _mm512_cmp_ps_mask(a, b, _CMP_GE_OQ)
83
#define vec_cmp_result(a) ({ a; })
James Willis's avatar
James Willis committed
84
#define vec_is_mask_true(a) ({ a; })
James Willis's avatar
James Willis committed
85
#define vec_and(a, b) _mm512_and_ps(a, b)
86
#define vec_mask_and(a, b) _mm512_kand(a, b)
87
#define vec_and_mask(a, mask) _mm512_maskz_mov_ps(mask, a)
88
89
90
#define vec_init_mask_true(mask) ({ mask = 0xFFFF; })
#define vec_zero_mask(mask) ({ mask = 0; })
#define vec_create_mask(mask, cond) ({ mask = cond; })
91
92
#define vec_combine_masks(mask1, mask2) \
  ({ mask1 = vec_mask_and(mask1, mask2); })
93
#define vec_pad_mask(mask, pad) ({ mask = mask >> (pad); })
94
#define vec_blend(mask, a, b) _mm512_mask_blend_ps(mask, a, b)
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#define vec_todbl_lo(a) _mm512_cvtps_pd(_mm512_extract128_ps(a, 0))
#define vec_todbl_hi(a) _mm512_cvtps_pd(_mm512_extract128_ps(a, 1))
#define vec_dbl_tofloat(a, b) _mm512_insertf128(_mm512_castps128_ps512(a), b, 1)
#define vec_dbl_load(a) _mm512_load_pd(a)
#define vec_dbl_set1(a) _mm512_set1_pd(a)
#define vec_dbl_sqrt(a) _mm512_sqrt_pd(a)
#define vec_dbl_rcp(a) _mm512_rcp_pd(a)
#define vec_dbl_rsqrt(a) _mm512_rsqrt_pd(a)
#define vec_dbl_ftoi(a) _mm512_cvttpd_epi32(a)
#define vec_dbl_fmin(a, b) _mm512_min_pd(a, b)
#define vec_dbl_fmax(a, b) _mm512_max_pd(a, b)
#define vec_getoffsets(ptrs)                                                \
  _mm512_insertf64x4(                                                       \
      _mm512_insertf64x4(_mm512_setzero_pd(),                               \
                         _mm512_cvtepi64_epi32(_mm512_load_epi64(ptrs) -    \
                                               _mm512_set1_epi64(ptrs[0])), \
                         0),                                                \
      _mm512_cvtepi64_epi32(_mm512_load_epi64(&ptrs[4]) -                   \
                            _mm512_set1_epi64(ptrs[0])),                    \
      1)
#define vec_gather(base, offsets) _mm512_i32gather_ps(offsets.m, base, 1)
James Willis's avatar
James Willis committed
116
117

/* Initialises a vector struct with a default value. */
Matthieu Schaller's avatar
Matthieu Schaller committed
118
#define FILL_VEC(a)                                                     \
James Willis's avatar
James Willis committed
119
120
121
122
123
  {                                                                     \
    .f[0] = a, .f[1] = a, .f[2] = a, .f[3] = a, .f[4] = a, .f[5] = a,   \
    .f[6] = a, .f[7] = a, .f[8] = a, .f[9] = a, .f[10] = a, .f[11] = a, \
    .f[12] = a, .f[13] = a, .f[14] = a, .f[15] = a                      \
  }
James Willis's avatar
James Willis committed
124
125

/* Performs a horizontal add on the vector and adds the result to a float. */
126
#ifdef __ICC
James Willis's avatar
James Willis committed
127
#define VEC_HADD(a, b) b += _mm512_reduce_add_ps(a.v)
Matthieu Schaller's avatar
Matthieu Schaller committed
128
129
130
131
132
133
#else /* _mm512_reduce_add_ps not present in GCC compiler. \
       TODO: Implement intrinsic version.*/
#define VEC_HADD(a, b)                              \
  {                                                 \
    for (int i = 0; i < VEC_SIZE; i++) b += a.f[i]; \
  }
134
#endif
135
136
137
138
139
140
141

/* Do nothing in the case of AVX-512 as there are already
 * instructions for left-packing.*/
#define VEC_FORM_PACKED_MASK(mask, packed_mask) packed_mask = mask

/* Finds the horizontal maximum of vector b and returns a float. */
#define VEC_HMAX(a, b) b = _mm512_reduce_max_ps(a.v)
James Willis's avatar
James Willis committed
142
143

/* Performs a left-pack on a vector based upon a mask and returns the result. */
James Willis's avatar
James Willis committed
144
145
#define VEC_LEFT_PACK(a, mask, result) \
  _mm512_mask_compressstoreu_ps(result, mask, a)
James Willis's avatar
James Willis committed
146
147

/* AVX intrinsics */
148
#elif defined(HAVE_AVX)
149
150
151
152
153
#define VEC_SIZE 8
#define VEC_FLOAT __m256
#define VEC_DBL __m256d
#define VEC_INT __m256i
#define vec_load(a) _mm256_load_ps(a)
James Willis's avatar
James Willis committed
154
#define vec_unaligned_load(a) _mm256_loadu_ps(a)
James Willis's avatar
James Willis committed
155
#define vec_store(a, addr) _mm256_store_ps(addr, a)
156
#define vec_unaligned_store(a, addr) _mm256_storeu_ps(addr, a)
157
158
#define vec_setzero() _mm256_setzero_ps()
#define vec_setintzero() _mm256_setzero_si256()
159
#define vec_set1(a) _mm256_set1_ps(a)
160
#define vec_setint1(a) _mm256_set1_epi32(a)
161
162
#define vec_set(a, b, c, d, e, f, g, h) _mm256_set_ps(h, g, f, e, d, c, b, a)
#define vec_dbl_set(a, b, c, d) _mm256_set_pd(d, c, b, a)
163
#define vec_add(a, b) _mm256_add_ps(a, b)
164
#define vec_mask_add(a, b, mask) vec_add(a, vec_and(b, mask.v))
165
#define vec_sub(a, b) _mm256_sub_ps(a, b)
166
#define vec_mask_sub(a, b, mask) vec_sub(a, vec_and(b, mask.v))
167
#define vec_mul(a, b) _mm256_mul_ps(a, b)
168
#define vec_div(a, b) _mm256_div_ps(a, b)
169
170
171
172
173
174
175
#define vec_sqrt(a) _mm256_sqrt_ps(a)
#define vec_rcp(a) _mm256_rcp_ps(a)
#define vec_rsqrt(a) _mm256_rsqrt_ps(a)
#define vec_ftoi(a) _mm256_cvttps_epi32(a)
#define vec_fmin(a, b) _mm256_min_ps(a, b)
#define vec_fmax(a, b) _mm256_max_ps(a, b)
#define vec_fabs(a) _mm256_andnot_ps(_mm256_set1_ps(-0.f), a)
176
177
178
179
#define vec_floor(a) _mm256_floor_ps(a)
#define vec_cmp_lt(a, b) _mm256_cmp_ps(a, b, _CMP_LT_OQ)
#define vec_cmp_gt(a, b) _mm256_cmp_ps(a, b, _CMP_GT_OQ)
#define vec_cmp_lte(a, b) _mm256_cmp_ps(a, b, _CMP_LE_OQ)
180
#define vec_cmp_gte(a, b) _mm256_cmp_ps(a, b, _CMP_GE_OQ)
181
#define vec_cmp_result(a) _mm256_movemask_ps(a)
James Willis's avatar
James Willis committed
182
#define vec_is_mask_true(a) _mm256_movemask_ps(a.v)
James Willis's avatar
James Willis committed
183
#define vec_and(a, b) _mm256_and_ps(a, b)
184
185
#define vec_mask_and(a, b) _mm256_and_ps(a.v, b.v)
#define vec_and_mask(a, mask) _mm256_and_ps(a, mask.v)
186
#define vec_init_mask_true(mask) mask.m = vec_setint1(0xFFFFFFFF)
187
#define vec_create_mask(mask, cond) mask.v = cond
188
189
#define vec_combine_masks(mask1, mask2) \
  ({ mask1.v = vec_mask_and(mask1, mask2); })
190
191
192
#define vec_zero_mask(mask) mask.v = vec_setzero()
#define vec_pad_mask(mask, pad) \
  for (int i = VEC_SIZE - (pad); i < VEC_SIZE; i++) mask.i[i] = 0
193
#define vec_blend(mask, a, b) _mm256_blendv_ps(a, b, mask.v)
194
195
196
197
198
199
200
201
202
203
204
#define vec_todbl_lo(a) _mm256_cvtps_pd(_mm256_extract128_ps(a, 0))
#define vec_todbl_hi(a) _mm256_cvtps_pd(_mm256_extract128_ps(a, 1))
#define vec_dbl_tofloat(a, b) _mm256_insertf128(_mm256_castps128_ps256(a), b, 1)
#define vec_dbl_load(a) _mm256_load_pd(a)
#define vec_dbl_set1(a) _mm256_set1_pd(a)
#define vec_dbl_sqrt(a) _mm256_sqrt_pd(a)
#define vec_dbl_rcp(a) _mm256_rcp_pd(a)
#define vec_dbl_rsqrt(a) _mm256_rsqrt_pd(a)
#define vec_dbl_ftoi(a) _mm256_cvttpd_epi32(a)
#define vec_dbl_fmin(a, b) _mm256_min_pd(a, b)
#define vec_dbl_fmax(a, b) _mm256_max_pd(a, b)
James Willis's avatar
James Willis committed
205
206

/* Initialises a vector struct with a default value. */
Matthieu Schaller's avatar
Matthieu Schaller committed
207
208
209
210
211
#define FILL_VEC(a)                                                   \
  {                                                                   \
    .f[0] = a, .f[1] = a, .f[2] = a, .f[3] = a, .f[4] = a, .f[5] = a, \
    .f[6] = a, .f[7] = a                                              \
  }
James Willis's avatar
James Willis committed
212
213

/* Performs a horizontal add on the vector and adds the result to a float. */
James Willis's avatar
James Willis committed
214
#define VEC_HADD(a, b)            \
215
216
217
  a.v = _mm256_hadd_ps(a.v, a.v); \
  a.v = _mm256_hadd_ps(a.v, a.v); \
  b += a.f[0] + a.f[4];
James Willis's avatar
James Willis committed
218

219
220
221
222
223
224
225
/* Performs a horizontal maximum on the vector and takes the maximum of the
 * result with a float, b. */
#define VEC_HMAX(a, b)                                     \
  {                                                        \
    for (int k = 0; k < VEC_SIZE; k++) b = max(b, a.f[k]); \
  }

James Willis's avatar
James Willis committed
226
/* Returns the lower 128-bits of the 256-bit vector. */
227
#define VEC_GET_LOW(a) _mm256_castps256_ps128(a)
James Willis's avatar
James Willis committed
228
229

/* Returns the higher 128-bits of the 256-bit vector. */
James Willis's avatar
James Willis committed
230
#define VEC_GET_HIGH(a) _mm256_extractf128_ps(a, 1)
James Willis's avatar
James Willis committed
231
232

/* Check if we have AVX2 intrinsics alongside AVX */
233
#ifdef HAVE_AVX2
234
#define vec_fma(a, b, c) _mm256_fmadd_ps(a, b, c)
235
#define vec_fnma(a, b, c) _mm256_fnmadd_ps(a, b, c)
James Willis's avatar
James Willis committed
236
237

/* Used in VEC_FORM_PACKED_MASK */
238
#define identity_indices 0x0706050403020100
239
240
#define VEC_HAVE_GATHER
#define vec_gather(base, offsets) _mm256_i32gather_ps(base, offsets.m, 1)
James Willis's avatar
James Willis committed
241

James Willis's avatar
James Willis committed
242
243
/* Takes an integer mask and forms a left-packed integer vector
 * containing indices of the set bits in the integer mask.
James Willis's avatar
James Willis committed
244
 * Also returns the total number of bits set in the mask. */
245
#define VEC_FORM_PACKED_MASK(mask, packed_mask)                                \
James Willis's avatar
James Willis committed
246
247
248
249
250
  {                                                                            \
    unsigned long expanded_mask = _pdep_u64(mask, 0x0101010101010101);         \
    expanded_mask *= 0xFF;                                                     \
    unsigned long wanted_indices = _pext_u64(identity_indices, expanded_mask); \
    __m128i bytevec = _mm_cvtsi64_si128(wanted_indices);                       \
251
    packed_mask.m = _mm256_cvtepu8_epi32(bytevec);                             \
James Willis's avatar
James Willis committed
252
  }
James Willis's avatar
James Willis committed
253
254

/* Performs a left-pack on a vector based upon a mask and returns the result. */
James Willis's avatar
James Willis committed
255
#define VEC_LEFT_PACK(a, mask, result) \
256
  vec_unaligned_store(_mm256_permutevar8x32_ps(a, mask.m), result)
James Willis's avatar
James Willis committed
257
258
259
#endif /* HAVE_AVX2 */

/* Create an FMA using vec_add and vec_mul if AVX2 is not present. */
260
261
262
#ifndef vec_fma
#define vec_fma(a, b, c) vec_add(vec_mul(a, b), c)
#endif
James Willis's avatar
James Willis committed
263

264
265
266
267
268
/* Create a negated FMA using vec_sub and vec_mul if AVX2 is not present. */
#ifndef vec_fnma
#define vec_fnma(a, b, c) vec_sub(c, vec_mul(a, b))
#endif

James Willis's avatar
James Willis committed
269
/* Form a packed mask without intrinsics if AVX2 is not present. */
270
#ifndef VEC_FORM_PACKED_MASK
James Willis's avatar
James Willis committed
271

James Willis's avatar
James Willis committed
272
273
/* Takes an integer mask and forms a left-packed integer vector
 * containing indices of the set bits in the integer mask.
James Willis's avatar
James Willis committed
274
 * Also returns the total number of bits set in the mask. */
James Willis's avatar
James Willis committed
275
276
277
278
279
#define VEC_FORM_PACKED_MASK(mask, v_mask, pack)   \
  {                                                \
    for (int i = 0; i < VEC_SIZE; i++)             \
      if ((mask & (1 << i))) v_mask.i[pack++] = i; \
  }
James Willis's avatar
James Willis committed
280

James Willis's avatar
James Willis committed
281
282
/* Takes two integer masks and forms two left-packed integer vectors
 * containing indices of the set bits in each corresponding integer mask.
James Willis's avatar
James Willis committed
283
 * Also returns the total number of bits set in the mask. */
James Willis's avatar
James Willis committed
284
285
286
287
288
289
290
#define VEC_FORM_PACKED_MASK_2(mask, v_mask, pack, mask2, v_mask2, pack2) \
  {                                                                       \
    for (int i = 0; i < VEC_SIZE; i++) {                                  \
      if ((mask & (1 << i))) v_mask.i[pack++] = i;                        \
      if ((mask2 & (1 << i))) v_mask2.i[pack2++] = i;                     \
    }                                                                     \
  }
291
#endif
James Willis's avatar
James Willis committed
292
293

/* Performs a left-pack on a vector based upon a mask and returns the result. */
James Willis's avatar
James Willis committed
294
/* This uses AVX intrinsics, but this is slower than performing the left-pack
James Willis's avatar
James Willis committed
295
 * manually by looping over the vectors. */
296
#ifndef VEC_LEFT_PACK
James Willis's avatar
James Willis committed
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
#define VEC_LEFT_PACK(a, mask, result)                                     \
  {                                                                        \
    __m256 t1 = _mm256_castps128_ps256(_mm256_extractf128_ps(a, 1));       \
    __m256 t2 = _mm256_insertf128_ps(t1, _mm256_castps256_ps128(a), 1);    \
    __m256 r0 = _mm256_permutevar_ps(a, mask);                             \
    __m256 r1 = _mm256_permutevar_ps(t2, mask);                            \
    __m128i k1 = _mm_slli_epi32(                                           \
        (__m128i)(_mm_xor_si128((__m128i)VEC_GET_HIGH((__m256)mask),       \
                                (__m128i)_mm_set1_epi32(4))),              \
        29);                                                               \
    __m128i k0 = _mm_slli_epi32((__m128i)(VEC_GET_LOW((__m256)mask)), 29); \
    __m256 kk =                                                            \
        _mm256_insertf128_ps(_mm256_castps128_ps256(_mm_castsi128_ps(k0)), \
                             _mm_castsi128_ps(k1), 1);                     \
    *((__m256 *)(result)) = _mm256_blendv_ps(r0, r1, kk);                  \
  }
James Willis's avatar
James Willis committed
313
314
315
#endif /* HAVE_AVX2 */

/* SSE intrinsics*/
316
#elif defined(HAVE_SSE2)
317
318
319
320
321
#define VEC_SIZE 4
#define VEC_FLOAT __m128
#define VEC_DBL __m128d
#define VEC_INT __m128i
#define vec_load(a) _mm_load_ps(a)
James Willis's avatar
James Willis committed
322
#define vec_store(a, addr) _mm_store_ps(addr, a)
323
324
#define vec_setzero() _mm_setzero_ps()
#define vec_setintzero() _mm_setzero_si256()
325
#define vec_set1(a) _mm_set1_ps(a)
326
#define vec_setint1(a) _mm_set1_epi32(a)
327
328
#define vec_set(a, b, c, d) _mm_set_ps(d, c, b, a)
#define vec_dbl_set(a, b) _mm_set_pd(b, a)
329
#define vec_add(a, b) _mm_add_ps(a, b)
James Willis's avatar
James Willis committed
330
#define vec_mask_add(a, b, mask) vec_add(a, vec_and(b, mask.v))
331
#define vec_sub(a, b) _mm_sub_ps(a, b)
James Willis's avatar
James Willis committed
332
#define vec_mask_sub(a, b, mask) vec_sub(a, vec_and(b, mask.v))
333
#define vec_mul(a, b) _mm_mul_ps(a, b)
334
#define vec_div(a, b) _mm_div_ps(a, b)
335
336
337
338
339
340
341
#define vec_sqrt(a) _mm_sqrt_ps(a)
#define vec_rcp(a) _mm_rcp_ps(a)
#define vec_rsqrt(a) _mm_rsqrt_ps(a)
#define vec_ftoi(a) _mm_cvttps_epi32(a)
#define vec_fmin(a, b) _mm_min_ps(a, b)
#define vec_fmax(a, b) _mm_max_ps(a, b)
#define vec_fabs(a) _mm_andnot_ps(_mm_set1_ps(-0.f), a)
342
#define vec_floor(a) _mm_floor_ps(a)
343
#define vec_cmp_gt(a, b) _mm_cmpgt_ps(a, b)
344
#define vec_cmp_lt(a, b) _mm_cmplt_ps(a, b)
James Willis's avatar
James Willis committed
345
346
#define vec_cmp_lte(a, b) _mm_cmple_ps(a, b)
#define vec_cmp_gte(a, b) _mm_cmpge_ps(a, b)
347
#define vec_cmp_result(a) _mm_movemask_ps(a)
James Willis's avatar
James Willis committed
348
#define vec_is_mask_true(a) _mm_movemask_ps(a.v)
349
#define vec_and(a, b) _mm_and_ps(a, b)
James Willis's avatar
James Willis committed
350
351
352
353
354
355
356
357
358
#define vec_mask_and(a, b) _mm_and_ps(a.v, b.v)
#define vec_and_mask(a, mask) _mm_and_ps(a, mask.v)
#define vec_init_mask_true(mask) mask.m = vec_setint1(0xFFFFFFFF)
#define vec_create_mask(mask, cond) mask.v = cond
#define vec_combine_masks(mask1, mask2) \
  ({ mask1.v = vec_mask_and(mask1, mask2); })
#define vec_zero_mask(mask) mask.v = vec_setzero()
#define vec_pad_mask(mask, pad) \
  for (int i = VEC_SIZE - (pad); i < VEC_SIZE; i++) mask.i[i] = 0
359
360
/* If SSE4.1 doesn't exist on architecture use alternative blend strategy. */
#ifdef HAVE_SSE4_1
James Willis's avatar
James Willis committed
361
#define vec_blend(mask, a, b) _mm_blendv_ps(a, b, mask.v)
362
363
364
#else
#define vec_blend(mask, a, b) _mm_or_ps(_mm_and_ps(mask.v,b), _mm_andnot_ps(mask.v,a))
#endif
365
366
367
368
369
370
371
372
373
374
375
#define vec_todbl_lo(a) _mm_cvtps_pd(a)
#define vec_todbl_hi(a) _mm_cvtps_pd(_mm_movehl_ps(a, a))
#define vec_dbl_tofloat(a, b) _mm_movelh_ps(_mm_cvtpd_ps(a), _mm_cvtpd_ps(b))
#define vec_dbl_load(a) _mm_load_pd(a)
#define vec_dbl_set1(a) _mm_set1_pd(a)
#define vec_dbl_sqrt(a) _mm_sqrt_pd(a)
#define vec_dbl_rcp(a) _mm_rcp_pd(a)
#define vec_dbl_rsqrt(a) _mm_rsqrt_pd(a)
#define vec_dbl_ftoi(a) _mm_cvttpd_epi32(a)
#define vec_dbl_fmin(a, b) _mm_min_pd(a, b)
#define vec_dbl_fmax(a, b) _mm_max_pd(a, b)
James Willis's avatar
James Willis committed
376
377

/* Initialises a vector struct with a default value. */
Matthieu Schaller's avatar
Matthieu Schaller committed
378
379
#define FILL_VEC(a) \
  { .f[0] = a, .f[1] = a, .f[2] = a, .f[3] = a }
James Willis's avatar
James Willis committed
380
381

/* Performs a horizontal add on the vector and adds the result to a float. */
Matthieu Schaller's avatar
Matthieu Schaller committed
382
#define VEC_HADD(a, b)         \
383
384
  a.v = _mm_hadd_ps(a.v, a.v); \
  b += a.f[0] + a.f[1];
James Willis's avatar
James Willis committed
385

James Willis's avatar
James Willis committed
386
387
388
389
390
391
392
/* Performs a horizontal maximum on the vector and takes the maximum of the
 * result with a float, b. */
#define VEC_HMAX(a, b)                                     \
  {                                                        \
    for (int k = 0; k < VEC_SIZE; k++) b = max(b, a.f[k]); \
  }

James Willis's avatar
James Willis committed
393
/* Create an FMA using vec_add and vec_mul if AVX2 is not present. */
394
395
396
#ifndef vec_fma
#define vec_fma(a, b, c) vec_add(vec_mul(a, b), c)
#endif
James Willis's avatar
James Willis committed
397
398
399
400
401
402

/* Create a negated FMA using vec_sub and vec_mul if AVX2 is not present. */
#ifndef vec_fnma
#define vec_fnma(a, b, c) vec_sub(c, vec_mul(a, b))
#endif

403
404
#else
#define VEC_SIZE 4
James Willis's avatar
James Willis committed
405
#endif /* HAVE_SSE2 */
Pedro Gonnet's avatar
Pedro Gonnet committed
406

407
408
409
410
411
412
413
414
415
416
/* Define the composite types for element access. */
typedef union {
  VEC_FLOAT v;
  VEC_DBL vd;
  VEC_INT m;
  float f[VEC_SIZE];
  double d[VEC_SIZE / 2];
  int i[VEC_SIZE];
} vector;

417
418
419
420
421
422
423
/* Define the mask type depending on the instruction set used. */
#ifdef HAVE_AVX512_F
typedef __mmask16 mask_t;
#else
typedef vector mask_t;
#endif

424
/**
Matthieu Schaller's avatar
Matthieu Schaller committed
425
426
 * @brief Calculates the inverse ($1/x$) of a vector using intrinsics and a
 * Newton iteration to obtain the correct level of accuracy.
427
428
429
430
431
432
433
434
435
 *
 * @param x #vector to be inverted.
 * @return x_inv #vector inverted x.
 */
__attribute__((always_inline)) INLINE vector vec_reciprocal(vector x) {

  vector x_inv;

  x_inv.v = vec_rcp(x.v);
Matthieu Schaller's avatar
Matthieu Schaller committed
436
437
  x_inv.v = vec_sub(x_inv.v,
                    vec_mul(x_inv.v, (vec_fma(x.v, x_inv.v, vec_set1(-1.0f)))));
438
439
440
441
442

  return x_inv;
}

/**
Matthieu Schaller's avatar
Matthieu Schaller committed
443
 * @brief Calculates the inverse and square root (\f$1/\sqrt{x}\f$) of a vector
Matthieu Schaller's avatar
Matthieu Schaller committed
444
445
 * using intrinsics and a Newton iteration to obtain the correct level of
 * accuracy.
446
447
448
449
450
451
452
453
454
 *
 * @param x #vector to be inverted.
 * @return x_inv #vector inverted x.
 */
__attribute__((always_inline)) INLINE vector vec_reciprocal_sqrt(vector x) {

  vector x_inv;

  x_inv.v = vec_rsqrt(x.v);
Matthieu Schaller's avatar
Matthieu Schaller committed
455
456
457
458
459
  x_inv.v = vec_sub(
      x_inv.v,
      vec_mul(vec_mul(vec_set1(0.5f), x_inv.v),
              (vec_fma(x.v, vec_mul(x_inv.v, x_inv.v), vec_set1(-1.0f)))));

460
461
462
  return x_inv;
}

463
/**
464
 * @brief Returns a new vector with data loaded from a memory address.
465
 *
466
467
 * @param x memory address to load from.
 * @return Loaded #vector.
468
469
470
471
472
473
474
475
 */
__attribute__((always_inline)) INLINE vector vector_load(float *const x) {

  vector temp;
  temp.v = vec_load(x);
  return temp;
}

476
/**
477
 * @brief Returns a vector filled with one value.
478
479
 *
 * @param x value to set each element.
480
 * @return A #vector filled with a given constant.
481
482
483
484
485
486
487
488
 */
__attribute__((always_inline)) INLINE vector vector_set1(const float x) {

  vector temp;
  temp.v = vec_set1(x);
  return temp;
}

489
/**
490
 * @brief Returns a new vector filled with zeros.
491
492
 *
 * @return temp set #vector.
493
 * @return A #vector filled with zeros.
494
495
496
497
498
499
500
501
 */
__attribute__((always_inline)) INLINE vector vector_setzero() {

  vector temp;
  temp.v = vec_setzero();
  return temp;
}

502
503
#else
/* Needed for cache alignment. */
504
#define VEC_SIZE 8
505
506
#endif /* WITH_VECTORIZATION */

507
#endif /* SWIFT_VECTOR_H */