packetmath: fix signed-integer-overflow UB in test reference helpers
libeigen/eigen!2671 Co-authored-by: Rasmus Munk Larsen <rmlarsen@gmail.com>
This commit is contained in:
co-authored by
Rasmus Munk Larsen
parent
cf9f1075a6
commit
b4e88b90d9
+16
-15
@@ -30,13 +30,16 @@ inline T REF_SUB(const T& a, const T& b) {
|
||||
using UnsignedT = std::make_unsigned_t<T>;
|
||||
return static_cast<T>(static_cast<UnsignedT>(a) - static_cast<UnsignedT>(b));
|
||||
}
|
||||
template <typename T, std::enable_if_t<!NumTraits<T>::IsInteger || !NumTraits<T>::IsSigned, int> = 0>
|
||||
template <typename T, std::enable_if_t<!NumTraits<T>::IsInteger || std::is_same<T, bool>::value, int> = 0>
|
||||
inline T REF_MUL(const T& a, const T& b) {
|
||||
return a * b;
|
||||
}
|
||||
template <typename T, std::enable_if_t<NumTraits<T>::IsInteger && NumTraits<T>::IsSigned, int> = 0>
|
||||
template <typename T, std::enable_if_t<NumTraits<T>::IsInteger && !std::is_same<T, bool>::value, int> = 0>
|
||||
inline T REF_MUL(const T& a, const T& b) {
|
||||
using UnsignedT = std::make_unsigned_t<T>;
|
||||
// Evaluate in an unsigned type at least as wide as int so that sub-int
|
||||
// operands are not promoted back to signed int (whose product can overflow);
|
||||
// the result then wraps modulo 2^bits just like pmul.
|
||||
using UnsignedT = std::common_type_t<std::make_unsigned_t<T>, unsigned>;
|
||||
return static_cast<T>(static_cast<UnsignedT>(a) * static_cast<UnsignedT>(b));
|
||||
}
|
||||
|
||||
@@ -57,8 +60,11 @@ struct madd_impl {
|
||||
};
|
||||
|
||||
template <typename Scalar>
|
||||
struct madd_impl<Scalar, std::enable_if_t<NumTraits<Scalar>::IsInteger && NumTraits<Scalar>::IsSigned>> {
|
||||
using UnsignedScalar = std::make_unsigned_t<Scalar>;
|
||||
struct madd_impl<Scalar, std::enable_if_t<NumTraits<Scalar>::IsInteger && !std::is_same<Scalar, bool>::value>> {
|
||||
// Unsigned type at least as wide as int, so sub-int operands are not promoted
|
||||
// back to signed int (whose products/sums can overflow); results wrap modulo
|
||||
// 2^bits like the packet madd/msub ops.
|
||||
using UnsignedScalar = std::common_type_t<std::make_unsigned_t<Scalar>, unsigned>;
|
||||
|
||||
static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE Scalar madd(const Scalar& a, const Scalar& b, const Scalar& c) {
|
||||
return static_cast<Scalar>(static_cast<UnsignedScalar>(a) * static_cast<UnsignedScalar>(b) +
|
||||
@@ -721,25 +727,20 @@ void packetmath() {
|
||||
}
|
||||
}
|
||||
|
||||
// C4804: unsafe use of type 'bool' in operation. Unavoidable when Scalar=bool.
|
||||
#if EIGEN_COMP_MSVC
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4804)
|
||||
#endif
|
||||
// REF_ADD folds with defined wraparound for signed integers (matching predux,
|
||||
// which wraps mod 2^N) and with || for bool, avoiding both signed-overflow UB
|
||||
// and the MSVC C4804 "unsafe use of bool" warning that raw operator+ triggers.
|
||||
ref[0] = Scalar(0);
|
||||
for (int i = 0; i < PacketSize; ++i) ref[0] += data1[i];
|
||||
for (int i = 0; i < PacketSize; ++i) ref[0] = REF_ADD(ref[0], data1[i]);
|
||||
VERIFY(test::isApproxAbs(ref[0], internal::predux(internal::pload<Packet>(data1)), refvalue) && "internal::predux");
|
||||
|
||||
if (!std::is_same<Packet, typename internal::unpacket_traits<Packet>::half>::value) {
|
||||
int HalfPacketSize = PacketSize > 4 ? PacketSize / 2 : PacketSize;
|
||||
for (int i = 0; i < HalfPacketSize; ++i) ref[i] = Scalar(0);
|
||||
for (int i = 0; i < PacketSize; ++i) ref[i % HalfPacketSize] += data1[i];
|
||||
for (int i = 0; i < PacketSize; ++i) ref[i % HalfPacketSize] = REF_ADD(ref[i % HalfPacketSize], data1[i]);
|
||||
internal::pstore(data2, internal::predux_half(internal::pload<Packet>(data1)));
|
||||
VERIFY(test::areApprox(ref, data2, HalfPacketSize) && "internal::predux_half");
|
||||
}
|
||||
#if EIGEN_COMP_MSVC
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
// Avoid overflows.
|
||||
if (NumTraits<Scalar>::IsInteger && NumTraits<Scalar>::IsSigned &&
|
||||
|
||||
Reference in New Issue
Block a user