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