Dune Core Modules (2.7.0)

vc.hh
Go to the documentation of this file.
1 #ifndef DUNE_COMMON_SIMD_VC_HH
2 #define DUNE_COMMON_SIMD_VC_HH
3 
9 #include <cstddef>
10 #include <type_traits>
11 #include <utility>
12 
13 #include <dune/common/indices.hh>
14 #include <dune/common/simd/base.hh>
15 #include <dune/common/simd/defaults.hh> // for anyFalse()
16 #include <dune/common/simd/loop.hh>
18 #include <dune/common/vc.hh>
19 
150 namespace Dune {
151  namespace Simd {
152 
153  namespace VcImpl {
154 
156  template<class V, class SFINAE = void>
157  struct IsMask : std::false_type {};
158 
159  template<typename T, typename A>
160  struct IsMask<Vc::Mask<T, A> > : std::true_type {};
161 
162  template<typename T, std::size_t n, typename V, std::size_t m>
163  struct IsMask<Vc::SimdMaskArray<T, n, V, m> > : std::true_type {};
164 
166  template<class V, class SFINAE = void>
167  struct IsVector : IsMask<V> {};
168 
169  template<typename T, typename A>
170  struct IsVector<Vc::Vector<T, A> > : std::true_type {};
171 
172  template<typename T, std::size_t n, typename V, std::size_t m>
173  struct IsVector<Vc::SimdArray<T, n, V, m> > : std::true_type {};
174 
175  template<typename T> struct IsVectorizable : std::false_type {};
176  template<> struct IsVectorizable<double> : std::true_type {};
177  template<> struct IsVectorizable<float> : std::true_type {};
178  template<> struct IsVectorizable<std::int32_t> : std::true_type {};
179  template<> struct IsVectorizable<std::uint32_t> : std::true_type {};
180  template<> struct IsVectorizable<std::int16_t> : std::true_type {};
181  template<> struct IsVectorizable<std::uint16_t> : std::true_type {};
182 
184 
196  template<class V>
197  class Proxy
198  {
199  static_assert(std::is_same<V, std::decay_t<V> >::value, "Class Proxy "
200  "may only be instantiated with unqualified types");
201  public:
202  using value_type = typename V::value_type;
203 
204  private:
205  static_assert(std::is_arithmetic<value_type>::value,
206  "Only artihmetic types are supported");
207  V &vec_;
208  std::size_t idx_;
209 
210  public:
211  Proxy(std::size_t idx, V &vec)
212  : vec_(vec), idx_(idx)
213  { }
214 
215  Proxy(const Proxy&) = delete;
216  // allow move construction so we can return proxies from functions
217  Proxy(Proxy&&) = default;
218 
219  operator value_type() const { return vec_[idx_]; }
220 
221  // assignment operators
222 #define DUNE_SIMD_VC_ASSIGNMENT(OP) \
223  template<class T, \
224  class = decltype(std::declval<value_type&>() OP \
225  autoCopy(std::declval<T>()) )> \
226  Proxy operator OP(T &&o) && \
227  { \
228  vec_[idx_] OP autoCopy(std::forward<T>(o)); \
229  return { idx_, vec_ }; \
230  }
231  DUNE_SIMD_VC_ASSIGNMENT(=);
232  DUNE_SIMD_VC_ASSIGNMENT(*=);
233  DUNE_SIMD_VC_ASSIGNMENT(/=);
234  DUNE_SIMD_VC_ASSIGNMENT(%=);
235  DUNE_SIMD_VC_ASSIGNMENT(+=);
236  DUNE_SIMD_VC_ASSIGNMENT(-=);
237  DUNE_SIMD_VC_ASSIGNMENT(<<=);
238  DUNE_SIMD_VC_ASSIGNMENT(>>=);
239  DUNE_SIMD_VC_ASSIGNMENT(&=);
240  DUNE_SIMD_VC_ASSIGNMENT(^=);
241  DUNE_SIMD_VC_ASSIGNMENT(|=);
242 #undef DUNE_SIMD_VC_ASSIGNMENT
243 
244  // unary (prefix) operators
245  template<class T = value_type,
246  class = std::enable_if_t<!std::is_same<T, bool>::value> >
247  Proxy operator++() { ++(vec_[idx_]); return *this; }
248  template<class T = value_type,
249  class = std::enable_if_t<!std::is_same<T, bool>::value> >
250  Proxy operator--() { --(vec_[idx_]); return *this; }
251 
252  // postfix operators
253  template<class T = value_type,
254  class = std::enable_if_t<!std::is_same<T, bool>::value> >
255  value_type operator++(int) { return vec_[idx_]++; }
256  template<class T = value_type,
257  class = std::enable_if_t<!std::is_same<T, bool>::value> >
258  value_type operator--(int) { return vec_[idx_]--; }
259 
260 
261  // swap on proxies swaps the proxied vector entries. As such, it
262  // applies to rvalues of proxies too, not just lvalues
263  friend void swap(const Proxy &a, const Proxy &b) {
264  // don't use swap() ourselves -- not supported by Vc 1.3.0 (but is
265  // supported by Vc 1.3.2)
266  value_type tmp = std::move(a.vec_[a.idx_]);
267  a.vec_[a.idx_] = std::move(b.vec_[b.idx_]);
268  b.vec_[b.idx_] = std::move(tmp);
269  }
270  friend void swap(value_type &a, const Proxy &b) {
271  // don't use swap() ourselves -- not supported by Vc 1.3.0 (but is
272  // supported by Vc 1.3.2)
273  value_type tmp = std::move(a);
274  a = std::move(b.vec_[b.idx_]);
275  b.vec_[b.idx_] = std::move(tmp);
276  }
277  friend void swap(const Proxy &a, value_type &b) {
278  // don't use swap() ourselves -- not supported by Vc 1.3.0 (but is
279  // supported by Vc 1.3.2)
280  value_type tmp = std::move(a.vec_[a.idx_]);
281  a.vec_[a.idx_] = std::move(b);
282  b = std::move(tmp);
283  }
284 
285  // binary operators
286  //
287  // Normally, these are provided by the conversion operator in
288  // combination with C++'s builtin binary operators. Other classes
289  // that need to provide the binary operators themselves should either
290  // 1. deduce the "foreign" operand type independently, i.e. use
291  // template<class... Args, class Foreign>
292  // auto operator@(MyClass<Args...>, Foreign);
293  // or
294  // 2. not deduce anything from the foreign argument, i.e.
295  // template<class... Args>
296  // auto operator@(MyClass<Args...>,
297  // typename MyClass<Args...>::value_type);
298  // or
299  // template<class T, class... Args>
300  // struct MyClass {
301  // auto operator@(T);
302  // }
303  // or
304  // template<class T, class... Args>
305  // struct MyClass {
306  // friend auto operator@(MyClass, T);
307  // }
308  //
309  // This allows either for an exact match (in the case of option 1.) or
310  // for conversions to be applied to the foreign argument (options 2.).
311  // In contrast, allowing some of the template parameters being deduced
312  // from the self argument also being deduced from the foreign argument
313  // will likely lead to ambigous deduction when the foreign argument is
314  // a proxy:
315  // template<class T, class... Args>
316  // auto operator@(MyClass<T, Args...>, T);
317  // One class that suffers from this problem ist std::complex.
318  //
319  // Note that option 1. is a bit dangerous, as the foreign argument is
320  // catch-all. This seems tempting in the case of a proxy class, as
321  // the operator could just be forwarded to the proxied object with the
322  // foreign argument unchanged, immediately creating interoperability
323  // with arbitrary foreign classes. However, if the foreign class also
324  // choses option 1., this will result in ambigous overloads, and there
325  // is no clear guide to decide which class should provide the overload
326  // and which should not.
327  //
328  // Fortunately, deferring to the conversion and the built-in operators
329  // mostly works in the case of this proxy class, because only built-in
330  // types can be proxied anyway. Unfortunately, the Vc vectors and
331  // arrays suffer from a slightly different problem. They chose option
332  // 1., but they can't just accept the argument type they are given,
333  // since they need to somehow implement the operation in terms of
334  // intrinsics. So they check the argument whether it is one of the
335  // expected types, and remove the operator from the overload set if it
336  // isn't via SFINAE. Of course, this proxy class is not one of the
337  // expected types, even though it would convert to them...
338  //
339  // So what we have to do here, unfortunately, is to provide operators
340  // for the Vc types explicitly, and hope that there won't be some Vc
341  // version that gets the operators right, thus creating ambigous
342  // overloads. Well, if guess it will be #ifdef time if it comes to
343  // that.
344 #define DUNE_SIMD_VC_BINARY(OP) \
345  template<class T, class Abi> \
346  friend auto operator OP(const Vc::Vector<T, Abi> &l, Proxy&& r) \
347  -> decltype(l OP std::declval<value_type>()) \
348  { \
349  return l OP value_type(r); \
350  } \
351  template<class T, class Abi> \
352  auto operator OP(const Vc::Vector<T, Abi> &r) && \
353  -> decltype(std::declval<value_type>() OP r) \
354  { \
355  return value_type(*this) OP r; \
356  } \
357  template<class T, std::size_t n, class Vec, std::size_t m> \
358  friend auto \
359  operator OP(const Vc::SimdArray<T, n, Vec, m> &l, Proxy&& r) \
360  -> decltype(l OP std::declval<value_type>()) \
361  { \
362  return l OP value_type(r); \
363  } \
364  template<class T, std::size_t n, class Vec, std::size_t m> \
365  auto operator OP(const Vc::SimdArray<T, n, Vec, m> &r) && \
366  -> decltype(std::declval<value_type>() OP r) \
367  { \
368  return value_type(*this) OP r; \
369  }
370 
371  DUNE_SIMD_VC_BINARY(*);
372  DUNE_SIMD_VC_BINARY(/);
373  DUNE_SIMD_VC_BINARY(%);
374  DUNE_SIMD_VC_BINARY(+);
375  DUNE_SIMD_VC_BINARY(-);
376  DUNE_SIMD_VC_BINARY(<<);
377  DUNE_SIMD_VC_BINARY(>>);
378  DUNE_SIMD_VC_BINARY(&);
379  DUNE_SIMD_VC_BINARY(^);
380  DUNE_SIMD_VC_BINARY(|);
381  DUNE_SIMD_VC_BINARY(<);
382  DUNE_SIMD_VC_BINARY(>);
383  DUNE_SIMD_VC_BINARY(<=);
384  DUNE_SIMD_VC_BINARY(>=);
385  DUNE_SIMD_VC_BINARY(==);
386  DUNE_SIMD_VC_BINARY(!=);
387 #undef DUNE_SIMD_VC_BINARY
388 
389  // this is needed to implement broadcast construction from proxy as
390  // the unadorned assignment operator cannot be a non-member
391  template<class T, class Abi,
392  class = std::enable_if_t<std::is_convertible<value_type,
393  T>::value> >
394  operator Vc::Vector<T, Abi>() &&
395  {
396  return value_type(*this);
397  }
398  template<class T, std::size_t n, class Vec, std::size_t m,
399  class = std::enable_if_t<std::is_convertible<value_type,
400  T>::value> >
401  operator Vc::SimdArray<T, n, Vec, m>() &&
402  {
403  return value_type(*this);
404  }
405 
406 #define DUNE_SIMD_VC_ASSIGN(OP) \
407  template<class T, class Abi> \
408  friend auto operator OP(Vc::Vector<T, Abi> &l, Proxy&& r) \
409  -> decltype(l OP std::declval<value_type>()) \
410  { \
411  return l OP value_type(r); \
412  }
413 
414  DUNE_SIMD_VC_ASSIGN(*=);
415  DUNE_SIMD_VC_ASSIGN(/=);
416  DUNE_SIMD_VC_ASSIGN(%=);
417  DUNE_SIMD_VC_ASSIGN(+=);
418  DUNE_SIMD_VC_ASSIGN(-=);
419  DUNE_SIMD_VC_ASSIGN(&=);
420  DUNE_SIMD_VC_ASSIGN(^=);
421  DUNE_SIMD_VC_ASSIGN(|=);
422  // The shift assignment would not be needed for Vc::Vector since it
423  // has overloads for `int` rhs and the proxy can convert to that --
424  // except that there is also overloads for Vector, and because of the
425  // conversion operator needed to support unadorned assignments, the
426  // proxy can convert to that, too.
427  DUNE_SIMD_VC_ASSIGN(<<=);
428  DUNE_SIMD_VC_ASSIGN(>>=);
429 #undef DUNE_SIMD_VC_ASSIGN
430  };
431 
432  } // namespace VcImpl
433 
434  namespace Overloads {
435 
442 
445  template<class V>
446  struct ScalarType<V, std::enable_if_t<VcImpl::IsVector<V>::value> >
447  {
448  using type = typename V::value_type;
449  };
450 
452 
459  template<class V>
460  struct RebindType<Simd::Scalar<V>, V,
461  std::enable_if_t<VcImpl::IsVector<V>::value> >
462  {
463  using type = V;
464  };
465 
467 
473  template<class V>
474  struct RebindType<bool, V, std::enable_if_t<VcImpl::IsVector<V>::value &&
475  !VcImpl::IsMask<V>::value>>
476  {
477  using type = typename V::mask_type;
478  };
479 
481 
487  template<class M>
488  struct RebindType<Scalar<typename M::Vector>, M,
489  std::enable_if_t<VcImpl::IsMask<M>::value>>
490  {
491  using type = typename M::Vector;
492  };
493 
495 
501  template<class S, class M>
502  struct RebindType<S, M,
503  std::enable_if_t<
504  VcImpl::IsMask<M>::value &&
505  VcImpl::IsVectorizable<S>::value &&
506  !std::is_same<S, Scalar<typename M::Vector> >::value
507  > >
508  {
509  using type = Vc::SimdArray<S, Simd::lanes<M>()>;
510  };
511 
513 
519  template<class S, class V>
520  struct RebindType<S, V,
521  std::enable_if_t<VcImpl::IsVector<V>::value &&
522  !VcImpl::IsMask<V>::value &&
523  VcImpl::IsVectorizable<S>::value &&
524  !std::is_same<S, Scalar<V> >::value> >
525  {
526  using type = Vc::SimdArray<S, Simd::lanes<V>()>;
527  };
528 
530 
537  template<class S, class V>
538  struct RebindType<S, V,
539  std::enable_if_t<VcImpl::IsVector<V>::value &&
540  !VcImpl::IsVectorizable<S>::value &&
541  !std::is_same<S, bool>::value &&
542  !std::is_same<S, Scalar<V> >::value> >
543  {
544  using type = LoopSIMD<S, Simd::lanes<V>()>;
545  };
546 
548 
551  template<class V>
552  struct LaneCount<V, std::enable_if_t<VcImpl::IsVector<V>::value> >
553  : public index_constant<V::size()>
554  { };
555 
557  template<class V>
559  std::size_t l, V &v)
560  {
561  return { l, v };
562  }
563 
565  template<class V>
567  std::size_t l, const V &v)
568  {
569  return v[l];
570  }
571 
573  /*
574  * The hack with the SFINAE is necessary, because if I use just
575  * Scalar<V> as the return type, the compiler still errors out if V is
576  * an lvalue-reference T&. You'd think he'd notice that he can't
577  * instantiate this declaration for this template parameter, and would
578  * simply remove it from the overload set, but no...
579  */
580  template<class V,
581  class = std::enable_if_t<!std::is_reference<V>::value> >
583  std::size_t l, V &&v)
584  {
585  return std::forward<V>(v)[l];
586  }
587 
589  template<class V>
592  const Mask<V> &mask, const V &ifTrue, const V &ifFalse)
593  {
594  return Vc::iif(mask, ifTrue, ifFalse);
595  }
596 
598  /*
599  * Kludge because iif seems to be unimplemented for masks
600  */
601  template<class V>
603  const V &mask, const V &ifTrue, const V &ifFalse)
604  {
605  return (mask && ifTrue) || (!mask && ifFalse);
606  }
607 
609  template<class V>
612  const V &v1, const V &v2)
613  {
614  return Simd::cond(v1 < v2, v2, v1);
615  }
616 
618  template<class M>
620  const M &m1, const M &m2)
621  {
622  return m1 || m2;
623  }
624 
626  template<class V>
629  const V &v1, const V &v2)
630  {
631  return Simd::cond(v1 < v2, v1, v2);
632  }
633 
635  template<class M>
637  const M &m1, const M &m2)
638  {
639  return m1 && m2;
640  }
641 
643  template<class M>
644  bool anyTrue (ADLTag<5, VcImpl::IsMask<M>::value>, const M &mask)
645  {
646  return Vc::any_of(mask);
647  }
648 
650  template<class M>
652  {
653  return Vc::all_of(mask);
654  }
655 
656  // nothing like anyFalse() in Vc, so let defaults.hh handle it
657 
659  template<class M>
661  {
662  return Vc::none_of(mask);
663  }
664 
666  template<class V>
669  const V &v)
670  {
671  return v.max();
672  }
673 
675  template<class M>
677  {
678  return Vc::any_of(mask);
679  }
680 
682  template<class V>
685  const V &v)
686  {
687  return v.min();
688  }
689 
691  template<class M>
693  {
694  return !Vc::any_of(!mask);
695  }
696 
698  template<class S1, class V2>
699  auto maskAnd(ADLTag<5, std::is_same<Mask<S1>, bool>::value &&
701  const S1 &s1, const V2 &v2)
702  {
703  return Simd::Mask<V2>(Simd::mask(s1)) && Simd::mask(v2);
704  }
705 
707  template<class V1, class S2>
709  std::is_same<Mask<S2>, bool>::value>,
710  const V1 &v1, const S2 &s2)
711  {
712  return Simd::mask(v1) && Simd::Mask<V1>(Simd::mask(s2));
713  }
714 
716  template<class S1, class V2>
717  auto maskOr(ADLTag<5, std::is_same<Mask<S1>, bool>::value &&
719  const S1 &s1, const V2 &v2)
720  {
721  return Simd::Mask<V2>(Simd::mask(s1)) || Simd::mask(v2);
722  }
723 
725  template<class V1, class S2>
727  std::is_same<Mask<S2>, bool>::value>,
728  const V1 &v1, const S2 &s2)
729  {
730  return Simd::mask(v1) || Simd::Mask<V1>(Simd::mask(s2));
731  }
732 
734 
735  } // namespace Overloads
736 
737  } // namespace Simd
738 
739  /*
740  * Specialize IsNumber for Vc::SimdArray and Vc::Vector to be able to use
741  * it as a scalar in DenseMatrix etc.
742  */
743  template <typename T, std::size_t N>
744  struct IsNumber<Vc::SimdArray<T, N>>
745  : public std::integral_constant<bool, IsNumber<T>::value> {
746  };
747 
748  template <typename T, typename Abi>
749  struct IsNumber<Vc::Vector<T, Abi>>
750  : public std::integral_constant<bool, IsNumber<T>::value> {
751  };
752 
754  template<class V>
755  struct AutonomousValueType<Simd::VcImpl::Proxy<V> > :
756  AutonomousValueType<typename Simd::VcImpl::Proxy<V>::value_type> {};
757 
758 } // namespace Dune
759 
760 #endif // DUNE_COMMON_SIMD_VC_HH
Basic definitions for SIMD Implementations.
Definition: loop.hh:34
A reference-like proxy for elements of random-access vectors.
Definition: vc.hh:198
Default implementations for SIMD Implementations.
std::integral_constant< std::size_t, i > index_constant
An index constant with value i.
Definition: indices.hh:28
bool allFalse(ADLTag< 0 >, const Mask &mask)
implements Simd::allFalse()
Definition: defaults.hh:122
bool allTrue(ADLTag< 0 >, const Mask &mask)
implements Simd::allTrue()
Definition: defaults.hh:102
Mask< V > mask(ADLTag< 0, std::is_same< V, Mask< V > >::value >, const V &v)
implements Simd::mask()
Definition: defaults.hh:151
auto maskAnd(ADLTag< 0 >, const V1 &v1, const V2 &v2)
implements Simd::maskAnd()
Definition: defaults.hh:175
auto maskOr(ADLTag< 0 >, const V1 &v1, const V2 &v2)
implements Simd::maskOr()
Definition: defaults.hh:168
auto min(ADLTag< 0 >, const V &v1, const V &v2)
implements binary Simd::min()
Definition: defaults.hh:87
auto max(ADLTag< 0 >, const V &v1, const V &v2)
implements binary Simd::max()
Definition: defaults.hh:79
V cond(M &&mask, const V &ifTrue, const V &ifFalse)
Like the ?: operator.
Definition: interface.hh:384
auto mask(const V &v)
Convert to mask, analogue of bool(s) for scalars.
Definition: interface.hh:487
Rebind< bool, V > Mask
Mask type type of some SIMD type.
Definition: interface.hh:287
typename Overloads::ScalarType< std::decay_t< V > >::type Scalar
Element type of some SIMD type.
Definition: interface.hh:233
Dune namespace.
Definition: alignedallocator.hh:14
const T1 cond(bool b, const T1 &v1, const T2 &v2)
conditional evaluate
Definition: conditional.hh:26
Type free of internal references that T can be converted to.
Definition: typetraits.hh:582
Tag used to force late-binding lookup in Dune::Simd::Overloads.
Definition: base.hh:180
should be derived from a Dune::index_constant
Definition: standard.hh:72
should have a member type type
Definition: standard.hh:65
should have a member type type
Definition: standard.hh:58
specialized to true for Vc mask types
Definition: vc.hh:157
specialized to true for Vc vector and mask types
Definition: vc.hh:167
Traits for type conversions and type information.
Compatibility header for including <Vc/Vc>
Creative Commons License   |  Legal Statements / Impressum  |  Hosted by TU Dresden  |  generated with Hugo v0.80.0 (May 13, 22:30, 2024)