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()
23
163namespace Dune {
164 namespace Simd {
165
166 namespace VcImpl {
167
169 template<class V, class SFINAE = void>
171
172 template<typename T, typename A>
173 struct IsMask<Vc::Mask<T, A> > : std::true_type {};
174
175 template<typename T, std::size_t n, typename V, std::size_t m>
176 struct IsMask<Vc::SimdMaskArray<T, n, V, m> > : std::true_type {};
177
179 template<class V, class SFINAE = void>
180 struct IsVector : IsMask<V> {};
181
182 template<typename T, typename A>
183 struct IsVector<Vc::Vector<T, A> > : std::true_type {};
184
185 template<typename T, std::size_t n, typename V, std::size_t m>
186 struct IsVector<Vc::SimdArray<T, n, V, m> > : std::true_type {};
187
188 template<typename T> struct IsVectorizable : std::false_type {};
189 template<> struct IsVectorizable<double> : std::true_type {};
190 template<> struct IsVectorizable<float> : std::true_type {};
191 template<> struct IsVectorizable<std::int32_t> : std::true_type {};
192 template<> struct IsVectorizable<std::uint32_t> : std::true_type {};
193 template<> struct IsVectorizable<std::int16_t> : std::true_type {};
194 template<> struct IsVectorizable<std::uint16_t> : std::true_type {};
195
197
201 template<class Proxy, class ValueType>
203 {
204 using value_type = ValueType;
205
206 // binary operators
207 //
208 // Normally, these are provided by the conversion operator in
209 // combination with C++'s builtin binary operators. Other classes
210 // that need to provide the binary operators themselves should either
211 // 1. deduce the "foreign" operand type independently, i.e. use
212 // template<class... Args, class Foreign>
213 // auto operator@(MyClass<Args...>, Foreign);
214 // or
215 // 2. not deduce anything from the foreign argument, i.e.
216 // template<class... Args>
217 // auto operator@(MyClass<Args...>,
218 // typename MyClass<Args...>::value_type);
219 // or
220 // template<class T, class... Args>
221 // struct MyClass {
222 // auto operator@(T);
223 // }
224 // or
225 // template<class T, class... Args>
226 // struct MyClass {
227 // friend auto operator@(MyClass, T);
228 // }
229 //
230 // This allows either for an exact match (in the case of option 1.) or
231 // for conversions to be applied to the foreign argument (options 2.).
232 // In contrast, allowing some of the template parameters being deduced
233 // from the self argument also being deduced from the foreign argument
234 // will likely lead to ambiguous deduction when the foreign argument is
235 // a proxy:
236 // template<class T, class... Args>
237 // auto operator@(MyClass<T, Args...>, T);
238 // One class that suffers from this problem is std::complex.
239 //
240 // Note that option 1. is a bit dangerous, as the foreign argument is
241 // catch-all. This seems tempting in the case of a proxy class, as
242 // the operator could just be forwarded to the proxied object with the
243 // foreign argument unchanged, immediately creating interoperability
244 // with arbitrary foreign classes. However, if the foreign class also
245 // choses option 1., this will result in ambiguous overloads, and there
246 // is no clear guide to decide which class should provide the overload
247 // and which should not.
248 //
249 // Fortunately, deferring to the conversion and the built-in operators
250 // mostly works in the case of this proxy class, because only built-in
251 // types can be proxied anyway. Unfortunately, the Vc vectors and
252 // arrays suffer from a slightly different problem. They chose option
253 // 1., but they can't just accept the argument type they are given,
254 // since they need to somehow implement the operation in terms of
255 // intrinsics. So they check the argument whether it is one of the
256 // expected types, and remove the operator from the overload set if it
257 // isn't via SFINAE. Of course, this proxy class is not one of the
258 // expected types, even though it would convert to them...
259 //
260 // So what we have to do here, unfortunately, is to provide operators
261 // for the Vc types explicitly, and hope that there won't be some Vc
262 // version that gets the operators right, thus creating ambiguous
263 // overloads. Well, if guess it will be #ifdef time if it comes to
264 // that.
265#define DUNE_SIMD_VC_BINARY(OP) \
266 template<class T, class Abi> \
267 friend auto operator OP(const Vc::Vector<T, Abi> &l, Proxy&& r) \
268 -> decltype(l OP std::declval<value_type>()) \
269 { \
270 return l OP value_type(r); \
271 } \
272 template<class T, class Abi> \
273 friend auto operator OP(Proxy&& l, const Vc::Vector<T, Abi> &r) \
274 -> decltype(std::declval<value_type>() OP r) \
275 { \
276 return value_type(l) OP r; \
277 } \
278 template<class T, std::size_t n, class Vec, std::size_t m> \
279 friend auto \
280 operator OP(const Vc::SimdArray<T, n, Vec, m> &l, Proxy&& r) \
281 -> decltype(l OP std::declval<value_type>()) \
282 { \
283 return l OP value_type(r); \
284 } \
285 template<class T, std::size_t n, class Vec, std::size_t m> \
286 friend auto \
287 operator OP(Proxy&& l, const Vc::SimdArray<T, n, Vec, m> &r) \
288 -> decltype(std::declval<value_type>() OP r) \
289 { \
290 return value_type(l) OP r; \
291 }
292
309#undef DUNE_SIMD_VC_BINARY
310
311 // this is needed to implement broadcast construction from proxy as
312 // the unadorned assignment operator cannot be a non-member
313 template<class T, class Abi,
315 T>::value> >
316 operator Vc::Vector<T, Abi>() &&
317 {
318 return value_type(static_cast<const Proxy &>(*this));
319 }
320 template<class T, std::size_t n, class Vec, std::size_t m,
322 T>::value> >
323 operator Vc::SimdArray<T, n, Vec, m>() &&
324 {
325 return value_type(static_cast<const Proxy &>(*this));
326 }
327
328#define DUNE_SIMD_VC_ASSIGN(OP) \
329 template<class T, class Abi> \
330 friend auto operator OP(Vc::Vector<T, Abi> &l, Proxy&& r) \
331 -> decltype(l OP std::declval<value_type>()) \
332 { \
333 return l OP value_type(r); \
334 }
335
344 // The shift assignment would not be needed for Vc::Vector since it
345 // has overloads for `int` rhs and the proxy can convert to that --
346 // except that there is also overloads for Vector, and because of the
347 // conversion operator needed to support unadorned assignments, the
348 // proxy can convert to that, too.
351#undef DUNE_SIMD_VC_ASSIGN
352 };
353
355
360 template<class V>
361 using Proxy = Impl::ValueProxy<V, ProxyOperations>;
362
363 } // namespace VcImpl
364
365 namespace Overloads {
366
373
376 template<class V>
377 struct ScalarType<V, std::enable_if_t<VcImpl::IsVector<V>::value> >
378 {
379 using type = typename V::value_type;
380 };
381
383
390 template<class V>
391 struct RebindType<Simd::Scalar<V>, V,
392 std::enable_if_t<VcImpl::IsVector<V>::value> >
393 {
394 using type = V;
395 };
396
398
404 template<class V>
405 struct RebindType<bool, V, std::enable_if_t<VcImpl::IsVector<V>::value &&
406 !VcImpl::IsMask<V>::value>>
407 {
408 using type = typename V::mask_type;
409 };
410
412
418 template<class M>
419 struct RebindType<Scalar<typename M::Vector>, M,
420 std::enable_if_t<VcImpl::IsMask<M>::value>>
421 {
422 using type = typename M::Vector;
423 };
424
426
435 template<class S, class M>
436 struct RebindType<S, M,
437 std::enable_if_t<
438 VcImpl::IsMask<M>::value &&
439 VcImpl::IsVectorizable<S>::value &&
440 !std::is_same<S, Scalar<typename M::Vector> >::value
441 > >
442 {
443 using type = Vc::fixed_size_simd<S, int(Simd::lanes<M>())>;
444 };
445
447
456 template<class S, class V>
457 struct RebindType<S, V,
458 std::enable_if_t<VcImpl::IsVector<V>::value &&
459 !VcImpl::IsMask<V>::value &&
460 VcImpl::IsVectorizable<S>::value &&
461 !std::is_same<S, Scalar<V> >::value> >
462 {
463 using type = Vc::fixed_size_simd<S, int(Simd::lanes<V>())>;
464 };
465
467
474 template<class S, class V>
475 struct RebindType<S, V,
476 std::enable_if_t<VcImpl::IsVector<V>::value &&
477 !VcImpl::IsVectorizable<S>::value &&
478 !std::is_same<S, bool>::value &&
479 !std::is_same<S, Scalar<V> >::value> >
480 {
482 };
483
485
488 template<class V>
489 struct LaneCount<V, std::enable_if_t<VcImpl::IsVector<V>::value> >
490 : public index_constant<V::size()>
491 { };
492
494 template<class V>
496 std::size_t l, V &v)
497 {
498 return { l, v };
499 }
500
502 template<class V>
504 std::size_t l, const V &v)
505 {
506 return v[l];
507 }
508
510 /*
511 * The hack with the SFINAE is necessary, because if I use just
512 * Scalar<V> as the return type, the compiler still errors out if V is
513 * an lvalue-reference T&. You'd think he'd notice that he can't
514 * instantiate this declaration for this template parameter, and would
515 * simply remove it from the overload set, but no...
516 */
517 template<class V,
520 std::size_t l, V &&v)
521 {
522 return std::forward<V>(v)[l];
523 }
524
526 template<class V>
529 const Mask<V> &mask, const V &ifTrue, const V &ifFalse)
530 {
531 return Vc::iif(mask, ifTrue, ifFalse);
532 }
533
535 /*
536 * Kludge because iif seems to be unimplemented for masks
537 */
538 template<class V>
540 const V &mask, const V &ifTrue, const V &ifFalse)
541 {
542 return (mask && ifTrue) || (!mask && ifFalse);
543 }
544
546 template<class V>
549 const V &v1, const V &v2)
550 {
551 return Simd::cond(v1 < v2, v2, v1);
552 }
553
555 template<class M>
557 const M &m1, const M &m2)
558 {
559 // for SimdMaskArray, Vc returns the derived fixed_size_simd_mask
560 return m1 || m2;
561 }
562
564 template<class V>
567 const V &v1, const V &v2)
568 {
569 return Simd::cond(v1 < v2, v1, v2);
570 }
571
573 template<class M>
575 const M &m1, const M &m2)
576 {
577 // for SimdMaskArray, Vc returns the derived fixed_size_simd_mask
578 return m1 && m2;
579 }
580
582 template<class M>
584 {
585 return Vc::any_of(mask);
586 }
587
589 template<class M>
591 {
592 return Vc::all_of(mask);
593 }
594
595 // nothing like anyFalse() in Vc, so let defaults.hh handle it
596
598 template<class M>
600 {
601 return Vc::none_of(mask);
602 }
603
605 template<class V>
608 const V &v)
609 {
610 return v.max();
611 }
612
614 template<class M>
616 {
617 return Vc::any_of(mask);
618 }
619
621 template<class V>
624 const V &v)
625 {
626 return v.min();
627 }
628
630 template<class M>
632 {
633 return !Vc::any_of(!mask);
634 }
635
637 template<class S1, class V2>
638 auto maskAnd(ADLTag<5, std::is_same<Mask<S1>, bool>::value &&
640 const S1 &s1, const V2 &v2)
641 {
642 return Simd::Mask<V2>(Simd::mask(s1)) && Simd::mask(v2);
643 }
644
646 template<class V1, class S2>
648 std::is_same<Mask<S2>, bool>::value>,
649 const V1 &v1, const S2 &s2)
650 {
651 return Simd::mask(v1) && Simd::Mask<V1>(Simd::mask(s2));
652 }
653
655 template<class S1, class V2>
656 auto maskOr(ADLTag<5, std::is_same<Mask<S1>, bool>::value &&
658 const S1 &s1, const V2 &v2)
659 {
660 return Simd::Mask<V2>(Simd::mask(s1)) || Simd::mask(v2);
661 }
662
664 template<class V1, class S2>
666 std::is_same<Mask<S2>, bool>::value>,
667 const V1 &v1, const S2 &s2)
668 {
669 return Simd::mask(v1) || Simd::Mask<V1>(Simd::mask(s2));
670 }
671
673
674 } // namespace Overloads
675
676 } // namespace Simd
677
678 /*
679 * Specialize IsNumber for Vc::SimdArray and Vc::Vector to be able to use
680 * it as a scalar in DenseMatrix etc.
681 */
682 template <typename T, std::size_t N, class V, size_t Wt>
683 struct IsNumber<Vc::SimdArray<T, N, V, Wt>>
684 : public std::integral_constant<bool, IsNumber<T>::value> {
685 };
686
687 template <typename T, typename Abi>
688 struct IsNumber<Vc::Vector<T, Abi>>
689 : public std::integral_constant<bool, IsNumber<T>::value> {
690 };
691
692} // namespace Dune
693
694namespace Vc_VERSIONED_NAMESPACE {
695
697
714 template<class T, int N>
715 fixed_size_simd<T, N> max(const fixed_size_simd<T, N> &a,
716 const fixed_size_simd<T, N> &b)
717 {
718 using A = SimdArray<T, std::size_t(N)>;
719 return max(static_cast<const A &>(a), static_cast<const A &>(b));
720 }
721
723
728 template<class T, int N>
729 fixed_size_simd<T, N> min(const fixed_size_simd<T, N> &a,
730 const fixed_size_simd<T, N> &b)
731 {
732 using A = SimdArray<T, std::size_t(N)>;
733 return min(static_cast<const A &>(a), static_cast<const A &>(b));
734 }
735
736} // namespace Vc_VERSIONED_NAMESPACE
737
738#endif // DUNE_COMMON_SIMD_VC_HH
A proxy for lanes of SIMD types with operator[]
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:393
auto mask(const V &v)
Convert to mask, analogue of bool(s) for scalars.
Definition simd/interface.hh:502
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
Impl::ValueProxy< V, ProxyOperations > Proxy
A reference-like proxy for elements of Vc vectors and masks.
Definition simd/vc.hh:361
Definition simd/vc.hh:694
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:170
specialized to true for Vc vector and mask types
Definition simd/vc.hh:180
Definition simd/vc.hh:188
Operations of lane proxies with Vc vector types.
Definition simd/vc.hh:203
DUNE_SIMD_VC_BINARY & DUNE_SIMD_VC_BINARY(^);DUNE_SIMD_VC_BINARY(|
ValueType value_type
Definition simd/vc.hh:204
DUNE_SIMD_VC_BINARY * DUNE_SIMD_VC_BINARY(/);DUNE_SIMD_VC_BINARY(%
DUNE_SIMD_VC_ASSIGN * DUNE_SIMD_VC_ASSIGN(/=);DUNE_SIMD_VC_ASSIGN(%=
DUNE_SIMD_VC_ASSIGN & DUNE_SIMD_VC_ASSIGN(^=);DUNE_SIMD_VC_ASSIGN(|=
#define DUNE_SIMD_VC_ASSIGN(OP)
Definition simd/vc.hh:328