summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThiago Macieira <thiago.macieira@intel.com>2020-08-11 18:24:34 -0700
committerThiago Macieira <thiago.macieira@intel.com>2020-09-29 14:55:48 -0700
commite4fece60aaef43e8a8139c16b276f76e22d3cc9d (patch)
tree827edc5e9e3f77625122eb6dc4a76477fd6ee072
parent589d37e155f3f6704e32d56d86dd3af164e0dbf7 (diff)
Add {add,sub,mul}_overflow detection when one operand is a constant
We were missing 64-bit signed mul_overflow on 32-bit platforms and in those where we did have it, the detection was awful (both for signed and for unsigned). So if one of the parameters is a constant, we can simplify the code generated. Change-Id: Ia99afccf0c474e20b3ddfffd162a60d269eb1892 Reviewed-by: Andrei Golubev <andrei.golubev@qt.io> Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
-rw-r--r--src/corelib/global/qnumeric_p.h83
-rw-r--r--tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp318
2 files changed, 252 insertions, 149 deletions
diff --git a/src/corelib/global/qnumeric_p.h b/src/corelib/global/qnumeric_p.h
index 2c5d19d274..c7d44591cc 100644
--- a/src/corelib/global/qnumeric_p.h
+++ b/src/corelib/global/qnumeric_p.h
@@ -1,7 +1,7 @@
/****************************************************************************
**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Copyright (C) 2018 Intel Corporation.
+** Copyright (C) 2020 The Qt Company Ltd.
+** Copyright (C) 2020 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@@ -55,6 +55,7 @@
#include "QtCore/private/qglobal_p.h"
#include <cmath>
#include <limits>
+#include <type_traits>
#if defined(Q_CC_MSVC)
# include <intrin.h>
@@ -255,6 +256,7 @@ QT_WARNING_POP
#if ((defined(Q_CC_INTEL) ? (Q_CC_INTEL >= 1800 && !defined(Q_OS_WIN)) : defined(Q_CC_GNU)) \
&& Q_CC_GNU >= 500) || __has_builtin(__builtin_add_overflow)
// GCC 5, ICC 18, and Clang 3.8 have builtins to detect overflows
+#define Q_INTRINSIC_MUL_OVERFLOW64
template <typename T> inline
typename std::enable_if<std::is_unsigned<T>::value || std::is_signed<T>::value, bool>::type
@@ -409,6 +411,83 @@ template <> inline bool add_overflow(quint64 v1, quint64 v2, quint64 *r)
}
# endif // MSVC X86
#endif // !GCC
+
+// Implementations for addition, subtraction or multiplication by a
+// compile-time constant. For addition and subtraction, we simply call the code
+// that detects overflow at runtime. For multiplication, we compare to the
+// maximum possible values before multiplying to ensure no overflow happens.
+
+template <typename T, T V2> bool add_overflow(T v1, std::integral_constant<T, V2>, T *r)
+{
+ return add_overflow(v1, V2, r);
+}
+
+template <auto V2, typename T> bool add_overflow(T v1, T *r)
+{
+ return add_overflow(v1, std::integral_constant<T, V2>{}, r);
+}
+
+template <typename T, T V2> bool sub_overflow(T v1, std::integral_constant<T, V2>, T *r)
+{
+ return sub_overflow(v1, V2, r);
+}
+
+template <auto V2, typename T> bool sub_overflow(T v1, T *r)
+{
+ return sub_overflow(v1, std::integral_constant<T, V2>{}, r);
+}
+
+template <typename T, T V2> bool mul_overflow(T v1, std::integral_constant<T, V2>, T *r)
+{
+ // Runtime detection for anything smaller than or equal to a register
+ // width, as most architectures' multiplication instructions actually
+ // produce a result twice as wide as the input registers, allowing us to
+ // efficiently detect the overflow.
+ if constexpr (sizeof(T) <= sizeof(qregisteruint)) {
+ return mul_overflow(v1, V2, r);
+
+#ifdef Q_INTRINSIC_MUL_OVERFLOW64
+ } else if constexpr (sizeof(T) <= sizeof(quint64)) {
+ // If we have intrinsics detecting overflow of 64-bit multiplications,
+ // then detect overflows through them up to 64 bits.
+ return mul_overflow(v1, V2, r);
+#endif
+
+ } else if constexpr (V2 == 0 || V2 == 1) {
+ // trivial cases (and simplify logic below due to division by zero)
+ *r = v1 * V2;
+ return false;
+ } else if constexpr (V2 == -1) {
+ // multiplication by -1 is valid *except* for signed minimum values
+ // (necessary to avoid diving min() by -1, which is an overflow)
+ if (v1 < 0 && v1 == std::numeric_limits<T>::min())
+ return true;
+ *r = -v1;
+ return false;
+ } else {
+ // For 64-bit multiplications on 32-bit platforms, let's instead compare v1
+ // against the bounds that would overflow.
+ constexpr T Highest = std::numeric_limits<T>::max() / V2;
+ constexpr T Lowest = std::numeric_limits<T>::min() / V2;
+ if constexpr (Highest > Lowest) {
+ if (v1 > Highest || v1 < Lowest)
+ return true;
+ } else {
+ // this can only happen if V2 < 0
+ static_assert(V2 < 0);
+ if (v1 > Lowest || v1 < Highest)
+ return true;
+ }
+
+ *r = v1 * V2;
+ return false;
+ }
+}
+
+template <auto V2, typename T> bool mul_overflow(T v1, T *r)
+{
+ return mul_overflow(v1, std::integral_constant<T, V2>{}, r);
+}
}
#endif // Q_CLANG_QDOC
diff --git a/tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp b/tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp
index da52e6e026..9dc2c02c3b 100644
--- a/tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp
+++ b/tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp
@@ -400,119 +400,138 @@ template <typename Int> static void addOverflow_template()
#if defined(Q_CC_MSVC) && Q_CC_MSVC < 2000
QSKIP("Test disabled, this test generates an Internal Compiler Error compiling in release mode");
#else
- const Int max = std::numeric_limits<Int>::max();
- const Int min = std::numeric_limits<Int>::min();
+ constexpr Int max = std::numeric_limits<Int>::max();
+ constexpr Int min = std::numeric_limits<Int>::min();
Int r;
+#define ADD_COMPARE_NONOVF(v1, v2, expected) \
+ do { \
+ QCOMPARE(add_overflow(Int(v1), Int(v2), &r), false); \
+ QCOMPARE(r, Int(expected)); \
+ QCOMPARE(add_overflow(Int(v1), (std::integral_constant<Int, Int(v2)>()), &r), false); \
+ QCOMPARE(r, Int(expected)); \
+ QCOMPARE(add_overflow<v2>(Int(v1), &r), false); \
+ QCOMPARE(r, Int(expected)); \
+ } while (false)
+#define ADD_COMPARE_OVF(v1, v2) \
+ do { \
+ QCOMPARE(add_overflow(Int(v1), Int(v2), &r), true); \
+ QCOMPARE(add_overflow(Int(v1), (std::integral_constant<Int, Int(v2)>()), &r), true); \
+ QCOMPARE(add_overflow<v2>(Int(v1), &r), true); \
+ } while (false)
+#define SUB_COMPARE_NONOVF(v1, v2, expected) \
+ do { \
+ QCOMPARE(sub_overflow(Int(v1), Int(v2), &r), false); \
+ QCOMPARE(r, Int(expected)); \
+ QCOMPARE(sub_overflow(Int(v1), (std::integral_constant<Int, Int(v2)>()), &r), false); \
+ QCOMPARE(r, Int(expected)); \
+ QCOMPARE(sub_overflow<v2>(Int(v1), &r), false); \
+ QCOMPARE(r, Int(expected)); \
+ } while (false)
+#define SUB_COMPARE_OVF(v1, v2) \
+ do { \
+ QCOMPARE(sub_overflow(Int(v1), Int(v2), &r), true); \
+ QCOMPARE(sub_overflow(Int(v1), (std::integral_constant<Int, Int(v2)>()), &r), true); \
+ QCOMPARE(sub_overflow<v2>(Int(v1), &r), true); \
+ } while (false)
+
// basic values
- QCOMPARE(add_overflow(Int(0), Int(0), &r), false);
- QCOMPARE(r, Int(0));
- QCOMPARE(add_overflow(Int(1), Int(0), &r), false);
- QCOMPARE(r, Int(1));
- QCOMPARE(add_overflow(Int(0), Int(1), &r), false);
- QCOMPARE(r, Int(1));
-
- QCOMPARE(sub_overflow(Int(0), Int(0), &r), false);
- QCOMPARE(r, Int(0));
- QCOMPARE(sub_overflow(Int(1), Int(0), &r), false);
- QCOMPARE(r, Int(1));
- QCOMPARE(sub_overflow(Int(1), Int(1), &r), false);
- QCOMPARE(r, Int(0));
- QCOMPARE(sub_overflow(Int(0), Int(1), &r), !min);
+ ADD_COMPARE_NONOVF(0, 0, 0);
+ ADD_COMPARE_NONOVF(1, 0, 1);
+ ADD_COMPARE_NONOVF(0, 1, 1);
+
+ SUB_COMPARE_NONOVF(0, 0, 0);
+ SUB_COMPARE_NONOVF(1, 0, 1);
+ SUB_COMPARE_NONOVF(1, 1, 0);
if (min)
- QCOMPARE(r, Int(-1));
+ SUB_COMPARE_NONOVF(0, 1, -1);
+ else
+ SUB_COMPARE_OVF(0, 1);
// half-way through max
- QCOMPARE(add_overflow(Int(max/2), Int(max/2), &r), false);
- QCOMPARE(r, Int(max / 2 * 2));
- QCOMPARE(sub_overflow(Int(max/2), Int(max/2), &r), false);
- QCOMPARE(r, Int(0));
- QCOMPARE(add_overflow(Int(max/2 - 1), Int(max/2 + 1), &r), false);
- QCOMPARE(r, Int(max / 2 * 2));
- QCOMPARE(sub_overflow(Int(max/2 - 1), Int(max/2 + 1), &r), !min);
+ ADD_COMPARE_NONOVF(max/2, max/2, max / 2 * 2);
+ SUB_COMPARE_NONOVF(max/2, max/2, 0);
+ ADD_COMPARE_NONOVF(max/2 - 1, max/2 + 1, max / 2 * 2);
if (min)
- QCOMPARE(r, Int(-2));
- QCOMPARE(add_overflow(Int(max/2 + 1), Int(max/2), &r), false);
- QCOMPARE(r, max);
- QCOMPARE(sub_overflow(Int(max/2 + 1), Int(max/2), &r), false);
- QCOMPARE(r, Int(1));
- QCOMPARE(add_overflow(Int(max/2), Int(max/2 + 1), &r), false);
- QCOMPARE(r, max);
- QCOMPARE(sub_overflow(Int(max/2), Int(max/2 + 1), &r), !min);
+ SUB_COMPARE_NONOVF(max/2 - 1, max/2 + 1, -2);
+ else
+ SUB_COMPARE_OVF(max/2 - 1, max/2 + 1);
+
+ ADD_COMPARE_NONOVF(max/2 + 1, max/2, max);
+ SUB_COMPARE_NONOVF(max/2 + 1, max/2, 1);
+ ADD_COMPARE_NONOVF(max/2, max/2 + 1, max);
if (min)
- QCOMPARE(r, Int(-1));
+ SUB_COMPARE_NONOVF(max/2, max/2 + 1, -1);
+ else
+ SUB_COMPARE_OVF(max/2, max/2 + 1);
- QCOMPARE(add_overflow(Int(min/2), Int(min/2), &r), false);
- QCOMPARE(r, Int(min / 2 * 2));
- QCOMPARE(sub_overflow(Int(min/2), Int(min/2), &r), false);
- QCOMPARE(r, Int(0));
- QCOMPARE(add_overflow(Int(min/2 - 1), Int(min/2 + 1), &r), !min);
+ ADD_COMPARE_NONOVF(min/2, min/2, min / 2 * 2);
+ SUB_COMPARE_NONOVF(min/2, min/2, 0);
if (min)
- QCOMPARE(r, Int(min / 2 * 2));
- QCOMPARE(sub_overflow(Int(min/2 - 1), Int(min/2 + 1), &r), false);
- QCOMPARE(r, Int(-2));
- QCOMPARE(sub_overflow(Int(min/2 + 1), Int(min/2), &r), false);
- QCOMPARE(r, Int(1));
- QCOMPARE(sub_overflow(Int(min/2), Int(min/2 + 1), &r), !min);
+ ADD_COMPARE_NONOVF(min/2 - 1, min/2 + 1, min / 2 * 2);
+ else
+ ADD_COMPARE_OVF(min/2 - 1, min/2 + 1);
+ SUB_COMPARE_NONOVF(min/2 - 1, min/2 + 1, -2);
+ SUB_COMPARE_NONOVF(min/2 + 1, min/2, 1);
if (min)
- QCOMPARE(r, Int(-1));
+ SUB_COMPARE_NONOVF(min/2, min/2 + 1, -1);
+ else
+ SUB_COMPARE_OVF(min/2, min/2 + 1);
// more than half
- QCOMPARE(add_overflow(Int(max/4 * 3), Int(max/4), &r), false);
- QCOMPARE(r, Int(max / 4 * 4));
+ ADD_COMPARE_NONOVF(max/4 * 3, max/4, max / 4 * 4);
// max
- QCOMPARE(add_overflow(max, Int(0), &r), false);
- QCOMPARE(r, max);
- QCOMPARE(sub_overflow(max, Int(0), &r), false);
- QCOMPARE(r, max);
- QCOMPARE(add_overflow(Int(0), max, &r), false);
- QCOMPARE(r, max);
- QCOMPARE(sub_overflow(Int(0), max, &r), !min);
+ ADD_COMPARE_NONOVF(max, 0, max);
+ SUB_COMPARE_NONOVF(max, 0, max);
+ ADD_COMPARE_NONOVF(0, max, max);
if (min)
- QCOMPARE(r, Int(-max));
-
- QCOMPARE(add_overflow(min, Int(0), &r), false);
- QCOMPARE(r, min);
- QCOMPARE(sub_overflow(min, Int(0), &r), false);
- QCOMPARE(r, min);
- QCOMPARE(add_overflow(Int(0), min, &r), false);
- QCOMPARE(r, min);
- QCOMPARE(sub_overflow(Int(0), Int(min+1), &r), !min);
+ SUB_COMPARE_NONOVF(0, max, -max);
+ else
+ SUB_COMPARE_OVF(0, max);
+
+ ADD_COMPARE_NONOVF(min, 0, min);
+ SUB_COMPARE_NONOVF(min, 0, min);
+ ADD_COMPARE_NONOVF(0, min, min);
if (min)
- QCOMPARE(r, Int(-(min+1)));
+ SUB_COMPARE_NONOVF(0, min+1, -(min+1));
+ else
+ SUB_COMPARE_OVF(0, min+1);
// 64-bit issues
- if (max > std::numeric_limits<uint>::max()) {
- QCOMPARE(add_overflow(Int(std::numeric_limits<uint>::max()), Int(std::numeric_limits<uint>::max()), &r), false);
- QCOMPARE(r, Int(2 * Int(std::numeric_limits<uint>::max())));
- QCOMPARE(sub_overflow(Int(std::numeric_limits<uint>::max()), Int(std::numeric_limits<uint>::max()), &r), false);
- QCOMPARE(r, Int(0));
+ if constexpr (max > std::numeric_limits<uint>::max()) {
+ ADD_COMPARE_NONOVF(std::numeric_limits<uint>::max(), std::numeric_limits<uint>::max(), 2 * Int(std::numeric_limits<uint>::max()));
+ SUB_COMPARE_NONOVF(std::numeric_limits<uint>::max(), std::numeric_limits<uint>::max(), 0);
}
- if (min && min < -Int(std::numeric_limits<uint>::max())) {
- QCOMPARE(add_overflow(Int(-Int(std::numeric_limits<uint>::max())), Int(-Int(std::numeric_limits<uint>::max())), &r), false);
- QCOMPARE(r, Int(-2 * Int(std::numeric_limits<uint>::max())));
- QCOMPARE(sub_overflow(Int(-Int(std::numeric_limits<uint>::max())), Int(-Int(std::numeric_limits<uint>::max())), &r), false);
- QCOMPARE(r, Int(0));
+ if constexpr (min != 0) {
+ if (qint64(min) < qint64(-std::numeric_limits<uint>::max())) {
+ ADD_COMPARE_NONOVF(-Int(std::numeric_limits<uint>::max()), -Int(std::numeric_limits<uint>::max()), -2 * Int(std::numeric_limits<uint>::max()));
+ SUB_COMPARE_NONOVF(-Int(std::numeric_limits<uint>::max()), -Int(std::numeric_limits<uint>::max()), 0);
+ }
}
// overflows past max
- QCOMPARE(add_overflow(max, Int(1), &r), true);
- QCOMPARE(add_overflow(Int(1), max, &r), true);
- QCOMPARE(add_overflow(Int(max/2 + 1), Int(max/2 + 1), &r), true);
- if (!min) {
- QCOMPARE(sub_overflow(Int(-max), Int(-2), &r), true);
- QCOMPARE(sub_overflow(Int(max/2 - 1), Int(max/2 + 1), &r), true);
+ ADD_COMPARE_OVF(max, 1);
+ ADD_COMPARE_OVF(1, max);
+ ADD_COMPARE_OVF(max/2 + 1, max/2 + 1);
+
+ // overflows past min
+ if constexpr (min != 0) {
+ ADD_COMPARE_OVF(-max, -2);
+ SUB_COMPARE_OVF(-max, 2);
+ SUB_COMPARE_OVF(-max/2 - 1, max/2 + 2);
+
+ SUB_COMPARE_OVF(min, 1);
+ SUB_COMPARE_OVF(1, min);
+ SUB_COMPARE_OVF(min/2 - 1, -Int(min/2));
+ ADD_COMPARE_OVF(min, -1);
+ ADD_COMPARE_OVF(-1, min);
}
- // overflows past min (in case of min == 0, repeats some tests above)
- if (min) {
- QCOMPARE(sub_overflow(min, Int(1), &r), true);
- QCOMPARE(sub_overflow(Int(1), min, &r), true);
- QCOMPARE(sub_overflow(Int(min/2 - 1), Int(-Int(min/2)), &r), true);
- QCOMPARE(add_overflow(min, Int(-1), &r), true);
- QCOMPARE(add_overflow(Int(-1), min, &r), true);
- }
+#undef ADD_COMPARE_NONOVF
+#undef ADD_COMPARE_OVF
+#undef SUB_COMPARE_NONOVF
+#undef SUB_COMPARE_OVF
#endif
}
@@ -552,77 +571,82 @@ template <typename Int> static void mulOverflow_template()
#if defined(Q_CC_MSVC) && Q_CC_MSVC < 1900
QSKIP("Test disabled, this test generates an Internal Compiler Error compiling");
#else
- const Int max = std::numeric_limits<Int>::max();
- const Int min = std::numeric_limits<Int>::min();
+ constexpr Int max = std::numeric_limits<Int>::max();
+ constexpr Int min = std::numeric_limits<Int>::min();
// for unsigned (even number of significant bits): mid2 = mid1 - 1
// for signed (odd number of significant bits): mid2 = mid1 / 2 - 1
- const Int mid1 = Int(Int(1) << sizeof(Int) * CHAR_BIT / 2);
- const Int mid2 = (std::numeric_limits<Int>::digits % 2 ? mid1 / 2 : mid1) - 1;
+ constexpr Int mid1 = Int(Int(1) << (sizeof(Int) * CHAR_BIT / 2));
+ constexpr Int mid2 = (std::numeric_limits<Int>::digits % 2 ? mid1 / 2 : mid1) - 1;
Int r;
+#define MUL_COMPARE_NONOVF(v1, v2, expected) \
+ do { \
+ QCOMPARE(mul_overflow(Int(v1), Int(v2), &r), false); \
+ QCOMPARE(r, Int(expected)); \
+ QCOMPARE(mul_overflow(Int(v1), (std::integral_constant<Int, v2>()), &r), false); \
+ QCOMPARE(r, Int(expected)); \
+ QCOMPARE(mul_overflow<v2>(Int(v1), &r), false); \
+ QCOMPARE(r, Int(expected)); \
+ } while (false);
+#define MUL_COMPARE_OVF(v1, v2) \
+ do { \
+ QCOMPARE(mul_overflow(Int(v1), Int(v2), &r), true); \
+ QCOMPARE(mul_overflow(Int(v1), (std::integral_constant<Int, v2>()), &r), true); \
+ QCOMPARE(mul_overflow<v2>(Int(v1), &r), true); \
+ } while (false);
+
// basic multiplications
- QCOMPARE(mul_overflow(Int(0), Int(0), &r), false);
- QCOMPARE(r, Int(0));
- QCOMPARE(mul_overflow(Int(1), Int(0), &r), false);
- QCOMPARE(r, Int(0));
- QCOMPARE(mul_overflow(Int(0), Int(1), &r), false);
- QCOMPARE(r, Int(0));
- QCOMPARE(mul_overflow(max, Int(0), &r), false);
- QCOMPARE(r, Int(0));
- QCOMPARE(mul_overflow(Int(0), max, &r), false);
- QCOMPARE(r, Int(0));
- QCOMPARE(mul_overflow(min, Int(0), &r), false);
- QCOMPARE(r, Int(0));
- QCOMPARE(mul_overflow(Int(0), min, &r), false);
- QCOMPARE(r, Int(0));
-
- QCOMPARE(mul_overflow(Int(1), Int(1), &r), false);
- QCOMPARE(r, Int(1));
- QCOMPARE(mul_overflow(Int(1), max, &r), false);
- QCOMPARE(r, max);
- QCOMPARE(mul_overflow(max, Int(1), &r), false);
- QCOMPARE(r, max);
- QCOMPARE(mul_overflow(Int(1), min, &r), false);
- QCOMPARE(r, min);
- QCOMPARE(mul_overflow(min, Int(1), &r), false);
- QCOMPARE(r, min);
+ MUL_COMPARE_NONOVF(0, 0, 0);
+ MUL_COMPARE_NONOVF(1, 0, 0);
+ MUL_COMPARE_NONOVF(0, 1, 0);
+ MUL_COMPARE_NONOVF(max, 0, 0);
+ MUL_COMPARE_NONOVF(0, max, 0);
+ MUL_COMPARE_NONOVF(min, 0, 0);
+ MUL_COMPARE_NONOVF(0, min, 0);
+ if constexpr (min != 0) {
+ MUL_COMPARE_NONOVF(0, -1, 0);
+ MUL_COMPARE_NONOVF(1, -1, -1);
+ MUL_COMPARE_NONOVF(max, -1, -max);
+ }
+
+ MUL_COMPARE_NONOVF(1, 1, 1);
+ MUL_COMPARE_NONOVF(1, max, max);
+ MUL_COMPARE_NONOVF(max, 1, max);
+ MUL_COMPARE_NONOVF(1, min, min);
+ MUL_COMPARE_NONOVF(min, 1, min);
// almost max
- QCOMPARE(mul_overflow(mid1, mid2, &r), false);
- QCOMPARE(r, Int(max - mid1 + 1));
- QCOMPARE(mul_overflow(Int(max / 2), Int(2), &r), false);
- QCOMPARE(r, Int(max & ~Int(1)));
- QCOMPARE(mul_overflow(Int(max / 4), Int(4), &r), false);
- QCOMPARE(r, Int(max & ~Int(3)));
- if (min) {
- QCOMPARE(mul_overflow(Int(-mid1), mid2, &r), false);
- QCOMPARE(r, Int(-max + mid1 - 1));
- QCOMPARE(mul_overflow(Int(-max / 2), Int(2), &r), false);
- QCOMPARE(r, Int(-max + 1));
- QCOMPARE(mul_overflow(Int(-max / 4), Int(4), &r), false);
- QCOMPARE(r, Int(-max + 3));
-
- QCOMPARE(mul_overflow(Int(-mid1), Int(mid2 + 1), &r), false);
- QCOMPARE(r, min);
- QCOMPARE(mul_overflow(mid1, Int(-mid2 - 1), &r), false);
- QCOMPARE(r, min);
+ MUL_COMPARE_NONOVF(mid1, mid2, max - mid1 + 1);
+ MUL_COMPARE_NONOVF(max / 2, 2, max & ~Int(1));
+ MUL_COMPARE_NONOVF(max / 4, 4, max & ~Int(3));
+ if constexpr (min != 0) {
+ MUL_COMPARE_NONOVF(-mid1, mid2, -max + mid1 - 1);
+ MUL_COMPARE_NONOVF(-max / 2, 2, -max + 1);
+ MUL_COMPARE_NONOVF(-max / 4, 4, -max + 3);
+
+ MUL_COMPARE_NONOVF(-mid1, mid2 + 1, min);
+ MUL_COMPARE_NONOVF(mid1, -mid2 - 1, min);
}
// overflows
- QCOMPARE(mul_overflow(max, Int(2), &r), true);
- QCOMPARE(mul_overflow(Int(max / 2), Int(3), &r), true);
- QCOMPARE(mul_overflow(mid1, Int(mid2 + 1), &r), true);
- QCOMPARE(mul_overflow(Int(max / 2 + 2), Int(2), &r), true);
- QCOMPARE(mul_overflow(Int(max - max / 2), Int(2), &r), true);
- QCOMPARE(mul_overflow(Int(1ULL << (std::numeric_limits<Int>::digits - 1)), Int(2), &r), true);
-
- if (min) {
- QCOMPARE(mul_overflow(min, Int(2), &r), true);
- QCOMPARE(mul_overflow(Int(min / 2), Int(3), &r), true);
- QCOMPARE(mul_overflow(Int(min / 2 - 1), Int(2), &r), true);
+ MUL_COMPARE_OVF(max, 2);
+ MUL_COMPARE_OVF(max / 2, 3);
+ MUL_COMPARE_OVF(mid1, mid2 + 1);
+ MUL_COMPARE_OVF(max / 2 + 2, 2);
+ MUL_COMPARE_OVF(max - max / 2, 2);
+ MUL_COMPARE_OVF(1ULL << (std::numeric_limits<Int>::digits - 1), 2);
+
+ if constexpr (min != 0) {
+ MUL_COMPARE_OVF(min, -1);
+ MUL_COMPARE_OVF(min, 2);
+ MUL_COMPARE_OVF(min / 2, 3);
+ MUL_COMPARE_OVF(min / 2 - 1, 2);
}
+
+#undef MUL_COMPARE_NONOVF
+#undef MUL_COMPARE_OVF
#endif
}
@@ -657,7 +681,7 @@ void tst_QNumeric::mulOverflow()
if (size == -32)
MulOverflowDispatch<qint32>()();
if (size == -64) {
-#if QT_POINTER_SIZE == 8
+#if QT_POINTER_SIZE == 8 || defined(Q_INTRINSIC_MUL_OVERFLOW64)
MulOverflowDispatch<qint64>()();
#else
QFAIL("128-bit multiplication not supported on this platform");