1 #ifndef DUNE_COMMON_SIMD_TEST_HH
2 #define DUNE_COMMON_SIMD_TEST_HH
15 #include <type_traits>
18 #include <unordered_set>
38 template<
class Expr,
class SFINAE =
void>
40 template<
class Op,
class... Args,
class SFINAE>
41 struct CanCall<Op(Args...), SFINAE> : std::false_type {};
42 template<
class Op,
class... Args>
43 struct CanCall<Op(Args...),
void_t<std::result_of_t<Op(Args...)> > >
47 template<
class T,
class SFINAE =
void>
48 struct LessThenComparable : std::false_type {};
50 struct LessThenComparable<T,
void_t<decltype(std::declval<T>()
51 < std::declval<T>())> > :
55 template<
class Dst,
class Src>
56 struct CopyConstHelper
60 template<
class Dst,
class Src>
61 struct CopyConstHelper<Dst, const Src>
63 using type = std::add_const_t<Dst>;
66 template<
class Dst,
class Src>
67 struct CopyVolatileHelper
71 template<
class Dst,
class Src>
72 struct CopyVolatileHelper<Dst, volatile Src>
74 using type = std::add_volatile_t<Dst>;
77 template<
class Dst,
class Src>
78 struct CopyReferenceHelper
82 template<
class Dst,
class Src>
83 struct CopyReferenceHelper<Dst, Src&>
85 using type = std::add_lvalue_reference_t<Dst>;
88 template<
class Dst,
class Src>
89 struct CopyReferenceHelper<Dst, Src&&>
91 using type = std::add_rvalue_reference_t<Dst>;
94 template<
class Dst,
class Src>
95 using CopyRefQual =
typename CopyReferenceHelper<
96 typename CopyVolatileHelper<
97 typename CopyConstHelper<
99 std::remove_reference_t<Src>
101 std::remove_reference_t<Src>
106 template<
class Mark,
class Types,
108 std::make_index_sequence<TypeListSize<Types>::value - 1> >
110 template<
class Mark,
class Types, std::size_t... I>
111 struct RemoveEnd<Mark, Types, std::index_sequence<I...>>
113 using Back = TypeListEntry_t<TypeListSize<Types>::value - 1, Types>;
114 static_assert(std::is_same<Mark, Back>::value,
115 "TypeList not terminated by proper EndMark");
116 using type = TypeList<TypeListEntry_t<I, Types>...>;
119 template<
class T,
class List,
class =
void>
123 struct TypeInList<T,
TypeList<> > : std::false_type {};
125 template<
class T,
class... Rest>
126 struct TypeInList<T,
TypeList<T, Rest...> > : std::true_type {};
128 template<
class T,
class Head,
class... Rest>
129 struct TypeInList<T,
TypeList<Head, Rest...>,
130 std::enable_if_t<!std::is_same<T, Head>::value> > :
131 TypeInList<T, TypeList<Rest...> >::type
135 struct IsLoop : std::false_type {};
136 template<
class T, std::
size_t S>
137 struct IsLoop<LoopSIMD<T, S> > : std::true_type {};
146 constexpr
bool debugTypes(std::true_type) {
return true; }
147 template<
class... Types>
149 constexpr
bool debugTypes(std::false_type) {
return false; }
165 template<
class... Types>
171 using IsLoop =
typename Impl::IsLoop<T>::type;
175 std::ostream &log_ = std::cerr;
178 std::unordered_set<std::type_index> seen_;
185 void complain(
const char *file,
int line,
const char *func,
188 void complain(
const char *file,
int line,
const char *func,
189 const std::string &opname,
const char *expr);
195 #define DUNE_SIMD_CHECK(expr) \
196 ((expr) ? void() : complain(__FILE__, __LINE__, __func__, #expr))
200 #define DUNE_SIMD_CHECK_OP(expr) \
201 ((expr) ? void() : complain(__FILE__, __LINE__, __func__, \
202 DUNE_SIMD_OPNAME, #expr))
206 static std::decay_t<T> prvalue(T &&t)
208 return std::forward<T>(t);
213 static bool is42(
const V &v)
217 for(std::size_t l = 0; l <
lanes(v); ++l)
219 good &= (
lane(l, v) == Scalar<V>(42));
233 for(std::size_t l = 0; l <
lanes(vec); ++l)
234 lane(l, vec) = l + 1;
240 static bool is123(
const V &v)
244 for(std::size_t l = 0; l <
lanes(v); ++l)
246 good &= (
lane(l, v) == Scalar<V>(l+1));
252 static V leftVector()
257 for(std::size_t l = 0; l <
lanes(res); ++l)
258 lane(l, res) = Scalar<V>(l+1);
263 static V rightVector()
268 for(std::size_t l = 0; l <
lanes(res); ++l)
271 lane(l, res) = Scalar<V>((l)%7+1);
276 static T leftScalar()
282 static T rightScalar()
290 using CanCall = Impl::CanCall<Call>;
292 template<
class Dst,
class Src>
293 using CopyRefQual = Impl::CopyRefQual<Dst, Src>;
300 template<
class Op,
class... Vectors>
302 decltype(std::declval<Op>().
303 scalar(std::declval<CopyRefQual<Scalar<Vectors>,
317 static_assert(std::is_same<T, std::decay_t<T> >::value,
"Scalar types "
318 "must not be references, and must not include "
324 DUNE_DEPRECATED_MSG(
"Warning: please include bool in the Rebinds for "
325 "simd type V, as Masks are not checked otherwise.")
326 void warnMissingMaskRebind(std::true_type) {}
328 void warnMissingMaskRebind(std::false_type) {}
330 template<
class V,
class Rebinds,
template<
class>
class RebindPrune,
331 template<
class>
class RebindAccept,
class Recurse>
332 void checkRebindOf(Recurse recurse)
335 using T =
typename decltype(target)::type;
338 using W = Rebind<T, V>;
339 log_ <<
"Type " << className<V>() <<
" rebound to "
340 << className<T>() <<
" is " << className<W>() << std::endl;
342 static_assert(std::is_same<W, std::decay_t<W> >::value,
"Rebound "
343 "types must not be references, and must not include "
345 static_assert(lanes<V>() == lanes<W>(),
"Rebound types must have "
346 "the same number of lanes as the original vector "
348 static_assert(std::is_same<T, Scalar<W> >::value,
"Rebound types "
349 "must have the bound-to scalar type");
353 log_ <<
"Pruning check of Simd type " << className<W>()
357 using Impl::debugTypes;
358 static_assert(debugTypes<T, V, W>(
id(RebindAccept<W>{})),
359 "Rebind<T, V> is W, but that is not accepted "
361 recurse(
id(MetaType<W>{}));
365 static_assert(std::is_same<
Rebind<Scalar<V>, V>, V>::value,
"A type "
366 "rebound to its own scalar type must be the same type "
367 "as the original type");
368 static_assert(std::is_same<Rebind<bool, V>, Mask<V> >::value,
"A type "
369 "rebound to bool must be the mask type for that type");
371 constexpr
bool hasBool = Impl::TypeInList<bool, Rebinds>::value;
372 warnMissingMaskRebind<V>(Std::bool_constant<!hasBool>{});
384 static_assert(std::is_same<std::size_t, decltype(lanes<V>())>::value,
385 "return type of lanes<V>() should be std::size_t");
386 static_assert(std::is_same<std::size_t, decltype(
lanes(V{}))>::value,
387 "return type of lanes(V{}) should be std::size_t");
396 void checkDefaultConstruct()
410 for(std::size_t l = 0; l <
lanes(vec); ++l)
411 lane(l, vec) = l + 1;
412 for(std::size_t l = 0; l <
lanes(vec); ++l)
414 using MLRes = decltype(
lane(0, vec));
415 static_assert(std::is_same<MLRes, Scalar<V>&>::value ||
416 std::is_same<MLRes, std::decay_t<MLRes> >::value,
417 "Result of lane() on a mutable lvalue vector must "
418 "either be a mutable reference to a scalar of that "
419 "vector or a proxy object (which itself may not be a "
420 "reference nor const).");
424 for(std::size_t l = 0; l <
lanes(vec); ++l)
426 using CLRes = decltype(
lane(0, vec2));
427 static_assert(std::is_same<CLRes,
const Scalar<V>&>::value ||
428 std::is_same<CLRes, std::decay_t<CLRes> >::value,
429 "Result of lane() on a const lvalue vector must "
430 "either be a const lvalue reference to a scalar of that "
431 "vector or a proxy object (which itself may not be a "
432 "reference nor const).");
433 static_assert(!std::is_assignable<CLRes, Scalar<V> >::value,
434 "Result of lane() on a const lvalue vector must not be "
435 "assignable from a scalar.");
438 for(std::size_t l = 0; l <
lanes(vec); ++l)
440 using RRes = decltype(
lane(0, prvalue(vec)));
449 static_assert(std::is_same<RRes, Scalar<V> >::value ||
450 std::is_same<RRes, Scalar<V>&&>::value,
451 "Result of lane() on a rvalue vector V must be "
452 "Scalar<V> or Scalar<V>&&.");
463 void checkCopyMoveConstruct()
472 { V ref(make123<V>()); V vec (ref);
474 { V ref(make123<V>()); V vec = ref ;
476 { V ref(make123<V>()); V vec {ref};
478 { V ref(make123<V>()); V vec = {ref};
480 {
const V ref(make123<V>()); V vec (ref);
482 {
const V ref(make123<V>()); V vec = ref ;
484 {
const V ref(make123<V>()); V vec {ref};
486 {
const V ref(make123<V>()); V vec = {ref};
490 { V ref(make123<V>()); V vec (std::move(ref));
492 { V ref(make123<V>()); V vec = std::move(ref) ;
494 { V ref(make123<V>()); V vec {std::move(ref)};
496 { V ref(make123<V>()); V vec = {std::move(ref)};
501 void checkBroadcastVectorConstruct()
504 { Scalar<V> ref = 42; V vec (ref);
506 { Scalar<V> ref = 42; V vec = ref ;
512 {
const Scalar<V> ref = 42; V vec (ref);
514 {
const Scalar<V> ref = 42; V vec = ref ;
522 { Scalar<V> ref = 42; V vec (std::move(ref));
524 { Scalar<V> ref = 42; V vec = std::move(ref) ;
533 void checkBroadcastMaskConstruct()
536 { Scalar<V> ref = 42; V vec (ref);
540 { Scalar<V> ref = 42; V vec {ref};
544 {
const Scalar<V> ref = 42; V vec (ref);
548 {
const Scalar<V> ref = 42; V vec {ref};
554 { Scalar<V> ref = 42; V vec (std::move(ref));
558 { Scalar<V> ref = 42; V vec {std::move(ref)};
565 template<
class FromV,
class ToV>
569 FromV fromVec = make123<FromV>();
570 auto toVec = implCast<ToV>(fromVec);
571 static_assert(std::is_same<decltype(toVec), ToV>::value,
572 "Unexpected result type for implCast<ToV>(FromV&)");
578 const FromV fromVec = make123<FromV>();
579 auto toVec = implCast<ToV>(fromVec);
580 static_assert(std::is_same<decltype(toVec), ToV>::value,
581 "Unexpected result type for implCast<ToV>(const "
587 auto toVec = implCast<ToV>(make123<FromV>());
588 static_assert(std::is_same<decltype(toVec), ToV>::value,
589 "Unexpected result type for implCast<ToV>(FromV&&)");
601 checkImplCast<V, V>();
602 checkImplCast<V, LoopV>();
603 checkImplCast<LoopV, V>();
608 void checkBroadcast()
613 auto vec = broadcast<V>(ref);
614 static_assert(std::is_same<decltype(vec), V>::value,
615 "Unexpected result type for broadcast<V>()");
621 const Scalar<V> ref = 42;
622 auto vec = broadcast<V>(ref);
623 static_assert(std::is_same<decltype(vec), V>::value,
624 "Unexpected result type for broadcast<V>()");
629 auto vec = broadcast<V>(Scalar<V>(42));
630 static_assert(std::is_same<decltype(vec), V>::value,
631 "Unexpected result type for broadcast<V>()");
636 auto vec = broadcast<V>(42);
637 static_assert(std::is_same<decltype(vec), V>::value,
638 "Unexpected result type for broadcast<V>()");
643 auto vec = broadcast<V>(42.0);
644 static_assert(std::is_same<decltype(vec), V>::value,
645 "Unexpected result type for broadcast<V>()");
651 void checkBracedAssign()
654 { V ref = make123<V>(); V vec; vec = {ref};
656 {
const V ref = make123<V>(); V vec; vec = {ref};
664 void checkBracedBroadcastAssign()
683 #define DUNE_SIMD_POSTFIX_OP(NAME, SYMBOL) \
684 struct OpPostfix##NAME \
687 auto operator()(V&& v) const \
688 -> decltype(std::forward<V>(v) SYMBOL) \
690 return std::forward<V>(v) SYMBOL; \
694 #define DUNE_SIMD_PREFIX_OP(NAME, SYMBOL) \
695 struct OpPrefix##NAME \
698 auto operator()(V&& v) const \
699 -> decltype(SYMBOL std::forward<V>(v)) \
701 return SYMBOL std::forward<V>(v); \
705 DUNE_SIMD_POSTFIX_OP(Decrement, -- );
706 DUNE_SIMD_POSTFIX_OP(Increment, ++ );
708 DUNE_SIMD_PREFIX_OP (Decrement, -- );
709 DUNE_SIMD_PREFIX_OP (Increment, ++ );
711 DUNE_SIMD_PREFIX_OP (Plus, + );
712 DUNE_SIMD_PREFIX_OP (Minus, - );
713 DUNE_SIMD_PREFIX_OP (LogicNot, ! );
719 #pragma GCC diagnostic push
720 #pragma GCC diagnostic ignored "-Wpragmas"
721 #pragma GCC diagnostic ignored "-Wunknown-warning-option" // clang 6.0.1
722 #pragma GCC diagnostic ignored "-Wbool-operation"
723 DUNE_SIMD_PREFIX_OP (BitNot, ~ );
724 #pragma GCC diagnostic pop
726 #undef DUNE_SIMD_POSTFIX_OP
727 #undef DUNE_SIMD_PREFIX_OP
729 template<
class V,
class Op>
731 CanCall<Op(decltype(lane(0, std::declval<V>())))>::value>
734 #define DUNE_SIMD_OPNAME (className<Op(V)>())
736 auto val = leftVector<std::decay_t<V>>();
740 auto &&result = op(
static_cast<V
>(arg));
741 using T =
Scalar<std::decay_t<decltype(result)> >;
742 for(std::size_t l = 0; l <
lanes(val); ++l)
759 ==
static_cast<T
>(op(
lane(l,
static_cast<V
>(val)))));
763 for(std::size_t l = 0; l < lanes<std::decay_t<V> >(); ++l)
765 #undef DUNE_SIMD_OPNAME
768 template<
class V,
class Op>
770 !CanCall<Op(decltype(lane(0, std::declval<V>())))>::value>
778 template<
class V,
class Op>
779 void checkUnaryOpsV(Op op)
781 checkUnaryOpV<V&>(op);
782 checkUnaryOpV<const V&>(op);
783 checkUnaryOpV<V&&>(op);
797 #define DUNE_SIMD_INFIX_OP(NAME, SYMBOL) \
798 struct OpInfix##NAME \
800 template<class V1, class V2> \
801 decltype(auto) operator()(V1&& v1, V2&& v2) const \
803 return std::forward<V1>(v1) SYMBOL std::forward<V2>(v2); \
805 template<class S1, class S2> \
806 auto scalar(S1&& s1, S2&& s2) const \
807 -> decltype(std::forward<S1>(s1) SYMBOL std::forward<S2>(s2)); \
818 #define DUNE_SIMD_ASSIGN_OP(NAME, SYMBOL) \
819 struct OpInfix##NAME \
821 template<class V1, class V2> \
822 decltype(auto) operator()(V1&& v1, V2&& v2) const \
824 return std::forward<V1>(v1) SYMBOL std::forward<V2>(v2); \
826 template<class S1, class S2> \
827 auto scalar(S1& s1, S2&& s2) const \
828 -> decltype(s1 SYMBOL std::forward<S2>(s2)); \
831 #define DUNE_SIMD_REPL_OP(NAME, REPLFN, SYMBOL) \
832 struct OpInfix##NAME \
834 template<class V1, class V2> \
835 decltype(auto) operator()(V1&& v1, V2&& v2) const \
837 return Simd::REPLFN(std::forward<V1>(v1), std::forward<V2>(v2)); \
839 template<class S1, class S2> \
840 auto scalar(S1&& s1, S2&& s2) const \
841 -> decltype(std::forward<S1>(s1) SYMBOL std::forward<S2>(s2)); \
844 DUNE_SIMD_INFIX_OP(Mul, * );
845 DUNE_SIMD_INFIX_OP(Div, / );
846 DUNE_SIMD_INFIX_OP(Remainder, % );
848 DUNE_SIMD_INFIX_OP(Plus, + );
849 DUNE_SIMD_INFIX_OP(Minus, - );
851 DUNE_SIMD_INFIX_OP(LeftShift, << );
852 DUNE_SIMD_INFIX_OP(RightShift, >> );
854 DUNE_SIMD_INFIX_OP(Less, < );
855 DUNE_SIMD_INFIX_OP(Greater, > );
856 DUNE_SIMD_INFIX_OP(LessEqual, <= );
857 DUNE_SIMD_INFIX_OP(GreaterEqual, >= );
859 DUNE_SIMD_INFIX_OP(Equal, == );
860 DUNE_SIMD_INFIX_OP(NotEqual, != );
862 DUNE_SIMD_INFIX_OP(BitAnd, & );
863 DUNE_SIMD_INFIX_OP(BitXor, ^ );
864 DUNE_SIMD_INFIX_OP(BitOr, | );
868 DUNE_SIMD_REPL_OP(LogicAnd,
maskAnd, && );
869 DUNE_SIMD_REPL_OP(LogicOr,
maskOr, || );
871 DUNE_SIMD_ASSIGN_OP(Assign, = );
872 DUNE_SIMD_ASSIGN_OP(AssignMul, *= );
873 DUNE_SIMD_ASSIGN_OP(AssignDiv, /= );
874 DUNE_SIMD_ASSIGN_OP(AssignRemainder, %= );
875 DUNE_SIMD_ASSIGN_OP(AssignPlus, += );
876 DUNE_SIMD_ASSIGN_OP(AssignMinus, -= );
877 DUNE_SIMD_ASSIGN_OP(AssignLeftShift, <<=);
878 DUNE_SIMD_ASSIGN_OP(AssignRightShift, >>=);
879 DUNE_SIMD_ASSIGN_OP(AssignAnd, &= );
880 DUNE_SIMD_ASSIGN_OP(AssignXor, ^= );
881 DUNE_SIMD_ASSIGN_OP(AssignOr, |= );
883 #undef DUNE_SIMD_INFIX_OP
884 #undef DUNE_SIMD_REPL_OP
885 #undef DUNE_SIMD_ASSIGN_OP
888 struct OpInfixComma {};
890 template<
class T1,
class T2>
891 void checkCommaOp(
const std::decay_t<T1> &val1,
892 const std::decay_t<T2> &val2)
894 #define DUNE_SIMD_OPNAME (className<OpInfixComma(T1, T2)>())
895 static_assert(std::is_same<decltype((std::declval<T1>(),
896 std::declval<T2>())), T2>::value,
897 "Type and value category of the comma operator must "
898 "match that of the second operand");
907 #pragma GCC diagnostic push
908 #pragma GCC diagnostic ignored "-Wunused-value"
909 auto &&result = (
static_cast<T1
>(arg1),
910 static_cast<T2
>(arg2));
911 #pragma GCC diagnostic pop
912 if(std::is_reference<T2>::value)
931 #undef DUNE_SIMD_OPNAME
956 template<
class V1,
class V2,
class Op>
957 std::enable_if_t<Std::is_detected_v<ScalarResult, Op, V1, V2> >
958 checkBinaryOpVV(MetaType<V1>, MetaType<V2>, Op op)
960 #define DUNE_SIMD_OPNAME (className<Op(V1, V2)>())
961 static_assert(std::is_same<std::decay_t<V1>, std::decay_t<V2> >::value,
962 "Internal testsystem error: called with two types that "
963 "don't decay to the same thing");
966 auto vref1 = leftVector<std::decay_t<V1>>();
967 auto vref2 = rightVector<std::decay_t<V2>>();
974 auto &&vopres = op(
static_cast<V1
>(vop1),
static_cast<V2
>(vop2));
975 using VR = decltype(vopres);
978 static_assert(
lanes<std::decay_t<VR> >() ==
lanes<std::decay_t<V1> >(),
979 "The result must have the same number of lanes as the "
984 using T = Scalar<std::decay_t<VR> >;
991 ==
static_cast<T
>(op(
lane(l,
static_cast<V1
>(vref1)),
992 lane(l,
static_cast<V2
>(vref2)))));
1003 #undef DUNE_SIMD_OPNAME
1006 template<
class V1,
class V2,
class Op>
1007 std::enable_if_t<!Std::is_detected_v<ScalarResult, Op, V1, V2> >
1008 checkBinaryOpVV(MetaType<V1>, MetaType<V2>, Op op)
1016 template<
class V1,
class V2>
1017 void checkBinaryOpVV(MetaType<V1>, MetaType<V2>, OpInfixComma)
1019 static_assert(std::is_same<std::decay_t<V1>, std::decay_t<V2> >::value,
1020 "Internal testsystem error: called with two types that "
1021 "don't decay to the same thing");
1023 checkCommaOp<V1, V2>(leftVector<std::decay_t<V1>>(),
1024 rightVector<std::decay_t<V2>>());
1060 template<
class V1,
class T2,
class Op>
1061 std::enable_if_t<Std::is_detected_v<ScalarResult, Op, V1, T2> >
1062 checkBinaryOpVS(MetaType<V1>, MetaType<T2>, Op op)
1064 #define DUNE_SIMD_OPNAME (className<Op(V1, T2)>())
1065 static_assert(std::is_same<
Scalar<std::decay_t<V1> >,
1066 std::decay_t<T2> >::value,
1067 "Internal testsystem error: called with a scalar that "
1068 "does not match the vector type.");
1071 auto sinit2 = rightScalar<std::decay_t<T2>>();
1074 auto vref1 = leftVector<std::decay_t<V1>>();
1075 auto sref2 = sinit2;
1082 auto &&vopres = op(
static_cast<V1
>(vop1),
static_cast<T2
>(sop2));
1083 using VR = decltype(vopres);
1086 static_assert(
lanes<std::decay_t<VR> >() ==
lanes<std::decay_t<V1> >(),
1087 "The result must have the same number of lanes as the "
1094 using T =
Scalar<std::decay_t<decltype(vopres)> >;
1102 ==
static_cast<T
>(op(
lane(l,
static_cast<V1
>(vref1)),
1103 static_cast<T2
>(sref2) )));
1112 #undef DUNE_SIMD_OPNAME
1115 template<
class V1,
class T2,
class Op>
1116 std::enable_if_t<!Std::is_detected_v<ScalarResult, Op, V1, T2> >
1117 checkBinaryOpVS(MetaType<V1>, MetaType<T2>, Op op)
1125 template<
class V1,
class T2>
1126 void checkBinaryOpVS(MetaType<V1>, MetaType<T2>, OpInfixComma)
1128 static_assert(std::is_same<
Scalar<std::decay_t<V1> >,
1129 std::decay_t<T2> >::value,
1130 "Internal testsystem error: called with a scalar that "
1131 "does not match the vector type.");
1133 checkCommaOp<V1, T2>(leftVector<std::decay_t<V1>>(),
1134 rightScalar<std::decay_t<T2>>());
1163 template<
class V1,
class T2,
class Op>
1164 std::enable_if_t<Std::is_detected_v<ScalarResult, Op, V1, T2> >
1165 checkBinaryOpVVAgainstVS(MetaType<V1>, MetaType<T2>, Op op)
1167 #define DUNE_SIMD_OPNAME (className<Op(V1, T2)>())
1168 static_assert(std::is_same<
Scalar<std::decay_t<V1> >,
1169 std::decay_t<T2> >::value,
1170 "Internal testsystem error: called with a scalar that "
1171 "does not match the vector type.");
1174 auto sinit2 = rightScalar<std::decay_t<T2>>();
1177 auto vop1 = leftVector<std::decay_t<V1>>();
1178 using V2 = CopyRefQual<V1, T2>;
1179 std::decay_t<V2> vop2(sinit2);
1182 op(
static_cast<V1
>(vop1),
static_cast<V2
>(vop2));
1188 #undef DUNE_SIMD_OPNAME
1191 template<
class V1,
class T2,
class Op>
1192 std::enable_if_t<!Std::is_detected_v<ScalarResult, Op, V1, T2> >
1193 checkBinaryOpVVAgainstVS(MetaType<V1>, MetaType<T2>, Op op)
1201 template<
class V1,
class T2>
1202 void checkBinaryOpVVAgainstVS(MetaType<V1>, MetaType<T2>, OpInfixComma)
1239 template<
class V1,
class V2,
class Op>
1240 std::enable_if_t<Std::is_detected_v<ScalarResult, Op, V1, V2> >
1241 checkBinaryOpVP(MetaType<V1>, MetaType<V2>, Op op)
1243 using P2 = decltype(
lane(0, std::declval<V2>()));
1244 using T2 = CopyRefQual<Scalar<V2>, V2>;
1245 #define DUNE_SIMD_OPNAME (className<Op(V1, P2)>())
1246 static_assert(std::is_same<Scalar<V1>, Scalar<V2> >::value,
1247 "Internal testsystem error: called with two vector "
1248 "types whose scalar types don't match.");
1251 auto sinit2 = rightScalar<Scalar<V2>>();
1254 auto vref1 = leftVector<std::decay_t<V1>>();
1255 auto sref2 = sinit2;
1259 auto vop2 = std::decay_t<V2>(Scalar<V2>(0));
1260 lane(0, vop2) = sref2;
1264 op(
static_cast<V1
>(vop1),
lane(0,
static_cast<V2
>(vop2)));
1265 using VR = decltype(vopres);
1268 static_assert(
lanes<std::decay_t<VR> >() ==
lanes<std::decay_t<V1> >(),
1269 "The result must have the same number of lanes as the "
1276 using T =
Scalar<decltype(vopres)>;
1284 ==
static_cast<T
>(op(
lane(l,
static_cast<V1
>(vref1)),
1285 static_cast<T2
>(sref2) )));
1294 #undef DUNE_SIMD_OPNAME
1297 template<
class V1,
class V2,
class Op>
1298 std::enable_if_t<!Std::is_detected_v<ScalarResult, Op, V1, V2> >
1299 checkBinaryOpVP(MetaType<V1>, MetaType<V2>, Op op)
1307 template<
class V1,
class V2>
1308 void checkBinaryOpVP(MetaType<V1>, MetaType<V2>, OpInfixComma)
1319 struct OpInfixSwappedArgs
1323 template<
class V1,
class V2>
1324 decltype(
auto) operator()(V1&& v1, V2&& v2)
const
1326 return orig(std::forward<V2>(v2), std::forward<V1>(v1));
1328 template<
class S1,
class S2>
1329 auto scalar(S1&& s1, S2&& s2)
const
1330 -> decltype(orig.scalar(std::forward<S2>(s2), std::forward<S1>(s1)));
1333 template<
class T1,
class V2,
class Op>
1334 void checkBinaryOpSV(MetaType<T1> t1, MetaType<V2> v2, Op op)
1336 checkBinaryOpVS(v2, t1, OpInfixSwappedArgs<Op>{op});
1339 template<
class T1,
class V2>
1340 void checkBinaryOpSV(MetaType<T1>, MetaType<V2>, OpInfixComma)
1342 static_assert(std::is_same<std::decay_t<T1>,
1343 Scalar<std::decay_t<V2> > >::value,
1344 "Internal testsystem error: called with a scalar that "
1345 "does not match the vector type.");
1347 checkCommaOp<T1, V2>(leftScalar<std::decay_t<T1>>(),
1348 rightVector<std::decay_t<V2>>());
1351 template<
class V1,
class V2,
class Op>
1352 void checkBinaryOpPV(MetaType<V1> v1, MetaType<V2> v2, Op op)
1354 checkBinaryOpVP(v2, v1, OpInfixSwappedArgs<Op>{op});
1357 template<
class V1,
class V2>
1358 void checkBinaryOpPV(MetaType<V1>, MetaType<V2>, OpInfixComma)
1389 template<
class T1,
class V2,
class Op>
1390 void checkBinaryOpVVAgainstSV(MetaType<T1> t1, MetaType<V2> v2, Op op)
1392 checkBinaryOpVVAgainstVS(v2, t1, OpInfixSwappedArgs<Op>{op});
1395 template<
class V1,
class T2>
1396 void checkBinaryOpVVAgainstSV(MetaType<V1>, MetaType<T2>, OpInfixComma)
1404 template<
class T1,
class T2,
bool condition,
class Checker>
1405 void checkBinaryRefQual(Checker checker)
1412 [=] (
auto t2) { id(checker)(t1, t2); });
1417 template<
class V,
class Checker>
1422 constexpr
bool isMask = std::is_same<Scalar<V>,
bool>::value;
1424 constexpr
bool do_ =
false;
1425 constexpr
bool do_SV =
true;
1426 constexpr
bool do_VV =
true;
1427 constexpr
bool do_VS =
true;
1429 #define DUNE_SIMD_DO(M1, M2, M3, V1, V2, V3, NAME) \
1430 checker(bool_constant<isMask ? do_##M1 : do_##V1>{}, \
1431 bool_constant<isMask ? do_##M2 : do_##V2>{}, \
1432 bool_constant<isMask ? do_##M3 : do_##V3>{}, \
1485 void checkAutoCopy()
1487 using RValueResult = decltype(
autoCopy(
lane(0, std::declval<V>())));
1488 static_assert(std::is_same<RValueResult, Scalar<V> >::value,
1489 "Result of autoCopy() must always be Scalar<V>");
1491 using MutableLValueResult =
1493 static_assert(std::is_same<MutableLValueResult, Scalar<V> >::value,
1494 "Result of autoCopy() must always be Scalar<V>");
1496 using ConstLValueResult =
1498 static_assert(std::is_same<ConstLValueResult, Scalar<V> >::value,
1499 "Result of autoCopy() must always be Scalar<V>");
1501 V vec = make123<V>();
1502 for(std::size_t l = 0; l <
lanes(vec); ++l)
1508 void checkBoolReductions()
1550 auto mixedVec = broadcast<M>(0);
1551 for(std::size_t l = 0; l <
lanes(mixedVec); ++l)
1552 lane(l, mixedVec) = (l % 2);
1556 (
allTrue (
static_cast<M&
>(mixedVec)) ==
false);
1558 (
anyTrue (
static_cast<M&
>(mixedVec)) == (lanes<M>() > 1));
1560 (
allFalse(
static_cast<M&
>(mixedVec)) == (lanes<M>() == 1));
1562 (
anyFalse(
static_cast<M&
>(mixedVec)) ==
true);
1566 (
allTrue (
static_cast<const M&
>(mixedVec)) ==
false);
1568 (
anyTrue (
static_cast<const M&
>(mixedVec)) == (lanes<M>() > 1));
1570 (
allFalse(
static_cast<const M&
>(mixedVec)) == (lanes<M>() == 1));
1572 (
anyFalse(
static_cast<const M&
>(mixedVec)) ==
true);
1587 (std::is_same<decltype(
cond(std::declval<M>(), std::declval<V>(),
1588 std::declval<V>())), V>::value,
1589 "The result of cond(M, V, V) should have exactly the type V");
1592 (std::is_same<decltype(
cond(std::declval<const M&>(),
1593 std::declval<const V&>(),
1594 std::declval<const V&>())), V>::value,
1595 "The result of cond(const M&, const V&, const V&) should have "
1596 "exactly the type V");
1599 (std::is_same<decltype(
cond(std::declval<M&>(), std::declval<V&>(),
1600 std::declval<V&>())), V>::value,
1601 "The result of cond(M&, V&, V&) should have exactly the type V");
1603 V vec1 = leftVector<V>();
1604 V vec2 = rightVector<V>();
1609 auto mixedResult = broadcast<V>(0);
1610 auto mixedMask = broadcast<M>(
false);
1611 for(std::size_t l = 0; l <
lanes(mixedMask); ++l)
1613 lane(l, mixedMask ) = (l % 2);
1614 lane(l, mixedResult) =
lane(l, (l % 2) ? vec1 : vec2);
1621 void checkBoolCond()
1624 (std::is_same<decltype(
cond(std::declval<bool>(), std::declval<V>(),
1625 std::declval<V>())), V>::value,
1626 "The result of cond(bool, V, V) should have exactly the type V");
1629 (std::is_same<decltype(
cond(std::declval<const bool&>(),
1630 std::declval<const V&>(),
1631 std::declval<const V&>())), V>::value,
1632 "The result of cond(const bool&, const V&, const V&) should have "
1633 "exactly the type V");
1636 (std::is_same<decltype(
cond(std::declval<bool&>(),
1638 std::declval<V&>())), V>::value,
1639 "The result of cond(bool&, V&, V&) should have exactly the type V");
1641 V vec1 = leftVector<V>();
1642 V vec2 = rightVector<V>();
1649 std::enable_if_t<!Impl::LessThenComparable<Scalar<V> >::value>
1650 checkHorizontalMinMax() {}
1653 std::enable_if_t<Impl::LessThenComparable<Scalar<V> >::value>
1654 checkHorizontalMinMax()
1657 (std::is_same<decltype(
max(std::declval<V>())), Scalar<V> >::value,
1658 "The result of max(V) should be exactly Scalar<V>");
1661 (std::is_same<decltype(
min(std::declval<V>())), Scalar<V> >::value,
1662 "The result of min(V) should be exactly Scalar<V>");
1665 (std::is_same<decltype(
max(std::declval<V&>())), Scalar<V> >::value,
1666 "The result of max(V) should be exactly Scalar<V>");
1669 (std::is_same<decltype(
min(std::declval<V&>())), Scalar<V> >::value,
1670 "The result of min(V) should be exactly Scalar<V>");
1672 const V vec1 = leftVector<V>();
1679 std::enable_if_t<!Impl::LessThenComparable<Scalar<V> >::value>
1680 checkBinaryMinMax() {}
1683 std::enable_if_t<Impl::LessThenComparable<Scalar<V> >::value>
1690 (std::is_same<decltype(
Simd::max(std::declval<V>(),
1691 std::declval<V>())), V>::value,
1692 "The result of Simd::max(V, V) should be exactly V");
1694 (std::is_same<decltype(
Simd::min(std::declval<V>(),
1695 std::declval<V>())), V>::value,
1696 "The result of Simd::min(V, V) should be exactly V");
1699 (std::is_same<decltype(
Simd::max(std::declval<V&>(),
1700 std::declval<V&>())), V>::value,
1701 "The result of Simd::max(V&, V&) should be exactly V");
1703 (std::is_same<decltype(
Simd::min(std::declval<V&>(),
1704 std::declval<V&>())), V>::value,
1705 "The result of Simd::min(V&, V&) should be exactly V");
1707 const V arg1 = leftVector<V>();
1708 const V arg2 = rightVector<V>();
1710 V maxExp(Scalar<V>(0)), minExp(Scalar<V>(0));
1711 for(
auto l :
range(lanes<V>()))
1724 const V vec1 = leftVector<V>();
1726 std::string reference;
1728 const char *sep =
"";
1731 std::ostringstream stream;
1732 stream <<
lane(l, vec1);
1735 reference += stream.str();
1741 std::ostringstream stream;
1743 if(
lanes(vec1) == 1)
1750 std::ostringstream stream;
1751 stream <<
vio(vec1);
1756 #undef DUNE_SIMD_CHECK
1825 template<
class V,
class Rebinds,
1826 template<
class>
class RebindPrune =
IsLoop,
1827 template<
class>
class RebindAccept = Std::to_true_type>
1830 if(seen_.emplace(typeid (V)).second ==
false)
1838 auto recurse = [
this](
auto w) {
1839 using W =
typename decltype(w)::type;
1840 this->
template check<W, Rebinds, RebindPrune, RebindAccept>();
1842 checkRebindOf<V, Rebinds, RebindPrune, RebindAccept>(recurse);
1888 template<
class V,
class Rebinds,
template<
class>
class Prune =
IsLoop>
1889 DUNE_DEPRECATED_MSG(
"Call check() instead, and explicitly instantiate "
1890 "checkType() and friends instead")
1903 static_assert(std::is_same<V, std::decay_t<V> >::value,
"Simd types "
1904 "must not be references, and must not include "
1907 log_ <<
"Checking SIMD type " << className<V>() << std::endl;
1911 checkBinaryOps<V>();
1915 constexpr
auto isMask =
typename std::is_same<Scalar<V>,
bool>::type{};
1920 checkDefaultConstruct<V>();
1922 checkCopyMoveConstruct<V>();
1924 checkBroadcast<V>();
1926 [
this](
auto id) { id(
this)->template checkBroadcastMaskConstruct<V>(); },
1927 [
this](
auto id) { id(
this)->template checkBroadcastVectorConstruct<V>(); });
1928 checkBracedAssign<V>();
1929 checkBracedBroadcastAssign<V>();
1936 [
this](
auto id) { id(
this)->template checkBoolReductions<V>(); });
1939 checkHorizontalMinMax<V>();
1940 checkBinaryMinMax<V>();
1945 auto checkMask = [=](
auto id) {
1946 auto check = [=](
auto op) {
1947 id(
this)->template checkUnaryOpsV<V>(op);
1962 check(OpPrefixLogicNot{});
1966 auto checkVector = [=](
auto id) {
1967 auto check = [=](
auto op) {
1968 id(
this)->template checkUnaryOpsV<V>(op);
1980 check(OpPrefixMinus{});
1981 check(OpPrefixLogicNot{});
1982 check(OpPrefixBitNot{});
1989 checkBinaryOpsVectorVector<V>();
1990 checkBinaryOpsScalarVector<V>();
1991 checkBinaryOpsVectorScalar<V>();
1992 checkBinaryOpsProxyVector<V>();
1993 checkBinaryOpsVectorProxy<V>();
1997 auto checker = [=](
auto doSV,
auto doVV,
auto doVS,
auto op) {
1998 auto check = [=](
auto t1,
auto t2) {
1999 this->checkBinaryOpVV(t1, t2, op);
2001 this->checkBinaryRefQual<V, V, doVV>(
check);
2003 checkBinaryOps<V>(checker);
2007 auto checker = [=](
auto doSV,
auto doVV,
auto doVS,
auto op) {
2008 auto check = [=](
auto t1,
auto t2) {
2009 this->checkBinaryOpSV(t1, t2, op);
2011 this->checkBinaryRefQual<Scalar<V>, V, doSV>(
check);
2013 auto crossCheck = [=](
auto t1,
auto t2) {
2014 this->checkBinaryOpVVAgainstSV(t1, t2, op);
2016 this->checkBinaryRefQual<Scalar<V>, V, doSV && doVV>(crossCheck);
2018 checkBinaryOps<V>(checker);
2022 auto checker = [=](
auto doSV,
auto doVV,
auto doVS,
auto op) {
2023 auto check = [=](
auto t1,
auto t2) {
2024 this->checkBinaryOpVS(t1, t2, op);
2026 this->checkBinaryRefQual<V, Scalar<V>, doVS>(
check);
2028 auto crossCheck = [=](
auto t1,
auto t2) {
2029 this->checkBinaryOpVVAgainstVS(t1, t2, op);
2031 this->checkBinaryRefQual<V, Scalar<V>, doVV && doVS>(crossCheck);
2033 checkBinaryOps<V>(checker);
2037 auto checker = [=](
auto doSV,
auto doVV,
auto doVS,
auto op) {
2038 auto check = [=](
auto t1,
auto t2) {
2039 this->checkBinaryOpPV(t1, t2, op);
2041 this->checkBinaryRefQual<V, V, doSV>(
check);
2043 checkBinaryOps<V>(checker);
2047 auto checker = [=](
auto doSV,
auto doVV,
auto doVS,
auto op) {
2048 auto check = [=](
auto t1,
auto t2) {
2049 this->checkBinaryOpVP(t1, t2, op);
2051 this->checkBinaryRefQual<V, V, doVS>(
check);
2053 checkBinaryOps<V>(checker);
2058 template<
class V,
class Rebinds,
template<
class>
class Prune>
2059 void UnitTest::checkVector()
2062 if(seen_.emplace(typeid (V)).second ==
false)
2070 auto recurse = [
this](
auto w) {
2071 using W =
typename decltype(w)::type;
2072 this->
template checkVector<W, Rebinds, Prune>();
2074 checkRebindOf<V, Rebinds, Prune, Std::to_true_type>(recurse);
2082 #endif // DUNE_COMMON_SIMD_TEST_HH