From 58fc1b50d8b0719460e1bf08734c0e7632fb03c4 Mon Sep 17 00:00:00 2001 From: Marshall Clow Date: Fri, 10 Feb 2017 20:49:08 +0000 Subject: [PATCH] Make lcm/gcd work better in edge cases. Fixes a UBSAN failure. llvm-svn: 294779 --- libcxx/include/experimental/numeric | 27 ++++++++++------- libcxx/include/numeric | 29 +++++++++++-------- .../numeric.ops/numeric.ops.gcd/gcd.pass.cpp | 7 +++++ .../numeric.ops/numeric.ops.lcm/lcm.pass.cpp | 8 +++++ .../numeric.ops/numeric.ops.gcd/gcd.pass.cpp | 1 - .../numeric.ops/numeric.ops.lcm/lcm.pass.cpp | 8 ++--- 6 files changed, 52 insertions(+), 28 deletions(-) diff --git a/libcxx/include/experimental/numeric b/libcxx/include/experimental/numeric index 832055365f92..d1209dbec15b 100644 --- a/libcxx/include/experimental/numeric +++ b/libcxx/include/experimental/numeric @@ -45,18 +45,23 @@ inline namespace fundamentals_v2 { _LIBCPP_BEGIN_NAMESPACE_LFTS_V2 -template ::value> struct __abs; +template ::value> struct __abs; -template -struct __abs<_Tp, true> { +template +struct __abs<_Result, _Source, true> { _LIBCPP_CONSTEXPR _LIBCPP_INLINE_VISIBILITY - _Tp operator()(_Tp __t) const noexcept { return __t >= 0 ? __t : -__t; } + _Result operator()(_Source __t) const noexcept + { + if (__t >= 0) return __t; + if (__t == numeric_limits<_Source>::min()) return -static_cast<_Result>(__t); + return -__t; + } }; -template -struct __abs<_Tp, false> { +template +struct __abs<_Result, _Source, false> { _LIBCPP_CONSTEXPR _LIBCPP_INLINE_VISIBILITY - _Tp operator()(_Tp __t) const noexcept { return __t; } + _Result operator()(_Source __t) const noexcept { return __t; } }; @@ -79,8 +84,8 @@ gcd(_Tp __m, _Up __n) static_assert((!is_same::type, bool>::value), "Second argument to gcd cannot be bool" ); using _Rp = common_type_t<_Tp,_Up>; using _Wp = make_unsigned_t<_Rp>; - return static_cast<_Rp>(__gcd(static_cast<_Wp>(__abs<_Tp>()(__m)), - static_cast<_Wp>(__abs<_Up>()(__n)))); + return static_cast<_Rp>(__gcd(static_cast<_Wp>(__abs<_Rp, _Tp>()(__m)), + static_cast<_Wp>(__abs<_Rp, _Up>()(__n)))); } template @@ -95,8 +100,8 @@ lcm(_Tp __m, _Up __n) return 0; using _Rp = common_type_t<_Tp,_Up>; - _Rp __val1 = __abs<_Tp>()(__m) / gcd(__m,__n); - _Up __val2 = __abs<_Up>()(__n); + _Rp __val1 = __abs<_Rp, _Tp>()(__m) / gcd(__m, __n); + _Rp __val2 = __abs<_Rp, _Up>()(__n); _LIBCPP_ASSERT((numeric_limits<_Rp>::max() / __val1 > __val2), "Overflow in lcm"); return __val1 * __val2; } diff --git a/libcxx/include/numeric b/libcxx/include/numeric index ff2853e917a6..8f25146938a4 100644 --- a/libcxx/include/numeric +++ b/libcxx/include/numeric @@ -201,18 +201,23 @@ iota(_ForwardIterator __first, _ForwardIterator __last, _Tp __value_) #if _LIBCPP_STD_VER > 14 -template ::value> struct __abs; +template ::value> struct __abs; -template -struct __abs<_Tp, true> { +template +struct __abs<_Result, _Source, true> { _LIBCPP_CONSTEXPR _LIBCPP_INLINE_VISIBILITY - _Tp operator()(_Tp __t) const noexcept { return __t >= 0 ? __t : -__t; } + _Result operator()(_Source __t) const noexcept + { + if (__t >= 0) return __t; + if (__t == numeric_limits<_Source>::min()) return -static_cast<_Result>(__t); + return -__t; + } }; -template -struct __abs<_Tp, false> { +template +struct __abs<_Result, _Source, false> { _LIBCPP_CONSTEXPR _LIBCPP_INLINE_VISIBILITY - _Tp operator()(_Tp __t) const noexcept { return __t; } + _Result operator()(_Source __t) const noexcept { return __t; } }; @@ -220,7 +225,7 @@ template _LIBCPP_CONSTEXPR _LIBCPP_INLINE_VISIBILITY _Tp __gcd(_Tp __m, _Tp __n) { - static_assert((!is_signed<_Tp>::value), "" ); + static_assert((!is_signed<_Tp>::value), ""); return __n == 0 ? __m : __gcd<_Tp>(__n, __m % __n); } @@ -235,8 +240,8 @@ gcd(_Tp __m, _Up __n) static_assert((!is_same::type, bool>::value), "Second argument to gcd cannot be bool" ); using _Rp = common_type_t<_Tp,_Up>; using _Wp = make_unsigned_t<_Rp>; - return static_cast<_Rp>(__gcd(static_cast<_Wp>(__abs<_Tp>()(__m)), - static_cast<_Wp>(__abs<_Up>()(__n)))); + return static_cast<_Rp>(__gcd(static_cast<_Wp>(__abs<_Rp, _Tp>()(__m)), + static_cast<_Wp>(__abs<_Rp, _Up>()(__n)))); } template @@ -251,8 +256,8 @@ lcm(_Tp __m, _Up __n) return 0; using _Rp = common_type_t<_Tp,_Up>; - _Rp __val1 = __abs<_Tp>()(__m) / gcd(__m,__n); - _Up __val2 = __abs<_Up>()(__n); + _Rp __val1 = __abs<_Rp, _Tp>()(__m) / gcd(__m, __n); + _Rp __val2 = __abs<_Rp, _Up>()(__n); _LIBCPP_ASSERT((numeric_limits<_Rp>::max() / __val1 > __val2), "Overflow in lcm"); return __val1 * __val2; } diff --git a/libcxx/test/std/experimental/numeric/numeric.ops/numeric.ops.gcd/gcd.pass.cpp b/libcxx/test/std/experimental/numeric/numeric.ops/numeric.ops.gcd/gcd.pass.cpp index a52b50b391cd..2c1a3b8f3fd6 100644 --- a/libcxx/test/std/experimental/numeric/numeric.ops/numeric.ops.gcd/gcd.pass.cpp +++ b/libcxx/test/std/experimental/numeric/numeric.ops/numeric.ops.gcd/gcd.pass.cpp @@ -129,4 +129,11 @@ int main() assert((do_test(non_cce))); assert((do_test(non_cce))); assert((do_test(non_cce))); + +// LWG#2792 + { + auto res = std::gcd((int64_t)1234, (int32_t)-2147483648); + static_assert( std::is_same::type>::value, ""); + assert(res == 2); + } } diff --git a/libcxx/test/std/experimental/numeric/numeric.ops/numeric.ops.lcm/lcm.pass.cpp b/libcxx/test/std/experimental/numeric/numeric.ops/numeric.ops.lcm/lcm.pass.cpp index e3c109f7447b..4dd139a8df8c 100644 --- a/libcxx/test/std/experimental/numeric/numeric.ops/numeric.ops.lcm/lcm.pass.cpp +++ b/libcxx/test/std/experimental/numeric/numeric.ops/numeric.ops.lcm/lcm.pass.cpp @@ -128,4 +128,12 @@ int main() assert((do_test(non_cce))); assert((do_test(non_cce))); assert((do_test(non_cce))); + +// LWG#2792 + { + auto res1 = std::lcm((int64_t)1234, (int32_t)-2147483648); + (void) std::lcm(INT_MIN, 2); // this used to trigger UBSAN + static_assert( std::is_same::type>::value, ""); + assert(res1 == 1324997410816LL); + } } diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.gcd/gcd.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.gcd/gcd.pass.cpp index 0774bff40d15..961b515ef8d8 100644 --- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.gcd/gcd.pass.cpp +++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.gcd/gcd.pass.cpp @@ -8,7 +8,6 @@ //===----------------------------------------------------------------------===// // // UNSUPPORTED: c++98, c++03, c++11, c++14 -// XFAIL: ubsan // diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.lcm/lcm.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.lcm/lcm.pass.cpp index a74ac76a3213..90d48398f54a 100644 --- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.lcm/lcm.pass.cpp +++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.lcm/lcm.pass.cpp @@ -8,7 +8,6 @@ //===----------------------------------------------------------------------===// // // UNSUPPORTED: c++98, c++03, c++11, c++14 -// XFAIL: ubsan // // template @@ -132,8 +131,9 @@ int main() // LWG#2837 { - auto res = std::lcm((int64_t)1234, (int32_t)-2147483648); - static_assert( std::is_same::type>::value, ""); - assert(res == -1324997410816LL); + auto res1 = std::lcm((int64_t)1234, (int32_t)-2147483648); + (void) std::lcm(INT_MIN, 2); // this used to trigger UBSAN + static_assert( std::is_same::type>::value, ""); + assert(res1 == 1324997410816LL); } }