From 43c44d05ca6af4ec78c1dea84635375a637ff80d Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Thu, 30 Nov 2017 20:37:57 -0800 Subject: Update the overflow functions to include qsizetype Commit 29bc68cf169b8cf87d306a1c72e12eb9b6fbfce7 added support for unsigned and commit 5ff7a3d96e0ce0dcb3d388b53d038cdd40c7a975 later added support for int. This commit adds support for qsizetype, which isn't int on 64-bit platforms. We do this by reorganizing the code and using the generic version of __builtin_{add,sub,mul}_overflow from GCC 5 and Clang 3.8, which ICC 18 seems to support now too on Linux. That leaves older versions of GCC and Clang, as well as MSVC, ICC on Windows, and the GHS compiler, to use the generic implementations, as I've removed the assembly code those versions of GCC and Clang on x86 are now uncommon. Note: any older version of ICC probably breaks. We only support the latest. Change-Id: I9e2892cb6c374e93bcb7fffd14fc11bcd5f067a7 Reviewed-by: Allan Sandfeld Jensen --- .../auto/corelib/global/qnumeric/tst_qnumeric.cpp | 154 +++++++++++++++++++-- 1 file changed, 145 insertions(+), 9 deletions(-) (limited to 'tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp') diff --git a/tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp b/tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp index 8232ca8fba..03300c6dbe 100644 --- a/tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp +++ b/tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp @@ -223,11 +223,20 @@ void tst_QNumeric::floatDistance_double() void tst_QNumeric::addOverflow_data() { QTest::addColumn("size"); + + // for unsigned, all sizes are supported QTest::newRow("quint8") << 8; QTest::newRow("quint16") << 16; QTest::newRow("quint32") << 32; QTest::newRow("quint64") << 64; QTest::newRow("ulong") << 48; // it's either 32- or 64-bit, so on average it's 48 :-) + + // for signed, we can't guarantee 64-bit + QTest::newRow("qint8") << -8; + QTest::newRow("qint16") << -16; + QTest::newRow("qint32") << -32; + if (sizeof(void *) == sizeof(qint64)) + QTest::newRow("qint64") << -64; } // Note: in release mode, all the tests may be statically determined and only the calls @@ -238,6 +247,7 @@ template static void addOverflow_template() QSKIP("Test disabled, this test generates an Internal Compiler Error compiling in release mode"); #else const Int max = std::numeric_limits::max(); + const Int min = std::numeric_limits::min(); Int r; // basic values @@ -248,15 +258,50 @@ template static void addOverflow_template() 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); + if (min) + QCOMPARE(r, Int(-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); + 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); + if (min) + QCOMPARE(r, Int(-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); + 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); + if (min) + QCOMPARE(r, Int(-1)); // more than half QCOMPARE(add_overflow(Int(max/4 * 3), Int(max/4), &r), false); @@ -265,19 +310,55 @@ template static void addOverflow_template() // 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); + 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); + if (min) + QCOMPARE(r, Int(-(min+1))); // 64-bit issues if (max > std::numeric_limits::max()) { QCOMPARE(add_overflow(Int(std::numeric_limits::max()), Int(std::numeric_limits::max()), &r), false); QCOMPARE(r, Int(2 * Int(std::numeric_limits::max()))); + QCOMPARE(sub_overflow(Int(std::numeric_limits::max()), Int(std::numeric_limits::max()), &r), false); + QCOMPARE(r, Int(0)); + } + if (min && min < -Int(std::numeric_limits::max())) { + QCOMPARE(add_overflow(Int(-Int(std::numeric_limits::max())), Int(-Int(std::numeric_limits::max())), &r), false); + QCOMPARE(r, Int(-2 * Int(std::numeric_limits::max()))); + QCOMPARE(sub_overflow(Int(-Int(std::numeric_limits::max())), Int(-Int(std::numeric_limits::max())), &r), false); + QCOMPARE(r, Int(0)); } - // overflows + // 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); + } + + // 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); + } #endif } @@ -294,6 +375,15 @@ void tst_QNumeric::addOverflow() addOverflow_template(); // not really 48-bit if (size == 64) addOverflow_template(); + + if (size == -8) + addOverflow_template(); + if (size == -16) + addOverflow_template(); + if (size == -32) + addOverflow_template(); + if (size == -64) + addOverflow_template(); } void tst_QNumeric::mulOverflow_data() @@ -309,7 +399,13 @@ template static void mulOverflow_template() QSKIP("Test disabled, this test generates an Internal Compiler Error compiling"); #else const Int max = std::numeric_limits::max(); - const Int middle = Int(max >> (sizeof(Int) * CHAR_BIT / 2)); + const Int min = std::numeric_limits::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::digits % 2 ? mid1 / 2 : mid1) - 1; + Int r; // basic multiplications @@ -323,6 +419,10 @@ template static void mulOverflow_template() 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)); @@ -330,23 +430,45 @@ template static void mulOverflow_template() 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); // almost max - QCOMPARE(mul_overflow(middle, middle, &r), false); - QCOMPARE(r, Int(max - 2 * middle)); - QCOMPARE(mul_overflow(Int(middle + 1), middle, &r), false); - QCOMPARE(r, Int(middle << (sizeof(Int) * CHAR_BIT / 2))); - QCOMPARE(mul_overflow(middle, Int(middle + 1), &r), false); - QCOMPARE(r, Int(middle << (sizeof(Int) * CHAR_BIT / 2))); + 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); + } // overflows QCOMPARE(mul_overflow(max, Int(2), &r), true); QCOMPARE(mul_overflow(Int(max / 2), Int(3), &r), true); - QCOMPARE(mul_overflow(Int(middle + 1), Int(middle + 1), &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(1ULL << (std::numeric_limits::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); + QCOMPARE(mul_overflow(Int(min + min/2), Int(2), &r), true); + } #endif } @@ -373,6 +495,20 @@ void tst_QNumeric::mulOverflow() MulOverflowDispatch()(); // not really 48-bit if (size == 64) MulOverflowDispatch()(); + + if (size == -8) + MulOverflowDispatch()(); + if (size == -16) + MulOverflowDispatch()(); + if (size == -32) + MulOverflowDispatch()(); + if (size == -64) { +#if QT_POINTER_SIZE == 8 + MulOverflowDispatch()(); +#else + QFAIL("128-bit multiplication not supported on this platform"); +#endif + } } void tst_QNumeric::signedOverflow() -- cgit v1.2.3