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