176 void complain(
const char *file,
int line,
const char *func,
179 void complain(
const char *file,
int line,
const char *func,
186#define DUNE_SIMD_CHECK(expr) \
187 ((expr) ? void() : complain(__FILE__, __LINE__, __func__, #expr))
191#define DUNE_SIMD_CHECK_OP(expr) \
192 ((expr) ? void() : complain(__FILE__, __LINE__, __func__, \
193 DUNE_SIMD_OPNAME, #expr))
199 return std::forward<T>(t);
204 static bool is42(
const V &v)
210 good &= (
lane(l, v) == Scalar<V>(42));
225 lane(l, vec) = l + 1;
231 static bool is123(
const V &v)
237 good &= (
lane(l, v) == Scalar<V>(l+1));
243 static V leftVector()
249 lane(l, res) = Scalar<V>(l+1);
254 static V rightVector()
262 lane(l, res) = Scalar<V>((l)%7+1);
267 static T leftScalar()
273 static T rightScalar()
280 template<
class Dst,
class Src>
281 using CopyRefQual = Impl::CopyRefQual<Dst, Src>;
288 template<
class Op,
class... Vectors>
290 decltype(std::declval<Op>().
306 "must not be references, and must not include "
308 [[maybe_unused]] T a{};
312 [[deprecated(
"Warning: please include bool in the Rebinds for "
313 "simd type V, as Masks are not checked otherwise.")]]
318 template<
class V,
class Rebinds,
template<
class>
class RebindPrune,
319 template<
class>
class RebindAccept,
class Recurse>
320 void checkRebindOf(Recurse recurse)
323 using T =
typename decltype(target)::type;
326 using W = Rebind<T, V>;
327 log_ <<
"Type " << className<V>() <<
" rebound to "
328 << className<T>() <<
" is " << className<W>() <<
std::endl;
331 "types must not be references, and must not include "
333 static_assert(lanes<V>() == lanes<W>(),
"Rebound types must have "
334 "the same number of lanes as the original vector "
337 "must have the bound-to scalar type");
339 if constexpr (RebindPrune<W>{}) {
340 log_ <<
"Pruning check of Simd type " << className<W>()
344 using Impl::debugTypes;
345 static_assert(debugTypes<T, V, W>(RebindAccept<W>{}),
346 "Rebind<T, V> is W, but that is not accepted "
348 recurse(MetaType<W>{});
353 "rebound to its own scalar type must be the same type "
354 "as the original type");
356 "rebound to bool must be the mask type for that type");
358 constexpr bool hasBool = Impl::TypeInList<bool, Rebinds>::value;
372 "return type of lanes<V>() should be std::size_t");
374 "return type of lanes(V{}) should be std::size_t");
377 [[maybe_unused]]
constexpr auto size = lanes<V>();
383 void checkDefaultConstruct()
385 { [[maybe_unused]] V vec; }
386 { [[maybe_unused]] V vec{}; }
387 { [[maybe_unused]] V vec = {}; }
398 lane(l, vec) = l + 1;
401 using MLRes =
decltype(
lane(0, vec));
404 "Result of lane() on a mutable lvalue vector must "
405 "either be a mutable reference to a scalar of that "
406 "vector or a proxy object (which itself may not be a "
407 "reference nor const).");
413 using CLRes =
decltype(
lane(0, vec2));
416 "Result of lane() on a const lvalue vector must "
417 "either be a const lvalue reference to a scalar of that "
418 "vector or a proxy object (which itself may not be a "
419 "reference nor const).");
421 "Result of lane() on a const lvalue vector must not be "
422 "assignable from a scalar.");
427 using RRes =
decltype(
lane(0, prvalue(vec)));
438 "Result of lane() on a rvalue vector V must be "
439 "Scalar<V> or Scalar<V>&&.");
450 void checkCopyMoveConstruct()
459 { V
ref(make123<V>()); V vec (ref);
461 { V
ref(make123<V>()); V vec =
ref ;
463 { V
ref(make123<V>()); V vec {
ref};
465 { V
ref(make123<V>()); V vec = {
ref};
467 {
const V
ref(make123<V>()); V vec (ref);
469 {
const V
ref(make123<V>()); V vec =
ref ;
471 {
const V
ref(make123<V>()); V vec {
ref};
473 {
const V
ref(make123<V>()); V vec = {
ref};
477 { V
ref(make123<V>()); V vec (std::move(ref));
479 { V
ref(make123<V>()); V vec = std::move(ref) ;
481 { V
ref(make123<V>()); V vec {std::move(ref)};
483 { V
ref(make123<V>()); V vec = {std::move(ref)};
488 void checkBroadcastVectorConstruct()
491 { Scalar<V>
ref = 42; V vec (ref);
493 { Scalar<V>
ref = 42; V vec =
ref ;
499 {
const Scalar<V>
ref = 42; V vec (ref);
501 {
const Scalar<V>
ref = 42; V vec =
ref ;
509 { Scalar<V>
ref = 42; V vec (std::move(ref));
511 { Scalar<V>
ref = 42; V vec = std::move(ref) ;
520 void checkBroadcastMaskConstruct()
523 { Scalar<V>
ref = 42; V vec (ref);
527 { Scalar<V>
ref = 42; V vec {
ref};
531 {
const Scalar<V>
ref = 42; V vec (ref);
535 {
const Scalar<V>
ref = 42; V vec {
ref};
541 { Scalar<V>
ref = 42; V vec (std::move(ref));
545 { Scalar<V>
ref = 42; V vec {std::move(ref)};
552 template<
class FromV,
class ToV>
556 FromV fromVec = make123<FromV>();
557 auto toVec = implCast<ToV>(fromVec);
558 static_assert(
std::is_same<
decltype(toVec), ToV>::value,
559 "Unexpected result type for implCast<ToV>(FromV&)");
565 const FromV fromVec = make123<FromV>();
566 auto toVec = implCast<ToV>(fromVec);
567 static_assert(
std::is_same<
decltype(toVec), ToV>::value,
568 "Unexpected result type for implCast<ToV>(const "
574 auto toVec = implCast<ToV>(make123<FromV>());
575 static_assert(
std::is_same<
decltype(toVec), ToV>::value,
576 "Unexpected result type for implCast<ToV>(FromV&&)");
588 checkImplCast<V, V>();
589 checkImplCast<V, LoopV>();
590 checkImplCast<LoopV, V>();
595 void checkBroadcast()
600 auto vec = broadcast<V>(ref);
602 "Unexpected result type for broadcast<V>()");
608 const Scalar<V>
ref = 42;
609 auto vec = broadcast<V>(ref);
611 "Unexpected result type for broadcast<V>()");
616 auto vec = broadcast<V>(Scalar<V>(42));
618 "Unexpected result type for broadcast<V>()");
623 auto vec = broadcast<V>(42);
625 "Unexpected result type for broadcast<V>()");
630 auto vec = broadcast<V>(42.0);
632 "Unexpected result type for broadcast<V>()");
638 void checkBracedAssign()
641 { V
ref = make123<V>(); V vec; vec = {
ref};
643 {
const V
ref = make123<V>(); V vec; vec = {
ref};
651 void checkBracedBroadcastAssign()
670#define DUNE_SIMD_POSTFIX_OP(NAME, SYMBOL) \
671 struct OpPostfix##NAME \
674 auto operator()(V&& v) const \
675 -> decltype(std::forward<V>(v) SYMBOL) \
677 return std::forward<V>(v) SYMBOL; \
681#define DUNE_SIMD_PREFIX_OP(NAME, SYMBOL) \
682 struct OpPrefix##NAME \
685 auto operator()(V&& v) const \
686 -> decltype(SYMBOL std::forward<V>(v)) \
688 return SYMBOL std::forward<V>(v); \
706#pragma GCC diagnostic push
707#pragma GCC diagnostic ignored "-Wpragmas"
708#pragma GCC diagnostic ignored "-Wunknown-warning-option"
709#pragma GCC diagnostic ignored "-Wbool-operation"
711#pragma GCC diagnostic pop
713#undef DUNE_SIMD_POSTFIX_OP
714#undef DUNE_SIMD_PREFIX_OP
716 template<
class V,
class Op>
718 IsCallable<Op(decltype(lane(0, std::declval<V>())))>::value>
721#define DUNE_SIMD_OPNAME (className<Op(V)>())
723 auto val = leftVector<std::decay_t<V>>();
727 auto &&result = op(
static_cast<V
>(arg));
746 ==
static_cast<T
>(op(
lane(l,
static_cast<V
>(val)))));
750 for(
std::size_t l = 0; l < lanes<std::decay_t<V> >(); ++l)
752#undef DUNE_SIMD_OPNAME
755 template<
class V,
class Op>
757 not IsCallable<Op(decltype(lane(0, std::declval<V>())))>::value>
765 template<
class V,
class Op>
766 void checkUnaryOpsV(Op op)
768 checkUnaryOpV<V&>(op);
769 checkUnaryOpV<const V&>(op);
770 checkUnaryOpV<V&&>(op);
784#define DUNE_SIMD_INFIX_OP(NAME, SYMBOL) \
785 struct OpInfix##NAME \
787 template<class V1, class V2> \
788 decltype(auto) operator()(V1&& v1, V2&& v2) const \
790 return std::forward<V1>(v1) SYMBOL std::forward<V2>(v2); \
792 template<class S1, class S2> \
793 auto scalar(S1&& s1, S2&& s2) const \
794 -> decltype(std::forward<S1>(s1) SYMBOL std::forward<S2>(s2)); \
805#define DUNE_SIMD_ASSIGN_OP(NAME, SYMBOL) \
806 struct OpInfix##NAME \
808 template<class V1, class V2> \
809 decltype(auto) operator()(V1&& v1, V2&& v2) const \
811 return std::forward<V1>(v1) SYMBOL std::forward<V2>(v2); \
813 template<class S1, class S2> \
814 auto scalar(S1& s1, S2&& s2) const \
815 -> decltype(s1 SYMBOL std::forward<S2>(s2)); \
818#define DUNE_SIMD_REPL_OP(NAME, REPLFN, SYMBOL) \
819 struct OpInfix##NAME \
821 template<class V1, class V2> \
822 decltype(auto) operator()(V1&& v1, V2&& v2) const \
824 return Simd::REPLFN(std::forward<V1>(v1), std::forward<V2>(v2)); \
826 template<class S1, class S2> \
827 auto scalar(S1&& s1, S2&& s2) const \
828 -> decltype(std::forward<S1>(s1) SYMBOL std::forward<S2>(s2)); \
870#undef DUNE_SIMD_INFIX_OP
871#undef DUNE_SIMD_REPL_OP
872#undef DUNE_SIMD_ASSIGN_OP
875 struct OpInfixComma {};
877 template<
class T1,
class T2>
881#define DUNE_SIMD_OPNAME (className<OpInfixComma(T1, T2)>())
883 std::declval<T2>())), T2>::value,
884 "Type and value category of the comma operator must "
885 "match that of the second operand");
894#pragma GCC diagnostic push
895#pragma GCC diagnostic ignored "-Wunused-value"
896 auto &&result = (
static_cast<T1
>(arg1),
897 static_cast<T2
>(arg2));
898#pragma GCC diagnostic pop
918#undef DUNE_SIMD_OPNAME
943 template<
class V1,
class V2,
class Op>
945 checkBinaryOpVV(MetaType<V1>, MetaType<V2>, Op op)
947#define DUNE_SIMD_OPNAME (className<Op(V1, V2)>())
949 "Internal testsystem error: called with two types that "
950 "don't decay to the same thing");
953 auto vref1 = leftVector<std::decay_t<V1>>();
954 auto vref2 = rightVector<std::decay_t<V2>>();
961 auto &&vopres = op(
static_cast<V1
>(vop1),
static_cast<V2
>(vop2));
962 using VR =
decltype(vopres);
966 "The result must have the same number of lanes as the "
971 using T = Scalar<std::decay_t<VR> >;
978 ==
static_cast<T
>(op(
lane(l,
static_cast<V1
>(vref1)),
979 lane(l,
static_cast<V2
>(vref2)))));
990#undef DUNE_SIMD_OPNAME
993 template<
class V1,
class V2,
class Op>
995 checkBinaryOpVV(MetaType<V1>, MetaType<V2>, Op op)
1003 template<
class V1,
class V2>
1004 void checkBinaryOpVV(MetaType<V1>, MetaType<V2>, OpInfixComma)
1007 "Internal testsystem error: called with two types that "
1008 "don't decay to the same thing");
1047 template<
class V1,
class T2,
class Op>
1049 checkBinaryOpVS(MetaType<V1>, MetaType<T2>, Op op)
1051#define DUNE_SIMD_OPNAME (className<Op(V1, T2)>())
1054 "Internal testsystem error: called with a scalar that "
1055 "does not match the vector type.");
1058 auto sinit2 = rightScalar<std::decay_t<T2>>();
1061 auto vref1 = leftVector<std::decay_t<V1>>();
1062 auto sref2 = sinit2;
1069 auto &&vopres = op(
static_cast<V1
>(vop1),
static_cast<T2
>(sop2));
1070 using VR =
decltype(vopres);
1074 "The result must have the same number of lanes as the "
1089 ==
static_cast<T
>(op(
lane(l,
static_cast<V1
>(vref1)),
1090 static_cast<T2
>(sref2) )));
1099#undef DUNE_SIMD_OPNAME
1102 template<
class V1,
class T2,
class Op>
1104 checkBinaryOpVS(MetaType<V1>, MetaType<T2>, Op op)
1112 template<
class V1,
class T2>
1113 void checkBinaryOpVS(MetaType<V1>, MetaType<T2>, OpInfixComma)
1117 "Internal testsystem error: called with a scalar that "
1118 "does not match the vector type.");
1150 template<
class V1,
class T2,
class Op>
1152 checkBinaryOpVVAgainstVS(MetaType<V1>, MetaType<T2>, Op op)
1154#define DUNE_SIMD_OPNAME (className<Op(V1, T2)>())
1157 "Internal testsystem error: called with a scalar that "
1158 "does not match the vector type.");
1161 auto sinit2 = rightScalar<std::decay_t<T2>>();
1164 auto vop1 = leftVector<std::decay_t<V1>>();
1165 using V2 = CopyRefQual<V1, T2>;
1169 op(
static_cast<V1
>(vop1),
static_cast<V2
>(vop2));
1175#undef DUNE_SIMD_OPNAME
1178 template<
class V1,
class T2,
class Op>
1180 checkBinaryOpVVAgainstVS(MetaType<V1>, MetaType<T2>, Op op)
1188 template<
class V1,
class T2>
1189 void checkBinaryOpVVAgainstVS(MetaType<V1>, MetaType<T2>, OpInfixComma)
1226 template<
class V1,
class V2,
class Op>
1228 checkBinaryOpVP(MetaType<V1>, MetaType<V2>, Op op)
1230 using P2 =
decltype(
lane(0, std::declval<V2>()));
1231 using T2 = CopyRefQual<Scalar<V2>, V2>;
1232#define DUNE_SIMD_OPNAME (className<Op(V1, P2)>())
1234 "Internal testsystem error: called with two vector "
1235 "types whose scalar types don't match.");
1238 auto sinit2 = rightScalar<Scalar<V2>>();
1241 auto vref1 = leftVector<std::decay_t<V1>>();
1242 auto sref2 = sinit2;
1247 lane(0, vop2) = sref2;
1251 op(
static_cast<V1
>(vop1),
lane(0,
static_cast<V2
>(vop2)));
1252 using VR =
decltype(vopres);
1256 "The result must have the same number of lanes as the "
1263 using T =
Scalar<
decltype(vopres)>;
1271 ==
static_cast<T
>(op(
lane(l,
static_cast<V1
>(vref1)),
1272 static_cast<T2
>(sref2) )));
1281#undef DUNE_SIMD_OPNAME
1284 template<
class V1,
class V2,
class Op>
1286 checkBinaryOpVP(MetaType<V1>, MetaType<V2>, Op op)
1294 template<
class V1,
class V2>
1295 void checkBinaryOpVP(MetaType<V1>, MetaType<V2>, OpInfixComma)
1306 struct OpInfixSwappedArgs
1310 template<
class V1,
class V2>
1311 decltype(
auto)
operator()(V1&& v1, V2&& v2)
const
1313 return orig(std::forward<V2>(v2), std::forward<V1>(v1));
1315 template<
class S1,
class S2>
1316 auto scalar(S1&& s1, S2&& s2)
const
1317 ->
decltype(orig.scalar(std::forward<S2>(s2), std::forward<S1>(s1)));
1320 template<
class T1,
class V2,
class Op>
1321 void checkBinaryOpSV(MetaType<T1> t1, MetaType<V2> v2, Op op)
1323 checkBinaryOpVS(v2, t1, OpInfixSwappedArgs<Op>{op});
1326 template<
class T1,
class V2>
1327 void checkBinaryOpSV(MetaType<T1>, MetaType<V2>, OpInfixComma)
1330 Scalar<std::decay_t<V2> > >::value,
1331 "Internal testsystem error: called with a scalar that "
1332 "does not match the vector type.");
1338 template<
class V1,
class V2,
class Op>
1339 void checkBinaryOpPV(MetaType<V1> v1, MetaType<V2> v2, Op op)
1341 checkBinaryOpVP(v2, v1, OpInfixSwappedArgs<Op>{op});
1344 template<
class V1,
class V2>
1345 void checkBinaryOpPV(MetaType<V1>, MetaType<V2>, OpInfixComma)
1376 template<
class T1,
class V2,
class Op>
1377 void checkBinaryOpVVAgainstSV(MetaType<T1> t1, MetaType<V2> v2, Op op)
1379 checkBinaryOpVVAgainstVS(v2, t1, OpInfixSwappedArgs<Op>{op});
1382 template<
class V1,
class T2>
1383 void checkBinaryOpVVAgainstSV(MetaType<V1>, MetaType<T2>, OpInfixComma)
1391 template<
class T1,
class T2,
bool condition,
class Checker>
1392 void checkBinaryRefQual(Checker checker)
1394 if constexpr (condition) {
1403 template<
class V,
class Checker>
1410 constexpr bool do_ =
false;
1411 constexpr bool do_SV =
true;
1412 constexpr bool do_VV =
true;
1413 constexpr bool do_VS =
true;
1415#define DUNE_SIMD_DO(M1, M2, M3, V1, V2, V3, NAME) \
1416 checker(bool_constant<isMask ? do_##M1 : do_##V1>{}, \
1417 bool_constant<isMask ? do_##M2 : do_##V2>{}, \
1418 bool_constant<isMask ? do_##M3 : do_##V3>{}, \
1471 void checkAutoCopy()
1473 using RValueResult =
decltype(
autoCopy(
lane(0, std::declval<V>())));
1475 "Result of autoCopy() must always be Scalar<V>");
1477 using MutableLValueResult =
1480 "Result of autoCopy() must always be Scalar<V>");
1482 using ConstLValueResult =
1485 "Result of autoCopy() must always be Scalar<V>");
1487 V vec = make123<V>();
1494 void checkBoolReductions()
1536 auto mixedVec = broadcast<M>(0);
1538 lane(l, mixedVec) = (l % 2);
1542 (
allTrue (
static_cast<M&
>(mixedVec)) ==
false);
1544 (
anyTrue (
static_cast<M&
>(mixedVec)) == (lanes<M>() > 1));
1546 (
allFalse(
static_cast<M&
>(mixedVec)) == (lanes<M>() == 1));
1548 (
anyFalse(
static_cast<M&
>(mixedVec)) ==
true);
1552 (
allTrue (
static_cast<const M&
>(mixedVec)) ==
false);
1554 (
anyTrue (
static_cast<const M&
>(mixedVec)) == (lanes<M>() > 1));
1556 (
allFalse(
static_cast<const M&
>(mixedVec)) == (lanes<M>() == 1));
1558 (
anyFalse(
static_cast<const M&
>(mixedVec)) ==
true);
1574 std::declval<V>())), V>::value,
1575 "The result of cond(M, V, V) should have exactly the type V");
1579 std::declval<const V&>(),
1580 std::declval<const V&>())), V>::value,
1581 "The result of cond(const M&, const V&, const V&) should have "
1582 "exactly the type V");
1586 std::declval<V&>())), V>::value,
1587 "The result of cond(M&, V&, V&) should have exactly the type V");
1589 V vec1 = leftVector<V>();
1590 V vec2 = rightVector<V>();
1595 auto mixedResult = broadcast<V>(0);
1596 auto mixedMask = broadcast<M>(
false);
1599 lane(l, mixedMask ) = (l % 2);
1600 lane(l, mixedResult) =
lane(l, (l % 2) ? vec1 : vec2);
1607 void checkBoolCond()
1611 std::declval<V>())), V>::value,
1612 "The result of cond(bool, V, V) should have exactly the type V");
1616 std::declval<const V&>(),
1617 std::declval<const V&>())), V>::value,
1618 "The result of cond(const bool&, const V&, const V&) should have "
1619 "exactly the type V");
1624 std::declval<V&>())), V>::value,
1625 "The result of cond(bool&, V&, V&) should have exactly the type V");
1627 V vec1 = leftVector<V>();
1628 V vec2 = rightVector<V>();
1636 checkHorizontalMinMax() {}
1640 checkHorizontalMinMax()
1644 "The result of max(V) should be exactly Scalar<V>");
1648 "The result of min(V) should be exactly Scalar<V>");
1652 "The result of max(V) should be exactly Scalar<V>");
1656 "The result of min(V) should be exactly Scalar<V>");
1658 const V vec1 = leftVector<V>();
1666 checkBinaryMinMax() {}
1677 std::declval<V>())), V>::value,
1678 "The result of Simd::max(V, V) should be exactly V");
1681 std::declval<V>())), V>::value,
1682 "The result of Simd::min(V, V) should be exactly V");
1686 std::declval<V&>())), V>::value,
1687 "The result of Simd::max(V&, V&) should be exactly V");
1690 std::declval<V&>())), V>::value,
1691 "The result of Simd::min(V&, V&) should be exactly V");
1693 const V arg1 = leftVector<V>();
1694 const V arg2 = rightVector<V>();
1696 V maxExp(Scalar<V>(0)), minExp(Scalar<V>(0));
1710 const V vec1 = leftVector<V>();
1714 const char *sep =
"";
1718 stream <<
lane(l, vec1);
1721 reference += stream.
str();
1729 if(
lanes(vec1) == 1)
1737 stream <<
vio(vec1);
1742#undef DUNE_SIMD_CHECK
1836 template<
class V,
class Rebinds,
1837 template<
class>
class RebindPrune =
IsLoop,
1841 if(seen_.
emplace(typeid (V)).second ==
false)
1849 auto recurse = [
this](
auto w) {
1850 using W =
typename decltype(w)::type;
1851 this->
template check<W, Rebinds, RebindPrune, RebindAccept>();
1853 checkRebindOf<V, Rebinds, RebindPrune, RebindAccept>(recurse);