From 29bc68cf169b8cf87d306a1c72e12eb9b6fbfce7 Mon Sep 17 00:00:00 2001
From: Thiago Macieira <thiago.macieira@intel.com>
Date: Tue, 9 Jun 2015 17:09:18 -0700
Subject: [PATCH] Add code to perform overflow-checking additions and
 multiplication

Most processors have carry flags which they set on addition overflow, so
it's a good idea to access them whenever possible. Most of them also
have widening multiply instructions that can be used to detect overflow
of the non-widening version.

Tested to compile on:
 Architecture     Compiler
  x86             GCC 4.9, GCC 5*, Clang 3.6*, ICC 16 beta
  x86-64          GCC 4.9, GCC 5*, Clang 3.6*, ICC 16 beta
  x86-64 ILP32    GCC 4.9, GCC 5*, Clang 3.6*
  IA-64 LP64      GCC 4.8
  ARMv7-A         GCC 4.9, Clang 3.6*
  AArch64         Clang 3.6*
  MIPS            GCC 4.9, Clang 3.6*
  MIPS64          GCC 4.9, Clang 3.6*
  PowerPC         GCC 4.9, Clang 3.6*
  PowerPC 64      GCC 4.9, Clang 3.6*
  SPARC           Clang 3.6*
  SPARCv9         Clang 3.6*

[*] supports the intrinsics

If the compiler does not offer a way to detect an overflow, we do it by
hand. For unsigned additions, that's easy, since the C++ language
specifies the behavior of the overflow. That's also the reason why this
code is implemented only for unsigned integers.

For the multiplication, if the compiler does not support widening
multiplications, we do it with a division instead. This is necessary for
GCC < 4.5 and compilers not compatible with GCC or MSVC.

Change-Id: I049a653beeb5454c9539ffff13e637de0f1338c1
Reviewed-by: Ulf Hermann <ulf.hermann@theqtcompany.com>
Reviewed-by: Alex Trotsenko <alex1973tr@gmail.com>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
---
 src/corelib/global/qglobal.h                  |   3 +
 src/corelib/global/qnumeric_p.h               | 117 +++++++++++++
 .../auto/corelib/global/qnumeric/qnumeric.pro |   2 +-
 .../corelib/global/qnumeric/tst_qnumeric.cpp  | 160 ++++++++++++++++++
 4 files changed, 281 insertions(+), 1 deletion(-)

diff --git a/src/corelib/global/qglobal.h b/src/corelib/global/qglobal.h
index f8f306f6147..6ee3e37ce63 100644
--- a/src/corelib/global/qglobal.h
+++ b/src/corelib/global/qglobal.h
@@ -448,6 +448,9 @@ template <>    struct QIntegerForSize<1> { typedef quint8  Unsigned; typedef qin
 template <>    struct QIntegerForSize<2> { typedef quint16 Unsigned; typedef qint16 Signed; };
 template <>    struct QIntegerForSize<4> { typedef quint32 Unsigned; typedef qint32 Signed; };
 template <>    struct QIntegerForSize<8> { typedef quint64 Unsigned; typedef qint64 Signed; };
+#if defined(Q_CC_GNU) && defined(__SIZEOF_INT128__)
+template <>    struct QIntegerForSize<16> { __extension__ typedef unsigned __int128 Unsigned; __extension__ typedef __int128 Signed; };
+#endif
 template <class T> struct QIntegerForSizeof: QIntegerForSize<sizeof(T)> { };
 typedef QIntegerForSize<Q_PROCESSOR_WORDSIZE>::Signed qregisterint;
 typedef QIntegerForSize<Q_PROCESSOR_WORDSIZE>::Unsigned qregisteruint;
diff --git a/src/corelib/global/qnumeric_p.h b/src/corelib/global/qnumeric_p.h
index 5fa0a881a37..e5f9d8e13e1 100644
--- a/src/corelib/global/qnumeric_p.h
+++ b/src/corelib/global/qnumeric_p.h
@@ -1,6 +1,7 @@
 /****************************************************************************
 **
 ** Copyright (C) 2015 The Qt Company Ltd.
+** Copyright (C) 2015 Intel Corporation.
 ** Contact: http://www.qt.io/licensing/
 **
 ** This file is part of the QtCore module of the Qt Toolkit.
@@ -47,6 +48,18 @@
 
 #include "QtCore/qglobal.h"
 
+#include <limits>
+
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+#  include <intrin.h>
+#elif defined(Q_CC_INTEL)
+#  include <immintrin.h>    // for _addcarry_u<nn>
+#endif
+
+#ifndef __has_builtin
+#  define __has_builtin(x)  0
+#endif
+
 QT_BEGIN_NAMESPACE
 
 #if !defined(Q_CC_MIPS)
@@ -188,6 +201,110 @@ static inline bool qt_is_finite(float d)
     }
 }
 
+//
+// Overflow math
+//
+namespace {
+template <typename T> inline typename QtPrivate::QEnableIf<QtPrivate::is_unsigned<T>::value, bool>::Type
+add_overflow(T v1, T v2, T *r)
+{
+    // unsigned additions are well-defined
+    *r = v1 + v2;
+    return v1 > T(v1 + v2);
+}
+
+template <typename T> inline typename QtPrivate::QEnableIf<QtPrivate::is_unsigned<T>::value, bool>::Type
+mul_overflow(T v1, T v2, T *r)
+{
+    // use the next biggest type
+    // Note: for 64-bit systems where __int128 isn't supported, this will cause an error.
+    // A fallback is present below.
+    typedef typename QIntegerForSize<sizeof(T) * 2>::Unsigned Larger;
+    Larger lr = Larger(v1) * Larger(v2);
+    *r = T(lr);
+    return lr > std::numeric_limits<T>::max();
+}
+
+#if defined(__SIZEOF_INT128__)
+#  define HAVE_MUL64_OVERFLOW
+#endif
+
+// GCC 5 and Clang have builtins to detect overflows
+#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || __has_builtin(__builtin_uadd_overflow)
+template <> inline bool add_overflow(unsigned v1, unsigned v2, unsigned *r)
+{ return __builtin_uadd_overflow(v1, v2, r); }
+#endif
+#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || __has_builtin(__builtin_uaddl_overflow)
+template <> inline bool add_overflow(unsigned long v1, unsigned long v2, unsigned long *r)
+{ return __builtin_uaddl_overflow(v1, v2, r); }
+#endif
+#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || __has_builtin(__builtin_uaddll_overflow)
+template <> inline bool add_overflow(unsigned long long v1, unsigned long long v2, unsigned long long *r)
+{ return __builtin_uaddll_overflow(v1, v2, r); }
+#endif
+
+#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || __has_builtin(__builtin_umul_overflow)
+template <> inline bool mul_overflow(unsigned v1, unsigned v2, unsigned *r)
+{ return __builtin_umul_overflow(v1, v2, r); }
+#endif
+#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || __has_builtin(__builtin_umull_overflow)
+template <> inline bool mul_overflow(unsigned long v1, unsigned long v2, unsigned long *r)
+{ return __builtin_umull_overflow(v1, v2, r); }
+#endif
+#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || __has_builtin(__builtin_umulll_overflow)
+template <> inline bool mul_overflow(unsigned long long v1, unsigned long long v2, unsigned long long *r)
+{ return __builtin_umulll_overflow(v1, v2, r); }
+#  define HAVE_MUL64_OVERFLOW
+#endif
+
+#if ((defined(Q_CC_MSVC) && _MSC_VER >= 1800) || defined(Q_CC_INTEL)) && defined(Q_PROCESSOR_X86)
+template <> inline bool add_overflow(unsigned v1, unsigned v2, unsigned *r)
+{ return _addcarry_u32(0, v1, v2, r); }
+#  ifdef Q_CC_MSVC      // longs are 32-bit
+template <> inline bool add_overflow(unsigned long v1, unsigned long v2, unsigned long *r)
+{ return _addcarry_u32(0, v1, v2, reinterpret_cast<unsigned *>(r)); }
+#  endif
+#endif
+#if ((defined(Q_CC_MSVC) && _MSC_VER >= 1800) || defined(Q_CC_INTEL)) && defined(Q_PROCESSOR_X86_64)
+template <> inline bool add_overflow(quint64 v1, quint64 v2, quint64 *r)
+{ return _addcarry_u64(0, v1, v2, reinterpret_cast<unsigned __int64 *>(r)); }
+#  ifndef Q_CC_MSVC      // longs are 64-bit
+template <> inline bool add_overflow(unsigned long v1, unsigned long v2, unsigned long *r)
+{ return _addcarry_u64(0, v1, v2, reinterpret_cast<unsigned __int64 *>(r)); }
+#  endif
+#endif
+
+#if defined(Q_CC_MSVC) && (defined(Q_PROCESSOR_X86_64) || defined(Q_PROCESSOR_IA64))
+#pragma intrinsic(_umul128)
+template <> inline bool mul_overflow(quint64 v1, quint64 v2, quint64 *r)
+{
+    // use 128-bit multiplication with the _umul128 intrinsic
+    // https://msdn.microsoft.com/en-us/library/3dayytw9.aspx
+    quint64 high;
+    *r = _umul128(v1, v2, &high);
+    return high;
+}
+#  define HAVE_MUL64_OVERFLOW
+#endif
+
+#if !defined(HAVE_MUL64_OVERFLOW) && defined(__LP64__)
+// no 128-bit multiplication, we need to figure out with a slow division
+template <> inline bool mul_overflow(quint64 v1, quint64 v2, quint64 *r)
+{
+    if (v2 && v1 > std::numeric_limits<quint64>::max() / v2)
+        return true;
+    *r = v1 * v2;
+    return false;
+}
+template <> inline bool mul_overflow(unsigned long v1, unsigned long v2, unsigned long *r)
+{
+    return mul_overflow<quint64>(v1, v2, reinterpret_cast<quint64 *>(r));
+}
+#else
+#  undef HAVE_MUL64_OVERFLOW
+#endif
+}
+
 QT_END_NAMESPACE
 
 #endif // QNUMERIC_P_H
diff --git a/tests/auto/corelib/global/qnumeric/qnumeric.pro b/tests/auto/corelib/global/qnumeric/qnumeric.pro
index 00f3635be95..0772ce6aab2 100644
--- a/tests/auto/corelib/global/qnumeric/qnumeric.pro
+++ b/tests/auto/corelib/global/qnumeric/qnumeric.pro
@@ -1,6 +1,6 @@
 CONFIG += testcase parallel_test
 TARGET = tst_qnumeric
-QT = core testlib
+QT = core-private testlib
 SOURCES = tst_qnumeric.cpp
 DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0
 intel_icc: QMAKE_CXXFLAGS += -fp-model strict
diff --git a/tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp b/tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp
index fdc8bc6aabc..59a536ed25b 100644
--- a/tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp
+++ b/tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp
@@ -34,6 +34,7 @@
 
 #include <QtTest/QtTest>
 #include <QtGlobal>
+#include "private/qnumeric_p.h"
 
 #include <math.h>
 #include <float.h>
@@ -50,6 +51,10 @@ private slots:
     void floatDistance();
     void floatDistance_double_data();
     void floatDistance_double();
+    void addOverflow_data();
+    void addOverflow();
+    void mulOverflow_data();
+    void mulOverflow();
 };
 
 void tst_QNumeric::fuzzyCompare_data()
@@ -206,5 +211,160 @@ void tst_QNumeric::floatDistance_double()
     QCOMPARE(qFloatDistance(val1, val2), expectedDistance);
 }
 
+void tst_QNumeric::addOverflow_data()
+{
+    QTest::addColumn<int>("size");
+    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 :-)
+}
+
+// Note: in release mode, all the tests may be statically determined and only the calls
+// to QTest::toString and QTest::qCompare will remain.
+template <typename Int> static void addOverflow_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();
+    Int r;
+
+    // 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));
+
+    // half-way through max
+    QCOMPARE(add_overflow(Int(max/2), Int(max/2), &r), false);
+    QCOMPARE(r, Int(max / 2 * 2));
+    QCOMPARE(add_overflow(Int(max/2 - 1), Int(max/2 + 1), &r), false);
+    QCOMPARE(r, Int(max / 2 * 2));
+    QCOMPARE(add_overflow(Int(max/2 + 1), Int(max/2), &r), false);
+    QCOMPARE(r, max);
+    QCOMPARE(add_overflow(Int(max/2), Int(max/2 + 1), &r), false);
+    QCOMPARE(r, max);
+
+    // more than half
+    QCOMPARE(add_overflow(Int(max/4 * 3), Int(max/4), &r), false);
+    QCOMPARE(r, Int(max / 4 * 4));
+
+    // max
+    QCOMPARE(add_overflow(max, Int(0), &r), false);
+    QCOMPARE(r, max);
+    QCOMPARE(add_overflow(Int(0), max, &r), false);
+    QCOMPARE(r, max);
+
+    // 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())));
+    }
+
+    // overflows
+    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);
+#endif
+}
+
+void tst_QNumeric::addOverflow()
+{
+    QFETCH(int, size);
+    if (size == 8)
+        addOverflow_template<quint8>();
+    if (size == 16)
+        addOverflow_template<quint16>();
+    if (size == 32)
+        addOverflow_template<quint32>();
+    if (size == 48)
+        addOverflow_template<ulong>();  // not really 48-bit
+    if (size == 64)
+        addOverflow_template<quint64>();
+}
+
+void tst_QNumeric::mulOverflow_data()
+{
+    addOverflow_data();
+}
+
+// Note: in release mode, all the tests may be statically determined and only the calls
+// to QTest::toString and QTest::qCompare will remain.
+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 middle = Int(max >> (sizeof(Int) * CHAR_BIT / 2));
+    Int r;
+
+    // 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(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);
+
+    // 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(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)));
+
+    // 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);
+#endif
+}
+
+template <typename Int, bool enabled = sizeof(Int) <= sizeof(void*)> struct MulOverflowDispatch;
+template <typename Int> struct MulOverflowDispatch<Int, true>
+{
+    void operator()() { mulOverflow_template<Int>(); }
+};
+template <typename Int> struct MulOverflowDispatch<Int, false>
+{
+    void operator()() { QSKIP("This type is too big for this architecture"); }
+};
+
+void tst_QNumeric::mulOverflow()
+{
+    QFETCH(int, size);
+    if (size == 8)
+        MulOverflowDispatch<quint8>()();
+    if (size == 16)
+        MulOverflowDispatch<quint16>()();
+    if (size == 32)
+        MulOverflowDispatch<quint32>()();
+    if (size == 48)
+        MulOverflowDispatch<ulong>()();     // not really 48-bit
+    if (size == 64)
+        MulOverflowDispatch<quint64>()();
+}
+
 QTEST_APPLESS_MAIN(tst_QNumeric)
 #include "tst_qnumeric.moc"
-- 
GitLab