[llvm] [OpenMP] Implement EnumSet container (PR #211323)
Krzysztof Parzyszek via llvm-commits
llvm-commits at lists.llvm.org
Wed Jul 22 14:57:30 PDT 2026
https://github.com/kparzysz updated https://github.com/llvm/llvm-project/pull/211323
>From 8be7044f3792249dd5c681a02d03b9e4a2330916 Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Wed, 1 Jul 2026 09:13:59 -0500
Subject: [PATCH 1/3] [OpenMP] Implement EnumSet container
This is close to flang's common::EnumSet with the difference being that
it provides forward iterators.
The reason for having an implementation that is separate from
common::EnumSet is that this is intended to be shared for all consumers
of llvm/lib/Frontend/OpenMP. This class is also planned to be one of the
core containers for representing auto-generated OpenMP data in the future.
---
llvm/include/llvm/Frontend/OpenMP/OMP.h | 104 ++++++++++++++++++++++++
1 file changed, 104 insertions(+)
diff --git a/llvm/include/llvm/Frontend/OpenMP/OMP.h b/llvm/include/llvm/Frontend/OpenMP/OMP.h
index f54352adaa8b8..a895bdb228f75 100644
--- a/llvm/include/llvm/Frontend/OpenMP/OMP.h
+++ b/llvm/include/llvm/Frontend/OpenMP/OMP.h
@@ -17,11 +17,115 @@
#include "llvm/Support/Compiler.h"
#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/Bitset.h"
#include "llvm/ADT/Sequence.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
namespace llvm::omp {
+template <typename Enum, size_t Size> struct EnumSet;
+
+namespace detail {
+template <size_t Size>
+static constexpr inline size_t findFirstSet(size_t Begin, size_t End,
+ const llvm::Bitset<Size> &Set) {
+ unsigned FirstWord = Begin / 64;
+ unsigned LastWord = End / 64;
+
+ for (unsigned I = FirstWord; I <= LastWord; ++I) {
+ uint64_t Word = Set.getWord64(I);
+ if (I == FirstWord && Begin % 64 != 0) {
+ Word &= ~uint64_t() << (Begin % 64);
+ }
+ auto Count = static_cast<unsigned>(llvm::countr_zero_constexpr(Word));
+ if (Count < 64) {
+ unsigned Idx = I * 64 + Count;
+ if (Idx >= Begin && Idx < End)
+ return Idx;
+ }
+ }
+ return Size;
+}
+
+template <typename Enum, size_t Size> struct EnumSetIterator {
+ constexpr EnumSetIterator(const EnumSet<Enum, Size> &Set, size_t At)
+ : Set(Set), At(At) {}
+
+ constexpr Enum operator*() const;
+ constexpr auto &operator++();
+
+ constexpr bool operator==(const EnumSetIterator<Enum, Size> &Other) const {
+ return &Set == &Other.Set && At == Other.At;
+ }
+ constexpr bool operator!=(const EnumSetIterator<Enum, Size> &Other) const {
+ return !operator==(Other);
+ }
+
+private:
+ const EnumSet<Enum, Size> &Set;
+ size_t At;
+};
+} // namespace detail
+
+template <typename Enum, size_t Size>
+struct EnumSet : public llvm::Bitset<Size> {
+ using value_type = Enum;
+ using Base = llvm::Bitset<Size>;
+ using Base::Base;
+ using iterator = detail::EnumSetIterator<Enum, Size>;
+
+ constexpr EnumSet(Base &&B) : Base(std::move(B)) {}
+ constexpr EnumSet(std::initializer_list<value_type> Init) {
+ for (value_type E : Init) {
+ auto Value = static_cast<unsigned>(E);
+ assert(Value < Base::size() && "Invalid enumeration value");
+ Base::set(Value);
+ }
+ }
+
+ constexpr bool empty() const { return Base::none(); }
+ constexpr size_t size() const { return Base::count(); }
+ constexpr size_t max_size() const { return Size; }
+
+ constexpr bool test(Enum E) const {
+ return Base::test(static_cast<unsigned>(E));
+ }
+ constexpr bool operator[](Enum E) const {
+ return Base::operator[](static_cast<unsigned>(E));
+ }
+ constexpr EnumSet &flip(Enum E) {
+ Base::flip(static_cast<unsigned>(E));
+ return *this;
+ }
+ constexpr EnumSet &reset(Enum E) {
+ Base::reset(static_cast<unsigned>(E));
+ return *this;
+ }
+ constexpr EnumSet &set(Enum E) {
+ Base::set(static_cast<unsigned>(E));
+ return *this;
+ }
+
+ constexpr iterator begin() const {
+ return iterator(*this, detail::findFirstSet<Size>(0, Size, *this));
+ }
+ constexpr iterator end() const { return iterator(*this, Size); }
+};
+
+namespace detail {
+template <typename Enum, size_t Size>
+constexpr Enum EnumSetIterator<Enum, Size>::operator*() const {
+ assert(Set.Base::test(At));
+ return static_cast<Enum>(At);
+}
+
+template <typename Enum, size_t Size>
+constexpr auto &EnumSetIterator<Enum, Size>::operator++() {
+ At = findFirstSet<Size>(At + 1, Size, Set);
+ return *this;
+}
+} // namespace detail
+
LLVM_ABI ArrayRef<Directive> getLeafConstructs(Directive D);
LLVM_ABI ArrayRef<Directive> getLeafConstructsOrSelf(Directive D);
>From 39f85b481cd28c8b102ef6674a0516d7ac9b1f71 Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Wed, 22 Jul 2026 16:56:31 -0500
Subject: [PATCH 2/3] Add some static helper members
---
llvm/include/llvm/Frontend/OpenMP/OMP.h | 3 +++
1 file changed, 3 insertions(+)
diff --git a/llvm/include/llvm/Frontend/OpenMP/OMP.h b/llvm/include/llvm/Frontend/OpenMP/OMP.h
index a895bdb228f75..db1871e38f0b3 100644
--- a/llvm/include/llvm/Frontend/OpenMP/OMP.h
+++ b/llvm/include/llvm/Frontend/OpenMP/OMP.h
@@ -48,6 +48,9 @@ static constexpr inline size_t findFirstSet(size_t Begin, size_t End,
}
template <typename Enum, size_t Size> struct EnumSetIterator {
+ using value_type = Enum;
+ static constexpr size_t enum_size = Size;
+
constexpr EnumSetIterator(const EnumSet<Enum, Size> &Set, size_t At)
: Set(Set), At(At) {}
>From 71add54b1d25b37a22af4a0784554928c1c4c7e6 Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Wed, 22 Jul 2026 16:56:52 -0500
Subject: [PATCH 3/3] Add unit test
---
llvm/unittests/Frontend/CMakeLists.txt | 1 +
llvm/unittests/Frontend/EnumSetTest.cpp | 134 ++++++++++++++++++++++++
2 files changed, 135 insertions(+)
create mode 100644 llvm/unittests/Frontend/EnumSetTest.cpp
diff --git a/llvm/unittests/Frontend/CMakeLists.txt b/llvm/unittests/Frontend/CMakeLists.txt
index 1ce34e77cb348..554c88990115c 100644
--- a/llvm/unittests/Frontend/CMakeLists.txt
+++ b/llvm/unittests/Frontend/CMakeLists.txt
@@ -13,6 +13,7 @@ set(LLVM_LINK_COMPONENTS
)
add_llvm_unittest(LLVMFrontendTests
+ EnumSetTest.cpp
HLSLBindingTest.cpp
HLSLRootSignatureDumpTest.cpp
OpenACCTest.cpp
diff --git a/llvm/unittests/Frontend/EnumSetTest.cpp b/llvm/unittests/Frontend/EnumSetTest.cpp
new file mode 100644
index 0000000000000..46eb529e5c7eb
--- /dev/null
+++ b/llvm/unittests/Frontend/EnumSetTest.cpp
@@ -0,0 +1,134 @@
+#include "llvm/Frontend/OpenMP/OMP.h"
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+using namespace llvm::omp;
+
+namespace {
+namespace detail {
+template <typename Elem, Elem...> struct is_one_of {
+ constexpr bool operator()(Elem) const { return false; }
+};
+
+template <typename Elem, Elem Value, Elem... Values>
+struct is_one_of<Elem, Value, Values...> {
+ constexpr bool operator()(Elem V) const {
+ return V == Value || is_one_of<Elem, Values...>{}(V);
+ }
+};
+
+template <typename Elem, Elem... Values, typename Range>
+constexpr bool ElementsAre(Range &&R) {
+ size_t Count = 0;
+ // This also serves as an EnumSetIterator test.
+ for (auto It = R.begin(), End = R.end(); It != End; ++It) {
+ if (!is_one_of<Elem, Values...>{}(*It))
+ return false;
+ ++Count;
+ }
+ if (Count != sizeof...(Values))
+ return false;
+ return true;
+}
+} // namespace detail
+
+using ClauseSet = EnumSet<Clause, Clause_enumSize>;
+
+TEST(EnumSetTest, DefaultInitialization) {
+ constexpr ClauseSet S;
+ EXPECT_THAT(S, testing::IsEmpty());
+ EXPECT_EQ(S.size(), static_cast<size_t>(0));
+
+ static_assert(S.empty());
+ static_assert(S.size() == 0);
+}
+
+TEST(EnumSetTest, ListInitialization) {
+ constexpr ClauseSet S{Clause::OMPC_private, Clause::OMPC_shared};
+ EXPECT_THAT(S, testing::ElementsAre(OMPC_private, OMPC_shared));
+
+ static_assert(
+ detail::ElementsAre<Clause, Clause::OMPC_private, Clause::OMPC_shared>(
+ S));
+}
+
+TEST(EnumSetTest, CopyInitialization) {
+ constexpr ClauseSet S(ClauseSet{Clause::OMPC_private, Clause::OMPC_shared});
+ EXPECT_THAT(S, testing::ElementsAre(OMPC_private, OMPC_shared));
+
+ static_assert(
+ detail::ElementsAre<Clause, Clause::OMPC_private, Clause::OMPC_shared>(
+ S));
+}
+
+TEST(EnumSetTest, Set) {
+ ClauseSet S;
+ S.set(Clause::OMPC_private);
+ EXPECT_THAT(S, testing::ElementsAre(OMPC_private));
+
+ static_assert(detail::ElementsAre<Clause, Clause::OMPC_private>(
+ ClauseSet{}.set(Clause::OMPC_private)));
+}
+
+TEST(EnumSetTest, Reset) {
+ ClauseSet S{Clause::OMPC_private, Clause::OMPC_shared};
+ S.reset(Clause::OMPC_private);
+ EXPECT_THAT(S, testing::ElementsAre(OMPC_shared));
+
+ static_assert(detail::ElementsAre<Clause, Clause::OMPC_shared>(
+ ClauseSet{Clause::OMPC_private, Clause::OMPC_shared}.reset(
+ Clause::OMPC_private)));
+}
+
+TEST(EnumSetTest, Flip) {
+ ClauseSet S{Clause::OMPC_private};
+ S.flip(Clause::OMPC_private);
+ S.flip(Clause::OMPC_shared);
+ EXPECT_THAT(S, testing::ElementsAre(OMPC_shared));
+
+ static_assert(detail::ElementsAre<Clause, Clause::OMPC_shared>(
+ ClauseSet{Clause::OMPC_private}
+ .flip(Clause::OMPC_private)
+ .flip(Clause::OMPC_shared)));
+}
+
+TEST(EnumSetTest, Test) {
+ constexpr ClauseSet S{Clause::OMPC_private};
+ ASSERT_TRUE(S.test(Clause::OMPC_private));
+ ASSERT_FALSE(S.test(Clause::OMPC_shared));
+
+ static_assert(S.test(Clause::OMPC_private));
+ static_assert(!S.test(Clause::OMPC_shared));
+}
+
+TEST(EnumSetTest, SquareBracket) {
+ constexpr ClauseSet S{Clause::OMPC_private};
+ ASSERT_TRUE(S[Clause::OMPC_private]);
+ ASSERT_FALSE(S[Clause::OMPC_shared]);
+
+ static_assert(S[Clause::OMPC_private]);
+ static_assert(!S[Clause::OMPC_shared]);
+}
+
+TEST(EnumSetTest, Union) {
+ constexpr ClauseSet A{Clause::OMPC_private, OMPC_shared};
+ constexpr ClauseSet B{Clause::OMPC_nowait};
+ constexpr ClauseSet S = A | B;
+ EXPECT_THAT(S, testing::ElementsAre(Clause::OMPC_nowait, Clause::OMPC_private,
+ OMPC_shared));
+
+ static_assert(detail::ElementsAre<Clause, Clause::OMPC_nowait,
+ Clause::OMPC_private, OMPC_shared>(S));
+}
+
+TEST(EnumSetTest, Intersection) {
+ constexpr ClauseSet A{Clause::OMPC_private, OMPC_shared};
+ constexpr ClauseSet B{Clause::OMPC_nowait, OMPC_shared};
+ constexpr ClauseSet S = A & B;
+ EXPECT_THAT(S, testing::ElementsAre(OMPC_shared));
+
+ static_assert(detail::ElementsAre<Clause, OMPC_shared>(S));
+}
+} // namespace
More information about the llvm-commits
mailing list