[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