Add {add,sub,mul}_overflow for signed integers.

In C++, signed overflow math is Undefined Behavior. However, many CPUs
do implement some way to check for overflow. Some compilers expose
intrinsics to use this functionality. If the no intrinsic is exposed,
overflow checking can be done by widening the result type and "manually"
checking for overflow. Or, for X86, by using inline assembly to use the
CPU features.

Used in QtQml.

Change-Id: I2ef2523ccaa98f6757a45e24862a2fa730a26bb0
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
bb10
Erik Verbruggen 2015-10-29 12:39:58 +01:00
parent 986a5262e3
commit 5ff7a3d96e
2 changed files with 123 additions and 1 deletions

View File

@ -161,7 +161,7 @@ static inline bool qt_is_finite(float f)
}
//
// Overflow math
// Unsigned overflow math
//
namespace {
template <typename T> inline typename QtPrivate::QEnableIf<QtPrivate::is_unsigned<T>::value, bool>::Type
@ -262,6 +262,97 @@ template <> inline bool mul_overflow(unsigned long v1, unsigned long v2, unsigne
#else
# undef HAVE_MUL64_OVERFLOW
#endif
//
// Signed overflow math
//
// In C++, signed overflow math is Undefined Behavior. However, many CPUs do implement some way to
// check for overflow. Some compilers expose intrinsics to use this functionality. If the no
// intrinsic is exposed, overflow checking can be done by widening the result type and "manually"
// checking for overflow. Or, alternatively, by using inline assembly to use the CPU features.
//
// Only int overflow checking is implemented, because it's the only one used.
#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || QT_HAS_BUILTIN(__builtin_sadd_overflow)
inline bool add_overflow(int v1, int v2, int *r)
{ return __builtin_sadd_overflow(v1, v2, r); }
#elif defined(Q_CC_GNU) && defined(Q_PROCESSOR_X86)
inline bool add_overflow(int v1, int v2, int *r)
{
quint8 overflow = 0;
int res = v1;
asm ("addl %2, %1\n"
"seto %0"
: "=q" (overflow), "=r" (res)
: "r" (v2), "1" (res)
: "cc"
);
*r = res;
return overflow;
}
#else
inline bool add_overflow(int v1, int v2, int *r)
{
qint64 t = qint64(v1) + v2;
*r = static_cast<int>(t);
return t > std::numeric_limits<int>::max() || t < std::numeric_limits<int>::min();
}
#endif
#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || QT_HAS_BUILTIN(__builtin_ssub_overflow)
inline bool sub_overflow(int v1, int v2, int *r)
{ return __builtin_ssub_overflow(v1, v2, r); }
#elif defined(Q_CC_GNU) && defined(Q_PROCESSOR_X86)
inline bool sub_overflow(int v1, int v2, int *r)
{
quint8 overflow = 0;
int res = v1;
asm ("subl %2, %1\n"
"seto %0"
: "=q" (overflow), "=r" (res)
: "r" (v2), "1" (res)
: "cc"
);
*r = res;
return overflow;
}
#else
inline bool sub_overflow(int v1, int v2, int *r)
{
qint64 t = qint64(v1) - v2;
*r = static_cast<int>(t);
return t > std::numeric_limits<int>::max() || t < std::numeric_limits<int>::min();
}
#endif
#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || QT_HAS_BUILTIN(__builtin_smul_overflow)
inline bool mul_overflow(int v1, int v2, int *r)
{ return __builtin_smul_overflow(v1, v2, r); }
#elif defined(Q_CC_GNU) && defined(Q_PROCESSOR_X86)
inline bool mul_overflow(int v1, int v2, int *r)
{
quint8 overflow = 0;
int res = v1;
asm ("imul %2, %1\n"
"seto %0"
: "=q" (overflow), "=r" (res)
: "r" (v2), "1" (res)
: "cc"
);
*r = res;
return overflow;
}
#else
inline bool mul_overflow(int v1, int v2, int *r)
{
qint64 t = qint64(v1) * v2;
*r = static_cast<int>(t);
return t > std::numeric_limits<int>::max() || t < std::numeric_limits<int>::min();
}
#endif
}
QT_END_NAMESPACE

View File

@ -55,6 +55,7 @@ private slots:
void addOverflow();
void mulOverflow_data();
void mulOverflow();
void signedOverflow();
};
void tst_QNumeric::fuzzyCompare_data()
@ -378,5 +379,35 @@ void tst_QNumeric::mulOverflow()
MulOverflowDispatch<quint64>()();
}
void tst_QNumeric::signedOverflow()
{
const int minInt = std::numeric_limits<int>::min();
const int maxInt = std::numeric_limits<int>::max();
int r;
QCOMPARE(add_overflow(minInt + 1, int(-1), &r), false);
QCOMPARE(add_overflow(minInt, int(-1), &r), true);
QCOMPARE(add_overflow(minInt, minInt, &r), true);
QCOMPARE(add_overflow(maxInt - 1, int(1), &r), false);
QCOMPARE(add_overflow(maxInt, int(1), &r), true);
QCOMPARE(add_overflow(maxInt, maxInt, &r), true);
QCOMPARE(sub_overflow(minInt + 1, int(1), &r), false);
QCOMPARE(sub_overflow(minInt, int(1), &r), true);
QCOMPARE(sub_overflow(minInt, maxInt, &r), true);
QCOMPARE(sub_overflow(maxInt - 1, int(-1), &r), false);
QCOMPARE(sub_overflow(maxInt, int(-1), &r), true);
QCOMPARE(sub_overflow(maxInt, minInt, &r), true);
QCOMPARE(mul_overflow(minInt, int(1), &r), false);
QCOMPARE(mul_overflow(minInt, int(-1), &r), true);
QCOMPARE(mul_overflow(minInt, int(2), &r), true);
QCOMPARE(mul_overflow(minInt, minInt, &r), true);
QCOMPARE(mul_overflow(maxInt, int(1), &r), false);
QCOMPARE(mul_overflow(maxInt, int(-1), &r), false);
QCOMPARE(mul_overflow(maxInt, int(2), &r), true);
QCOMPARE(mul_overflow(maxInt, maxInt, &r), true);
}
QTEST_APPLESS_MAIN(tst_QNumeric)
#include "tst_qnumeric.moc"