mirror of
https://github.com/intel/llvm.git
synced 2026-02-05 13:21:04 +08:00
[ORC-RT] Add bitmask-enum and bit_ceil utilities to the ORC runtime.
bitmask_enum.h is essentially a copy of llvm/ADT/BitmaskEnum.h, with some minor cleanup and renaming. The bit_ceil function is a placeholder for std::bit_ceil, which we can use once compiler-rt can use c++20. These utilities will be used to simplify bitfield enum usage in upcoming ORC-RT patches.
This commit is contained in:
151
compiler-rt/lib/orc/bitmask_enum.h
Normal file
151
compiler-rt/lib/orc/bitmask_enum.h
Normal file
@@ -0,0 +1,151 @@
|
||||
//===---- bitmask_enum.h - Enable bitmask operations on enums ---*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of the ORC runtime support library.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef ORC_RT_BITMASK_ENUM_H
|
||||
#define ORC_RT_BITMASK_ENUM_H
|
||||
|
||||
#include "stl_extras.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <type_traits>
|
||||
|
||||
namespace __orc_rt {
|
||||
|
||||
/// ORC_RT_MARK_AS_BITMASK_ENUM lets you opt in an individual enum type so you
|
||||
/// can perform bitwise operations on it without putting static_cast everywhere.
|
||||
///
|
||||
/// \code
|
||||
/// enum MyEnum {
|
||||
/// E1 = 1, E2 = 2, E3 = 4, E4 = 8,
|
||||
/// ORC_RT_MARK_AS_BITMASK_ENUM(/* LargestValue = */ E4)
|
||||
/// };
|
||||
///
|
||||
/// void Foo() {
|
||||
/// MyEnum A = (E1 | E2) & E3 ^ ~E4; // Look, ma: No static_cast!
|
||||
/// }
|
||||
/// \endcode
|
||||
///
|
||||
/// Normally when you do a bitwise operation on an enum value, you get back an
|
||||
/// instance of the underlying type (e.g. int). But using this macro, bitwise
|
||||
/// ops on your enum will return you back instances of the enum. This is
|
||||
/// particularly useful for enums which represent a combination of flags.
|
||||
///
|
||||
/// The parameter to ORC_RT_MARK_AS_BITMASK_ENUM should be the largest
|
||||
/// individual value in your enum.
|
||||
///
|
||||
/// All of the enum's values must be non-negative.
|
||||
#define ORC_RT_MARK_AS_BITMASK_ENUM(LargestValue) \
|
||||
ORC_RT_BITMASK_LARGEST_ENUMERATOR = LargestValue
|
||||
|
||||
/// ORC_RT_DECLARE_ENUM_AS_BITMASK can be used to declare an enum type as a bit
|
||||
/// set, so that bitwise operation on such enum does not require static_cast.
|
||||
///
|
||||
/// \code
|
||||
/// enum MyEnum { E1 = 1, E2 = 2, E3 = 4, E4 = 8 };
|
||||
/// ORC_RT_DECLARE_ENUM_AS_BITMASK(MyEnum, E4);
|
||||
///
|
||||
/// void Foo() {
|
||||
/// MyEnum A = (E1 | E2) & E3 ^ ~E4; // No static_cast
|
||||
/// }
|
||||
/// \endcode
|
||||
///
|
||||
/// The second parameter to ORC_RT_DECLARE_ENUM_AS_BITMASK specifies the largest
|
||||
/// bit value of the enum type.
|
||||
///
|
||||
/// ORC_RT_DECLARE_ENUM_AS_BITMASK should be used in __orc_rt namespace.
|
||||
///
|
||||
/// This a non-intrusive alternative for ORC_RT_MARK_AS_BITMASK_ENUM. It allows
|
||||
/// declaring more than one non-scoped enumerations as bitmask types in the same
|
||||
/// scope. Otherwise it provides the same functionality as
|
||||
/// ORC_RT_MARK_AS_BITMASK_ENUM.
|
||||
#define ORC_RT_DECLARE_ENUM_AS_BITMASK(Enum, LargestValue) \
|
||||
template <> struct is_bitmask_enum<Enum> : std::true_type {}; \
|
||||
template <> struct largest_bitmask_enum_bit<Enum> { \
|
||||
static constexpr std::underlying_type_t<Enum> value = LargestValue; \
|
||||
}
|
||||
|
||||
/// Traits class to determine whether an enum has been declared as a bitwise
|
||||
/// enum via ORC_RT_DECLARE_ENUM_AS_BITMASK.
|
||||
template <typename E, typename Enable = void>
|
||||
struct is_bitmask_enum : std::false_type {};
|
||||
|
||||
template <typename E>
|
||||
struct is_bitmask_enum<
|
||||
E, std::enable_if_t<sizeof(E::ORC_RT_BITMASK_LARGEST_ENUMERATOR) >= 0>>
|
||||
: std::true_type {};
|
||||
|
||||
template <typename E>
|
||||
inline constexpr bool is_bitmask_enum_v = is_bitmask_enum<E>::value;
|
||||
|
||||
/// Traits class to deermine bitmask enum largest bit.
|
||||
template <typename E, typename Enable = void> struct largest_bitmask_enum_bit;
|
||||
|
||||
template <typename E>
|
||||
struct largest_bitmask_enum_bit<
|
||||
E, std::enable_if_t<sizeof(E::ORC_RT_BITMASK_LARGEST_ENUMERATOR) >= 0>> {
|
||||
using UnderlyingTy = std::underlying_type_t<E>;
|
||||
static constexpr UnderlyingTy value =
|
||||
static_cast<UnderlyingTy>(E::ORC_RT_BITMASK_LARGEST_ENUMERATOR);
|
||||
};
|
||||
|
||||
template <typename E> constexpr std::underlying_type_t<E> Mask() {
|
||||
return bit_ceil(largest_bitmask_enum_bit<E>::value) - 1;
|
||||
}
|
||||
|
||||
template <typename E> constexpr std::underlying_type_t<E> Underlying(E Val) {
|
||||
auto U = static_cast<std::underlying_type_t<E>>(Val);
|
||||
assert(U >= 0 && "Negative enum values are not allowed");
|
||||
assert(U <= Mask<E>() && "Enum value too large (or langest val too small");
|
||||
return U;
|
||||
}
|
||||
|
||||
template <typename E, typename = std::enable_if_t<is_bitmask_enum_v<E>>>
|
||||
constexpr E operator~(E Val) {
|
||||
return static_cast<E>(~Underlying(Val) & Mask<E>());
|
||||
}
|
||||
|
||||
template <typename E, typename = std::enable_if_t<is_bitmask_enum_v<E>>>
|
||||
constexpr E operator|(E LHS, E RHS) {
|
||||
return static_cast<E>(Underlying(LHS) | Underlying(RHS));
|
||||
}
|
||||
|
||||
template <typename E, typename = std::enable_if_t<is_bitmask_enum_v<E>>>
|
||||
constexpr E operator&(E LHS, E RHS) {
|
||||
return static_cast<E>(Underlying(LHS) & Underlying(RHS));
|
||||
}
|
||||
|
||||
template <typename E, typename = std::enable_if_t<is_bitmask_enum_v<E>>>
|
||||
constexpr E operator^(E LHS, E RHS) {
|
||||
return static_cast<E>(Underlying(LHS) ^ Underlying(RHS));
|
||||
}
|
||||
|
||||
template <typename E, typename = std::enable_if_t<is_bitmask_enum_v<E>>>
|
||||
E &operator|=(E &LHS, E RHS) {
|
||||
LHS = LHS | RHS;
|
||||
return LHS;
|
||||
}
|
||||
|
||||
template <typename E, typename = std::enable_if_t<is_bitmask_enum_v<E>>>
|
||||
E &operator&=(E &LHS, E RHS) {
|
||||
LHS = LHS & RHS;
|
||||
return LHS;
|
||||
}
|
||||
|
||||
template <typename E, typename = std::enable_if_t<is_bitmask_enum_v<E>>>
|
||||
E &operator^=(E &LHS, E RHS) {
|
||||
LHS = LHS ^ RHS;
|
||||
return LHS;
|
||||
}
|
||||
|
||||
} // end namespace __orc_rt
|
||||
|
||||
#endif // ORC_RT_BITMASK_ENUM_H
|
||||
@@ -28,6 +28,17 @@ template <class Ty> struct identity {
|
||||
const Ty &operator()(const Ty &self) const { return self; }
|
||||
};
|
||||
|
||||
/// Substitute for std::bit_ceil.
|
||||
constexpr uint64_t bit_ceil(uint64_t Val) noexcept {
|
||||
Val |= (Val >> 1);
|
||||
Val |= (Val >> 2);
|
||||
Val |= (Val >> 4);
|
||||
Val |= (Val >> 8);
|
||||
Val |= (Val >> 16);
|
||||
Val |= (Val >> 32);
|
||||
return Val + 1;
|
||||
}
|
||||
|
||||
} // namespace __orc_rt
|
||||
|
||||
#endif // ORC_RT_STL_EXTRAS
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
set(UNITTEST_SOURCES
|
||||
adt_test.cpp
|
||||
bitmask_enum_test.cpp
|
||||
c_api_test.cpp
|
||||
endian_test.cpp
|
||||
error_test.cpp
|
||||
|
||||
143
compiler-rt/lib/orc/tests/unit/bitmask_enum_test.cpp
Normal file
143
compiler-rt/lib/orc/tests/unit/bitmask_enum_test.cpp
Normal file
@@ -0,0 +1,143 @@
|
||||
//===-- adt_test.cpp ------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of the ORC runtime.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "bitmask_enum.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
using namespace __orc_rt;
|
||||
|
||||
namespace {
|
||||
|
||||
enum Flags { F0 = 0, F1 = 1, F2 = 2, F3 = 4, F4 = 8 };
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace __orc_rt {
|
||||
ORC_RT_DECLARE_ENUM_AS_BITMASK(Flags, F4);
|
||||
} // namespace __orc_rt
|
||||
|
||||
static_assert(is_bitmask_enum<Flags>::value != 0);
|
||||
static_assert(largest_bitmask_enum_bit<Flags>::value == Flags::F4);
|
||||
|
||||
namespace {
|
||||
|
||||
static_assert(is_bitmask_enum<Flags>::value != 0);
|
||||
static_assert(largest_bitmask_enum_bit<Flags>::value == Flags::F4);
|
||||
|
||||
TEST(BitmaskEnumTest, BitwiseOr) {
|
||||
Flags f = F1 | F2;
|
||||
EXPECT_EQ(3, f);
|
||||
|
||||
f = f | F3;
|
||||
EXPECT_EQ(7, f);
|
||||
}
|
||||
|
||||
TEST(BitmaskEnumTest, BitwiseOrEquals) {
|
||||
Flags f = F1;
|
||||
f |= F3;
|
||||
EXPECT_EQ(5, f);
|
||||
|
||||
// |= should return a reference to the LHS.
|
||||
f = F2;
|
||||
(f |= F3) = F1;
|
||||
EXPECT_EQ(F1, f);
|
||||
}
|
||||
|
||||
TEST(BitmaskEnumTest, BitwiseAnd) {
|
||||
Flags f = static_cast<Flags>(3) & F2;
|
||||
EXPECT_EQ(F2, f);
|
||||
|
||||
f = (f | F3) & (F1 | F2 | F3);
|
||||
EXPECT_EQ(6, f);
|
||||
}
|
||||
|
||||
TEST(BitmaskEnumTest, BitwiseAndEquals) {
|
||||
Flags f = F1 | F2 | F3;
|
||||
f &= F1 | F2;
|
||||
EXPECT_EQ(3, f);
|
||||
|
||||
// &= should return a reference to the LHS.
|
||||
(f &= F1) = F3;
|
||||
EXPECT_EQ(F3, f);
|
||||
}
|
||||
|
||||
TEST(BitmaskEnumTest, BitwiseXor) {
|
||||
Flags f = (F1 | F2) ^ (F2 | F3);
|
||||
EXPECT_EQ(5, f);
|
||||
|
||||
f = f ^ F1;
|
||||
EXPECT_EQ(4, f);
|
||||
}
|
||||
|
||||
TEST(BitmaskEnumTest, BitwiseXorEquals) {
|
||||
Flags f = (F1 | F2);
|
||||
f ^= (F2 | F4);
|
||||
EXPECT_EQ(9, f);
|
||||
|
||||
// ^= should return a reference to the LHS.
|
||||
(f ^= F4) = F3;
|
||||
EXPECT_EQ(F3, f);
|
||||
}
|
||||
|
||||
TEST(BitmaskEnumTest, ConstantExpression) {
|
||||
constexpr Flags f1 = ~F1;
|
||||
constexpr Flags f2 = F1 | F2;
|
||||
constexpr Flags f3 = F1 & F2;
|
||||
constexpr Flags f4 = F1 ^ F2;
|
||||
EXPECT_EQ(f1, ~F1);
|
||||
EXPECT_EQ(f2, F1 | F2);
|
||||
EXPECT_EQ(f3, F1 & F2);
|
||||
EXPECT_EQ(f4, F1 ^ F2);
|
||||
}
|
||||
|
||||
TEST(BitmaskEnumTest, BitwiseNot) {
|
||||
Flags f = ~F1;
|
||||
EXPECT_EQ(14, f); // Largest value for f is 15.
|
||||
EXPECT_EQ(15, ~F0);
|
||||
}
|
||||
|
||||
enum class FlagsClass {
|
||||
F0 = 0,
|
||||
F1 = 1,
|
||||
F2 = 2,
|
||||
F3 = 4,
|
||||
ORC_RT_MARK_AS_BITMASK_ENUM(F3)
|
||||
};
|
||||
|
||||
TEST(BitmaskEnumTest, ScopedEnum) {
|
||||
FlagsClass f = (FlagsClass::F1 & ~FlagsClass::F0) | FlagsClass::F2;
|
||||
f |= FlagsClass::F3;
|
||||
EXPECT_EQ(7, static_cast<int>(f));
|
||||
}
|
||||
|
||||
struct Container {
|
||||
enum Flags {
|
||||
F0 = 0,
|
||||
F1 = 1,
|
||||
F2 = 2,
|
||||
F3 = 4,
|
||||
ORC_RT_MARK_AS_BITMASK_ENUM(F3)
|
||||
};
|
||||
|
||||
static Flags getFlags() {
|
||||
Flags f = F0 | F1;
|
||||
f |= F2;
|
||||
return f;
|
||||
}
|
||||
};
|
||||
|
||||
TEST(BitmaskEnumTest, EnumInStruct) { EXPECT_EQ(3, Container::getFlags()); }
|
||||
|
||||
} // namespace
|
||||
Reference in New Issue
Block a user