Vc  1.1.0
SIMD Vector Classes for C++
simdmaskarray.h
1 /* This file is part of the Vc library. {{{
2 Copyright © 2013-2015 Matthias Kretz <kretz@kde.org>
3 All rights reserved.
4 
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7  * Redistributions of source code must retain the above copyright
8  notice, this list of conditions and the following disclaimer.
9  * Redistributions in binary form must reproduce the above copyright
10  notice, this list of conditions and the following disclaimer in the
11  documentation and/or other materials provided with the distribution.
12  * Neither the names of contributing organizations nor the
13  names of its contributors may be used to endorse or promote products
14  derived from this software without specific prior written permission.
15 
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
20 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 
27 }}}*/
28 
29 #ifndef VC_COMMON_SIMDMASKARRAY_H_
30 #define VC_COMMON_SIMDMASKARRAY_H_
31 
32 #include <type_traits>
33 #include <array>
34 #include "simdarrayhelper.h"
35 #include "utility.h"
36 #include "maskentry.h"
37 
38 #include "macros.h"
39 
40 namespace Vc_VERSIONED_NAMESPACE
41 {
44 
45 template <typename T, std::size_t N, typename VectorType_>
46 class alignas(
48  ((Common::nextPowerOfTwo(N) * (sizeof(VectorType_) / VectorType_::size()) - 1) & 127) +
50  1) SimdMaskArray<T, N, VectorType_, N>
51 {
52 public:
53  using VectorType = VectorType_;
54  using vector_type = VectorType;
55  using mask_type = typename vector_type::Mask;
56  using storage_type = mask_type;
57 
58  friend storage_type &internal_data(SimdMaskArray &m) { return m.data; }
59  friend const storage_type &internal_data(const SimdMaskArray &m) { return m.data; }
60 
61  static constexpr std::size_t size() { return N; }
62  static constexpr std::size_t Size = size();
63  static constexpr std::size_t MemoryAlignment = storage_type::MemoryAlignment;
64  static_assert(Size == mask_type::Size, "size mismatch");
65 
66  using vectorentry_type = typename mask_type::VectorEntryType;
67  using vectorentry_reference = vectorentry_type &;
68  using value_type = typename mask_type::EntryType;
69  using Mask = mask_type;
70  using VectorEntryType = vectorentry_type;
71  using EntryType = value_type;
72  using EntryReference = typename mask_type::EntryReference;
73  using Vector = SimdArray<T, N, VectorType, N>;
74 
75  Vc_FREE_STORE_OPERATORS_ALIGNED(alignof(mask_type))
76 
77  // zero init
78  SimdMaskArray() = default;
79 
80  // broadcasts
81  Vc_INTRINSIC explicit SimdMaskArray(VectorSpecialInitializerOne one) : data(one) {}
82  Vc_INTRINSIC explicit SimdMaskArray(VectorSpecialInitializerZero zero) : data(zero) {}
83  Vc_INTRINSIC explicit SimdMaskArray(bool b) : data(b) {}
84  Vc_INTRINSIC static SimdMaskArray Zero() { return {storage_type::Zero()}; }
85  Vc_INTRINSIC static SimdMaskArray One() { return {storage_type::One()}; }
86 
87  // conversion (casts)
88  template <typename U, typename V>
89  Vc_INTRINSIC_L SimdMaskArray(const SimdMaskArray<U, N, V> &x,
90  enable_if<N == V::size()> = nullarg) Vc_INTRINSIC_R;
91  template <typename U, typename V>
92  Vc_INTRINSIC_L SimdMaskArray(const SimdMaskArray<U, N, V> &x,
93  enable_if<(N > V::size() && N <= 2 * V::size())> = nullarg)
94  Vc_INTRINSIC_R;
95  template <typename U, typename V>
96  Vc_INTRINSIC_L SimdMaskArray(const SimdMaskArray<U, N, V> &x,
97  enable_if<(N > 2 * V::size() && N <= 4 * V::size())> = nullarg)
98  Vc_INTRINSIC_R;
99 
100  // conversion from any Segment object (could be SimdMaskArray or Mask<T>)
101  template <typename M, std::size_t Pieces, std::size_t Index>
102  Vc_INTRINSIC_L SimdMaskArray(
103  Common::Segment<M, Pieces, Index> &&x,
104  enable_if<Traits::simd_vector_size<M>::value == Size * Pieces> = nullarg) Vc_INTRINSIC_R;
105 
106  // conversion from Mask<T>
107  template <typename M>
108  Vc_INTRINSIC_L SimdMaskArray(
109  M k,
110  enable_if<(Traits::is_simd_mask<M>::value && !Traits::isSimdMaskArray<M>::value &&
111  Traits::simd_vector_size<M>::value == Size)> = nullarg) Vc_INTRINSIC_R;
112 
113  // implicit conversion to Mask<U, AnyAbi> for if Mask<U, AnyAbi>::size() == N
114  template <typename M,
115  typename = enable_if<Traits::is_simd_mask<M>::value &&
116  !Traits::isSimdMaskArray<M>::value && M::size() == N>>
117  operator M() const
118  {
119  return simd_cast<M>(*this);
120  }
121 
122  // load/store (from/to bool arrays)
123  template <typename Flags = DefaultLoadTag>
124  Vc_INTRINSIC explicit SimdMaskArray(const bool *mem, Flags f = Flags())
125  : data(mem, f)
126  {
127  }
128 
129  Vc_INTRINSIC void load(const bool *mem) { data.load(mem); }
130  template <typename Flags> Vc_INTRINSIC void load(const bool *mem, Flags f)
131  {
132  data.load(mem, f);
133  }
134 
135  Vc_INTRINSIC void store(bool *mem) const { data.store(mem); }
136  template <typename Flags> Vc_INTRINSIC void store(bool *mem, Flags f) const
137  {
138  data.store(mem, f);
139  }
140 
141  // compares
142  Vc_INTRINSIC Vc_PURE bool operator==(const SimdMaskArray &rhs) const
143  {
144  return data == rhs.data;
145  }
146  Vc_INTRINSIC Vc_PURE bool operator!=(const SimdMaskArray &rhs) const
147  {
148  return data != rhs.data;
149  }
150 
151  // inversion
152  Vc_INTRINSIC Vc_PURE SimdMaskArray operator!() const
153  {
154  return {!data};
155  }
156 
157  // binary operators
158  Vc_INTRINSIC SimdMaskArray &operator&=(const SimdMaskArray &rhs)
159  {
160  data &= rhs.data;
161  return *this;
162  }
163  Vc_INTRINSIC SimdMaskArray &operator|=(const SimdMaskArray &rhs)
164  {
165  data |= rhs.data;
166  return *this;
167  }
168  Vc_INTRINSIC SimdMaskArray &operator^=(const SimdMaskArray &rhs)
169  {
170  data ^= rhs.data;
171  return *this;
172  }
173 
174  Vc_INTRINSIC Vc_PURE SimdMaskArray operator&(const SimdMaskArray &rhs) const
175  {
176  return {data & rhs.data};
177  }
178  Vc_INTRINSIC Vc_PURE SimdMaskArray operator|(const SimdMaskArray &rhs) const
179  {
180  return {data | rhs.data};
181  }
182  Vc_INTRINSIC Vc_PURE SimdMaskArray operator^(const SimdMaskArray &rhs) const
183  {
184  return {data ^ rhs.data};
185  }
186 
187  Vc_INTRINSIC Vc_PURE SimdMaskArray operator&&(const SimdMaskArray &rhs) const
188  {
189  return {data && rhs.data};
190  }
191  Vc_INTRINSIC Vc_PURE SimdMaskArray operator||(const SimdMaskArray &rhs) const
192  {
193  return {data || rhs.data};
194  }
195 
196  Vc_INTRINSIC Vc_PURE bool isFull() const { return data.isFull(); }
197  Vc_INTRINSIC Vc_PURE bool isNotEmpty() const { return data.isNotEmpty(); }
198  Vc_INTRINSIC Vc_PURE bool isEmpty() const { return data.isEmpty(); }
199  Vc_INTRINSIC Vc_PURE bool isMix() const { return data.isMix(); }
200 
201  Vc_INTRINSIC Vc_PURE int shiftMask() const { return data.shiftMask(); }
202 
203  Vc_INTRINSIC Vc_PURE int toInt() const { return data.toInt(); }
204 
205  Vc_INTRINSIC Vc_PURE EntryReference operator[](size_t index)
206  {
207  return data[index];
208  }
209  Vc_INTRINSIC Vc_PURE bool operator[](size_t index) const { return data[index]; }
210 
211  Vc_INTRINSIC Vc_PURE int count() const { return data.count(); }
212 
218  Vc_INTRINSIC Vc_PURE int firstOne() const { return data.firstOne(); }
219 
220  template <typename G> static Vc_INTRINSIC SimdMaskArray generate(const G &gen)
221  {
222  return {mask_type::generate(gen)};
223  }
224 
225  Vc_INTRINSIC Vc_PURE SimdMaskArray shifted(int amount) const
226  {
227  return {data.shifted(amount)};
228  }
229 
231  template <typename Op, typename... Args>
232  static Vc_INTRINSIC SimdMaskArray fromOperation(Op op, Args &&... args)
233  {
234  SimdMaskArray r;
235  Common::unpackArgumentsAuto(op, r.data, std::forward<Args>(args)...);
236  return r;
237  }
238 
240  Vc_INTRINSIC SimdMaskArray(mask_type &&x) : data(std::move(x)) {}
241 
243  void setEntry(size_t index, bool x) { data.setEntry(index, x); }
244 
245 private:
246  storage_type data;
247 };
248 
249 template <typename T, std::size_t N, typename VectorType> constexpr std::size_t SimdMaskArray<T, N, VectorType, N>::Size;
250 template <typename T, std::size_t N, typename VectorType>
252 
253 template <typename T, std::size_t N, typename VectorType, std::size_t>
254 class alignas(
256  ((Common::nextPowerOfTwo(N) * (sizeof(VectorType) / VectorType::size()) - 1) & 127) +
258  1) SimdMaskArray
259 {
260  static constexpr std::size_t N0 = Common::nextPowerOfTwo(N - N / 2);
261 
262  using Split = Common::Split<N0>;
263 
264 public:
265  using storage_type0 = SimdMaskArray<T, N0>;
266  using storage_type1 = SimdMaskArray<T, N - N0>;
267  static_assert(storage_type0::size() == N0, "");
268 
269  using vector_type = VectorType;
270 
271  friend storage_type0 &internal_data0(SimdMaskArray &m) { return m.data0; }
272  friend storage_type1 &internal_data1(SimdMaskArray &m) { return m.data1; }
273  friend const storage_type0 &internal_data0(const SimdMaskArray &m) { return m.data0; }
274  friend const storage_type1 &internal_data1(const SimdMaskArray &m) { return m.data1; }
275 
276  using mask_type = SimdMaskArray;
277  static constexpr std::size_t size() { return N; }
278  static constexpr std::size_t Size = size();
279  static constexpr std::size_t MemoryAlignment =
283  static_assert(Size == mask_type::Size, "size mismatch");
284 
285  using vectorentry_type = typename storage_type0::VectorEntryType;
286  using vectorentry_reference = vectorentry_type &;
287  using value_type = typename storage_type0::EntryType;
288  using Mask = mask_type;
289  using VectorEntryType = vectorentry_type;
290  using EntryType = value_type;
291  using EntryReference = typename std::conditional<
292  std::is_same<typename storage_type0::EntryReference,
293  typename storage_type1::EntryReference>::value,
294  typename storage_type0::EntryReference, Common::MaskEntry<SimdMaskArray>>::type;
295  using Vector = SimdArray<T, N, VectorType, VectorType::Size>;
296 
297  Vc_FREE_STORE_OPERATORS_ALIGNED(alignof(mask_type))
298 
299  // zero init
300  SimdMaskArray() = default;
301 
302  // default copy ctor/operator
303  SimdMaskArray(const SimdMaskArray &) = default;
304  SimdMaskArray(SimdMaskArray &&) = default;
305  SimdMaskArray &operator=(const SimdMaskArray &) = default;
306  SimdMaskArray &operator=(SimdMaskArray &&) = default;
307 
308  // implicit conversion from SimdMaskArray with same N
309  template <typename U, typename V>
310  Vc_INTRINSIC SimdMaskArray(const SimdMaskArray<U, N, V> &rhs)
311  : data0(Split::lo(rhs)), data1(Split::hi(rhs))
312  {
313  }
314 
315  // conversion from any Segment object (could be SimdMaskArray or Mask<T>)
316  template <typename M, std::size_t Pieces, std::size_t Index>
317  Vc_INTRINSIC SimdMaskArray(
318  Common::Segment<M, Pieces, Index> &&rhs,
319  enable_if<Traits::simd_vector_size<M>::value == Size * Pieces> = nullarg)
320  : data0(Split::lo(rhs)), data1(Split::hi(rhs))
321  {
322  }
323 
324  // conversion from Mask<T>
325  template <typename M>
326  Vc_INTRINSIC SimdMaskArray(
327  M k,
328  enable_if<(Traits::is_simd_mask<M>::value && !Traits::isSimdMaskArray<M>::value &&
329  Traits::simd_vector_size<M>::value == Size)> = nullarg)
330  : data0(Split::lo(k)), data1(Split::hi(k))
331  {
332  }
333 
334  // implicit conversion to Mask<U, AnyAbi> for if Mask<U, AnyAbi>::size() == N
335  template <typename M,
336  typename = enable_if<Traits::is_simd_mask<M>::value &&
337  !Traits::isSimdMaskArray<M>::value && M::size() == N>>
338  operator M() const
339  {
340  return simd_cast<M>(*this);
341  }
342 
343  Vc_INTRINSIC explicit SimdMaskArray(VectorSpecialInitializerOne one)
344  : data0(one), data1(one)
345  {
346  }
347  Vc_INTRINSIC explicit SimdMaskArray(VectorSpecialInitializerZero zero)
348  : data0(zero), data1(zero)
349  {
350  }
351  Vc_INTRINSIC explicit SimdMaskArray(bool b) : data0(b), data1(b) {}
352 
353  Vc_INTRINSIC static SimdMaskArray Zero() { return {storage_type0::Zero(), storage_type1::Zero()}; }
354  Vc_INTRINSIC static SimdMaskArray One() { return {storage_type0::One(), storage_type1::One()}; }
355 
356  template <typename Flags = DefaultLoadTag>
357  Vc_INTRINSIC explicit SimdMaskArray(const bool *mem, Flags f = Flags())
358  : data0(mem, f), data1(mem + storage_type0::size(), f)
359  {
360  }
361 
362  Vc_INTRINSIC void load(const bool *mem)
363  {
364  data0.load(mem);
365  data1.load(mem + storage_type0::size());
366  }
367  template <typename Flags> Vc_INTRINSIC void load(const bool *mem, Flags f)
368  {
369  data0.load(mem, f);
370  data1.load(mem + storage_type0::size(), f);
371  }
372 
373  Vc_INTRINSIC void store(bool *mem) const
374  {
375  data0.store(mem);
376  data1.store(mem + storage_type0::size());
377  }
378  template <typename Flags> Vc_INTRINSIC void store(bool *mem, Flags f) const
379  {
380  data0.store(mem, f);
381  data1.store(mem + storage_type0::size(), f);
382  }
383 
384  Vc_INTRINSIC Vc_PURE bool operator==(const SimdMaskArray &rhs) const
385  {
386  return data0 == rhs.data0 && data1 == rhs.data1;
387  }
388  Vc_INTRINSIC Vc_PURE bool operator!=(const SimdMaskArray &rhs) const
389  {
390  return data0 != rhs.data0 || data1 != rhs.data1;
391  }
392 
393  Vc_INTRINSIC Vc_PURE SimdMaskArray operator!() const
394  {
395  return {!data0, !data1};
396  }
397 
398  Vc_INTRINSIC SimdMaskArray &operator&=(const SimdMaskArray &rhs)
399  {
400  data0 &= rhs.data0;
401  data1 &= rhs.data1;
402  return *this;
403  }
404  Vc_INTRINSIC SimdMaskArray &operator|=(const SimdMaskArray &rhs)
405  {
406  data0 |= rhs.data0;
407  data1 |= rhs.data1;
408  return *this;
409  }
410  Vc_INTRINSIC SimdMaskArray &operator^=(const SimdMaskArray &rhs)
411  {
412  data0 ^= rhs.data0;
413  data1 ^= rhs.data1;
414  return *this;
415  }
416 
417  Vc_INTRINSIC Vc_PURE SimdMaskArray operator&(const SimdMaskArray &rhs) const
418  {
419  return {data0 & rhs.data0, data1 & rhs.data1};
420  }
421  Vc_INTRINSIC Vc_PURE SimdMaskArray operator|(const SimdMaskArray &rhs) const
422  {
423  return {data0 | rhs.data0, data1 | rhs.data1};
424  }
425  Vc_INTRINSIC Vc_PURE SimdMaskArray operator^(const SimdMaskArray &rhs) const
426  {
427  return {data0 ^ rhs.data0, data1 ^ rhs.data1};
428  }
429 
430  Vc_INTRINSIC Vc_PURE SimdMaskArray operator&&(const SimdMaskArray &rhs) const
431  {
432  return {data0 && rhs.data0, data1 && rhs.data1};
433  }
434  Vc_INTRINSIC Vc_PURE SimdMaskArray operator||(const SimdMaskArray &rhs) const
435  {
436  return {data0 || rhs.data0, data1 || rhs.data1};
437  }
438 
439  Vc_INTRINSIC Vc_PURE bool isFull() const { return data0.isFull() && data1.isFull(); }
440  Vc_INTRINSIC Vc_PURE bool isNotEmpty() const { return data0.isNotEmpty() || data1.isNotEmpty(); }
441  Vc_INTRINSIC Vc_PURE bool isEmpty() const { return data0.isEmpty() && data1.isEmpty(); }
442  Vc_INTRINSIC Vc_PURE bool isMix() const { return !isFull() && !isEmpty(); }
443 
444  Vc_INTRINSIC Vc_PURE int toInt() const
445  {
446  return data0.toInt() | (data1.toInt() << data0.size());
447  }
448 
449 private:
450  template <typename R>
451  R subscript_impl(
452  size_t index,
453  enable_if<std::is_same<R, typename storage_type0::EntryReference>::value> =
454  nullarg)
455  {
456  if (index < storage_type0::size()) {
457  return data0[index];
458  } else {
459  return data1[index - storage_type0::size()];
460  }
461  }
462  template <typename R>
463  R subscript_impl(
464  size_t index,
465  enable_if<!std::is_same<R, typename storage_type0::EntryReference>::value> =
466  nullarg)
467  {
468  return {*this, index};
469  }
470 
471 public:
473  void setEntry(size_t index, bool x)
474  {
475  if (index < data0.size()) {
476  data0.setEntry(index, x);
477  } else {
478  data1.setEntry(index - data0.size(), x);
479  }
480  }
481 
482  Vc_INTRINSIC Vc_PURE EntryReference operator[](size_t index) {
483  return subscript_impl<EntryReference>(index);
484  }
485  Vc_INTRINSIC Vc_PURE bool operator[](size_t index) const {
486  if (index < storage_type0::size()) {
487  return data0[index];
488  } else {
489  return data1[index - storage_type0::size()];
490  }
491  }
492 
493  Vc_INTRINSIC Vc_PURE int count() const { return data0.count() + data1.count(); }
494 
495  Vc_INTRINSIC Vc_PURE int firstOne() const {
496  if (data0.isEmpty()) {
497  return data1.firstOne() + storage_type0::size();
498  }
499  return data0.firstOne();
500  }
501 
502  template <typename G> static Vc_INTRINSIC SimdMaskArray generate(const G &gen)
503  {
504  return {storage_type0::generate(gen),
505  storage_type1::generate([&](std::size_t i) { return gen(i + N0); })};
506  }
507 
508  inline Vc_PURE SimdMaskArray shifted(int amount) const
509  {
510  if (Vc_IS_UNLIKELY(amount == 0)) {
511  return *this;
512  }
513  SimdMaskArray r{};
514  if (amount < 0) {
515  for (int i = 0; i < int(Size) + amount; ++i) {
516  r[i - amount] = operator[](i);
517  }
518  } else {
519  for (int i = 0; i < int(Size) - amount; ++i) {
520  r[i] = operator[](i + amount);
521  }
522  }
523  return r;
524  }
525 
527  template <typename Op, typename... Args>
528  static Vc_INTRINSIC SimdMaskArray fromOperation(Op op, Args &&... args)
529  {
530  SimdMaskArray r = {
531  storage_type0::fromOperation(op, Split::lo(args)...), // no forward here - it
532  // could move and thus
533  // break the next line
534  storage_type1::fromOperation(op, Split::hi(std::forward<Args>(args))...)};
535  return r;
536  }
537 
539  Vc_INTRINSIC SimdMaskArray(storage_type0 &&x, storage_type1 &&y)
540  : data0(std::move(x)), data1(std::move(y))
541  {
542  }
543 
544 private:
545  storage_type0 data0;
546  storage_type1 data1;
547 };
548 template <typename T, std::size_t N, typename VectorType, std::size_t M> constexpr std::size_t SimdMaskArray<T, N, VectorType, M>::Size;
549 template <typename T, std::size_t N, typename VectorType, std::size_t M>
551 
553 
554 } // namespace Vc
555 
556 // XXX: this include should be in <Vc/vector.h>. But at least clang 3.4 then fails to compile the
// code. Not sure yet what is going on, but it looks a lot like a bug in clang.
#include "simd_cast_caller.tcc"
#endif // VC_COMMON_SIMDMASKARRAY_H_
std
Definition: vector.h:258
Vc::Common::unpackArgumentsAuto
void unpackArgumentsAuto(Op op, R &&r, Args &&...args)
The interface to start the machinery.
Definition: simdarrayhelper.h:525
Vc::simd_cast
enable_if< std::is_same< To, Traits::decay< From > >::value, To > simd_cast(From &&x)
Casts the argument x from type From to type To.
Definition: simd_cast.h:49
Vc::Zero
constexpr VectorSpecialInitializerZero Zero
The special object Vc::Zero can be used to construct Vector and Mask objects initialized to zero/fals...
Definition: types.h:85
Vc::SimdizeDetail::shifted
Adapter< S, T, N > shifted(const Adapter< S, T, N > &a, int shift)
Returns a new vectorized object where each entry is shifted by shift.
Definition: simdize.h:999
Vc::One
constexpr VectorSpecialInitializerOne One
The special object Vc::One can be used to construct Vector and Mask objects initialized to one/true...
Definition: types.h:90
Vc::MemoryAlignment
constexpr std::size_t MemoryAlignment
Specifies the most conservative memory alignment necessary for aligned loads and stores of Vector typ...
Definition: vector.h:219