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