xref: /freebsd/contrib/llvm-project/compiler-rt/lib/orc/bitmask_enum.h (revision 5f757f3ff9144b609b3c433dfd370cc6bdc191ad)
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