summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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;
}