Skip to content

Commit cd561bf

Browse files
authored
[support] Make ScopedPrinter compatible with bitmask enums (#147512)
.. produced by ADT/BitmaskEnum.h. These aren't implicitly convertible to an integer, so I needed to tweak a couple of bitwise operations and add an explicit constructor for HexNumber and FlagEntry. Motivation: I'd like to use this in the SFrame data structures (#147264)
1 parent f0bc411 commit cd561bf

File tree

2 files changed

+45
-7
lines changed

2 files changed

+45
-7
lines changed

llvm/include/llvm/Support/ScopedPrinter.h

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "llvm/Support/Endian.h"
2020
#include "llvm/Support/JSON.h"
2121
#include "llvm/Support/raw_ostream.h"
22+
#include <type_traits>
2223

2324
namespace llvm {
2425

@@ -41,8 +42,8 @@ template <typename T> struct EnumEntry {
4142
struct HexNumber {
4243
// To avoid sign-extension we have to explicitly cast to the appropriate
4344
// unsigned type. The overloads are here so that every type that is implicitly
44-
// convertible to an integer (including enums and endian helpers) can be used
45-
// without requiring type traits or call-site changes.
45+
// convertible to an integer (including endian helpers) can be used without
46+
// requiring type traits or call-site changes.
4647
HexNumber(char Value) : Value(static_cast<unsigned char>(Value)) {}
4748
HexNumber(signed char Value) : Value(static_cast<unsigned char>(Value)) {}
4849
HexNumber(signed short Value) : Value(static_cast<unsigned short>(Value)) {}
@@ -55,6 +56,10 @@ struct HexNumber {
5556
HexNumber(unsigned int Value) : Value(Value) {}
5657
HexNumber(unsigned long Value) : Value(Value) {}
5758
HexNumber(unsigned long long Value) : Value(Value) {}
59+
template <typename EnumT, typename = std::enable_if_t<std::is_enum_v<EnumT>>>
60+
HexNumber(EnumT Value)
61+
: HexNumber(static_cast<std::underlying_type_t<EnumT>>(Value)) {}
62+
5863
uint64_t Value;
5964
};
6065

@@ -77,6 +82,10 @@ struct FlagEntry {
7782
FlagEntry(StringRef Name, unsigned long Value) : Name(Name), Value(Value) {}
7883
FlagEntry(StringRef Name, unsigned long long Value)
7984
: Name(Name), Value(Value) {}
85+
template <typename EnumT, typename = std::enable_if_t<std::is_enum_v<EnumT>>>
86+
FlagEntry(StringRef Name, EnumT Value)
87+
: FlagEntry(Name, static_cast<std::underlying_type_t<EnumT>>(Value)) {}
88+
8089
StringRef Name;
8190
uint64_t Value;
8291
};
@@ -165,17 +174,17 @@ class LLVM_ABI ScopedPrinter {
165174
SmallVector<FlagEntry, 10> SetFlags(ExtraFlags);
166175

167176
for (const auto &Flag : Flags) {
168-
if (Flag.Value == 0)
177+
if (Flag.Value == TFlag{})
169178
continue;
170179

171180
TFlag EnumMask{};
172-
if (Flag.Value & EnumMask1)
181+
if ((Flag.Value & EnumMask1) != TFlag{})
173182
EnumMask = EnumMask1;
174-
else if (Flag.Value & EnumMask2)
183+
else if ((Flag.Value & EnumMask2) != TFlag{})
175184
EnumMask = EnumMask2;
176-
else if (Flag.Value & EnumMask3)
185+
else if ((Flag.Value & EnumMask3) != TFlag{})
177186
EnumMask = EnumMask3;
178-
bool IsEnum = (Flag.Value & EnumMask) != 0;
187+
bool IsEnum = (Flag.Value & EnumMask) != TFlag{};
179188
if ((!IsEnum && (Value & Flag.Value) == Flag.Value) ||
180189
(IsEnum && (Value & EnumMask) == Flag.Value)) {
181190
SetFlags.emplace_back(Flag.Name, Flag.Value);

llvm/unittests/Support/ScopedPrinterTest.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,21 @@
88

99
#include "llvm/Support/ScopedPrinter.h"
1010
#include "llvm/ADT/APSInt.h"
11+
#include "llvm/ADT/BitmaskEnum.h"
1112
#include "llvm/Support/Format.h"
1213
#include "gtest/gtest.h"
1314
#include <cmath>
1415
#include <vector>
1516

17+
namespace {
18+
enum class BitmaskEnum : uint8_t {
19+
F1 = 0x1,
20+
F2 = 0x2,
21+
LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/F2),
22+
};
23+
LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();
24+
} // namespace
25+
1626
using namespace llvm;
1727

1828
TEST(JSONScopedPrinterTest, PrettyPrintCtor) {
@@ -261,6 +271,12 @@ TEST_F(ScopedPrinterTest, PrintFlag) {
261271
{"SecondByte2", "Second2", 0x20u}, {"SecondByte3", "Second3", 0x30u},
262272
{"ThirdByte1", "Third1", 0x100u}, {"ThirdByte2", "Third2", 0x200u},
263273
{"ThirdByte3", "Third3", 0x300u}};
274+
275+
const EnumEntry<BitmaskEnum> ScopedFlags[] = {
276+
{"F1", "AltF1", BitmaskEnum::F1},
277+
{"F2", "AltF2", BitmaskEnum::F2},
278+
};
279+
264280
W.printFlags("ZeroFlag", 0, ArrayRef(SingleBitFlags));
265281
W.printFlags("NoFlag", 1 << 3, ArrayRef(SingleBitFlags));
266282
W.printFlags("Flag1", SingleBitFlags[1].Value, ArrayRef(SingleBitFlags));
@@ -286,6 +302,7 @@ TEST_F(ScopedPrinterTest, PrintFlag) {
286302
FirstByteMask, SecondByteMask);
287303
W.printFlags("FirstSecondThirdByteMask", 0x333u, ArrayRef(EnumFlags),
288304
FirstByteMask, SecondByteMask, ThirdByteMask);
305+
W.printFlags("BitmaskEnum::F1", BitmaskEnum::F1, ArrayRef(ScopedFlags));
289306
};
290307

291308
const char *ExpectedOut = R"(ZeroFlag [ (0x0)
@@ -343,6 +360,9 @@ FirstSecondThirdByteMask [ (0x333)
343360
SecondByte3 (0x30)
344361
ThirdByte3 (0x300)
345362
]
363+
BitmaskEnum::F1 [ (0x1)
364+
F1 (0x1)
365+
]
346366
)";
347367

348368
const char *JSONExpectedOut = R"({
@@ -504,6 +524,15 @@ FirstSecondThirdByteMask [ (0x333)
504524
"Value": 768
505525
}
506526
]
527+
},
528+
"BitmaskEnum::F1": {
529+
"Value": 1,
530+
"Flags": [
531+
{
532+
"Name": "F1",
533+
"Value": 1
534+
}
535+
]
507536
}
508537
})";
509538
verifyAll(ExpectedOut, JSONExpectedOut, PrintFunc);

0 commit comments

Comments
 (0)