183 void complain(
const char *file,
int line,
const char *func,
186 void complain(
const char *file,
int line,
const char *func,
193#define DUNE_SIMD_CHECK(expr) \
194 ((expr) ? void() : complain(__FILE__, __LINE__, __func__, #expr))
198#define DUNE_SIMD_CHECK_OP(expr) \
199 ((expr) ? void() : complain(__FILE__, __LINE__, __func__, \
200 DUNE_SIMD_OPNAME, #expr))
206 return std::forward<T>(t);
211 static bool is42(
const V &v)
217 good &= (
lane(l, v) == Scalar<V>(42));
232 lane(l, vec) = l + 1;
238 static bool is123(
const V &v)
244 good &= (
lane(l, v) == Scalar<V>(l+1));
250 static V leftVector()
256 lane(l, res) = Scalar<V>(l+1);
261 static V rightVector()
269 lane(l, res) = Scalar<V>((l)%7+1);
274 static T leftScalar()
280 static T rightScalar()
288 using CanCall = Impl::CanCall<Call>;
290 template<
class Dst,
class Src>
291 using CopyRefQual = Impl::CopyRefQual<Dst, Src>;
298 template<
class Op,
class... Vectors>
300 decltype(std::declval<Op>().
316 "must not be references, and must not include "
318 [[maybe_unused]] T a{};
322 [[deprecated(
"Warning: please include bool in the Rebinds for "
323 "simd type V, as Masks are not checked otherwise.")]]
328 template<
class V,
class Rebinds,
template<
class>
class RebindPrune,
329 template<
class>
class RebindAccept,
class Recurse>
330 void checkRebindOf(Recurse recurse)
333 using T =
typename decltype(target)::type;
336 using W = Rebind<T, V>;
337 log_ <<
"Type " << className<V>() <<
" rebound to "
338 << className<T>() <<
" is " << className<W>() <<
std::endl;
341 "types must not be references, and must not include "
343 static_assert(lanes<V>() == lanes<W>(),
"Rebound types must have "
344 "the same number of lanes as the original vector "
347 "must have the bound-to scalar type");
349 if constexpr (RebindPrune<W>{}) {
350 log_ <<
"Pruning check of Simd type " << className<W>()
354 using Impl::debugTypes;
355 static_assert(debugTypes<T, V, W>(RebindAccept<W>{}),
356 "Rebind<T, V> is W, but that is not accepted "
358 recurse(MetaType<W>{});
363 "rebound to its own scalar type must be the same type "
364 "as the original type");
366 "rebound to bool must be the mask type for that type");
368 constexpr bool hasBool = Impl::TypeInList<bool, Rebinds>::value;
369 warnMissingMaskRebind<V>(Std::bool_constant<!hasBool>{});
382 "return type of lanes<V>() should be std::size_t");
384 "return type of lanes(V{}) should be std::size_t");
387 [[maybe_unused]]
constexpr auto size = lanes<V>();
393 void checkDefaultConstruct()
395 { [[maybe_unused]] V vec; }
396 { [[maybe_unused]] V vec{}; }
397 { [[maybe_unused]] V vec = {}; }
408 lane(l, vec) = l + 1;
411 using MLRes =
decltype(
lane(0, vec));
414 "Result of lane() on a mutable lvalue vector must "
415 "either be a mutable reference to a scalar of that "
416 "vector or a proxy object (which itself may not be a "
417 "reference nor const).");
423 using CLRes =
decltype(
lane(0, vec2));
426 "Result of lane() on a const lvalue vector must "
427 "either be a const lvalue reference to a scalar of that "
428 "vector or a proxy object (which itself may not be a "
429 "reference nor const).");
431 "Result of lane() on a const lvalue vector must not be "
432 "assignable from a scalar.");
437 using RRes =
decltype(
lane(0, prvalue(vec)));
448 "Result of lane() on a rvalue vector V must be "
449 "Scalar<V> or Scalar<V>&&.");
460 void checkCopyMoveConstruct()
469 { V
ref(make123<V>()); V vec (ref);
471 { V
ref(make123<V>()); V vec =
ref ;
473 { V
ref(make123<V>()); V vec {
ref};
475 { V
ref(make123<V>()); V vec = {
ref};
477 {
const V
ref(make123<V>()); V vec (ref);
479 {
const V
ref(make123<V>()); V vec =
ref ;
481 {
const V
ref(make123<V>()); V vec {
ref};
483 {
const V
ref(make123<V>()); V vec = {
ref};
487 { V
ref(make123<V>()); V vec (std::move(ref));
489 { V
ref(make123<V>()); V vec = std::move(ref) ;
491 { V
ref(make123<V>()); V vec {std::move(ref)};
493 { V
ref(make123<V>()); V vec = {std::move(ref)};
498 void checkBroadcastVectorConstruct()
501 { Scalar<V>
ref = 42; V vec (ref);
503 { Scalar<V>
ref = 42; V vec =
ref ;
509 {
const Scalar<V>
ref = 42; V vec (ref);
511 {
const Scalar<V>
ref = 42; V vec =
ref ;
519 { Scalar<V>
ref = 42; V vec (std::move(ref));
521 { Scalar<V>
ref = 42; V vec = std::move(ref) ;
530 void checkBroadcastMaskConstruct()
533 { Scalar<V>
ref = 42; V vec (ref);
537 { Scalar<V>
ref = 42; V vec {
ref};
541 {
const Scalar<V>
ref = 42; V vec (ref);
545 {
const Scalar<V>
ref = 42; V vec {
ref};
551 { Scalar<V>
ref = 42; V vec (std::move(ref));
555 { Scalar<V>
ref = 42; V vec {std::move(ref)};
562 template<
class FromV,
class ToV>
566 FromV fromVec = make123<FromV>();
567 auto toVec = implCast<ToV>(fromVec);
568 static_assert(
std::is_same<
decltype(toVec), ToV>::value,
569 "Unexpected result type for implCast<ToV>(FromV&)");
575 const FromV fromVec = make123<FromV>();
576 auto toVec = implCast<ToV>(fromVec);
577 static_assert(
std::is_same<
decltype(toVec), ToV>::value,
578 "Unexpected result type for implCast<ToV>(const "
584 auto toVec = implCast<ToV>(make123<FromV>());
585 static_assert(
std::is_same<
decltype(toVec), ToV>::value,
586 "Unexpected result type for implCast<ToV>(FromV&&)");
598 checkImplCast<V, V>();
599 checkImplCast<V, LoopV>();
600 checkImplCast<LoopV, V>();
605 void checkBroadcast()
610 auto vec = broadcast<V>(ref);
612 "Unexpected result type for broadcast<V>()");
618 const Scalar<V>
ref = 42;
619 auto vec = broadcast<V>(ref);
621 "Unexpected result type for broadcast<V>()");
626 auto vec = broadcast<V>(Scalar<V>(42));
628 "Unexpected result type for broadcast<V>()");
633 auto vec = broadcast<V>(42);
635 "Unexpected result type for broadcast<V>()");
640 auto vec = broadcast<V>(42.0);
642 "Unexpected result type for broadcast<V>()");
648 void checkBracedAssign()
651 { V
ref = make123<V>(); V vec; vec = {
ref};
653 {
const V
ref = make123<V>(); V vec; vec = {
ref};
661 void checkBracedBroadcastAssign()
680#define DUNE_SIMD_POSTFIX_OP(NAME, SYMBOL) \
681 struct OpPostfix##NAME \
684 auto operator()(V&& v) const \
685 -> decltype(std::forward<V>(v) SYMBOL) \
687 return std::forward<V>(v) SYMBOL; \
691#define DUNE_SIMD_PREFIX_OP(NAME, SYMBOL) \
692 struct OpPrefix##NAME \
695 auto operator()(V&& v) const \
696 -> decltype(SYMBOL std::forward<V>(v)) \
698 return SYMBOL std::forward<V>(v); \
716#pragma GCC diagnostic push
717#pragma GCC diagnostic ignored "-Wpragmas"
718#pragma GCC diagnostic ignored "-Wunknown-warning-option"
719#pragma GCC diagnostic ignored "-Wbool-operation"
721#pragma GCC diagnostic pop
723#undef DUNE_SIMD_POSTFIX_OP
724#undef DUNE_SIMD_PREFIX_OP
726 template<
class V,
class Op>
728 CanCall<Op(decltype(lane(0, std::declval<V>())))>::value>
731#define DUNE_SIMD_OPNAME (className<Op(V)>())
733 auto val = leftVector<std::decay_t<V>>();
737 auto &&result = op(
static_cast<V
>(arg));
756 ==
static_cast<T
>(op(
lane(l,
static_cast<V
>(val)))));
760 for(
std::size_t l = 0; l < lanes<std::decay_t<V> >(); ++l)
762#undef DUNE_SIMD_OPNAME
765 template<
class V,
class Op>
767 !CanCall<Op(decltype(lane(0, std::declval<V>())))>::value>
775 template<
class V,
class Op>
776 void checkUnaryOpsV(Op op)
778 checkUnaryOpV<V&>(op);
779 checkUnaryOpV<const V&>(op);
780 checkUnaryOpV<V&&>(op);
794#define DUNE_SIMD_INFIX_OP(NAME, SYMBOL) \
795 struct OpInfix##NAME \
797 template<class V1, class V2> \
798 decltype(auto) operator()(V1&& v1, V2&& v2) const \
800 return std::forward<V1>(v1) SYMBOL std::forward<V2>(v2); \
802 template<class S1, class S2> \
803 auto scalar(S1&& s1, S2&& s2) const \
804 -> decltype(std::forward<S1>(s1) SYMBOL std::forward<S2>(s2)); \
815#define DUNE_SIMD_ASSIGN_OP(NAME, SYMBOL) \
816 struct OpInfix##NAME \
818 template<class V1, class V2> \
819 decltype(auto) operator()(V1&& v1, V2&& v2) const \
821 return std::forward<V1>(v1) SYMBOL std::forward<V2>(v2); \
823 template<class S1, class S2> \
824 auto scalar(S1& s1, S2&& s2) const \
825 -> decltype(s1 SYMBOL std::forward<S2>(s2)); \
828#define DUNE_SIMD_REPL_OP(NAME, REPLFN, SYMBOL) \
829 struct OpInfix##NAME \
831 template<class V1, class V2> \
832 decltype(auto) operator()(V1&& v1, V2&& v2) const \
834 return Simd::REPLFN(std::forward<V1>(v1), std::forward<V2>(v2)); \
836 template<class S1, class S2> \
837 auto scalar(S1&& s1, S2&& s2) const \
838 -> decltype(std::forward<S1>(s1) SYMBOL std::forward<S2>(s2)); \
880#undef DUNE_SIMD_INFIX_OP
881#undef DUNE_SIMD_REPL_OP
882#undef DUNE_SIMD_ASSIGN_OP
885 struct OpInfixComma {};
887 template<
class T1,
class T2>
891#define DUNE_SIMD_OPNAME (className<OpInfixComma(T1, T2)>())
893 std::declval<T2>())), T2>::value,
894 "Type and value category of the comma operator must "
895 "match that of the second operand");
904#pragma GCC diagnostic push
905#pragma GCC diagnostic ignored "-Wunused-value"
906 auto &&result = (
static_cast<T1
>(arg1),
907 static_cast<T2
>(arg2));
908#pragma GCC diagnostic pop
928#undef DUNE_SIMD_OPNAME
953 template<
class V1,
class V2,
class Op>
955 checkBinaryOpVV(MetaType<V1>, MetaType<V2>, Op op)
957#define DUNE_SIMD_OPNAME (className<Op(V1, V2)>())
959 "Internal testsystem error: called with two types that "
960 "don't decay to the same thing");
963 auto vref1 = leftVector<std::decay_t<V1>>();
964 auto vref2 = rightVector<std::decay_t<V2>>();
971 auto &&vopres = op(
static_cast<V1
>(vop1),
static_cast<V2
>(vop2));
972 using VR =
decltype(vopres);
976 "The result must have the same number of lanes as the "
981 using T = Scalar<std::decay_t<VR> >;
988 ==
static_cast<T
>(op(
lane(l,
static_cast<V1
>(vref1)),
989 lane(l,
static_cast<V2
>(vref2)))));
1000#undef DUNE_SIMD_OPNAME
1003 template<
class V1,
class V2,
class Op>
1005 checkBinaryOpVV(MetaType<V1>, MetaType<V2>, Op op)
1013 template<
class V1,
class V2>
1014 void checkBinaryOpVV(MetaType<V1>, MetaType<V2>, OpInfixComma)
1017 "Internal testsystem error: called with two types that "
1018 "don't decay to the same thing");
1057 template<
class V1,
class T2,
class Op>
1059 checkBinaryOpVS(MetaType<V1>, MetaType<T2>, Op op)
1061#define DUNE_SIMD_OPNAME (className<Op(V1, T2)>())
1064 "Internal testsystem error: called with a scalar that "
1065 "does not match the vector type.");
1068 auto sinit2 = rightScalar<std::decay_t<T2>>();
1071 auto vref1 = leftVector<std::decay_t<V1>>();
1072 auto sref2 = sinit2;
1079 auto &&vopres = op(
static_cast<V1
>(vop1),
static_cast<T2
>(sop2));
1080 using VR =
decltype(vopres);
1084 "The result must have the same number of lanes as the "
1099 ==
static_cast<T
>(op(
lane(l,
static_cast<V1
>(vref1)),
1100 static_cast<T2
>(sref2) )));
1109#undef DUNE_SIMD_OPNAME
1112 template<
class V1,
class T2,
class Op>
1114 checkBinaryOpVS(MetaType<V1>, MetaType<T2>, Op op)
1122 template<
class V1,
class T2>
1123 void checkBinaryOpVS(MetaType<V1>, MetaType<T2>, OpInfixComma)
1127 "Internal testsystem error: called with a scalar that "
1128 "does not match the vector type.");
1160 template<
class V1,
class T2,
class Op>
1162 checkBinaryOpVVAgainstVS(MetaType<V1>, MetaType<T2>, Op op)
1164#define DUNE_SIMD_OPNAME (className<Op(V1, T2)>())
1167 "Internal testsystem error: called with a scalar that "
1168 "does not match the vector type.");
1171 auto sinit2 = rightScalar<std::decay_t<T2>>();
1174 auto vop1 = leftVector<std::decay_t<V1>>();
1175 using V2 = CopyRefQual<V1, T2>;
1179 op(
static_cast<V1
>(vop1),
static_cast<V2
>(vop2));
1185#undef DUNE_SIMD_OPNAME
1188 template<
class V1,
class T2,
class Op>
1190 checkBinaryOpVVAgainstVS(MetaType<V1>, MetaType<T2>, Op op)
1198 template<
class V1,
class T2>
1199 void checkBinaryOpVVAgainstVS(MetaType<V1>, MetaType<T2>, OpInfixComma)
1236 template<
class V1,
class V2,
class Op>
1238 checkBinaryOpVP(MetaType<V1>, MetaType<V2>, Op op)
1240 using P2 =
decltype(
lane(0, std::declval<V2>()));
1241 using T2 = CopyRefQual<Scalar<V2>, V2>;
1242#define DUNE_SIMD_OPNAME (className<Op(V1, P2)>())
1244 "Internal testsystem error: called with two vector "
1245 "types whose scalar types don't match.");
1248 auto sinit2 = rightScalar<Scalar<V2>>();
1251 auto vref1 = leftVector<std::decay_t<V1>>();
1252 auto sref2 = sinit2;
1257 lane(0, vop2) = sref2;
1261 op(
static_cast<V1
>(vop1),
lane(0,
static_cast<V2
>(vop2)));
1262 using VR =
decltype(vopres);
1266 "The result must have the same number of lanes as the "
1273 using T =
Scalar<
decltype(vopres)>;
1281 ==
static_cast<T
>(op(
lane(l,
static_cast<V1
>(vref1)),
1282 static_cast<T2
>(sref2) )));
1291#undef DUNE_SIMD_OPNAME
1294 template<
class V1,
class V2,
class Op>
1296 checkBinaryOpVP(MetaType<V1>, MetaType<V2>, Op op)
1304 template<
class V1,
class V2>
1305 void checkBinaryOpVP(MetaType<V1>, MetaType<V2>, OpInfixComma)
1316 struct OpInfixSwappedArgs
1320 template<
class V1,
class V2>
1321 decltype(
auto)
operator()(V1&& v1, V2&& v2)
const
1323 return orig(std::forward<V2>(v2), std::forward<V1>(v1));
1325 template<
class S1,
class S2>
1326 auto scalar(S1&& s1, S2&& s2)
const
1327 ->
decltype(orig.scalar(std::forward<S2>(s2), std::forward<S1>(s1)));
1330 template<
class T1,
class V2,
class Op>
1331 void checkBinaryOpSV(MetaType<T1> t1, MetaType<V2> v2, Op op)
1333 checkBinaryOpVS(v2, t1, OpInfixSwappedArgs<Op>{op});
1336 template<
class T1,
class V2>
1337 void checkBinaryOpSV(MetaType<T1>, MetaType<V2>, OpInfixComma)
1340 Scalar<std::decay_t<V2> > >::value,
1341 "Internal testsystem error: called with a scalar that "
1342 "does not match the vector type.");
1348 template<
class V1,
class V2,
class Op>
1349 void checkBinaryOpPV(MetaType<V1> v1, MetaType<V2> v2, Op op)
1351 checkBinaryOpVP(v2, v1, OpInfixSwappedArgs<Op>{op});
1354 template<
class V1,
class V2>
1355 void checkBinaryOpPV(MetaType<V1>, MetaType<V2>, OpInfixComma)
1386 template<
class T1,
class V2,
class Op>
1387 void checkBinaryOpVVAgainstSV(MetaType<T1> t1, MetaType<V2> v2, Op op)
1389 checkBinaryOpVVAgainstVS(v2, t1, OpInfixSwappedArgs<Op>{op});
1392 template<
class V1,
class T2>
1393 void checkBinaryOpVVAgainstSV(MetaType<V1>, MetaType<T2>, OpInfixComma)
1401 template<
class T1,
class T2,
bool condition,
class Checker>
1402 void checkBinaryRefQual(Checker checker)
1404 if constexpr (condition) {
1413 template<
class V,
class Checker>
1416 using Std::bool_constant;
1420 constexpr bool do_ =
false;
1421 constexpr bool do_SV =
true;
1422 constexpr bool do_VV =
true;
1423 constexpr bool do_VS =
true;
1425#define DUNE_SIMD_DO(M1, M2, M3, V1, V2, V3, NAME) \
1426 checker(bool_constant<isMask ? do_##M1 : do_##V1>{}, \
1427 bool_constant<isMask ? do_##M2 : do_##V2>{}, \
1428 bool_constant<isMask ? do_##M3 : do_##V3>{}, \
1481 void checkAutoCopy()
1483 using RValueResult =
decltype(
autoCopy(
lane(0, std::declval<V>())));
1485 "Result of autoCopy() must always be Scalar<V>");
1487 using MutableLValueResult =
1490 "Result of autoCopy() must always be Scalar<V>");
1492 using ConstLValueResult =
1495 "Result of autoCopy() must always be Scalar<V>");
1497 V vec = make123<V>();
1504 void checkBoolReductions()
1546 auto mixedVec = broadcast<M>(0);
1548 lane(l, mixedVec) = (l % 2);
1552 (
allTrue (
static_cast<M&
>(mixedVec)) ==
false);
1554 (
anyTrue (
static_cast<M&
>(mixedVec)) == (lanes<M>() > 1));
1556 (
allFalse(
static_cast<M&
>(mixedVec)) == (lanes<M>() == 1));
1558 (
anyFalse(
static_cast<M&
>(mixedVec)) ==
true);
1562 (
allTrue (
static_cast<const M&
>(mixedVec)) ==
false);
1564 (
anyTrue (
static_cast<const M&
>(mixedVec)) == (lanes<M>() > 1));
1566 (
allFalse(
static_cast<const M&
>(mixedVec)) == (lanes<M>() == 1));
1568 (
anyFalse(
static_cast<const M&
>(mixedVec)) ==
true);
1584 std::declval<V>())), V>::value,
1585 "The result of cond(M, V, V) should have exactly the type V");
1589 std::declval<const V&>(),
1590 std::declval<const V&>())), V>::value,
1591 "The result of cond(const M&, const V&, const V&) should have "
1592 "exactly the type V");
1596 std::declval<V&>())), V>::value,
1597 "The result of cond(M&, V&, V&) should have exactly the type V");
1599 V vec1 = leftVector<V>();
1600 V vec2 = rightVector<V>();
1605 auto mixedResult = broadcast<V>(0);
1606 auto mixedMask = broadcast<M>(
false);
1609 lane(l, mixedMask ) = (l % 2);
1610 lane(l, mixedResult) =
lane(l, (l % 2) ? vec1 : vec2);
1617 void checkBoolCond()
1621 std::declval<V>())), V>::value,
1622 "The result of cond(bool, V, V) should have exactly the type V");
1626 std::declval<const V&>(),
1627 std::declval<const V&>())), V>::value,
1628 "The result of cond(const bool&, const V&, const V&) should have "
1629 "exactly the type V");
1634 std::declval<V&>())), V>::value,
1635 "The result of cond(bool&, V&, V&) should have exactly the type V");
1637 V vec1 = leftVector<V>();
1638 V vec2 = rightVector<V>();
1646 checkHorizontalMinMax() {}
1650 checkHorizontalMinMax()
1654 "The result of max(V) should be exactly Scalar<V>");
1658 "The result of min(V) should be exactly Scalar<V>");
1662 "The result of max(V) should be exactly Scalar<V>");
1666 "The result of min(V) should be exactly Scalar<V>");
1668 const V vec1 = leftVector<V>();
1676 checkBinaryMinMax() {}
1687 std::declval<V>())), V>::value,
1688 "The result of Simd::max(V, V) should be exactly V");
1691 std::declval<V>())), V>::value,
1692 "The result of Simd::min(V, V) should be exactly V");
1696 std::declval<V&>())), V>::value,
1697 "The result of Simd::max(V&, V&) should be exactly V");
1700 std::declval<V&>())), V>::value,
1701 "The result of Simd::min(V&, V&) should be exactly V");
1703 const V arg1 = leftVector<V>();
1704 const V arg2 = rightVector<V>();
1706 V maxExp(Scalar<V>(0)), minExp(Scalar<V>(0));
1720 const V vec1 = leftVector<V>();
1724 const char *sep =
"";
1728 stream <<
lane(l, vec1);
1731 reference += stream.
str();
1739 if(
lanes(vec1) == 1)
1747 stream <<
vio(vec1);
1752#undef DUNE_SIMD_CHECK
1846 template<
class V,
class Rebinds,
1847 template<
class>
class RebindPrune =
IsLoop,
1851 if(seen_.
emplace(typeid (V)).second ==
false)
1859 auto recurse = [
this](
auto w) {
1860 using W =
typename decltype(w)::type;
1861 this->
template check<W, Rebinds, RebindPrune, RebindAccept>();
1863 checkRebindOf<V, Rebinds, RebindPrune, RebindAccept>(recurse);