dune-common 2.12-git
Loading...
Searching...
No Matches
simd/vc.hh
Go to the documentation of this file.
1// SPDX-FileCopyrightInfo: Copyright © DUNE Project contributors, see file LICENSE.md in module root
2// SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception
3#ifndef DUNE_COMMON_SIMD_VC_HH
4#define DUNE_COMMON_SIMD_VC_HH
5
11#include <Vc/Vc>
12
13#include <cstddef>
14#include <type_traits>
15#include <utility>
16
19#include <dune/common/simd/defaults.hh> // for anyFalse()
22
162namespace Dune {
163 namespace Simd {
164
165 namespace VcImpl {
166
168 template<class V, class SFINAE = void>
170
171 template<typename T, typename A>
172 struct IsMask<Vc::Mask<T, A> > : std::true_type {};
173
174 template<typename T, std::size_t n, typename V, std::size_t m>
175 struct IsMask<Vc::SimdMaskArray<T, n, V, m> > : std::true_type {};
176
178 template<class V, class SFINAE = void>
179 struct IsVector : IsMask<V> {};
180
181 template<typename T, typename A>
182 struct IsVector<Vc::Vector<T, A> > : std::true_type {};
183
184 template<typename T, std::size_t n, typename V, std::size_t m>
185 struct IsVector<Vc::SimdArray<T, n, V, m> > : std::true_type {};
186
187 template<typename T> struct IsVectorizable : std::false_type {};
188 template<> struct IsVectorizable<double> : std::true_type {};
189 template<> struct IsVectorizable<float> : std::true_type {};
190 template<> struct IsVectorizable<std::int32_t> : std::true_type {};
191 template<> struct IsVectorizable<std::uint32_t> : std::true_type {};
192 template<> struct IsVectorizable<std::int16_t> : std::true_type {};
193 template<> struct IsVectorizable<std::uint16_t> : std::true_type {};
194
196
208 template<class V>
209 class Proxy
210 {
211 static_assert(std::is_same<V, std::decay_t<V> >::value, "Class Proxy "
212 "may only be instantiated with unqualified types");
213 public:
214 using value_type = typename V::value_type;
215
216 private:
218 "Only arithmetic types are supported");
219 V &vec_;
220 std::size_t idx_;
221
222 public:
223 Proxy(std::size_t idx, V &vec)
224 : vec_(vec), idx_(idx)
225 { }
226
227 Proxy(const Proxy&) = delete;
228 // allow move construction so we can return proxies from functions
229 Proxy(Proxy&&) = default;
230
231 operator value_type() const { return vec_[idx_]; }
232
233 // assignment operators
234#define DUNE_SIMD_VC_ASSIGNMENT(OP) \
235 template<class T, \
236 class = decltype(std::declval<value_type&>() OP \
237 autoCopy(std::declval<T>()) )> \
238 Proxy operator OP(T &&o) && \
239 { \
240 vec_[idx_] OP autoCopy(std::forward<T>(o)); \
241 return { idx_, vec_ }; \
242 }
254#undef DUNE_SIMD_VC_ASSIGNMENT
255
256 // unary (prefix) operators
257 template<class T = value_type,
259 Proxy operator++() { ++(vec_[idx_]); return *this; }
260 template<class T = value_type,
262 Proxy operator--() { --(vec_[idx_]); return *this; }
263
264 // postfix operators
265 template<class T = value_type,
267 value_type operator++(int) { return vec_[idx_]++; }
268 template<class T = value_type,
270 value_type operator--(int) { return vec_[idx_]--; }
271
272
273 // swap on proxies swaps the proxied vector entries. As such, it
274 // applies to rvalues of proxies too, not just lvalues
275 friend void swap(const Proxy &a, const Proxy &b) {
276 // don't use swap() ourselves -- not supported by Vc 1.3.0 (but is
277 // supported by Vc 1.3.2)
278 value_type tmp = std::move(a.vec_[a.idx_]);
279 a.vec_[a.idx_] = std::move(b.vec_[b.idx_]);
280 b.vec_[b.idx_] = std::move(tmp);
281 }
282 friend void swap(value_type &a, const Proxy &b) {
283 // don't use swap() ourselves -- not supported by Vc 1.3.0 (but is
284 // supported by Vc 1.3.2)
285 value_type tmp = std::move(a);
286 a = std::move(b.vec_[b.idx_]);
287 b.vec_[b.idx_] = std::move(tmp);
288 }
289 friend void swap(const Proxy &a, value_type &b) {
290 // don't use swap() ourselves -- not supported by Vc 1.3.0 (but is
291 // supported by Vc 1.3.2)
292 value_type tmp = std::move(a.vec_[a.idx_]);
293 a.vec_[a.idx_] = std::move(b);
294 b = std::move(tmp);
295 }
296
297 // binary operators
298 //
299 // Normally, these are provided by the conversion operator in
300 // combination with C++'s builtin binary operators. Other classes
301 // that need to provide the binary operators themselves should either
302 // 1. deduce the "foreign" operand type independently, i.e. use
303 // template<class... Args, class Foreign>
304 // auto operator@(MyClass<Args...>, Foreign);
305 // or
306 // 2. not deduce anything from the foreign argument, i.e.
307 // template<class... Args>
308 // auto operator@(MyClass<Args...>,
309 // typename MyClass<Args...>::value_type);
310 // or
311 // template<class T, class... Args>
312 // struct MyClass {
313 // auto operator@(T);
314 // }
315 // or
316 // template<class T, class... Args>
317 // struct MyClass {
318 // friend auto operator@(MyClass, T);
319 // }
320 //
321 // This allows either for an exact match (in the case of option 1.) or
322 // for conversions to be applied to the foreign argument (options 2.).
323 // In contrast, allowing some of the template parameters being deduced
324 // from the self argument also being deduced from the foreign argument
325 // will likely lead to ambiguous deduction when the foreign argument is
326 // a proxy:
327 // template<class T, class... Args>
328 // auto operator@(MyClass<T, Args...>, T);
329 // One class that suffers from this problem is std::complex.
330 //
331 // Note that option 1. is a bit dangerous, as the foreign argument is
332 // catch-all. This seems tempting in the case of a proxy class, as
333 // the operator could just be forwarded to the proxied object with the
334 // foreign argument unchanged, immediately creating interoperability
335 // with arbitrary foreign classes. However, if the foreign class also
336 // choses option 1., this will result in ambiguous overloads, and there
337 // is no clear guide to decide which class should provide the overload
338 // and which should not.
339 //
340 // Fortunately, deferring to the conversion and the built-in operators
341 // mostly works in the case of this proxy class, because only built-in
342 // types can be proxied anyway. Unfortunately, the Vc vectors and
343 // arrays suffer from a slightly different problem. They chose option
344 // 1., but they can't just accept the argument type they are given,
345 // since they need to somehow implement the operation in terms of
346 // intrinsics. So they check the argument whether it is one of the
347 // expected types, and remove the operator from the overload set if it
348 // isn't via SFINAE. Of course, this proxy class is not one of the
349 // expected types, even though it would convert to them...
350 //
351 // So what we have to do here, unfortunately, is to provide operators
352 // for the Vc types explicitly, and hope that there won't be some Vc
353 // version that gets the operators right, thus creating ambiguous
354 // overloads. Well, if guess it will be #ifdef time if it comes to
355 // that.
356#define DUNE_SIMD_VC_BINARY(OP) \
357 template<class T, class Abi> \
358 friend auto operator OP(const Vc::Vector<T, Abi> &l, Proxy&& r) \
359 -> decltype(l OP std::declval<value_type>()) \
360 { \
361 return l OP value_type(r); \
362 } \
363 template<class T, class Abi> \
364 auto operator OP(const Vc::Vector<T, Abi> &r) && \
365 -> decltype(std::declval<value_type>() OP r) \
366 { \
367 return value_type(*this) OP r; \
368 } \
369 template<class T, std::size_t n, class Vec, std::size_t m> \
370 friend auto \
371 operator OP(const Vc::SimdArray<T, n, Vec, m> &l, Proxy&& r) \
372 -> decltype(l OP std::declval<value_type>()) \
373 { \
374 return l OP value_type(r); \
375 } \
376 template<class T, std::size_t n, class Vec, std::size_t m> \
377 auto operator OP(const Vc::SimdArray<T, n, Vec, m> &r) && \
378 -> decltype(std::declval<value_type>() OP r) \
379 { \
380 return value_type(*this) OP r; \
381 }
382
399#undef DUNE_SIMD_VC_BINARY
400
401 // this is needed to implement broadcast construction from proxy as
402 // the unadorned assignment operator cannot be a non-member
403 template<class T, class Abi,
405 T>::value> >
406 operator Vc::Vector<T, Abi>() &&
407 {
408 return value_type(*this);
409 }
410 template<class T, std::size_t n, class Vec, std::size_t m,
412 T>::value> >
413 operator Vc::SimdArray<T, n, Vec, m>() &&
414 {
415 return value_type(*this);
416 }
417
418#define DUNE_SIMD_VC_ASSIGN(OP) \
419 template<class T, class Abi> \
420 friend auto operator OP(Vc::Vector<T, Abi> &l, Proxy&& r) \
421 -> decltype(l OP std::declval<value_type>()) \
422 { \
423 return l OP value_type(r); \
424 }
425
434 // The shift assignment would not be needed for Vc::Vector since it
435 // has overloads for `int` rhs and the proxy can convert to that --
436 // except that there is also overloads for Vector, and because of the
437 // conversion operator needed to support unadorned assignments, the
438 // proxy can convert to that, too.
441#undef DUNE_SIMD_VC_ASSIGN
442 };
443
444 } // namespace VcImpl
445
446 namespace Overloads {
447
454
457 template<class V>
458 struct ScalarType<V, std::enable_if_t<VcImpl::IsVector<V>::value> >
459 {
460 using type = typename V::value_type;
461 };
462
464
471 template<class V>
472 struct RebindType<Simd::Scalar<V>, V,
473 std::enable_if_t<VcImpl::IsVector<V>::value> >
474 {
475 using type = V;
476 };
477
479
485 template<class V>
486 struct RebindType<bool, V, std::enable_if_t<VcImpl::IsVector<V>::value &&
487 !VcImpl::IsMask<V>::value>>
488 {
489 using type = typename V::mask_type;
490 };
491
493
499 template<class M>
500 struct RebindType<Scalar<typename M::Vector>, M,
501 std::enable_if_t<VcImpl::IsMask<M>::value>>
502 {
503 using type = typename M::Vector;
504 };
505
507
516 template<class S, class M>
517 struct RebindType<S, M,
518 std::enable_if_t<
519 VcImpl::IsMask<M>::value &&
520 VcImpl::IsVectorizable<S>::value &&
521 !std::is_same<S, Scalar<typename M::Vector> >::value
522 > >
523 {
524 using type = Vc::fixed_size_simd<S, int(Simd::lanes<M>())>;
525 };
526
528
537 template<class S, class V>
538 struct RebindType<S, V,
539 std::enable_if_t<VcImpl::IsVector<V>::value &&
540 !VcImpl::IsMask<V>::value &&
541 VcImpl::IsVectorizable<S>::value &&
542 !std::is_same<S, Scalar<V> >::value> >
543 {
544 using type = Vc::fixed_size_simd<S, int(Simd::lanes<V>())>;
545 };
546
548
555 template<class S, class V>
556 struct RebindType<S, V,
557 std::enable_if_t<VcImpl::IsVector<V>::value &&
558 !VcImpl::IsVectorizable<S>::value &&
559 !std::is_same<S, bool>::value &&
560 !std::is_same<S, Scalar<V> >::value> >
561 {
563 };
564
566
569 template<class V>
570 struct LaneCount<V, std::enable_if_t<VcImpl::IsVector<V>::value> >
571 : public index_constant<V::size()>
572 { };
573
575 template<class V>
577 std::size_t l, V &v)
578 {
579 return { l, v };
580 }
581
583 template<class V>
585 std::size_t l, const V &v)
586 {
587 return v[l];
588 }
589
591 /*
592 * The hack with the SFINAE is necessary, because if I use just
593 * Scalar<V> as the return type, the compiler still errors out if V is
594 * an lvalue-reference T&. You'd think he'd notice that he can't
595 * instantiate this declaration for this template parameter, and would
596 * simply remove it from the overload set, but no...
597 */
598 template<class V,
601 std::size_t l, V &&v)
602 {
603 return std::forward<V>(v)[l];
604 }
605
607 template<class V>
610 const Mask<V> &mask, const V &ifTrue, const V &ifFalse)
611 {
612 return Vc::iif(mask, ifTrue, ifFalse);
613 }
614
616 /*
617 * Kludge because iif seems to be unimplemented for masks
618 */
619 template<class V>
621 const V &mask, const V &ifTrue, const V &ifFalse)
622 {
623 return (mask && ifTrue) || (!mask && ifFalse);
624 }
625
627 template<class V>
630 const V &v1, const V &v2)
631 {
632 return Simd::cond(v1 < v2, v2, v1);
633 }
634
636 template<class M>
638 const M &m1, const M &m2)
639 {
640 // for SimdMaskArray, Vc returns the derived fixed_size_simd_mask
641 return m1 || m2;
642 }
643
645 template<class V>
648 const V &v1, const V &v2)
649 {
650 return Simd::cond(v1 < v2, v1, v2);
651 }
652
654 template<class M>
656 const M &m1, const M &m2)
657 {
658 // for SimdMaskArray, Vc returns the derived fixed_size_simd_mask
659 return m1 && m2;
660 }
661
663 template<class M>
665 {
666 return Vc::any_of(mask);
667 }
668
670 template<class M>
672 {
673 return Vc::all_of(mask);
674 }
675
676 // nothing like anyFalse() in Vc, so let defaults.hh handle it
677
679 template<class M>
681 {
682 return Vc::none_of(mask);
683 }
684
686 template<class V>
689 const V &v)
690 {
691 return v.max();
692 }
693
695 template<class M>
697 {
698 return Vc::any_of(mask);
699 }
700
702 template<class V>
705 const V &v)
706 {
707 return v.min();
708 }
709
711 template<class M>
713 {
714 return !Vc::any_of(!mask);
715 }
716
718 template<class S1, class V2>
719 auto maskAnd(ADLTag<5, std::is_same<Mask<S1>, bool>::value &&
721 const S1 &s1, const V2 &v2)
722 {
723 return Simd::Mask<V2>(Simd::mask(s1)) && Simd::mask(v2);
724 }
725
727 template<class V1, class S2>
729 std::is_same<Mask<S2>, bool>::value>,
730 const V1 &v1, const S2 &s2)
731 {
732 return Simd::mask(v1) && Simd::Mask<V1>(Simd::mask(s2));
733 }
734
736 template<class S1, class V2>
737 auto maskOr(ADLTag<5, std::is_same<Mask<S1>, bool>::value &&
739 const S1 &s1, const V2 &v2)
740 {
741 return Simd::Mask<V2>(Simd::mask(s1)) || Simd::mask(v2);
742 }
743
745 template<class V1, class S2>
747 std::is_same<Mask<S2>, bool>::value>,
748 const V1 &v1, const S2 &s2)
749 {
750 return Simd::mask(v1) || Simd::Mask<V1>(Simd::mask(s2));
751 }
752
754
755 } // namespace Overloads
756
757 } // namespace Simd
758
759 /*
760 * Specialize IsNumber for Vc::SimdArray and Vc::Vector to be able to use
761 * it as a scalar in DenseMatrix etc.
762 */
763 template <typename T, std::size_t N, class V, size_t Wt>
764 struct IsNumber<Vc::SimdArray<T, N, V, Wt>>
765 : public std::integral_constant<bool, IsNumber<T>::value> {
766 };
767
768 template <typename T, typename Abi>
769 struct IsNumber<Vc::Vector<T, Abi>>
770 : public std::integral_constant<bool, IsNumber<T>::value> {
771 };
772
774 template<class V>
775 struct AutonomousValueType<Simd::VcImpl::Proxy<V> > :
776 AutonomousValueType<typename Simd::VcImpl::Proxy<V>::value_type> {};
777
778} // namespace Dune
779
780namespace Vc_VERSIONED_NAMESPACE {
781
783
800 template<class T, int N>
801 fixed_size_simd<T, N> max(const fixed_size_simd<T, N> &a,
802 const fixed_size_simd<T, N> &b)
803 {
804 using A = SimdArray<T, std::size_t(N)>;
805 return max(static_cast<const A &>(a), static_cast<const A &>(b));
806 }
807
809
814 template<class T, int N>
815 fixed_size_simd<T, N> min(const fixed_size_simd<T, N> &a,
816 const fixed_size_simd<T, N> &b)
817 {
818 using A = SimdArray<T, std::size_t(N)>;
819 return min(static_cast<const A &>(a), static_cast<const A &>(b));
820 }
821
822} // namespace Vc_VERSIONED_NAMESPACE
823
824#endif // DUNE_COMMON_SIMD_VC_HH
Basic definitions for SIMD Implementations.
Default implementations for SIMD Implementations.
Traits for type conversions and type information.
V cond(M &&mask, const V &ifTrue, const V &ifFalse)
Like the ?: operator.
Definition simd/interface.hh:386
auto mask(const V &v)
Convert to mask, analogue of bool(s) for scalars.
Definition simd/interface.hh:495
Rebind< bool, V > Mask
Mask type type of some SIMD type.
Definition simd/interface.hh:289
typename Overloads::ScalarType< std::decay_t< V > >::type Scalar
Element type of some SIMD type.
Definition simd/interface.hh:235
Mask< V > mask(ADLTag< 0, std::is_same< V, Mask< V > >::value >, const V &v)
implements Simd::mask()
Definition defaults.hh:153
bool allFalse(ADLTag< 0 >, const Mask &mask)
implements Simd::allFalse()
Definition defaults.hh:124
bool allTrue(ADLTag< 0 >, const Mask &mask)
implements Simd::allTrue()
Definition defaults.hh:104
auto maskAnd(ADLTag< 0 >, const V1 &v1, const V2 &v2)
implements Simd::maskAnd()
Definition defaults.hh:177
auto maskOr(ADLTag< 0 >, const V1 &v1, const V2 &v2)
implements Simd::maskOr()
Definition defaults.hh:170
auto min(ADLTag< 0 >, const V &v1, const V &v2)
implements binary Simd::min()
Definition defaults.hh:89
auto max(ADLTag< 0 >, const V &v1, const V &v2)
implements binary Simd::max()
Definition defaults.hh:81
STL namespace.
Dune namespace
Definition alignedallocator.hh:13
bool anyTrue(ADLTag< 5 >, const AlignedNumber< bool, align > &mask)
Definition debugalign.hh:556
const AlignedNumber< T, align > & cond(ADLTag< 5 >, AlignedNumber< bool, align > mask, const AlignedNumber< T, align > &ifTrue, const AlignedNumber< T, align > &ifFalse)
Definition debugalign.hh:548
T & lane(ADLTag< 5 >, std::size_t l, AlignedNumber< T, align > &v)
Definition debugalign.hh:533
Definition simd/vc.hh:780
Whether this type acts as a scalar in the context of (hierarchically blocked) containers.
Definition typetraits.hh:194
Tag used to force late-binding lookup in Dune::Simd::Overloads.
Definition base.hh:182
should have a member type type
Definition standard.hh:60
should have a member type type
Definition standard.hh:67
should be derived from a Dune::index_constant
Definition standard.hh:74
Definition loop.hh:65
specialized to true for Vc mask types
Definition simd/vc.hh:169
specialized to true for Vc vector and mask types
Definition simd/vc.hh:179
Definition simd/vc.hh:187
A reference-like proxy for elements of random-access vectors.
Definition simd/vc.hh:210
DUNE_SIMD_VC_BINARY * DUNE_SIMD_VC_BINARY(/);DUNE_SIMD_VC_BINARY(%
Proxy operator--()
Definition simd/vc.hh:262
friend void swap(const Proxy &a, value_type &b)
Definition simd/vc.hh:289
Proxy(Proxy &&)=default
value_type operator++(int)
Definition simd/vc.hh:267
DUNE_SIMD_VC_ASSIGN * DUNE_SIMD_VC_ASSIGN(/=);DUNE_SIMD_VC_ASSIGN(%=
friend void swap(value_type &a, const Proxy &b)
Definition simd/vc.hh:282
DUNE_SIMD_VC_BINARY & DUNE_SIMD_VC_BINARY(^);DUNE_SIMD_VC_BINARY(|
value_type operator--(int)
Definition simd/vc.hh:270
Proxy(std::size_t idx, V &vec)
Definition simd/vc.hh:223
friend void swap(const Proxy &a, const Proxy &b)
Definition simd/vc.hh:275
DUNE_SIMD_VC_ASSIGN & DUNE_SIMD_VC_ASSIGN(^=);DUNE_SIMD_VC_ASSIGN(|=
DUNE_SIMD_VC_ASSIGNMENT & DUNE_SIMD_VC_ASSIGNMENT(^=);DUNE_SIMD_VC_ASSIGNMENT(|=
Proxy(const Proxy &)=delete
DUNE_SIMD_VC_ASSIGNMENT * DUNE_SIMD_VC_ASSIGNMENT(/=);DUNE_SIMD_VC_ASSIGNMENT(%=
typename V::value_type value_type
Definition simd/vc.hh:214
Proxy operator++()
Definition simd/vc.hh:259
Type free of internal references that T can be converted to.
Definition typetraits.hh:509
#define DUNE_SIMD_VC_ASSIGN(OP)
Definition simd/vc.hh:418