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:
Rasmus Munk Larsen
2026-06-30 16:32:22 -07:00
co-authored by Rasmus Munk Larsen
parent cf9f1075a6
commit b4e88b90d9
+16 -15
View File
@@ -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 &&