diff options
author | Thiago Macieira <thiago.macieira@intel.com> | 2018-12-14 12:34:55 -0800 |
---|---|---|
committer | Thiago Macieira <thiago.macieira@intel.com> | 2018-12-21 17:48:56 +0000 |
commit | a7cea16005ea657d5de82d99a1f716a267eaf9bb (patch) | |
tree | 952856a24a51c592380e533fdbf84944c0b94950 /src/corelib/global | |
parent | 9ab04795e2eb8ae3fdb6ab6ef75f26db9d25e876 (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')
-rw-r--r-- | src/corelib/global/qnumeric_p.h | 14 |
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 } |