Avoid undefined shifts in ceil2 operations
authorJonathan Wakely <jwakely@redhat.com>
Thu, 30 Aug 2018 15:07:35 +0000 (16:07 +0100)
committerJonathan Wakely <redi@gcc.gnu.org>
Thu, 30 Aug 2018 15:07:35 +0000 (16:07 +0100)
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.

From-SVN: r263986

libstdc++-v3/ChangeLog
libstdc++-v3/include/bits/hashtable_policy.h
libstdc++-v3/include/std/bit
libstdc++-v3/testsuite/26_numerics/bit/bit.pow.two/ceil2.cc

index a198549f0f3120fe78e90c5dd5fcad213dd539cd..98288521abd41eaa7b06af546bb058af039375ef 100644 (file)
@@ -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
index d7497711071bc02278dcb4fcbfbe79f7968bfb38..66fbfbe5f21746566abd6f608976a1f9bfa8056e 100644 (file)
@@ -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
index 0aebac2875861a7983a7e1f5dd59ffaf434e6350..bc2ade75b3586dd819f2328806f93af5a1cee2bf 100644 (file)
@@ -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>
index 65e1569c2778eddfaee946f2a7d79fc98a48435a..e41f82c8bb8314df816aa33ff9f0baa4085821a8 100644 (file)
@@ -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;
 }