RAJA
RAJA provides a collection of platform portability abstractions for C++ HPC applications.
avx2_int64.hpp
Go to the documentation of this file.
1 
11 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
12 // Copyright (c) Lawrence Livermore National Security, LLC and other
13 // RAJA Project Developers. See top-level LICENSE and COPYRIGHT
14 // files for dates and other details. No copyright assignment is required
15 // to contribute to RAJA.
16 //
17 // SPDX-License-Identifier: (BSD-3-Clause)
18 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
19 
20 #ifdef __AVX2__
21 
22 #ifndef RAJA_policy_vector_register_avx2_int64_HPP
23 #define RAJA_policy_vector_register_avx2_int64_HPP
24 
25 #include "RAJA/config.hpp"
26 #include "RAJA/util/macros.hpp"
28 
29 // Include SIMD intrinsics header file
30 #include <immintrin.h>
31 #include <cmath>
32 
33 namespace RAJA
34 {
35 namespace expt
36 {
37 template<>
38 class Register<int64_t, avx2_register>
39  : public internal::expt::RegisterBase<Register<int64_t, avx2_register>>
40 {
41 public:
42  using base_type =
43  internal::expt::RegisterBase<Register<int64_t, avx2_register>>;
44 
45  using register_policy = avx2_register;
46  using self_type = Register<int64_t, avx2_register>;
47  using element_type = int64_t;
48  using register_type = __m256i;
49 
50  using int_vector_type = Register<int64_t, avx2_register>;
51 
52 private:
53  register_type m_value;
54 
55  RAJA_INLINE
56  __m256i createMask(camp::idx_t N) const
57  {
58  // Generate a mask
59  return _mm256_set_epi64x(N >= 4 ? -1 : 0, N >= 3 ? -1 : 0, N >= 2 ? -1 : 0,
60  N >= 1 ? -1 : 0);
61  }
62 
63  RAJA_INLINE
64  __m256i createStridedOffsets(camp::idx_t stride) const
65  {
66  // Generate a strided offset list
67  return _mm256_set_epi64x(3 * stride, 2 * stride, stride, 0);
68  }
69 
70  /*
71  * Use the packed-double permute function because there isn't one
72  * specifically for int64
73  *
74  * Just adds a bunch of casting, should be same cost
75  */
76  template<int perm>
77  RAJA_INLINE __m256i permute(__m256i x) const
78  {
79  return _mm256_castpd_si256(_mm256_permute_pd(_mm256_castsi256_pd(x), perm));
80  }
81 
82 public:
83  static constexpr camp::idx_t s_num_elem = 4;
84 
88  RAJA_INLINE
89  Register() : m_value(_mm256_setzero_si256()) {}
90 
94  RAJA_INLINE
95  Register(element_type x0, element_type x1, element_type x2, element_type x3)
96  : m_value(_mm256_set_epi64x(x3, x2, x1, x0))
97  {}
98 
102  RAJA_INLINE
103  explicit Register(register_type const& c) : m_value(c) {}
104 
108  RAJA_INLINE
109  Register(self_type const& c) : base_type(c), m_value(c.m_value) {}
110 
114  RAJA_INLINE
115  self_type& operator=(self_type const& c)
116  {
117  m_value = c.m_value;
118  return *this;
119  }
120 
125  RAJA_INLINE
126  Register(element_type const& c) : m_value(_mm256_set1_epi64x(c)) {}
127 
131  RAJA_INLINE
132  constexpr register_type get_register() const { return m_value; }
133 
138  RAJA_INLINE
139  self_type& load_packed(element_type const* ptr)
140  {
141  m_value = _mm256_loadu_si256(reinterpret_cast<__m256i const*>(ptr));
142  return *this;
143  }
144 
150  RAJA_INLINE
151  self_type& load_packed_n(element_type const* ptr, camp::idx_t N)
152  {
153  m_value = _mm256_castpd_si256(_mm256_maskload_pd(
154  reinterpret_cast<double const*>(ptr), createMask(N)));
155  return *this;
156  }
157 
162  RAJA_INLINE
163  self_type& load_strided(int64_t const* ptr, camp::idx_t stride)
164  {
165  m_value = _mm256_i64gather_epi64(reinterpret_cast<long long const*>(ptr),
166  createStridedOffsets(stride),
167  sizeof(element_type));
168  return *this;
169  }
170 
176  RAJA_INLINE
177  self_type& load_strided_n(element_type const* ptr,
178  camp::idx_t stride,
179  camp::idx_t N)
180  {
181  m_value = _mm256_mask_i64gather_epi64(
182  _mm256_set1_epi64x(0), reinterpret_cast<long long const*>(ptr),
183  createStridedOffsets(stride), createMask(N), sizeof(element_type));
184  return *this;
185  }
186 
196  RAJA_INLINE
197  self_type& gather(element_type const* ptr, int_vector_type offsets)
198  {
199 #ifdef RAJA_ENABLE_VECTOR_STATS
200  RAJA::tensor_stats::num_vector_load_strided_n++;
201 #endif
202  m_value =
203  _mm256_i64gather_epi64(reinterpret_cast<long long const*>(ptr),
204  offsets.get_register(), sizeof(element_type));
205  return *this;
206  }
207 
217  RAJA_INLINE
218  self_type& gather_n(element_type const* ptr,
219  int_vector_type offsets,
220  camp::idx_t N)
221  {
222 #ifdef RAJA_ENABLE_VECTOR_STATS
223  RAJA::tensor_stats::num_vector_load_strided_n++;
224 #endif
225  m_value = _mm256_mask_i64gather_epi64(
226  _mm256_setzero_si256(), reinterpret_cast<long long const*>(ptr),
227  offsets.get_register(), createMask(N), sizeof(element_type));
228  return *this;
229  }
230 
235  RAJA_INLINE
236  self_type const& store_packed(element_type* ptr) const
237  {
238  _mm256_storeu_si256(reinterpret_cast<__m256i*>(ptr), m_value);
239  return *this;
240  }
241 
246  RAJA_INLINE
247  self_type const& store_packed_n(element_type* ptr, camp::idx_t N) const
248  {
249  _mm256_maskstore_epi64(reinterpret_cast<long long*>(ptr), createMask(N),
250  m_value);
251  return *this;
252  }
253 
258  RAJA_INLINE
259  self_type const& store_strided(element_type* ptr, camp::idx_t stride) const
260  {
261  for (camp::idx_t i = 0; i < 4; ++i)
262  {
263  ptr[i * stride] = m_value[i];
264  }
265  return *this;
266  }
267 
272  RAJA_INLINE
273  self_type const& store_strided_n(element_type* ptr,
274  camp::idx_t stride,
275  camp::idx_t N) const
276  {
277  for (camp::idx_t i = 0; i < N; ++i)
278  {
279  ptr[i * stride] = m_value[i];
280  }
281  return *this;
282  }
283 
289  RAJA_INLINE
290  element_type get(camp::idx_t i) const
291  {
292  // got to be a nicer way to do this!?!?
293  switch (i)
294  {
295  case 0:
296  return _mm256_extract_epi64(m_value, 0);
297  case 1:
298  return _mm256_extract_epi64(m_value, 1);
299  case 2:
300  return _mm256_extract_epi64(m_value, 2);
301  case 3:
302  return _mm256_extract_epi64(m_value, 3);
303  }
304  return 0;
305  }
306 
312  RAJA_INLINE
313  self_type& set(element_type value, camp::idx_t i)
314  {
315  // got to be a nicer way to do this!?!?
316  switch (i)
317  {
318  case 0:
319  m_value = _mm256_insert_epi64(m_value, value, 0);
320  break;
321  case 1:
322  m_value = _mm256_insert_epi64(m_value, value, 1);
323  break;
324  case 2:
325  m_value = _mm256_insert_epi64(m_value, value, 2);
326  break;
327  case 3:
328  m_value = _mm256_insert_epi64(m_value, value, 3);
329  break;
330  }
331 
332  return *this;
333  }
334 
336 
337  RAJA_INLINE
338  self_type& broadcast(element_type const& value)
339  {
340  m_value = _mm256_set1_epi64x(value);
341  return *this;
342  }
343 
345 
346  RAJA_INLINE
347  self_type& copy(self_type const& src)
348  {
349  m_value = src.m_value;
350  return *this;
351  }
352 
354 
355  RAJA_INLINE
356  self_type add(self_type const& b) const
357  {
358  return self_type(_mm256_add_epi64(m_value, b.m_value));
359  }
360 
362 
363  RAJA_INLINE
364  self_type subtract(self_type const& b) const
365  {
366  return self_type(_mm256_sub_epi64(m_value, b.m_value));
367  }
368 
370 
371  RAJA_INLINE
372  self_type multiply(self_type const& b) const
373  {
374  // AVX2 does not supply an int64_t multiply, so do it manually
375  return self_type(_mm256_set_epi64x(get(3) * b.get(3), get(2) * b.get(2),
376  get(1) * b.get(1), get(0) * b.get(0)));
377  }
378 
380 
381  RAJA_INLINE
382  self_type divide(self_type const& b) const
383  {
384  // AVX2 does not supply an integer divide, so do it manually
385  return self_type(_mm256_set_epi64x(get(3) / b.get(3), get(2) / b.get(2),
386  get(1) / b.get(1), get(0) / b.get(0)));
387  }
388 
390 
391  RAJA_INLINE
392  self_type divide_n(self_type const& b, camp::idx_t N) const
393  {
394  // AVX2 does not supply an integer divide, so do it manually
395  return self_type(_mm256_set_epi64x(
396  N >= 4 ? get(3) / b.get(3) : 0, N >= 3 ? get(2) / b.get(2) : 0,
397  N >= 2 ? get(1) / b.get(1) : 0, N >= 1 ? get(0) / b.get(0) : 0));
398  }
399 
404  RAJA_INLINE
405  element_type sum() const
406  {
407 
408  // swap pairs and add
409  auto sh1 = permute<0x5>(m_value);
410  auto red1 = _mm256_add_epi64(m_value, sh1);
411 
412  // add lower and upper
413  return _mm256_extract_epi64(red1, 0) + _mm256_extract_epi64(red1, 2);
414  }
415 
420  RAJA_INLINE
421  element_type max() const
422  {
423  // AVX2 does not supply an 64bit integer max?!?
424  auto red = get(0);
425 
426  auto v1 = get(1);
427  red = red < v1 ? v1 : red;
428 
429  auto v2 = get(2);
430  red = red < v2 ? v2 : red;
431 
432  auto v3 = get(3);
433  red = red < v3 ? v3 : red;
434 
435  return red;
436  }
437 
442  RAJA_INLINE
443  element_type max_n(camp::idx_t N) const
444  {
445  if (N <= 0 || N > 4)
446  {
448  }
449 
450  // AVX2 does not supply an 64bit integer max?!?
451  auto red = get(0);
452 
453  if (N > 1)
454  {
455  auto v1 = get(1);
456  red = red < v1 ? v1 : red;
457  }
458  if (N > 2)
459  {
460  auto v2 = get(2);
461  red = red < v2 ? v2 : red;
462  }
463  if (N > 3)
464  {
465  auto v3 = get(3);
466  red = red < v3 ? v3 : red;
467  }
468 
469  return red;
470  }
471 
476  RAJA_INLINE
477  self_type vmax(self_type a) const
478  {
479  return self_type(_mm256_set_epi64x(get(3) > a.get(3) ? get(3) : a.get(3),
480  get(2) > a.get(2) ? get(2) : a.get(2),
481  get(1) > a.get(1) ? get(1) : a.get(1),
482  get(0) > a.get(0) ? get(0) : a.get(0)));
483  }
484 
489  RAJA_INLINE
490  element_type min() const
491  {
492  // AVX2 does not supply an 64bit integer max?!?
493  auto red = get(0);
494 
495  auto v1 = get(1);
496  red = red > v1 ? v1 : red;
497 
498  auto v2 = get(2);
499  red = red > v2 ? v2 : red;
500 
501  auto v3 = get(3);
502  red = red > v3 ? v3 : red;
503 
504  return red;
505  }
506 
511  RAJA_INLINE
512  element_type min_n(camp::idx_t N) const
513  {
514  if (N <= 0 || N > 4)
515  {
517  }
518 
519  // AVX2 does not supply an 64bit integer max?!?
520  auto red = get(0);
521 
522  if (N > 1)
523  {
524  auto v1 = get(1);
525  red = red > v1 ? v1 : red;
526  }
527  if (N > 2)
528  {
529  auto v2 = get(2);
530  red = red > v2 ? v2 : red;
531  }
532  if (N > 3)
533  {
534  auto v3 = get(3);
535  red = red > v3 ? v3 : red;
536  }
537 
538  return red;
539  }
540 
545  RAJA_INLINE
546  self_type vmin(self_type a) const
547  {
548  return self_type(_mm256_set_epi64x(get(3) < a.get(3) ? get(3) : a.get(3),
549  get(2) < a.get(2) ? get(2) : a.get(2),
550  get(1) < a.get(1) ? get(1) : a.get(1),
551  get(0) < a.get(0) ? get(0) : a.get(0)));
552  }
553 };
554 
555 
556 } // namespace expt
557 
558 } // namespace RAJA
559 
560 
561 #endif
562 
563 #endif //__AVX2__
RAJA header file defining SIMD/SIMT register operations.
Header file for common RAJA internal macro definitions.
#define RAJA_HOST_DEVICE
Definition: macros.hpp:65
Definition: AlignedRangeIndexSetBuilders.cpp:35
RAJA_HOST_DEVICE constexpr RAJA_INLINE Result min(Args... args)
Definition: foldl.hpp:161
RAJA_HOST_DEVICE constexpr RAJA_INLINE Result sum(Args... args)
Definition: foldl.hpp:143
RAJA_HOST_DEVICE constexpr RAJA_INLINE RAJA::zip_tuple_element_t< I, zip_tuple< is_val, Ts... > > & get(zip_tuple< is_val, Ts... > &z) noexcept
Definition: zip_tuple.hpp:56
RAJA_HOST_DEVICE constexpr RAJA_INLINE Result max(Args... args)
Definition: foldl.hpp:155