Skip to content

Commit ca0a8d9

Browse files
committed
[orc-rt] Add bitmask-enum helper utilities.
ORC_RT_MARK_AS_BITMASK_ENUM and ORC_RT_DECLARE_ENUM_AS_BITMASK can be used to easily add support for bitmask operators (&, |, ^, ~) to enum types. This code was derived from LLVM's include/llvm/ADT/BitmaskEnum.h header.
1 parent 2380d0a commit ca0a8d9

File tree

4 files changed

+306
-0
lines changed

4 files changed

+306
-0
lines changed

orc-rt/include/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
set(ORC_RT_HEADERS
22
orc-rt-c/orc-rt.h
3+
orc-rt/bitmask-enum.h
34
orc-rt/math.h
45
orc-rt/span.h
56
)
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
//===---- bitmask-enum.h - Enable bitmask operations on enums ---*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// Provides utilities for easily adding bitmask operation support to enums.
10+
//
11+
// This code was derived from LLVM's include/llvm/ADT/BitmaskEnum.h header, and
12+
// adapted for the ORC runtime.
13+
//
14+
//===----------------------------------------------------------------------===//
15+
16+
#ifndef ORC_RT_BITMASK_ENUM_H
17+
#define ORC_RT_BITMASK_ENUM_H
18+
19+
#include "math.h"
20+
21+
#include <cassert>
22+
#include <type_traits>
23+
24+
namespace orc_rt {
25+
26+
/// ORC_RT_MARK_AS_BITMASK_ENUM lets you opt in an individual enum type so you
27+
/// can perform bitwise operations on it without putting static_cast everywhere.
28+
///
29+
/// \code
30+
/// enum MyEnum {
31+
/// E1 = 1, E2 = 2, E3 = 4, E4 = 8,
32+
/// ORC_RT_MARK_AS_BITMASK_ENUM(/* LargestValue = */ E4)
33+
/// };
34+
///
35+
/// void Foo() {
36+
/// MyEnum A = (E1 | E2) & E3 ^ ~E4; // Look, ma: No static_cast!
37+
/// }
38+
/// \endcode
39+
///
40+
/// Normally when you do a bitwise operation on an enum value, you get back an
41+
/// instance of the underlying type (e.g. int). But using this macro, bitwise
42+
/// ops on your enum will return you back instances of the enum. This is
43+
/// particularly useful for enums which represent a combination of flags.
44+
///
45+
/// The parameter to ORC_RT_MARK_AS_BITMASK_ENUM should be the largest
46+
/// individual value in your enum.
47+
///
48+
/// All of the enum's values must be non-negative.
49+
#define ORC_RT_MARK_AS_BITMASK_ENUM(LargestValue) \
50+
ORC_RT_BITMASK_LARGEST_ENUMERATOR = LargestValue
51+
52+
/// ORC_RT_DECLARE_ENUM_AS_BITMASK can be used to declare an enum type as a bit
53+
/// set, so that bitwise operation on such enum does not require static_cast.
54+
///
55+
/// \code
56+
/// enum MyEnum { E1 = 1, E2 = 2, E3 = 4, E4 = 8 };
57+
/// ORC_RT_DECLARE_ENUM_AS_BITMASK(MyEnum, E4);
58+
///
59+
/// void Foo() {
60+
/// MyEnum A = (E1 | E2) & E3 ^ ~E4; // No static_cast
61+
/// }
62+
/// \endcode
63+
///
64+
/// The second parameter to ORC_RT_DECLARE_ENUM_AS_BITMASK specifies the largest
65+
/// bit value of the enum type.
66+
///
67+
/// ORC_RT_DECLARE_ENUM_AS_BITMASK should be used in __orc_rt namespace.
68+
///
69+
/// This a non-intrusive alternative for ORC_RT_MARK_AS_BITMASK_ENUM. It allows
70+
/// declaring more than one non-scoped enumerations as bitmask types in the same
71+
/// scope. Otherwise it provides the same functionality as
72+
/// ORC_RT_MARK_AS_BITMASK_ENUM.
73+
#define ORC_RT_DECLARE_ENUM_AS_BITMASK(Enum, LargestValue) \
74+
template <> struct is_bitmask_enum<Enum> : std::true_type {}; \
75+
template <> struct largest_bitmask_enum_bit<Enum> { \
76+
static constexpr std::underlying_type_t<Enum> value = LargestValue; \
77+
}
78+
79+
/// Traits class to determine whether an enum has been declared as a bitwise
80+
/// enum via ORC_RT_DECLARE_ENUM_AS_BITMASK.
81+
template <typename E, typename Enable = void>
82+
struct is_bitmask_enum : std::false_type {};
83+
84+
template <typename E>
85+
struct is_bitmask_enum<
86+
E, std::enable_if_t<sizeof(E::ORC_RT_BITMASK_LARGEST_ENUMERATOR) >= 0>>
87+
: std::true_type {};
88+
89+
template <typename E>
90+
inline constexpr bool is_bitmask_enum_v = is_bitmask_enum<E>::value;
91+
92+
/// Traits class to deermine bitmask enum largest bit.
93+
template <typename E, typename Enable = void> struct largest_bitmask_enum_bit;
94+
95+
template <typename E>
96+
struct largest_bitmask_enum_bit<
97+
E, std::enable_if_t<sizeof(E::ORC_RT_BITMASK_LARGEST_ENUMERATOR) >= 0>> {
98+
using UnderlyingTy = std::underlying_type_t<E>;
99+
static constexpr UnderlyingTy value =
100+
static_cast<UnderlyingTy>(E::ORC_RT_BITMASK_LARGEST_ENUMERATOR);
101+
};
102+
103+
template <typename E>
104+
constexpr std::underlying_type_t<E> bitmask_enum_mask() noexcept {
105+
return nextPowerOf2(largest_bitmask_enum_bit<E>::value) - 1;
106+
}
107+
108+
template <typename E>
109+
constexpr std::underlying_type_t<E> bitmask_enum_to_underlying(E Val) noexcept {
110+
auto U = static_cast<std::underlying_type_t<E>>(Val);
111+
assert(U >= 0 && "Negative enum values are not allowed");
112+
assert(U <= bitmask_enum_mask<E>() &&
113+
"Enum value too large (or langest val too small");
114+
return U;
115+
}
116+
117+
template <typename E, typename = std::enable_if_t<is_bitmask_enum_v<E>>>
118+
constexpr E operator~(E Val) noexcept {
119+
return static_cast<E>(~bitmask_enum_to_underlying(Val) &
120+
bitmask_enum_mask<E>());
121+
}
122+
123+
template <typename E, typename = std::enable_if_t<is_bitmask_enum_v<E>>>
124+
constexpr E operator|(E LHS, E RHS) noexcept {
125+
return static_cast<E>(bitmask_enum_to_underlying(LHS) |
126+
bitmask_enum_to_underlying(RHS));
127+
}
128+
129+
template <typename E, typename = std::enable_if_t<is_bitmask_enum_v<E>>>
130+
constexpr E operator&(E LHS, E RHS) noexcept {
131+
return static_cast<E>(bitmask_enum_to_underlying(LHS) &
132+
bitmask_enum_to_underlying(RHS));
133+
}
134+
135+
template <typename E, typename = std::enable_if_t<is_bitmask_enum_v<E>>>
136+
constexpr E operator^(E LHS, E RHS) noexcept {
137+
return static_cast<E>(bitmask_enum_to_underlying(LHS) ^
138+
bitmask_enum_to_underlying(RHS));
139+
}
140+
141+
template <typename E, typename = std::enable_if_t<is_bitmask_enum_v<E>>>
142+
constexpr E &operator|=(E &LHS, E RHS) noexcept {
143+
LHS = LHS | RHS;
144+
return LHS;
145+
}
146+
147+
template <typename E, typename = std::enable_if_t<is_bitmask_enum_v<E>>>
148+
constexpr E &operator&=(E &LHS, E RHS) noexcept {
149+
LHS = LHS & RHS;
150+
return LHS;
151+
}
152+
153+
template <typename E, typename = std::enable_if_t<is_bitmask_enum_v<E>>>
154+
constexpr E &operator^=(E &LHS, E RHS) noexcept {
155+
LHS = LHS ^ RHS;
156+
return LHS;
157+
}
158+
159+
} // namespace orc_rt
160+
161+
#endif // ORC_RT_BITMASK_ENUM_H

orc-rt/unittests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ function(add_orc_rt_unittest test_dirname)
1212
endfunction()
1313

1414
add_orc_rt_unittest(CoreTests
15+
bitmask-enum-test.cpp
1516
math-test.cpp
1617
span-test.cpp
1718
DISABLE_LLVM_LINK_LLVM_DYLIB
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
//===-- bitmask-enum-test.cpp ---------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This file is a part of the ORC runtime.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "orc-rt/bitmask-enum.h"
14+
#include "gtest/gtest.h"
15+
16+
#include <sstream>
17+
#include <string>
18+
19+
using namespace orc_rt;
20+
21+
namespace {
22+
23+
enum Flags { F0 = 0, F1 = 1, F2 = 2, F3 = 4, F4 = 8 };
24+
25+
} // namespace
26+
27+
namespace orc_rt {
28+
ORC_RT_DECLARE_ENUM_AS_BITMASK(Flags, F4);
29+
} // namespace orc_rt
30+
31+
static_assert(is_bitmask_enum<Flags>::value != 0);
32+
static_assert(largest_bitmask_enum_bit<Flags>::value == Flags::F4);
33+
34+
namespace {
35+
36+
static_assert(is_bitmask_enum<Flags>::value != 0);
37+
static_assert(largest_bitmask_enum_bit<Flags>::value == Flags::F4);
38+
39+
TEST(BitmaskEnumTest, BitwiseOr) {
40+
Flags f = F1 | F2;
41+
EXPECT_EQ(3, f);
42+
43+
f = f | F3;
44+
EXPECT_EQ(7, f);
45+
}
46+
47+
TEST(BitmaskEnumTest, BitwiseOrEquals) {
48+
Flags f = F1;
49+
f |= F3;
50+
EXPECT_EQ(5, f);
51+
52+
// |= should return a reference to the LHS.
53+
f = F2;
54+
(f |= F3) = F1;
55+
EXPECT_EQ(F1, f);
56+
}
57+
58+
TEST(BitmaskEnumTest, BitwiseAnd) {
59+
Flags f = static_cast<Flags>(3) & F2;
60+
EXPECT_EQ(F2, f);
61+
62+
f = (f | F3) & (F1 | F2 | F3);
63+
EXPECT_EQ(6, f);
64+
}
65+
66+
TEST(BitmaskEnumTest, BitwiseAndEquals) {
67+
Flags f = F1 | F2 | F3;
68+
f &= F1 | F2;
69+
EXPECT_EQ(3, f);
70+
71+
// &= should return a reference to the LHS.
72+
(f &= F1) = F3;
73+
EXPECT_EQ(F3, f);
74+
}
75+
76+
TEST(BitmaskEnumTest, BitwiseXor) {
77+
Flags f = (F1 | F2) ^ (F2 | F3);
78+
EXPECT_EQ(5, f);
79+
80+
f = f ^ F1;
81+
EXPECT_EQ(4, f);
82+
}
83+
84+
TEST(BitmaskEnumTest, BitwiseXorEquals) {
85+
Flags f = (F1 | F2);
86+
f ^= (F2 | F4);
87+
EXPECT_EQ(9, f);
88+
89+
// ^= should return a reference to the LHS.
90+
(f ^= F4) = F3;
91+
EXPECT_EQ(F3, f);
92+
}
93+
94+
TEST(BitmaskEnumTest, ConstantExpression) {
95+
constexpr Flags f1 = ~F1;
96+
constexpr Flags f2 = F1 | F2;
97+
constexpr Flags f3 = F1 & F2;
98+
constexpr Flags f4 = F1 ^ F2;
99+
EXPECT_EQ(f1, ~F1);
100+
EXPECT_EQ(f2, F1 | F2);
101+
EXPECT_EQ(f3, F1 & F2);
102+
EXPECT_EQ(f4, F1 ^ F2);
103+
}
104+
105+
TEST(BitmaskEnumTest, BitwiseNot) {
106+
Flags f = ~F1;
107+
EXPECT_EQ(14, f); // Largest value for f is 15.
108+
EXPECT_EQ(15, ~F0);
109+
}
110+
111+
enum class FlagsClass {
112+
F0 = 0,
113+
F1 = 1,
114+
F2 = 2,
115+
F3 = 4,
116+
ORC_RT_MARK_AS_BITMASK_ENUM(F3)
117+
};
118+
119+
TEST(BitmaskEnumTest, ScopedEnum) {
120+
FlagsClass f = (FlagsClass::F1 & ~FlagsClass::F0) | FlagsClass::F2;
121+
f |= FlagsClass::F3;
122+
EXPECT_EQ(7, static_cast<int>(f));
123+
}
124+
125+
struct Container {
126+
enum Flags {
127+
F0 = 0,
128+
F1 = 1,
129+
F2 = 2,
130+
F3 = 4,
131+
ORC_RT_MARK_AS_BITMASK_ENUM(F3)
132+
};
133+
134+
static Flags getFlags() {
135+
Flags f = F0 | F1;
136+
f |= F2;
137+
return f;
138+
}
139+
};
140+
141+
TEST(BitmaskEnumTest, EnumInStruct) { EXPECT_EQ(3, Container::getFlags()); }
142+
143+
} // namespace

0 commit comments

Comments
 (0)