PointerLikeTypeTraits: Standardize NumLowBitsAvailable on static constexpr rather than anonymous enum

This is (more?) usable by GDB pretty printers and seems nicer to write.

There's one tricky caveat that in C++14 (LLVM's codebase today) the
static constexpr member declaration is not a definition - so odr use of
this constant requires an out of line definition, which won't be
provided (that'd make all these trait classes more annoyidng/expensive
to maintain). But the use of this constant in the library implementation
is/should always be in a non-odr context - only two unit tests needed to
be touched to cope with this/avoid odr using these constants.

Based on/expanded from D72590 by Christian Sigg.
This commit is contained in:
David Blaikie 2020-01-15 12:36:20 -08:00
parent 40942db2ce
commit 6bae49b565
8 changed files with 33 additions and 30 deletions

View File

@ -491,7 +491,7 @@ class NodeRef {
struct CacheAlignedPointerTraits { struct CacheAlignedPointerTraits {
static inline void *getAsVoidPointer(void *P) { return P; } static inline void *getAsVoidPointer(void *P) { return P; }
static inline void *getFromVoidPointer(void *P) { return P; } static inline void *getFromVoidPointer(void *P) { return P; }
enum { NumLowBitsAvailable = Log2CacheLine }; static constexpr int NumLowBitsAvailable = Log2CacheLine;
}; };
PointerIntPair<void*, Log2CacheLine, unsigned, CacheAlignedPointerTraits> pip; PointerIntPair<void*, Log2CacheLine, unsigned, CacheAlignedPointerTraits> pip;

View File

@ -94,7 +94,7 @@ struct PointerLikeTypeTraits<PointerEmbeddedInt<IntT, Bits>> {
return T(reinterpret_cast<uintptr_t>(P), typename T::RawValueTag()); return T(reinterpret_cast<uintptr_t>(P), typename T::RawValueTag());
} }
enum { NumLowBitsAvailable = T::Shift }; static constexpr int NumLowBitsAvailable = T::Shift;
}; };
// Teach DenseMap how to use PointerEmbeddedInt objects as keys if the Int type // Teach DenseMap how to use PointerEmbeddedInt objects as keys if the Int type

View File

@ -235,7 +235,8 @@ struct PointerLikeTypeTraits<
return PointerIntPair<PointerTy, IntBits, IntType>::getFromOpaqueValue(P); return PointerIntPair<PointerTy, IntBits, IntType>::getFromOpaqueValue(P);
} }
enum { NumLowBitsAvailable = PtrTraits::NumLowBitsAvailable - IntBits }; static constexpr int NumLowBitsAvailable =
PtrTraits::NumLowBitsAvailable - IntBits;
}; };
} // end namespace llvm } // end namespace llvm

View File

@ -69,7 +69,7 @@ public:
return (User *)P; return (User *)P;
} }
enum { NumLowBitsAvailable = 1 }; static constexpr int NumLowBitsAvailable = 1;
}; };
// A type for the word following an array of hung-off Uses in memory, which is // A type for the word following an array of hung-off Uses in memory, which is
@ -85,7 +85,7 @@ public:
return (Use **)P; return (Use **)P;
} }
enum { NumLowBitsAvailable = 2 }; static constexpr int NumLowBitsAvailable = 2;
}; };
private: private:

View File

@ -56,7 +56,8 @@ template <typename T> struct PointerLikeTypeTraits<T *> {
static inline void *getAsVoidPointer(T *P) { return P; } static inline void *getAsVoidPointer(T *P) { return P; }
static inline T *getFromVoidPointer(void *P) { return static_cast<T *>(P); } static inline T *getFromVoidPointer(void *P) { return static_cast<T *>(P); }
enum { NumLowBitsAvailable = detail::ConstantLog2<alignof(T)>::value }; static constexpr int NumLowBitsAvailable =
detail::ConstantLog2<alignof(T)>::value;
}; };
template <> struct PointerLikeTypeTraits<void *> { template <> struct PointerLikeTypeTraits<void *> {
@ -70,7 +71,7 @@ template <> struct PointerLikeTypeTraits<void *> {
/// ///
/// All clients should use assertions to do a run-time check to ensure that /// All clients should use assertions to do a run-time check to ensure that
/// this is actually true. /// this is actually true.
enum { NumLowBitsAvailable = 2 }; static constexpr int NumLowBitsAvailable = 2;
}; };
// Provide PointerLikeTypeTraits for const things. // Provide PointerLikeTypeTraits for const things.
@ -83,7 +84,7 @@ template <typename T> struct PointerLikeTypeTraits<const T> {
static inline const T getFromVoidPointer(const void *P) { static inline const T getFromVoidPointer(const void *P) {
return NonConst::getFromVoidPointer(const_cast<void *>(P)); return NonConst::getFromVoidPointer(const_cast<void *>(P));
} }
enum { NumLowBitsAvailable = NonConst::NumLowBitsAvailable }; static constexpr int NumLowBitsAvailable = NonConst::NumLowBitsAvailable;
}; };
// Provide PointerLikeTypeTraits for const pointers. // Provide PointerLikeTypeTraits for const pointers.
@ -96,7 +97,7 @@ template <typename T> struct PointerLikeTypeTraits<const T *> {
static inline const T *getFromVoidPointer(const void *P) { static inline const T *getFromVoidPointer(const void *P) {
return NonConst::getFromVoidPointer(const_cast<void *>(P)); return NonConst::getFromVoidPointer(const_cast<void *>(P));
} }
enum { NumLowBitsAvailable = NonConst::NumLowBitsAvailable }; static constexpr int NumLowBitsAvailable = NonConst::NumLowBitsAvailable;
}; };
// Provide PointerLikeTypeTraits for uintptr_t. // Provide PointerLikeTypeTraits for uintptr_t.
@ -108,7 +109,7 @@ template <> struct PointerLikeTypeTraits<uintptr_t> {
return reinterpret_cast<uintptr_t>(P); return reinterpret_cast<uintptr_t>(P);
} }
// No bits are available! // No bits are available!
enum { NumLowBitsAvailable = 0 }; static constexpr int NumLowBitsAvailable = 0;
}; };
/// Provide suitable custom traits struct for function pointers. /// Provide suitable custom traits struct for function pointers.
@ -121,7 +122,8 @@ template <> struct PointerLikeTypeTraits<uintptr_t> {
/// potentially use alignment attributes on functions to satisfy that. /// potentially use alignment attributes on functions to satisfy that.
template <int Alignment, typename FunctionPointerT> template <int Alignment, typename FunctionPointerT>
struct FunctionPointerLikeTypeTraits { struct FunctionPointerLikeTypeTraits {
enum { NumLowBitsAvailable = detail::ConstantLog2<Alignment>::value }; static constexpr int NumLowBitsAvailable =
detail::ConstantLog2<Alignment>::value;
static inline void *getAsVoidPointer(FunctionPointerT P) { static inline void *getAsVoidPointer(FunctionPointerT P) {
assert((reinterpret_cast<uintptr_t>(P) & assert((reinterpret_cast<uintptr_t>(P) &
~((uintptr_t)-1 << NumLowBitsAvailable)) == 0 && ~((uintptr_t)-1 << NumLowBitsAvailable)) == 0 &&

View File

@ -77,7 +77,7 @@ class GlobalsAAResult::FunctionInfo {
static inline AlignedMap *getFromVoidPointer(void *P) { static inline AlignedMap *getFromVoidPointer(void *P) {
return (AlignedMap *)P; return (AlignedMap *)P;
} }
enum { NumLowBitsAvailable = 3 }; static constexpr int NumLowBitsAvailable = 3;
static_assert(alignof(AlignedMap) >= (1 << NumLowBitsAvailable), static_assert(alignof(AlignedMap) >= (1 << NumLowBitsAvailable),
"AlignedMap insufficiently aligned to have enough low bits."); "AlignedMap insufficiently aligned to have enough low bits.");
}; };

View File

@ -17,8 +17,8 @@ TEST(PointerEmbeddedIntTest, Basic) {
EXPECT_EQ(42, I); EXPECT_EQ(42, I);
EXPECT_EQ(43, I + 1); EXPECT_EQ(43, I + 1);
EXPECT_EQ(sizeof(uintptr_t) * CHAR_BIT - CHAR_BIT, EXPECT_EQ((int)sizeof(uintptr_t) * CHAR_BIT - CHAR_BIT,
PointerLikeTypeTraits<decltype(I)>::NumLowBitsAvailable); (int)PointerLikeTypeTraits<decltype(I)>::NumLowBitsAvailable);
EXPECT_FALSE(I == J); EXPECT_FALSE(I == J);
EXPECT_TRUE(I != J); EXPECT_TRUE(I != J);

View File

@ -72,22 +72,22 @@ TEST(PointerIntPairTest, DefaultInitialize) {
EXPECT_EQ(0U, Pair.getInt()); EXPECT_EQ(0U, Pair.getInt());
} }
// In real code this would be a word-sized integer limited to 31 bits.
struct Fixnum31 {
uintptr_t Value;
};
struct FixnumPointerTraits {
static inline void *getAsVoidPointer(Fixnum31 Num) {
return reinterpret_cast<void *>(Num.Value << NumLowBitsAvailable);
}
static inline Fixnum31 getFromVoidPointer(void *P) {
// In real code this would assert that the value is in range.
return {reinterpret_cast<uintptr_t>(P) >> NumLowBitsAvailable};
}
static constexpr int NumLowBitsAvailable =
std::numeric_limits<uintptr_t>::digits - 31;
};
TEST(PointerIntPairTest, ManyUnusedBits) { TEST(PointerIntPairTest, ManyUnusedBits) {
// In real code this would be a word-sized integer limited to 31 bits.
struct Fixnum31 {
uintptr_t Value;
};
class FixnumPointerTraits {
public:
static inline void *getAsVoidPointer(Fixnum31 Num) {
return reinterpret_cast<void *>(Num.Value << NumLowBitsAvailable);
}
static inline Fixnum31 getFromVoidPointer(void *P) {
// In real code this would assert that the value is in range.
return { reinterpret_cast<uintptr_t>(P) >> NumLowBitsAvailable };
}
enum { NumLowBitsAvailable = std::numeric_limits<uintptr_t>::digits - 31 };
};
PointerIntPair<Fixnum31, 1, bool, FixnumPointerTraits> pair; PointerIntPair<Fixnum31, 1, bool, FixnumPointerTraits> pair;
EXPECT_EQ((uintptr_t)0, pair.getPointer().Value); EXPECT_EQ((uintptr_t)0, pair.getPointer().Value);
@ -98,7 +98,7 @@ TEST(PointerIntPairTest, ManyUnusedBits) {
EXPECT_TRUE(pair.getInt()); EXPECT_TRUE(pair.getInt());
EXPECT_EQ(FixnumPointerTraits::NumLowBitsAvailable - 1, EXPECT_EQ(FixnumPointerTraits::NumLowBitsAvailable - 1,
PointerLikeTypeTraits<decltype(pair)>::NumLowBitsAvailable); (int)PointerLikeTypeTraits<decltype(pair)>::NumLowBitsAvailable);
static_assert( static_assert(
is_trivially_copyable< is_trivially_copyable<