summaryrefslogtreecommitdiffstats
path: root/src/corelib/global/qnumeric_p.h
diff options
context:
space:
mode:
authorThiago Macieira <thiago.macieira@intel.com>2018-12-14 12:34:55 -0800
committerThiago Macieira <thiago.macieira@intel.com>2018-12-21 17:48:56 +0000
commita7cea16005ea657d5de82d99a1f716a267eaf9bb (patch)
tree952856a24a51c592380e533fdbf84944c0b94950 /src/corelib/global/qnumeric_p.h
parent9ab04795e2eb8ae3fdb6ab6ef75f26db9d25e876 (diff)
MSVC x86: implement add_overflow for quint64
There's no 64-bit ADD instruction, so we make do with ADD+ADC. This is what Clang generates. ICC uses the two as well, but then performs some subtractions to find out if it overflowed. GCC for some inexplicable reason attempts to use SSE2 if that's enabled, otherwise it performs the subtractions like ICC. Alternative implementation which generates better code, but violates strict aliasing: uint *low = reinterpret_cast<uint *>(r); uint *high = low + 1; return _addcarry_u32(_addcarry_u32(0, unsigned(v1), unsigned(v2), low), v1 >> 32, v2 >> 32, high); Manual testing shows this works. tst_qnumeric passes in debug mode. MSVC 2017 15.9 still miscompiles in release mode (reported to MS as [1]). [1] https://developercommunity.visualstudio.com/content/problem/409039/-addcarry-u32-wrong-results-with-constant-inputs.html Change-Id: I61ce366d57bc46c89db5fffd15704d53ebd4af3c Reviewed-by: Thomas Miller <thomaslmiller91@gmail.com> Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'src/corelib/global/qnumeric_p.h')
-rw-r--r--src/corelib/global/qnumeric_p.h14
1 files changed, 11 insertions, 3 deletions
diff --git a/src/corelib/global/qnumeric_p.h b/src/corelib/global/qnumeric_p.h
index 0a6db9afcd..c762da80d3 100644
--- a/src/corelib/global/qnumeric_p.h
+++ b/src/corelib/global/qnumeric_p.h
@@ -373,10 +373,18 @@ template <> inline bool add_overflow(unsigned v1, unsigned v2, unsigned *r)
// 32-bit mul_overflow is fine with the generic code above
-# if 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)); }
-# endif // x86-64
+{
+# if defined(Q_PROCESSOR_X86_64)
+ return _addcarry_u64(0, v1, v2, reinterpret_cast<unsigned __int64 *>(r));
+# else
+ uint low, high;
+ uchar carry = _addcarry_u32(0, unsigned(v1), unsigned(v2), &low);
+ carry = _addcarry_u32(carry, v1 >> 32, v2 >> 32, &high);
+ *r = (quint64(high) << 32) | low;
+ return carry;
+# endif // !x86-64
+}
# endif // MSVC X86
#endif // !GCC
}