diff --git a/llvm/include/llvm/ADT/BitmaskEnum.h b/llvm/include/llvm/ADT/BitmaskEnum.h --- a/llvm/include/llvm/ADT/BitmaskEnum.h +++ b/llvm/include/llvm/ADT/BitmaskEnum.h @@ -41,6 +41,33 @@ #define LLVM_MARK_AS_BITMASK_ENUM(LargestValue) \ LLVM_BITMASK_LARGEST_ENUMERATOR = LargestValue +/// LLVM_DECLARE_ENUM_AS_BITMASK can be used to declare an enum type as a bit +/// set, so that bitwise operation on such enum does not require static_cast. +/// +/// \code +/// enum MyEnum { E1 = 1, E2 = 2, E3 = 4, E4 = 8 }; +/// LLVM_DECLARE_ENUM_AS_BITMASK(MyEnum, E4); +/// +/// void Foo() { +/// MyEnum A = (E1 | E2) & E3 ^ ~E4; // No static_cast +/// } +/// \endcode +/// +/// The second parameter to LLVM_DECLARE_ENUM_AS_BITMASK specifies the largest +/// bit value of the enum type. +/// +/// LLVM_DECLARE_ENUM_AS_BITMASK should be used in global or llvm namespace. +/// +/// This a non-intrusive alternative for LLVM_MARK_AS_BITMASK_ENUM. It allows +/// declaring more than one non-scoped enumerations as bitmask types in the same +/// scope. Otherwise it provides the same functionality as +/// LLVM_MARK_AS_BITMASK_ENUM. +#define LLVM_DECLARE_ENUM_AS_BITMASK(Enum, LargestValue) \ + template <> struct llvm::is_bitmask_enum : std::true_type {}; \ + template <> struct llvm::largest_bitmask_enum_bit { \ + static constexpr std::underlying_type_t value = LargestValue; \ + } + /// LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE() pulls the operator overloads used /// by LLVM_MARK_AS_BITMASK_ENUM into the current namespace. /// @@ -73,6 +100,18 @@ struct is_bitmask_enum< E, std::enable_if_t= 0>> : std::true_type {}; + +/// Trait class to determine bitmask enumeration largest bit. +template struct largest_bitmask_enum_bit; + +template +struct largest_bitmask_enum_bit< + E, std::enable_if_t= 0>> { + using UnderlyingTy = std::underlying_type_t; + static constexpr UnderlyingTy value = + static_cast(E::LLVM_BITMASK_LARGEST_ENUMERATOR); +}; + namespace BitmaskEnumDetail { /// Get a bitmask with 1s in all places up to the high-order bit of E's largest @@ -80,9 +119,7 @@ template constexpr std::underlying_type_t Mask() { // On overflow, NextPowerOf2 returns zero with the type uint64_t, so // subtracting 1 gives us the mask with all bits set, like we want. - return NextPowerOf2(static_cast>( - E::LLVM_BITMASK_LARGEST_ENUMERATOR)) - - 1; + return NextPowerOf2(largest_bitmask_enum_bit::value) - 1; } /// Check that Val is in range for E, and return Val cast to E's underlying diff --git a/llvm/unittests/ADT/BitmaskEnumTest.cpp b/llvm/unittests/ADT/BitmaskEnumTest.cpp --- a/llvm/unittests/ADT/BitmaskEnumTest.cpp +++ b/llvm/unittests/ADT/BitmaskEnumTest.cpp @@ -21,12 +21,30 @@ LLVM_MARK_AS_BITMASK_ENUM(F4) }; +static_assert(is_bitmask_enum::value != 0); +static_assert(largest_bitmask_enum_bit::value == Flags::F4); + +enum Flags2 { V0 = 0, V1 = 1, V2 = 2, V3 = 4, V4 = 8 }; +} // namespace + +LLVM_DECLARE_ENUM_AS_BITMASK(Flags2, V4); + +static_assert(is_bitmask_enum::value != 0); +static_assert(largest_bitmask_enum_bit::value == Flags::F4); + +namespace { TEST(BitmaskEnumTest, BitwiseOr) { Flags f = F1 | F2; EXPECT_EQ(3, f); f = f | F3; EXPECT_EQ(7, f); + + Flags2 f2 = V1 | V2; + EXPECT_EQ(3, f2); + + f2 = f2 | V3; + EXPECT_EQ(7, f2); } TEST(BitmaskEnumTest, BitwiseOrEquals) { @@ -38,6 +56,14 @@ f = F2; (f |= F3) = F1; EXPECT_EQ(F1, f); + + Flags2 f2 = V1; + f2 |= V3; + EXPECT_EQ(5, f2); + + f2 = V2; + (f2 |= V3) = V1; + EXPECT_EQ(V1, f2); } TEST(BitmaskEnumTest, BitwiseAnd) { @@ -46,6 +72,12 @@ f = (f | F3) & (F1 | F2 | F3); EXPECT_EQ(6, f); + + Flags2 f2 = static_cast(3) & V2; + EXPECT_EQ(V2, f2); + + f2 = (f2 | V3) & (V1 | V2 | V3); + EXPECT_EQ(6, f2); } TEST(BitmaskEnumTest, BitwiseAndEquals) { @@ -56,6 +88,13 @@ // &= should return a reference to the LHS. (f &= F1) = F3; EXPECT_EQ(F3, f); + + Flags2 f2 = V1 | V2 | V3; + f2 &= V1 | V2; + EXPECT_EQ(3, f2); + + (f2 &= V1) = V3; + EXPECT_EQ(V3, f2); } TEST(BitmaskEnumTest, BitwiseXor) { @@ -64,6 +103,12 @@ f = f ^ F1; EXPECT_EQ(4, f); + + Flags2 f2 = (V1 | V2) ^ (V2 | V3); + EXPECT_EQ(5, f2); + + f2 = f2 ^ V1; + EXPECT_EQ(4, f2); } TEST(BitmaskEnumTest, BitwiseXorEquals) { @@ -74,6 +119,13 @@ // ^= should return a reference to the LHS. (f ^= F4) = F3; EXPECT_EQ(F3, f); + + Flags2 f2 = (V1 | V2); + f2 ^= (V2 | V4); + EXPECT_EQ(9, f2); + + (f2 ^= V4) = V3; + EXPECT_EQ(V3, f2); } TEST(BitmaskEnumTest, ConstantExpression) { @@ -85,12 +137,25 @@ EXPECT_EQ(f2, F1 | F2); EXPECT_EQ(f3, F1 & F2); EXPECT_EQ(f4, F1 ^ F2); + + constexpr Flags2 f21 = ~V1; + constexpr Flags2 f22 = V1 | V2; + constexpr Flags2 f23 = V1 & V2; + constexpr Flags2 f24 = V1 ^ V2; + EXPECT_EQ(f21, ~V1); + EXPECT_EQ(f22, V1 | V2); + EXPECT_EQ(f23, V1 & V2); + EXPECT_EQ(f24, V1 ^ V2); } TEST(BitmaskEnumTest, BitwiseNot) { Flags f = ~F1; EXPECT_EQ(14, f); // Largest value for f is 15. EXPECT_EQ(15, ~F0); + + Flags2 f2 = ~V1; + EXPECT_EQ(14, f2); + EXPECT_EQ(15, ~V0); } enum class FlagsClass {