aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathan Wakely <jwakely@redhat.com>2018-08-30 15:07:35 +0000
committerJonathan Wakely <jwakely@redhat.com>2018-08-30 15:07:35 +0000
commit1a6131488ff9c563c644b287d97e93094a71951e (patch)
tree111035d748bf025dcc5d4a417edde0802112332d
parent79a60846e3eef5c89edfe2aac1e0744c9ded5d00 (diff)
Avoid undefined shifts in ceil2 operations
For values where the result cannot be represented the shift width would be equal to the width of the type, which is undefined. Perform two well-defined shifts instead of one possible undefined shift. * include/bits/hashtable_policy.h (__clp2): Fix calculation for LLP64 targets where sizeof(size_t) > sizeof(long). Avoid undefined shifts of the number of bits in the type. * include/std/bit (__ceil2): Avoid undefined shifts. * testsuite/26_numerics/bit/bit.pow.two/ceil2.cc: Test values with the most signifiant bit set. git-svn-id: https://gcc.gnu.org/svn/gcc/trunk@263986 138bc75d-0d04-0410-961f-82ee72b054a4
-rw-r--r--libstdc++-v3/ChangeLog7
-rw-r--r--libstdc++-v3/include/bits/hashtable_policy.h7
-rw-r--r--libstdc++-v3/include/std/bit6
-rw-r--r--libstdc++-v3/testsuite/26_numerics/bit/bit.pow.two/ceil2.cc8
4 files changed, 24 insertions, 4 deletions
diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog
index a198549f0f3..98288521abd 100644
--- a/libstdc++-v3/ChangeLog
+++ b/libstdc++-v3/ChangeLog
@@ -1,5 +1,12 @@
2018-08-30 Jonathan Wakely <jwakely@redhat.com>
+ * include/bits/hashtable_policy.h (__clp2): Fix calculation for LLP64
+ targets where sizeof(size_t) > sizeof(long). Avoid undefined shifts
+ of the number of bits in the type.
+ * include/std/bit (__ceil2): Avoid undefined shifts.
+ * testsuite/26_numerics/bit/bit.pow.two/ceil2.cc: Test values with
+ the most signifiant bit set.
+
* config/abi/pre/gnu.ver: Add missing exports for mingw.
* include/ext/pointer.h (_Pointer_adapter): Define operators for
diff --git a/libstdc++-v3/include/bits/hashtable_policy.h b/libstdc++-v3/include/bits/hashtable_policy.h
index d7497711071..66fbfbe5f21 100644
--- a/libstdc++-v3/include/bits/hashtable_policy.h
+++ b/libstdc++-v3/include/bits/hashtable_policy.h
@@ -511,8 +511,11 @@ namespace __detail
// Equivalent to return __n ? std::ceil2(__n) : 0;
if (__n < 2)
return __n;
- return 1ul << (numeric_limits<unsigned long>::digits
- - __builtin_clzl(__n - 1ul));
+ const unsigned __lz = sizeof(size_t) > sizeof(long)
+ ? __builtin_clzll(__n - 1ull)
+ : __builtin_clzl(__n - 1ul);
+ // Doing two shifts avoids undefined behaviour when __lz == 0.
+ return (size_t(1) << (numeric_limits<size_t>::digits - __lz - 1)) << 1;
}
/// Rehash policy providing power of 2 bucket numbers. Avoids modulo
diff --git a/libstdc++-v3/include/std/bit b/libstdc++-v3/include/std/bit
index 0aebac28758..bc2ade75b35 100644
--- a/libstdc++-v3/include/std/bit
+++ b/libstdc++-v3/include/std/bit
@@ -195,9 +195,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
__ceil2(_Tp __x) noexcept
{
constexpr auto _Nd = numeric_limits<_Tp>::digits;
- if (__x == 0)
+ if (__x == 0 || __x == 1)
return 1;
- return (_Tp)1u << (_Nd - std::__countl_zero((_Tp)(__x - 1u)));
+ const unsigned __n = _Nd - std::__countl_zero((_Tp)(__x - 1u));
+ const _Tp __y_2 = (_Tp)1u << (__n - 1u);
+ return __y_2 << 1u;
}
template<typename _Tp>
diff --git a/libstdc++-v3/testsuite/26_numerics/bit/bit.pow.two/ceil2.cc b/libstdc++-v3/testsuite/26_numerics/bit/bit.pow.two/ceil2.cc
index 65e1569c277..e41f82c8bb8 100644
--- a/libstdc++-v3/testsuite/26_numerics/bit/bit.pow.two/ceil2.cc
+++ b/libstdc++-v3/testsuite/26_numerics/bit/bit.pow.two/ceil2.cc
@@ -55,6 +55,14 @@ test(UInt x)
static_assert( std::ceil2(UInt(3) << 64) == (UInt(4) << 64) );
}
+ constexpr UInt msb = UInt(1) << (std::numeric_limits<UInt>::digits - 1);
+ static_assert( std::ceil2( msb ) == msb );
+ // Larger values cannot be represented so the return value is unspecified,
+ // but must still be valid in constant expressions, i.e. not undefined.
+ static_assert( std::ceil2( UInt(msb + 1) ) != 77 );
+ static_assert( std::ceil2( UInt(msb + 2) ) != 77 );
+ static_assert( std::ceil2( UInt(msb + 77) ) != 77 );
+
return true;
}