Skip to content

Commit

Permalink
Avoid undefined shifts in ceil2 operations
Browse files Browse the repository at this point in the history
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: svn+ssh://gcc.gnu.org/svn/gcc/trunk@263986 138bc75d-0d04-0410-961f-82ee72b054a4
  • Loading branch information
redi committed Aug 30, 2018
1 parent abf0dec commit fe92dce
Show file tree
Hide file tree
Showing 4 changed files with 24 additions and 4 deletions.
7 changes: 7 additions & 0 deletions libstdc++-v3/ChangeLog
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
2018-08-30 Jonathan Wakely <[email protected]>

* 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
Expand Down
7 changes: 5 additions & 2 deletions libstdc++-v3/include/bits/hashtable_policy.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 4 additions & 2 deletions libstdc++-v3/include/std/bit
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
8 changes: 8 additions & 0 deletions libstdc++-v3/testsuite/26_numerics/bit/bit.pow.two/ceil2.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down

0 comments on commit fe92dce

Please sign in to comment.