[llvm] Add CondGroup infrastructure and unittests. (PR #170922)

James Player via llvm-commits llvm-commits at lists.llvm.org
Fri Dec 5 13:07:53 PST 2025


https://github.com/jameswplayer created https://github.com/llvm/llvm-project/pull/170922

Enable simple disjunctive comparison between one value and a group of values.

Groups of values can be constructed at runtime for groups involving non-consteval or non-integral values.  Or if the group consists entirely of integral literals, it can be expressed in an optimized compile-time bitset representation.

**Function construction example:**
```
bool isPostOp(StringRef Token) {
  return Token == cgrp::anyOf("++", "--");
}
```
In this case, comparing to the group returned from `cgrp::anyOf()` expands to:
```
Token == "++" || Token == "--"
```
The comparison is legal as long as there exists a valid `operator==()` comparison between the singleton value and every member of the group.  Note that the group is expressed as a tuple-like object, so types need not match.

As long as the elements of the group support `constexpr` copy construction and or equivalence comparison, the group may also be used in a `consteval` context.  For example:
```
static_assert("Baz" == cgrp::anyOf("Foo", "Baz", "Buz"));
```
or stored to a variable:
```
inline constexpr auto FooGroup = cgrp::makeGroup("Foo", "Baz", "Buz");
static_assert("Baz" == cgrp::anyOf(FooGroup));
```

**Optimized groups of integral / enum literals:**

We found that folks like creating large groups of integral / enum literals.  In order to optimize the representation of these groups at compile-time, the user must supply them as template arguments.  Therefore we distinguish these groups by creating them via `inline constexpr` template variable instantiations.  For example:
```
inline constexpr FivesGroup =
  cgrp::Literals<5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70>;

static_assert(1 != cgrp::anyOf(FivesGroup));
static_assert(35 == cgrp::anyOf(FivesGroup));
```
In this case, `FivesGroup` will be expressed with bitset-like representation requiring two 64-bit integers.  A disjunctive comparison is then a simple O(1) bit-extraction from that array.  The bitset-like representation must be evaluated at compile-time because all the information is stored in template parameters.

`cgrp::AnyOf<...>` serves as a short-cut to create and compare a group of integral literals inline:
```
static_assert(25 == cgrp::AnyOf<5, 10, 15, 20, 25, 30>);
```

These literal groups are automatically clustered to save space in the ultimate bitset representation.  For example:
```
inline constexpr auto Clusters = cgrp::Literals<1, 2, 3, 4, 5, 6, 1001, 1002, 1003, 1004, 1005>;
```
The `Clusters` derived representation will consist of two bitsets, one with a start offset of `1` and the other a start offset of `1001`.  A disjunctive comparison against `Clusters` results in two bit-extraction operations across the two clusters.

The clustering logic contains heuristics which decide whether to start a new cluster or treat a value as a singleton.  Singleton values get added to a catch-all sequence that are checked after all the bitset-like clusters.


**Composing groups of literals**

Literal groups may be composed via set operators at compile-time.  The resulting set has its representation re-optimized specifically for the values in the group.  The following set operators are supported: `|` (union), `&` (intersection), `-` (difference).  The composition operators are always evaluated at compile-time.  Example usage:
```
inline constexpr auto Evens = cgrp::Literals<2, 4, 6, 8, 10>;
inline constexpr auto Odds = cgrp::Literals<1, 3, 5, 7, 9>;

inline constexpr auto OneToTen = Evens | Odds;
static_assert(1 == cgrp::anyOf(OneToTen) && 10 == cgrp::anyOf(OneToTen));
```


**Iterating groups of literals**

In order to keep these groups as a single source of truth, it's also possible to iterate the values.  The underlying storage for the container interface is an array of the elements sorted at compile-time.  This array is not instantiated unless the user instantiates any member of the container interface.
```
void foo() {
  constexpr auto Fives = cgrp::Literals<5, 10, 15, 20, 25>;
  for (auto Elem : Fives)
    processElem(Elem);
}
```

>From 51c0d677ca7e2945235aabbbd08cbb57fe26c294 Mon Sep 17 00:00:00 2001
From: James Player <jplayer at nvidia.com>
Date: Fri, 5 Dec 2025 12:46:12 -0800
Subject: [PATCH] Add CondGroup infrastructure and unittests.

---
 llvm/include/llvm/ADT/CondGroup.h         |  983 ++++++++++++++++
 llvm/include/llvm/ADT/ConstexprUtils.h    |  469 ++++++++
 llvm/include/llvm/ADT/MetaSet.h           | 1083 ++++++++++++++++++
 llvm/unittests/ADT/CMakeLists.txt         |    3 +
 llvm/unittests/ADT/CondGroupTest.cpp      |  947 +++++++++++++++
 llvm/unittests/ADT/ConstexprUtilsTest.cpp |  973 ++++++++++++++++
 llvm/unittests/ADT/MetaSetTest.cpp        | 1267 +++++++++++++++++++++
 7 files changed, 5725 insertions(+)
 create mode 100644 llvm/include/llvm/ADT/CondGroup.h
 create mode 100644 llvm/include/llvm/ADT/ConstexprUtils.h
 create mode 100644 llvm/include/llvm/ADT/MetaSet.h
 create mode 100644 llvm/unittests/ADT/CondGroupTest.cpp
 create mode 100644 llvm/unittests/ADT/ConstexprUtilsTest.cpp
 create mode 100644 llvm/unittests/ADT/MetaSetTest.cpp

diff --git a/llvm/include/llvm/ADT/CondGroup.h b/llvm/include/llvm/ADT/CondGroup.h
new file mode 100644
index 0000000000000..4f75e7fb799a7
--- /dev/null
+++ b/llvm/include/llvm/ADT/CondGroup.h
@@ -0,0 +1,983 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implements the condition group and associated comparisons.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_ADT_CONDGROUP_H
+#define LLVM_ADT_CONDGROUP_H
+
+#include "llvm/ADT/ConstexprUtils.h"
+#include "llvm/ADT/MetaSet.h"
+
+#include <cstdint>
+#include <optional>
+#include <type_traits>
+#include <utility>
+
+namespace llvm::cgrp {
+
+/// Marker class used as a base for all CondGroup classes.
+///
+/// All `CondGroup`-like classes must derive from this base class in order for
+/// template logic to function correctly.
+struct CondGroupBase {};
+
+namespace cgrp_detail {
+
+template <typename T> using IsCondGroup = std::is_base_of<CondGroupBase, T>;
+
+template <typename T> struct IsStdOptional : std::false_type {};
+
+template <typename T>
+struct IsStdOptional<std::optional<T>> : std::true_type {};
+
+template <typename T, bool = std::is_enum_v<T>> struct UnderlyingTypeImpl {
+  using type = T;
+};
+
+/// Determine whether the specified type is an enum class type.
+template <typename T, bool = std::is_enum_v<T>>
+class IsEnumClass : public std::false_type {};
+
+template <typename EnumT>
+class IsEnumClass<EnumT, true>
+    : public std::bool_constant<
+          !std::is_convertible_v<EnumT, std::underlying_type_t<EnumT>>> {};
+
+template <typename EnumT>
+struct UnderlyingTypeImpl<EnumT, true> : std::underlying_type<EnumT> {};
+
+template <typename T>
+using UnderlyingType = typename UnderlyingTypeImpl<T>::type;
+
+/// No integral type could be determined from a type pack.
+struct BadIntegerType : CETypeError {};
+
+template <bool AllSigned, bool AllUnsigned, size_t MaxSize>
+class GetIntegralTypeImpl {
+public:
+  using type = BadIntegerType;
+};
+
+template <size_t MaxSize> class GetIntegralTypeImpl<true, false, MaxSize> {
+  static_assert(MaxSize <= 8u);
+
+  static constexpr auto chooseType() {
+    if constexpr (MaxSize == 0u)
+      // A zero MaxSize is only possible when the set of values is empty.  Use a
+      // simple `int` to satisfy this corner-case.
+      return int();
+
+    else if constexpr (MaxSize == 1u)
+      return int8_t();
+
+    else if constexpr (MaxSize == 2u)
+      return int16_t();
+
+    else if constexpr (MaxSize == 4u)
+      return int32_t();
+
+    else
+      return int64_t();
+  }
+
+public:
+  using type = decltype(chooseType());
+};
+
+template <size_t MaxSize> class GetIntegralTypeImpl<false, true, MaxSize> {
+  static_assert(MaxSize <= 8u);
+
+  static constexpr auto chooseType() {
+    if constexpr (MaxSize == 1u)
+      return uint8_t();
+
+    else if constexpr (MaxSize == 2u)
+      return uint16_t();
+
+    else if constexpr (MaxSize == 4u)
+      return uint32_t();
+
+    else
+      return uint64_t();
+  }
+
+public:
+  using type = decltype(chooseType());
+};
+
+/// `true` when all the types are unsigned integral or enums with unsigned
+/// underlying types; `false` otherwise.
+///
+/// An empty list of types is considered 'signed'.
+template <typename... Tn>
+inline constexpr bool AllUnsigned =
+    sizeof...(Tn) &&
+    std::conjunction_v<std::is_unsigned<UnderlyingType<Tn>>...>;
+
+/// `true` when all the types are signed integral or enums with signed
+/// underlying types; `false` otherwise.
+///
+/// An empty list of types is not considered 'unsigned'.
+template <typename... Tn>
+inline constexpr bool AllSigned =
+    std::conjunction_v<std::is_signed<UnderlyingType<Tn>>...>;
+
+template <typename... Tn>
+using GetIntegralType =
+    typename GetIntegralTypeImpl<AllSigned<Tn...>, AllUnsigned<Tn...>,
+                                 ce_max<size_t>(0, sizeof(Tn)...)>::type;
+
+/// Indicate that multiple distinct enum types were encountered during an
+/// `EnumMeet` operation.
+struct EnumMeetMultipleEnums : CETypeError {};
+
+template <typename T0, typename T1> class EnumMeetImpl {
+  template <typename ArgT>
+  static constexpr bool ValidArg =
+      std::is_integral_v<ArgT> || std::is_void_v<ArgT> || std::is_enum_v<ArgT>;
+
+  /// Determine the meet type given two non-error types.
+  ///
+  /// Assume that if only one type is an enum class, then it must be `U0`.
+  template <typename U0, typename U1> static constexpr auto chooseTypeLegal() {
+    if constexpr (IsEnumClass<U0>::value) {
+      if constexpr (!IsEnumClass<U1>::value || std::is_same_v<U0, U1>)
+        return U0();
+      else
+        // Return an error type from this context instead of `static_assert`
+        // here in order to provide the instantiating context in the compiler
+        // error output.
+        return EnumMeetMultipleEnums();
+    } else {
+      static_assert(
+          !IsEnumClass<U1>::value,
+          "Assumption that single enum class type occurs in 'U0' violated.");
+      // void return type.
+      return;
+    }
+  }
+
+  static constexpr auto chooseType() {
+    if constexpr (std::is_base_of_v<CETypeError, T0>) {
+      return T0();
+    } else if constexpr (std::is_base_of_v<CETypeError, T1>) {
+      return T1();
+    } else {
+      static_assert(ValidArg<T0>,
+                    "Unexpected type... expected integral, enum or void type.");
+      static_assert(ValidArg<T1>,
+                    "Unexpected type... expected integral, enum or void type.");
+
+      // Conditionally swap T0 and T1 before calling `chooseTypeLegal()` to
+      // ensure that if only one is an enum class, it occurs as the first type.
+      if constexpr (IsEnumClass<T0>::value)
+        return chooseTypeLegal<T0, T1>();
+      else
+        return chooseTypeLegal<T1, T0>();
+    }
+  }
+
+public:
+  using type = decltype(chooseType());
+};
+
+template <typename T0, typename T1>
+using EnumMeetT = typename EnumMeetImpl<T0, T1>::type;
+
+template <typename... Tn> class GetEnumTypeImpl {};
+
+template <typename T0, typename... Tn>
+class GetEnumTypeImpl<T0, Tn...> : public GetEnumTypeImpl<Tn...> {
+  using ChildEnumT = typename GetEnumTypeImpl<Tn...>::type;
+
+public:
+  using type = EnumMeetT<T0, ChildEnumT>;
+};
+
+template <> class GetEnumTypeImpl<> {
+public:
+  using type = void;
+};
+
+template <typename... Tn>
+using GetEnumType = typename GetEnumTypeImpl<Tn...>::type;
+
+} // namespace cgrp_detail
+
+/// Recursive tuple implementation.
+///
+/// \note This exists because it compiles faster on many toolchains compared to
+/// using std::tuple.  Certain gcc toolchains seem to blow up with extensive
+/// std::tuple use.
+template <typename... Tn> class CondGroupTuple {};
+
+namespace cgrp_detail {
+
+template <typename SingleT, typename GroupElemT>
+constexpr bool elemEqual(SingleT const &Single, GroupElemT const &Elem) {
+  if constexpr (IsCondGroup<GroupElemT>::value) {
+    return Elem.equalDisj(Single);
+  } else if constexpr (std::is_integral_v<SingleT> &&
+                       std::is_enum_v<GroupElemT>) {
+    return Single == std::underlying_type_t<GroupElemT>(Elem);
+  } else if constexpr (std::is_enum_v<SingleT> &&
+                       std::is_integral_v<GroupElemT>) {
+    return std::underlying_type_t<SingleT>(Single) == Elem;
+  } else if constexpr (std::is_same_v<std::nullopt_t, GroupElemT>) {
+    // `std::optional` uses its own operator==() to compare the wrapped
+    // optional type to any conditional group type.  That means we may be
+    // encountering an optional's wrapped type here.  If we are comparing a
+    // non-nullopt_t value, then equality is always `false` because the parent
+    // optional contained a valid value.
+    return std::is_same_v<std::nullopt_t, SingleT>;
+  } else {
+    return Single == Elem;
+  }
+}
+
+} // namespace cgrp_detail
+
+template <typename T0, typename... Tn>
+class CondGroupTuple<T0, Tn...> : public CondGroupTuple<Tn...> {
+  using RestT = CondGroupTuple<Tn...>;
+
+private:
+  T0 CurVal;
+
+public:
+  template <typename... Un>
+  constexpr CondGroupTuple(T0 const &Cur, Un &&...Rest)
+      : RestT(std::forward<Un>(Rest)...), CurVal(Cur) {}
+
+  template <typename... Un>
+  constexpr CondGroupTuple(T0 &&Cur, Un &&...Rest)
+      : RestT(std::forward<Un>(Rest)...), CurVal(std::move(Cur)) {}
+
+  /// Disjunction of '==' comparisons.
+  template <typename U> constexpr bool equalDisj(U const &Single) const {
+    return cgrp_detail::elemEqual(Single, CurVal) ||
+           getRest().equalDisj(Single);
+  }
+
+private:
+  constexpr RestT const &getRest() const { return *this; }
+};
+
+template <typename T0> class CondGroupTuple<T0> : public CondGroupBase {
+  T0 CurVal;
+
+public:
+  constexpr CondGroupTuple(T0 const &Cur) : CurVal(Cur) {}
+  constexpr CondGroupTuple(T0 &&Cur) : CurVal(std::move(Cur)) {}
+
+  template <typename U> constexpr bool equalDisj(U const &Single) const {
+    return cgrp_detail::elemEqual(Single, CurVal);
+  }
+};
+
+template <> class CondGroupTuple<> : public CondGroupBase {
+public:
+  template <typename U> bool equalDisj(U const &Single) const { return false; }
+};
+
+/// Class which distributes comparisons across all its data.
+///
+/// The basic `CondGroup` class uses `std::tuple`-like storage for elements of
+/// the group.
+///
+/// This class records a group of conditions which can be compared using
+/// the following functions:
+///  - `llvm::cgrp::anyOf()`
+///
+/// Above functions synthesize a comparison class which will distribute
+/// equal / inequal comparisons against a single value.  This enables
+/// the user to very succinctly specify a comparison across a group of
+/// possible values.
+///
+/// For example:
+/// \code{.cpp}
+///  enum class Op {
+///   Add, AddLo, AddHi,
+///   Sub,
+///   Mul, MulLo, MulHi,
+///   Mad,
+///   Load, Store,
+///   Barrier,
+///  };
+///
+///  constexpr auto ArithGroup =
+///    llvm::cgrp::makeGroup(Op::Add, Op::Sub, Op::Mul);
+///
+///  if (getOp() == cgrp::anyOf(ArithGroup)) {
+///    // Matched at least one of the arithmatic opcodes in `ArithGroup'.
+///  }
+/// \endcode
+///
+/// This comparison is functionally eqivalent to the long-form:
+/// \code{.cpp}
+///  if (getOp() == Op::Add || getOp() == Op::Sub || getOp() == Op::Mul) {
+///    // Process Arith op.
+///  }
+/// \endcode
+///
+/// The only difference here is that `getOp()` is only called once.
+/// Short-circuiting will occur if an early comparison matches (in
+/// the case of `cgrp::anyOf()`).
+///
+/// \note All of the expressions passed to `cgrp::anyOf()` will be evaluated,
+/// regardless of short-circuiting due to function call semantics.
+///
+///
+/// Users can nest `CondGroup` objects to compose larger logical groups, and
+/// the comparisons will behave efficiently & correctly.
+///
+/// For example:
+/// \code{.cpp}
+///  static constexpr auto AddGroup =
+///    llvm::cgrp::makeGroup(Op::AddLo, Op::AddHi, Op::Add);
+///
+///  static constexpr auto MulMadGroup =
+///    llvm::cgrp::makeGroup(Op::MulLo, Op::MulHi, Op::Mul, Op::Mad);
+///
+///  static constexpr auto StrangeGroup =
+///    llvm::cgrp::makeGroup(AddGroup, MulMadGroup, Op::Bar);
+///
+///  if (getOp() == cgrp::anyOf(StrangeGroup))
+///    // Opcode belongs to 'StrangeGroup`.
+/// \endcode
+template <typename... Tn> class CondGroup : public CondGroupTuple<Tn...> {
+public:
+  using CondGroup::CondGroupTuple::CondGroupTuple;
+};
+
+/// `CondGroup`-like class which stores integral or enum elements in a MetaSet
+/// (e.g. `MetaBitset` or `MetaSequenceSet`).
+///
+/// When applicable, this group can be much more space efficient than the tuple
+/// representation.  Additionally, the `MetaBitset` can save run-time by
+/// querying hundreds of elements in an O(1) bit extraction.
+///
+/// \tparam MetaSetT  MetaSet type to query for membership.
+/// \tparam EnumT  If `void` then all enum or integral types can be compared to
+/// the group; otherwise, all queried types must be integral or convertible to
+/// this type.
+template <typename MetaSetT, typename EnumT = void>
+class CondGroupMetaSet : public CondGroupBase,
+                         public MetaSetSortedContainer<MetaSetT> {
+public:
+  using set_type = MetaSetT;
+
+public:
+  constexpr CondGroupMetaSet() {}
+
+  /// Disjunction of '==' comparisons.
+  template <typename U> constexpr bool equalDisj(U const &Single) const {
+    if constexpr (cgrp_detail::IsEnumClass<U>::value) {
+      using EnumMeetT = cgrp_detail::EnumMeetT<U, EnumT>;
+
+      static_assert(
+          !std::is_base_of_v<CETypeError, EnumMeetT>,
+          "enum class type compared with a CondGroupMetaSet containing a "
+          "different enum class type.");
+    }
+    return MetaSetT::contains(Single);
+  }
+};
+
+/// Create a condition group object containing the specified \p Values.
+/// \relates CondGroup
+///
+/// \param Values  A list of values to forward to the `CondGroup` object.
+///
+/// \return A `CondGroup` containing all the specified values.
+template <typename... Tn> constexpr auto makeGroup(Tn &&...Values) {
+  return CondGroup<std::decay_t<Tn>...>(std::forward<Tn>(Values)...);
+}
+namespace cgrp_detail {
+
+/// Do not create any MetaBitset with more than this number of words (either
+/// dense or sparse).
+inline constexpr size_t BitsetWordLimit = 8u;
+
+/// Do not create any MetaBitset with more than this number of bits (either
+/// dense or sparse).
+inline constexpr size_t BitsetBitLimit = BitsetWordLimit * 64u;
+
+template <typename SeqT> class ChooseMetaSetImpl {};
+
+template <typename IntT, IntT... Values>
+class ChooseMetaSetImpl<std::integer_sequence<IntT, Values...>> {
+  // Don't use a bitset representation if there are too few values since the
+  // compiler seems to optimize these quite effectively.
+  static constexpr size_t MinValueCount = 4;
+
+  static constexpr auto chooseMetaBitsetType() {
+    constexpr IntT MinVal = ce_min<IntT>(Values...);
+    constexpr IntT MaxVal = ce_max<IntT>(Values...);
+
+    // Compute the number of words that would be required for a bitset
+    // representation.  Do not use a bitset if this exceeds a word limit imposed
+    // by the `MetaBitset` class.
+    constexpr size_t BitsetNumWords =
+        MetaBitsetNumWordsDetailed<IntT, MinVal, MaxVal>;
+
+    // The bitset representation will require this many bytes in .rodata.
+    constexpr size_t BitsetArraySize = BitsetNumWords * sizeof(uint64_t);
+
+    constexpr size_t BitsetPreferenceAddend = sizeof...(Values) < 8 ? 16U : 32U;
+
+    constexpr size_t TupleSize = sizeof(IntT) * sizeof...(Values);
+
+    constexpr size_t BitsetArrayUpperBound = ce_min<size_t>(
+        // (1.5 * TupleSize) + BitsetPreferenceAddend
+        TupleSize + (TupleSize >> 1) + BitsetPreferenceAddend,
+        // Max size we want for a single bitset buffer.
+        BitsetWordLimit * sizeof(uint64_t));
+
+    // BitsetSize <= 1.5*TupleSize + BitsetPreferenceAddend
+    constexpr bool UseSingleMetabitset =
+        BitsetArraySize <= BitsetArrayUpperBound;
+
+    if constexpr (UseSingleMetabitset)
+      return MakeMetaBitsetDetailed<IntT, MinVal, MaxVal, Values...>();
+    else
+      return MakeMetaSparseBitset<IntT, BitsetBitLimit, Values...>();
+  }
+
+  static constexpr auto chooseMetaSetType() {
+    if constexpr (sizeof...(Values) < MinValueCount)
+      return MakeMetaSequenceSet<IntT, Values...>();
+    else
+      return chooseMetaBitsetType();
+  }
+
+public:
+  using type = decltype(chooseMetaSetType());
+};
+
+template <auto... Values> class ChooseGroup {
+  using IntT = GetIntegralType<decltype(Values)...>;
+
+  static_assert(
+      std::is_integral_v<IntT>,
+      "CondGroups of literals must be either integral or enum types and must "
+      "all share the same sign.");
+
+  using EnumT = GetEnumType<decltype(Values)...>;
+
+  static_assert(
+      !std::is_same_v<cgrp_detail::EnumMeetMultipleEnums, EnumT>,
+      "Multiple enum class types encountered while building a CondGroup of "
+      "literals; at most one enum class type may be present in the value "
+      "pack.");
+
+  static_assert(!std::is_base_of_v<CETypeError, EnumT>,
+                "Unspecified error while deriving the enum type for a "
+                "CondGroup of literals.");
+
+  using SequenceType =
+      std::integer_sequence<IntT, static_cast<IntT>(Values)...>;
+  using MetaSetT = typename ChooseMetaSetImpl<SequenceType>::type;
+
+public:
+  using type = CondGroupMetaSet<MetaSetT, EnumT>;
+};
+
+template <auto... Values> constexpr auto makeLiteralGroup() {
+  using GroupT = typename ChooseGroup<Values...>::type;
+  return GroupT();
+}
+
+} // namespace cgrp_detail
+
+/// Instantiate a condition group of literal values which may be heavily
+/// optimized depending on the specified \p Values.
+///
+/// \tparam Values  Literal values to add to the condition group.
+///
+/// If the literals meet the following conditions:
+///  - All integral or enum types
+///  - All the same signedness
+///
+/// Then compile-time optimizations can be applied to the representation.  The
+/// ideal cases for optimizing the representation are:
+///
+///  1. A single cluster of literals which are close to eachother in value.
+///
+///  2. Multiple clusters of literals which are close to eachother in value,
+///     with a relatively small number of values scattered between the clusters.
+///
+/// \note The specification of literals does not need to be in any order to
+/// recieve representation optimizations.  Additionally, repeat elements in the
+/// group are tolerated but discouraged.
+///
+///
+/// \section single_cluster Single Cluster of Values
+///
+/// If single cluster of values is detected, then a single `MetaBitset` is used
+/// to represent the condition group.  This means that the storage will be
+/// limited to a `static constexpr` array of 64-bit integers.  Each bit in the
+/// array represents a value in the cluster offset from a potentially non-zero
+/// starting point.
+///
+/// The following is an example of a literal group which is represented as a
+/// single `MetaBitset`:
+/// \code{.cpp}
+///  static constexpr auto Fives =
+///     cgrp::Literals<5, 15, 25, 35, 45, 55, 65, 75, 85, 95, 10, 20, 30, 40,
+///                    50, 60, 70, 80, 90, 100>;
+///
+///  // Verify the representation.
+///  static_assert(std::is_same_v<
+///                  std::remove_cv_t<decltype(Fives)>,
+///                  CondGroupMetaSet<
+///                    MetaBitset<long, 5L,
+///                               0x1084210842108421, // Word 0
+///                               0x84210842          // Word 1
+///                    >
+///                  >
+///                >);
+/// \endcode
+///
+/// In this example, `Fives` is represented by a single `MetaBitset` with a
+/// start offset of `5` and two 64-bit words specified as template parameters.
+/// This means that group inclusion queries will be performed in O(1) as a
+/// bit-extraction from a 128-bit buffer.
+///
+///
+/// \section multi_cluster Multiple Clusters of Values
+///
+/// If the specified values span too large of a range to fit in a single
+/// `MetaBitset`, then cluster paritioning is performed.  The literals are
+/// sorted and then paritioned based on a sliding window of maximal allowed
+/// `MetaBitset` size.  Any clusters which are too small (less than a few
+/// elements) are added to a fall-back `MetaSequenceSet` (which is the
+/// meta-programming equivalent of the tuple representation).  The remaining
+/// clusters which are sufficiently large are converted to `MetaBitset`s.
+///
+/// \code{.cpp}
+///  static constexpr auto Clusters =
+///      cgrp::Literals<10000, 10002, 10004, 10006, 10008,
+///                     1000, 1002, 1004, 1006, 1008,
+///                     5000, 8000>;
+///
+///  static_assert(
+///      std::is_same_v<
+///          std::remove_cv_t<decltype(Clusters)>,
+///          CondGroupMetaSet<
+///              MetaBitset<long, 1000, 0x155>,
+///              MetaBitset<long, 10000, 0x155>,
+///              MetaSequenceSet<std::integer_sequence<long, 5000, 8000>>
+///          >
+///        >
+///      );
+/// \endcode
+///
+/// In the example above, the type infrastructure is able to identify two
+/// distinct clusters that are efficiently represented as `MetaBitset`s and two
+/// singletons.  One cluster starts at `10,000` and requires one 64-bit word in
+/// the `MetaBitset`.  The other cluster starts at `1,000` and also requires a
+/// single 64-bit word in its `MetaBitset`.  Finally the two singleton values
+/// are tracked in a seperate `MetaSequenceSet` and checked at the end during
+/// inclusion queries.
+///
+///
+/// \section singletons Singleton Values
+///
+/// In the case where no clusters can be formed, the representation is the same
+/// as the multi-cluster case except the `MetaBitset`s are omitted (i.e. a
+/// single `MetaSequenceSet` is used).
+///
+/// For example:
+/// \code{.cpp}
+///     static constexpr auto Singletons =
+///         cgrp::Literals<10000, 5000, 8000, 20000, 90000>;
+///
+///     static_assert(
+///         std::is_same_v<
+///           std::remove_cv_t<decltype(Singletons)>,
+///           CondGroupMetaSet<
+///             MetaSequenceSet<
+///               std::integer_sequence<
+///                 long, 5000, 8000, 10000, 20000, 90000
+///               >
+///             >
+///           >
+///         >);
+/// \endcode
+///
+/// No clusters could be identified in the `Singletons` group, so all the values
+/// get pushed to a `MetaSequenceSet`.
+///
+/// \note That the order of the singleton values will always be in ascending
+/// order. This is because we already applied a constexpr sort to the original
+/// sequence in order to efficiently identify clusters.
+///
+/// \section restrictions  Literal type restrictions
+///
+/// Extra type restrictions are applied to literal groups because their values
+/// are effectively down-cast to an integral type in order to enable a `MetaSet`
+/// representation.  These restrictions are implied in the tuple-like
+/// `CondGroup` because each element of the group retains its original type.
+///
+///
+/// **Literal group formation:**
+///
+/// - Literals may only be integral or enum types.
+/// - Integral and enum types may be mixed in a group definition, but at most
+///   one enum type may be used.
+/// - The signedness of all types must agree (i.e. the signedness of the
+///   underlying_type for an enum).
+///
+/// These group formation restrictions ensure that the group of values can be
+/// represented by a `MetaSet` as well as prevent incompatible enum types from
+/// inadvertently being cast down to an integral type.
+///
+/// The following are examples of permitted literal groups:
+/// \code{.cpp}
+///  enum class Letter : unsigned { A, B, C, D, E, F };
+///
+///  // All integral values of the same type.
+///  static constexpr auto G0 = cgrp::Literals<5, 10, 15>;
+///
+///  // All integral values, mixed types with same sign.
+///  static constexpr auto G1 = cgrp::Literals<5, short(10), long long(15)>;
+///
+///  // All enum values of the same type.
+///  static constexpr auto G2 = cgrp::Literals<Letter::A, Letter::F>;
+///
+///  // Mixed enum and integral values; same signedness.
+///  static constexpr auto G3 = cgrp::Literals<Letter::D, 0u, 1000ull>;
+/// \endcode
+///
+/// The following are examples of illegal literal groups, which will result in a
+/// compilation error:
+/// \code{.cpp}
+///  enum class Letter : unsigned { A, B, C, D, E, F };
+///  enum       Number : unsigned { Zero, One, Two, Three };
+///
+///  // All integral values but mixed signedness.
+///  static constexpr auto G0 = cgrp::Literals<1, 2u, 3>;
+///
+///  // Integral values mixed with enum values differing in signedness of
+///  // underlying type.
+///  static constexpr auto G1 = cgrp::Literals<One, 2>;
+///
+///  // Different enum types.
+///  static constexpr auto G2 = cgrp::Literals<Zero, Letter::B>;
+/// \endcode
+///
+///
+/// \section future_work  Future work
+///
+/// \todo Apply binary search to elements in a `MetaSequenceSet` when the
+/// `size()` is sufficiently large.
+///
+/// \todo Make literal groups iterable ranges.
+///
+/// \todo Implement constexpr set operations (`|`, `&`, `-`) for groups of
+/// literals which results in an optimized representation of the resulting
+/// group.
+template <auto... Values>
+inline constexpr auto Literals = cgrp_detail::makeLiteralGroup<Values...>();
+
+/// Group which implements disjunctive equivalence queries.
+///
+/// \tparam GroupT  The type of the group to query for membership.
+template <typename GroupT> class AnyOfGroup : public GroupT {
+  template <typename T> using IsOptional = cgrp_detail::IsStdOptional<T>;
+
+public:
+  using GroupT::GroupT;
+
+  constexpr AnyOfGroup(GroupT const &Group) : GroupT(Group) {}
+  constexpr AnyOfGroup(GroupT &&Group) : GroupT(std::move(Group)) {}
+
+  template <typename U, std::enable_if_t<!IsOptional<U>::value, int> = 0>
+  friend constexpr bool operator==(U const &Single, AnyOfGroup const &Group) {
+    return Group.equalDisj(Single);
+  }
+
+  template <typename U, std::enable_if_t<!IsOptional<U>::value, int> = 0>
+  friend constexpr bool operator==(AnyOfGroup const &Group, U const &Single) {
+    return Group.equalDisj(Single);
+  }
+
+  template <typename U, std::enable_if_t<!IsOptional<U>::value, int> = 0>
+  friend constexpr bool operator!=(U const &Single, AnyOfGroup const &Group) {
+    return !Group.equalDisj(Single);
+  }
+
+  template <typename U, std::enable_if_t<!IsOptional<U>::value, int> = 0>
+  friend constexpr bool operator!=(AnyOfGroup const &Group, U const &Single) {
+    return !Group.equalDisj(Single);
+  }
+};
+
+/// Create a specialized comparison `CondGroup` object which can be compared
+/// against a single value for equality (inclusion) or inequality (exclusion).
+/// \relates AnyOfGroup
+///
+/// \section equality   Equality: Inclusion in parameters
+///
+/// An equality comparison against the return result of `cgrp::anyOf()` will
+/// return `true` if the specified value is equal to any parameters passed to
+/// `cgrp::anyOf()`; otherwise it will return `false`.  For example:
+///
+/// \code{.cpp}
+///  assert(  1 == cgrp::anyOf(5, 4, 3, 2, 1));
+///  assert( (0 == cgrp::anyOf(5, 4, 3, 2, 1)) == false);
+/// \endcode
+///
+/// An equality comparison of an `cgrp::anyOf()` result logically expands to
+/// the following:
+/// \code{.cpp}
+///  if ( VAL == cgrp::anyOf(A, B, C, D, ...))
+///
+///    ==>
+///
+///  auto TMP = VAL;
+///  if ( TMP == A || TMP == B || TMP == C || TMP == D || ... )
+/// \endcode
+///
+///
+/// \section inequality   Inequality: Exclusion from parameters
+///
+/// An inequality comparison against the return result of `cgrp::anyOf()` will
+/// result in `false` if the specified value is equal to any parameters passed
+/// to `cgrp::anyOf()`; otherwise it will return `true`.
+///
+/// \code{.cpp}
+///  assert( (1 != cgrp::anyOf(5, 4, 3, 2, 1)) == false);
+///  assert(  0 != cgrp::anyOf(5, 4, 3, 2, 1));
+/// \endcode
+///
+/// An inequality comparison of an `cgrp::anyOf()` result expands to the
+/// following:
+/// \code{.cpp}
+///  if ( VAL != cgrp::anyOf(A, B, C, D, ...))
+///
+///    ==>
+///
+///  auto TMP = VAL;
+///  if ( TMP != A && TMP != B && TMP != C && TMP != D && ... )
+/// \endcode
+///
+/// Users may also combine simple types with `CondGroup` objects as parameters
+/// to `cgrp::anyOf()`. This will still implement short circuiting logic in the
+/// order that values appear.  For example:
+///
+/// \code{.cpp}
+///  auto powersOfTwo = llvm::cgrp::makeGroup(1, 2, 4, 8, 16);
+///  auto multiplesOfThree = llvm::cgrp::makeGroup(3, 6, 9, 12, 15);
+///
+///  // 7 does not exist in either group, nor does it match 10 or 11.
+///  assert(7 != cgrp::anyOf(powersOfTwo, multiplesOfThree, 10, 11));
+/// \endcode
+template <typename... Tn> constexpr auto anyOf(Tn &&...Values) {
+  using FirstT = std::decay_t<PackElementT<0, Tn...>>;
+  if constexpr (sizeof...(Values) == 1 &&
+                cgrp_detail::IsCondGroup<FirstT>::value)
+    // A single group was passed in, so avoid wrapping it in another
+    // CondGroup tuple.
+    return AnyOfGroup<FirstT>(std::forward<Tn>(Values)...);
+  else
+    return AnyOfGroup<CondGroup<std::decay_t<Tn>...>>(
+        std::forward<Tn>(Values)...);
+}
+
+namespace cgrp_detail {
+
+template <auto... Values> constexpr auto anyOfLiterals() {
+  using GroupT = typename cgrp_detail::ChooseGroup<Values...>::type;
+
+  return AnyOfGroup<GroupT>();
+}
+
+} // namespace cgrp_detail
+
+/// Instantiate a disjunctive equivalence comparison group which is strongly
+/// optimized for the literals specified as template parameters.
+///
+/// \tparam Values  Literal values to add to the `AnyOf` comparison.
+///
+/// Functionally equivalent to:
+/// \code{.cpp}
+///  cgrp::anyOf(cgrp::Literals<Values...>)
+/// \endcode
+///
+/// All of the representation optimizations employed by `cgrp::Literals` are
+/// applied, and the instantiated group is wrapped in an `AnyOfGroup`.
+///
+/// The `AnyOf` group is a specialized comparison `CondGroup` object which can
+/// be compared against a single value for equality (inclusion) or inequality
+/// (exclusion).
+///
+///
+/// \section equality   Equality: Inclusion in parameters
+///
+/// An equality comparison against `cgrp::AnyOf<...>` will return `true` if the
+/// specified value is equal to any of the template parameters passed to
+/// `cgrp::AnyOf<...>`; otherwise it will return `false`.
+///
+/// For example:
+/// \code{.cpp}
+///  assert(  1 == cgrp::AnyOf<5, 4, 3, 2, 1>);
+///  assert( (0 == cgrp::AnyOf<5, 4, 3, 2, 1>) == false);
+/// \endcode
+///
+/// An equality comparison of an `cgrp::AnyOf<>` logically expands to the
+/// following:
+/// \code{.cpp}
+///  if ( VAL == cgrp::AnyOf<A, B, C, D, ...>)
+///
+///    ==>
+///
+///  auto TMP = VAL;
+///  if ( TMP == A || TMP == B || TMP == C || TMP == D || ... )
+/// \endcode
+///
+///
+/// \section inequality   Inequality: Exclusion from parameters
+///
+/// An inequality comparison against an `cgrp::AnyOf` instantiation will return
+/// in `false` if the specified value is equal to any of the template
+/// parameters; otherwise it will return `true`.
+///
+/// \code{.cpp}
+///  assert( (1 != cgrp::AnyOf<5, 4, 3, 2, 1>) == false);
+///  assert(  0 != cgrp::AnyOf<5, 4, 3, 2, 1>);
+/// \endcode
+///
+/// An inequality comparison of an `cgrp::AnyOf` logically expands to the
+/// following:
+/// \code{.cpp}
+///  if ( VAL != cgrp::AnyOf<A, B, C, D, ...>)
+///
+///    ==>
+///
+///  auto TMP = VAL;
+///  if ( TMP != A && TMP != B && TMP != C && TMP != D && ... )
+/// \endcode
+///
+///
+/// \section restrictions  Literal type restrictions
+///
+/// Extra type restrictions are applied to literal groups because their values
+/// are effectively down-cast to an integral type in order to enable a `MetaSet`
+/// representation.  These restrictions are implied in the tuple-like
+/// `CondGroup` because each element of the group retains its original type.
+///
+///
+/// **Literal group formation:**
+///
+/// - Literals may only be integral or enum types.
+/// - Integral and enum types may be mixed in a group definition, but at most
+///   one enum type may be used.
+/// - The signedness of all types must agree (i.e. the signedness of the
+///   underlying_type for an enum).
+///
+/// These group formation restrictions ensure that the group of values can be
+/// represented by a `MetaSet` as well as prevent incompatible enum types from
+/// inadvertently being cast down to an integral type.
+///
+/// The following are examples of permitted literal groups:
+/// \code{.cpp}
+///  enum class Letter : unsigned { A, B, C, D, E, F };
+///
+///  // All integral values of the same type.
+///  static constexpr auto G0 = cgrp::Literals<5, 10, 15>;
+///
+///  // All integral values, mixed types with same sign.
+///  static constexpr auto G1 = cgrp::Literals<5, short(10), long long(15)>;
+///
+///  // All enum values of the same type.
+///  static constexpr auto G2 = cgrp::Literals<Letter::A, Letter::F>;
+///
+///  // Mixed enum and integral values; same signedness.
+///  static constexpr auto G3 = cgrp::Literals<Letter::D, 0u, 1000ull>;
+/// \endcode
+///
+/// The following are examples of illegal literal groups, which will result in a
+/// compilation error:
+/// \code{.cpp}
+///  enum class Letter : unsigned { A, B, C, D, E, F };
+///  enum       Number : unsigned { Zero, One, Two, Three };
+///
+///  // All integral values but mixed signedness.
+///  static constexpr auto G0 = cgrp::Literals<1, 2u, 3>;
+///
+///  // Integral values mixed with enum values differing in signedness of
+///  // underlying type.
+///  static constexpr auto G1 = cgrp::Literals<One, 2>;
+///
+///  // Different enum types.
+///  static constexpr auto G2 = cgrp::Literals<Zero, Letter::B>;
+/// \endcode
+///
+///
+/// **Literal group comparisons:**
+///
+/// - The single value compared to the group must be convertible to the
+///   underlying integral type used by the `MetaSet` representation.
+/// - If the literals included an enum type, then the single value compared to a
+///   group must be convertible to that enum type.
+///
+/// The above comparison restrictions are meant to make the `CondGroup`
+/// infrastructure both permissive in the sense that integral values can be
+/// comparered to a group derived from an enum class, but not completely
+/// circumvent the type system by disallowing comparisons across multiple enum
+/// types.
+///
+/// For example, the following comparison is permitted:
+/// \code{.cpp}
+///  enum class Letter { A, B, C, D, E, F };
+///
+///  static_assert(1 == cgrp::AnyOf<Letter::A, Letter::B>);
+/// \endcode
+///
+/// However, the following results in a compilation error:
+/// \code{.cpp}
+///  enum class Letter { A, B, C, D, E, F };
+///  enum Number { Zero, One, Two, Three, Four };
+///
+///  // Compile error: incomptible enum types.
+///  static_assert(Letter::A == cgrp::AnyOf<0, One, 2>);
+/// \endcode
+template <auto... Values>
+inline constexpr auto AnyOf = cgrp_detail::anyOfLiterals<Values...>();
+
+/// Compute the union of two literal condition groups at compile time.
+template <typename SetL, typename SetR>
+constexpr auto operator|(CondGroupMetaSet<SetL>, CondGroupMetaSet<SetR>) {
+  return CondGroupMetaSet<
+      MetaSetUnion<SetL, SetR, cgrp_detail::BitsetBitLimit>>();
+}
+
+/// Compute the intersection of two literal condition groups at compile time.
+template <typename SetL, typename SetR>
+constexpr auto operator&(CondGroupMetaSet<SetL>, CondGroupMetaSet<SetR>) {
+  return CondGroupMetaSet<
+      MetaSetIntersection<SetL, SetR, cgrp_detail::BitsetBitLimit>>();
+}
+
+/// Compute the set resulting from subtracting \p SetR from \p SetL at compile
+/// time.
+template <typename SetL, typename SetR>
+constexpr auto operator-(CondGroupMetaSet<SetL>, CondGroupMetaSet<SetR>) {
+  return CondGroupMetaSet<
+      MetaSetMinus<SetL, SetR, cgrp_detail::BitsetBitLimit>>();
+}
+
+} // namespace llvm::cgrp
+
+#endif // LLVM_ADT_CONDGROUP_H
diff --git a/llvm/include/llvm/ADT/ConstexprUtils.h b/llvm/include/llvm/ADT/ConstexprUtils.h
new file mode 100644
index 0000000000000..c0758f54da7e0
--- /dev/null
+++ b/llvm/include/llvm/ADT/ConstexprUtils.h
@@ -0,0 +1,469 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implement common constexpr utilities including sorting and integer
+/// sequence manipulation.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_ADT_CONSTEXPRUTILS_H
+#define LLVM_ADT_CONSTEXPRUTILS_H
+
+#include "llvm/Support/MathExtras.h"
+
+#include <array>
+#include <cstddef>
+#include <type_traits>
+#include <utility>
+
+namespace llvm {
+
+/// This is a tag class returned by meta-programming to indicate an error.
+///
+/// Users may derive from this class to provide more context on the failure.
+///
+/// Using a tag class allows the infrastructure to `static_assert` at higher,
+/// more relevant scopes and generates much more friendly template errors.
+struct CETypeError {};
+
+namespace constexpr_detail {
+
+template <std::size_t I, typename... Tn> struct PackElement {};
+
+template <std::size_t I, typename T0, typename... Tn>
+struct PackElement<I, T0, Tn...> {
+  static_assert(I <= sizeof...(Tn), "Pack index out of bounds.");
+
+  using type = typename PackElement<I - 1, Tn...>::type;
+};
+
+template <typename T0, typename... Tn> struct PackElement<0, T0, Tn...> {
+  using type = T0;
+};
+
+/// An extraction cannot occur from an empty type pack.
+struct PackElementEmptyPack : CETypeError {};
+
+template <std::size_t I> struct PackElement<I> {
+  using type = PackElementEmptyPack;
+};
+
+} // namespace constexpr_detail
+
+template <std::size_t Idx, typename... Tn>
+using PackElementT = typename constexpr_detail::PackElement<Idx, Tn...>::type;
+
+/// Swap values of two specified references at compile-time.
+template <typename T>
+constexpr void ce_swap(T &L, T &R) // NOLINT (readability-identifier-naming)
+{
+  T Temp = L;
+  L = R;
+  R = Temp;
+}
+
+/// Compute the minimum value from a list of compile-time constants.
+///
+/// \tparam RetT  Type to return and `static_cast` each value to for comparison.
+///
+/// \param Val0   First value in the list to compute the minimum of.
+/// \param ValN   Remaining values in the list to compute the minimum of.
+template <typename RetT, typename T0, typename... Tn>
+constexpr RetT                //
+ce_min(T0 Val0, Tn... ValN) { // NOLINT(readability-identifier-naming)
+  RetT Min = static_cast<RetT>(Val0);
+  ((Min = static_cast<RetT>(ValN) < Min ? static_cast<RetT>(ValN) : Min), ...);
+  return Min;
+}
+
+/// Compute the maximum value from a list of compile-time constants.
+///
+/// \tparam RetT  Type to return and `static_cast` each value to for comparison.
+///
+/// \param Val0  First value in the list to compute the maximum of.
+/// \param ValN  Remaining values in the list to compute the maximum of.
+template <typename RetT, typename T0, typename... Tn>
+constexpr RetT                //
+ce_max(T0 Val0, Tn... ValN) { // NOLINT(readability-identifier-naming)
+  RetT Max = static_cast<RetT>(Val0);
+  ((Max = static_cast<RetT>(ValN) > Max ? static_cast<RetT>(ValN) : Max), ...);
+  return Max;
+}
+
+namespace constexpr_detail {
+
+template <typename T, std::size_t N, std::size_t... I>
+constexpr auto toArray(T const (&Arr)[N], std::index_sequence<I...>) {
+  return std::array<std::remove_cv_t<T>, N>{{Arr[I]...}};
+}
+
+} // namespace constexpr_detail
+
+/// Convert a C array to an equivalent `std::array`.
+template <typename T, std::size_t N>
+constexpr auto
+to_array(T const (&Arr)[N]) // NOLINT (readability-identifier-naming)
+{
+  return constexpr_detail::toArray(Arr, std::make_index_sequence<N>());
+}
+
+/// Convert a `std::integer_sequence` to a `std::array` with the same contents.
+template <typename T, T... Vals>
+constexpr auto to_array( // NOLINT (readability-identifier-naming)
+    std::integer_sequence<T, Vals...>) {
+  return std::array<T, sizeof...(Vals)>{{Vals...}};
+}
+
+namespace constexpr_detail {
+
+template <typename IterT, typename LessT>
+constexpr void bubbleSort(IterT B, IterT E, LessT const &Less) {
+  for (auto Last = E, ELast = std::next(B); Last != ELast; --Last) {
+    bool Swapped = false;
+
+    for (auto I = ELast; I != Last; ++I) {
+      if (Less(*I, *std::prev(I))) {
+        ce_swap(*I, *std::prev(I));
+        Swapped = true;
+      }
+    }
+
+    // Remaining items are in sorted order if no swaps occurred during the
+    // inner loop.
+    if (!Swapped)
+      return;
+  }
+}
+
+template <typename T, std::size_t N, typename LessT>
+constexpr T &quickSortGetPivot(std::array<T, N> &Arr, std::size_t Low,
+                               std::size_t High, LessT const &Less) {
+  std::size_t Middle = Low + (((High - Low) + 1) >> 1);
+  // Use "median of three" (repositioned into the 'High' position) as pivot...
+  if (Less(Arr[High], Arr[Low]))
+    ce_swap(Arr[High], Arr[Low]);
+
+  if (Middle != High) {
+    if (Less(Arr[Middle], Arr[Low]))
+      ce_swap(Arr[Middle], Arr[Low]);
+
+    if (Less(Arr[Middle], Arr[High]))
+      ce_swap(Arr[Middle], Arr[High]);
+  }
+  return Arr[High];
+}
+
+template <typename T, std::size_t N, typename LessT>
+constexpr std::size_t quickSortPartition(std::array<T, N> &Arr, std::size_t Low,
+                                         std::size_t High, LessT const &Less) {
+  auto &Pivot = quickSortGetPivot(Arr, Low, High, Less);
+
+  std::size_t I = Low;
+  for (std::size_t J = Low; J < High; ++J)
+    if (Less(Arr[J], Pivot))
+      ce_swap(Arr[I++], Arr[J]);
+
+  ce_swap(Arr[I], Arr[High]);
+  return I;
+}
+
+struct QuickSortIndexPair {
+  std::size_t Low = 0, High = 0;
+  constexpr void set(std::size_t L, std::size_t H) { Low = L, High = H; }
+};
+
+template <std::size_t N> class QuickSortStack {
+  std::size_t Size = 0;
+  std::array<QuickSortIndexPair, N> Arr;
+
+public:
+  constexpr bool empty() const { return Size == 0; }
+  constexpr void push(std::size_t L, std::size_t H) { Arr[Size++].set(L, H); }
+  constexpr QuickSortIndexPair pop() { return Arr[--Size]; }
+  constexpr std::size_t availableSlots() const { return N - Size; }
+};
+
+// Use bubble sort on partitions smaller than this size.  Bubble will be
+// more efficient on small partitions.
+inline constexpr std::size_t MinQuickSortPartitionSize = 10u;
+
+template <typename T, std::size_t N, typename LessT>
+constexpr void quickSort(std::array<T, N> &Arr, LessT const &Less) {
+  if constexpr (N <= 1)
+    return;
+
+  // The maximum stack depth is O(log N) on average.  O(N) is possible in
+  // corner-cases, so the partition loop will fall back to bubble sorting
+  // partitions if the stack is exhausted.
+  constexpr std::size_t StackSize = ConstantLog2<NextPowerOf2(N)>() + 1;
+  constexpr_detail::QuickSortStack<StackSize> Stack;
+
+  Stack.push(0, N - 1);
+
+  while (!Stack.empty()) {
+    auto const [Low, High] = Stack.pop();
+
+    if ((High - Low) + 1u < MinQuickSortPartitionSize ||
+        Stack.availableSlots() < 2u) {
+      bubbleSort(Arr.begin() + Low, Arr.begin() + High + 1, Less);
+      continue;
+    }
+
+    std::size_t const PivotIndex =
+        constexpr_detail::quickSortPartition(Arr, Low, High, Less);
+
+    std::size_t PivotIndexLo = PivotIndex - 1;
+    std::size_t PivotIndexHi = PivotIndex + 1;
+
+    // Pivot is in sorted position; so are any adjacent elements which are
+    // equivalent.  Scan past elements which are equal to the pivot before
+    // sub-dividing the current range.
+    while (PivotIndexLo > Low && PivotIndexLo < High &&
+           !Less(Arr[PivotIndexLo], Arr[PivotIndex]))
+      --PivotIndexLo;
+    while (PivotIndexHi < High && !Less(Arr[PivotIndex], Arr[PivotIndexHi]))
+      ++PivotIndexHi;
+
+    // Push right sub-array boundaries to the Stack if it exists
+    if (High > PivotIndexHi)
+      Stack.push(PivotIndexHi, High);
+
+    // Push left sub-array boundaries to the Stack if it exists
+    if (Low < PivotIndexLo && PivotIndexLo < N)
+      Stack.push(Low, PivotIndexLo);
+  }
+}
+
+} // namespace constexpr_detail
+
+/// Iterative sort implementation.
+///
+/// \param[in,out] Arr  Array of elements to sort in-place.
+/// \param Less  Less-than comparison functor which compares two `T`s.
+template <typename T, std::size_t N, typename LessT>
+constexpr void ce_sort_inplace( // NOLINT (readability-identifier-naming)
+    std::array<T, N> &Arr, LessT const &Less) {
+  if constexpr (N >= constexpr_detail::MinQuickSortPartitionSize)
+    constexpr_detail::quickSort(Arr, Less);
+  else if (N > 1)
+    // Avoid instantiating the quickSort stack if we won't even parition
+    // elements once.
+    constexpr_detail::bubbleSort(Arr.begin(), Arr.end(), Less);
+}
+
+/// Iterative sort implementation.
+///
+/// \param[in,out] Arr  Array of elements to sort in-place.
+template <typename T, std::size_t N>
+constexpr void
+ce_sort_inplace(std::array<T, N> &Arr) // NOLINT (readability-identifier-naming)
+{
+  constexpr auto Less = [](T const &L, T const &R) constexpr { return L < R; };
+  ce_sort_inplace(Arr, Less);
+}
+
+/// Iterative sort implementation.
+///
+/// The sort implementation is chosen at compile-time based on the size of the
+/// specified array.
+///
+/// \param ArrIn Input sequence to sort.
+/// \param Less  Less-than comparison functor which compares two `T`s.
+///
+/// \return A `std::array` of elements in ascending order according to the
+/// specified \p Less comparison.
+template <typename T, std::size_t N, typename LessT>
+constexpr std::array<std::remove_cv_t<T>, N>
+ce_sort( // NOLINT (readability-identifier-naming)
+    T const (&ArrIn)[N], LessT const &Less) {
+  auto Arr = to_array(ArrIn);
+  ce_sort_inplace(Arr, Less);
+  return Arr;
+}
+
+/// Iterative sort implementation.
+///
+/// The sort implementation is chosen at compile-time based on the size of the
+/// specified array.
+///
+/// \param ArrIn Input sequence to sort.
+///
+/// \return A `std::array` of elements in ascending order according based on the
+/// `operator<()` associated with `T`.
+template <typename T, std::size_t N>
+constexpr std::array<std::remove_cv_t<T>, N>
+ce_sort(T const (&ArrIn)[N]) // NOLINT (readability-identifier-naming)
+{
+  auto Arr = to_array(ArrIn);
+  ce_sort_inplace(Arr);
+  return Arr;
+}
+
+namespace constexpr_detail {
+
+template <typename T, T... Is> class SortLiteralsImpl {
+  static constexpr auto makeSortedArray() {
+    std::array<T, sizeof...(Is)> Arr{{Is...}};
+    ce_sort_inplace(Arr);
+    return Arr;
+  }
+
+  // Helper to reconstruct integer_sequence from the sorted array
+  template <std::size_t... Idxs>
+  static constexpr auto makeSortedSequence(std::index_sequence<Idxs...>) {
+    constexpr auto SortedArray = makeSortedArray();
+    return std::integer_sequence<T, SortedArray[Idxs]...>{};
+  }
+
+public:
+  using type =
+      decltype(makeSortedSequence(std::make_index_sequence<sizeof...(Is)>()));
+};
+
+} // namespace constexpr_detail
+
+/// Create a sorted `std::integer_sequence` from a list of literals.
+///
+/// \tparam T  Integral type of output `std::integer_sequence` as well as all
+/// the specified literals.
+/// \tparam Is  List of literals to sort.
+template <typename T, T... Is>
+using SortLiterals =
+    typename constexpr_detail::SortLiteralsImpl<T, Is...>::type;
+
+namespace constexpr_detail {
+
+template <typename SeqT> class SortSequenceImpl {};
+
+template <typename T, T... Is>
+class SortSequenceImpl<std::integer_sequence<T, Is...>> {
+public:
+  using type = typename SortLiteralsImpl<T, Is...>::type;
+};
+
+} // namespace constexpr_detail
+
+/// Create a sorted `std::integer_sequence` from a specified
+/// `std::integer_sequence`.
+///
+/// \tparam SeqT  `std::integer_sequence` which specifies the literals to be
+/// sorted.
+template <typename SeqT>
+using SortSequence = typename constexpr_detail::SortSequenceImpl<SeqT>::type;
+
+namespace constexpr_detail {
+
+template <std::size_t First, typename T, std::size_t N, std::size_t... Idx>
+constexpr auto ce_slice_impl( // NOLINT (readability-identifier-naming)
+    std::array<T, N> const &Arr, std::index_sequence<Idx...>) {
+  return std::array<T, sizeof...(Idx)>{{Arr[First + Idx]...}};
+}
+
+} // namespace constexpr_detail
+
+/// Create a `std::array` representing a slice of an input `std::array`.
+///
+/// \tparam First Index of the first element of the output slice.
+/// \tparam Len   Number of elements int the output slice.
+///
+/// \param Arr    Array to slice from.
+///
+/// \return A new `std::array` of length `Len` representing the specified slice
+/// of \p Arr.
+template <std::size_t First, std::size_t Len, typename T, std::size_t N>
+constexpr auto
+ce_slice(std::array<T, N> const &Arr) // NOLINT (readability-identifier-naming)
+{
+  static_assert(First + Len <= N, "End of slice is out of bounds.");
+  return constexpr_detail::ce_slice_impl<First>(
+      Arr, std::make_index_sequence<Len>());
+}
+
+namespace constexpr_detail {
+
+template <std::size_t First, std::size_t Len, typename T, T... Is>
+class SliceLiteralsImpl {
+  static_assert(First + Len <= sizeof...(Is), "End of slice is out of bounds.");
+
+  template <std::size_t... Idxs>
+  static constexpr auto makeSlicedSequence(std::index_sequence<Idxs...>) {
+    constexpr std::array<T, sizeof...(Is)> InArr{{Is...}};
+    return std::integer_sequence<T, InArr[First + Idxs]...>{};
+  }
+
+public:
+  using type = decltype(makeSlicedSequence(std::make_index_sequence<Len>()));
+};
+
+template <std::size_t First, typename T, T... Is>
+class SliceLiteralsImpl<First, 0, T, Is...> {
+  static_assert(First <= sizeof...(Is));
+
+public:
+  using type = std::integer_sequence<T>;
+};
+
+} // namespace constexpr_detail
+
+/// Create a `std::index_sequence` representing a slice of list of literals.
+///
+/// \tparam First Index of the first element of the output slice.
+/// \tparam Len   Number of elements int the output slice.
+/// \tparam T     Type of the literals being sliced.
+/// \tparam Is    List of literals to slice.
+template <std::size_t First, std::size_t Len, typename T, T... Is>
+using SliceLiterals =
+    typename constexpr_detail::SliceLiteralsImpl<First, Len, T, Is...>::type;
+
+namespace constexpr_detail {
+
+template <std::size_t First, std::size_t Len, typename SeqT>
+class SliceSequenceImpl {};
+
+template <std::size_t First, std::size_t Len, typename T, T... Is>
+class SliceSequenceImpl<First, Len, std::integer_sequence<T, Is...>> {
+public:
+  using type = SliceLiterals<First, Len, T, Is...>;
+};
+
+} // namespace constexpr_detail
+
+/// Generate a `std::integer_sequence` representing a sub-range of a specified
+/// `std::integer_sequence`.
+///
+/// \tparam First Index of the first element of the output slice.
+/// \tparam Len   Number of elements int the output slice.
+/// \tparam SeqT  `std::integer_sequence` to slice from.
+template <std::size_t First, std::size_t Len, typename SeqT>
+using SliceSequence =
+    typename constexpr_detail::SliceSequenceImpl<First, Len, SeqT>::type;
+
+namespace constexpr_detail {
+
+template <auto Val, typename SeqT> class PushBackSequenceImpl {};
+
+template <auto Val, typename T, T... Is>
+class PushBackSequenceImpl<Val, std::integer_sequence<T, Is...>> {
+public:
+  using type = std::integer_sequence<T, Is..., T(Val)>;
+};
+
+} // namespace constexpr_detail
+
+/// Add an integer value to the end of an `std::integer_sequence`.
+///
+/// \tparam SeqT `std::integer_sequence` to append to.
+/// \tparam Val  Integral value to append to \p SeqT.
+template <typename SeqT, auto Val>
+using PushBackSequence =
+    typename constexpr_detail::PushBackSequenceImpl<Val, SeqT>::type;
+
+} // namespace llvm
+
+#endif // LLVM_ADT_CONSTEXPRUTILS_H
diff --git a/llvm/include/llvm/ADT/MetaSet.h b/llvm/include/llvm/ADT/MetaSet.h
new file mode 100644
index 0000000000000..49d3719511158
--- /dev/null
+++ b/llvm/include/llvm/ADT/MetaSet.h
@@ -0,0 +1,1083 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implements template meta-programming set representations for use in
+/// `constexpr` expressions.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_ADT_METASET_H
+#define LLVM_ADT_METASET_H
+
+#include "llvm/ADT/ConstexprUtils.h"
+#include "llvm/ADT/bit.h"
+
+#include <array>
+#include <cstddef>
+#include <cstdint>
+#include <type_traits>
+#include <utility>
+
+namespace llvm {
+
+/// Max number of words allowed in a MetaBitset.
+///
+/// This limit prevents unbounded host compile-time / space in .rodata.
+static constexpr size_t MetaBitsetWordLimit = 512;
+
+struct MetaSetTag {};
+struct MetaSetDuplicateElemsTag {};
+struct MetaBitsetTag : MetaSetTag {};
+struct MetaSequenceSetTag : MetaSetTag, MetaSetDuplicateElemsTag {};
+struct MetaSparseBitsetTag : MetaSetTag {};
+
+template <typename T>
+inline constexpr bool IsMetaSet = std::is_base_of_v<MetaSetTag, T>;
+
+template <typename T>
+inline constexpr bool IsMetaBitset = std::is_base_of_v<MetaBitsetTag, T>;
+
+template <typename T>
+inline constexpr bool IsMetaSequenceSet =
+    std::is_base_of_v<MetaSequenceSetTag, T>;
+
+template <typename T>
+inline constexpr bool IsMetaSparseBitset =
+    std::is_base_of_v<MetaSparseBitsetTag, T>;
+
+template <typename T>
+inline constexpr bool IsMetaSetWithDuplicateElements =
+    std::is_base_of_v<MetaSetDuplicateElemsTag, T>;
+
+namespace metaset_detail {
+
+template <typename PosT, PosT Offset> struct TypeInfo {
+  static_assert(std::is_integral_v<PosT>,
+                "The position type of a `MetaBitset` must be integral.");
+
+  using WordT = uint64_t;
+  using UPosT = std::make_unsigned_t<PosT>;
+  using WordIndexT = UPosT;
+
+  static constexpr UPosT WordNBits = 64;
+  static constexpr UPosT IdxShift = 6; // log2(64)
+
+  class NormalizedT {
+    UPosT NormalizedPos;
+
+    constexpr NormalizedT(PosT Pos) : NormalizedPos(UPosT(Pos - Offset)) {}
+
+    // Allow TypeInfo access to the constructor.
+    friend struct TypeInfo;
+
+  public:
+    constexpr UPosT wordIndex() const { return NormalizedPos >> IdxShift; }
+
+    constexpr WordT bitMask() const {
+      constexpr UPosT BitIdxMask = (UPosT(1) << IdxShift) - UPosT(1);
+      return WordT(1) << (NormalizedPos & BitIdxMask);
+    }
+  };
+
+  static constexpr NormalizedT normalizePos(PosT Pos) { return Pos; }
+};
+
+} // namespace metaset_detail
+
+/// Represent a set of integral values using a dense bitset representation.
+///
+/// The `MetaBitset` has no storage on the class itself, but it refers to a
+/// `static constexpr` array of words that represent the elements in the set.
+///
+/// This class must be built with helper type aliases.  See `MakeMetaBitset`
+/// and `MakeMetaBitsetFromSequence`.  If converting a sorted
+/// `std::integer_sequence` to a `MetaBitset`, use
+/// `MakeMetaBitsetFromSortedSequence` to save some build time.
+///
+/// \tparam PosT  Integral or enum type used to represent the position / offset
+/// in the bitset.
+/// \tparam Offset  Adjustment applied to all positions in the bitset, thus
+/// enabling efficient representation of clusters of large numbers.
+/// \tparam Words  The unsigned words whose bits represent the set.
+template <typename PosT, PosT Offset, uint64_t... Words>
+class MetaBitset : metaset_detail::TypeInfo<PosT, Offset>,
+                   public MetaBitsetTag {
+  using WordT = typename MetaBitset::TypeInfo::WordT;
+  using UPosT = typename MetaBitset::TypeInfo::UPosT;
+
+  static constexpr std::array<WordT, sizeof...(Words)> WordArr{{Words...}};
+
+  static_assert(WordArr.size() == 0 || WordArr[0] != 0ULL,
+                "A MetaBitset is malformed if its low-order word is zero.");
+
+public:
+  using value_type = PosT;
+
+public:
+  constexpr MetaBitset() {}
+
+  static constexpr std::size_t count() { return (0u + ... + popcount(Words)); }
+
+  static constexpr bool empty() { return sizeof...(Words) == 0; }
+
+  /// Determine whether \p Pos is equivalent to position with a `1` in the
+  /// bitset.
+  ///
+  /// \return `true` if \p Pos is in the set; `false` otherwise.
+  ///
+  /// \note Complexity = `O(1)`.
+  template <typename T> static constexpr bool contains(T Pos) {
+    auto NPos = MetaBitset::normalizePos(PosT(Pos));
+
+    UPosT Idx = NPos.wordIndex();
+    if (Idx >= sizeof...(Words))
+      return false;
+
+    return WordArr[Idx] & NPos.bitMask();
+  }
+
+  static constexpr auto to_array() // NOLINT (readability-identifier-naming)
+  {
+    return toArrayImpl<count()>();
+  }
+
+  static constexpr auto
+  to_sorted_array() // NOLINT (readability-identifier-naming)
+  {
+    return to_array();
+  }
+
+private:
+  template <std::size_t N> static constexpr std::array<PosT, N> toArrayImpl() {
+    std::array<PosT, N> Arr{{}};
+
+    std::size_t SeqIdx = 0;
+    for (std::size_t I = 0, E = WordArr.size(); I != E; ++I) {
+      uint64_t Word = WordArr[I];
+      std::size_t WordBaseIdx = Offset + (I << 6);
+
+      std::size_t B = countr_zero_constexpr(Word);
+      while (B < MetaBitset::TypeInfo::WordNBits) {
+        Arr[SeqIdx++] = WordBaseIdx + B;
+        Word &= ~(1ULL << B);
+        B = countr_zero_constexpr(Word);
+      }
+    }
+    return Arr;
+  }
+
+  template <std::size_t... I>
+  static constexpr auto toSequenceImpl(std::index_sequence<I...> IdxSeq) {
+    constexpr auto SeqArr = toArrayImpl<IdxSeq.size()>();
+    return std::integer_sequence<PosT, SeqArr[I]...>();
+  }
+
+public:
+  using sequence_type =
+      decltype(toSequenceImpl(std::make_index_sequence<count()>()));
+  using sorted_sequence_type = sequence_type;
+};
+
+/// Compute the number of words a `MetaBitset` would need to represent a set
+/// with the specified \p MinVal and \p MaxVal.
+///
+/// \tparam MinVal  The minimum value in the set to be represented as a
+/// `MetaBitset`.
+/// \tparam MaxVal  The maximum value in the set to be represented as a
+/// `MetaBitset`.
+template <typename PosT, PosT MinVal, PosT MaxVal>
+inline constexpr size_t MetaBitsetNumWordsDetailed =
+    (size_t(MaxVal - MinVal) + 64U) / 64U;
+
+namespace metaset_detail {
+
+template <typename PosT, PosT MinVal, PosT MaxVal, PosT... Values>
+struct MakeMetaBitsetImpl {
+  using TypeInfo = metaset_detail::TypeInfo<PosT, MinVal>;
+
+  static constexpr size_t NumWords =
+      MetaBitsetNumWordsDetailed<PosT, MinVal, MaxVal>;
+
+  static_assert(NumWords < MetaBitsetWordLimit,
+                "MetaBitset exceeds word limit (llvm::MetaBitsetWordLimit).");
+
+  static constexpr auto makeWordsArray() {
+    constexpr std::size_t NumValues = sizeof...(Values);
+    constexpr std::array<PosT, NumValues> BitPositions{
+        {static_cast<PosT>(Values)...}};
+    std::array<uint64_t, NumWords> Words{};
+    for (PosT BitPos : BitPositions) {
+      auto NPos = TypeInfo::normalizePos(BitPos);
+      Words[NPos.wordIndex()] |= NPos.bitMask();
+    }
+    return Words;
+  }
+
+  template <size_t... Is>
+  static constexpr auto makeBitset(std::index_sequence<Is...>) {
+    constexpr auto WordsArr = makeWordsArray();
+    return MetaBitset<PosT, MinVal, WordsArr[Is]...>();
+  }
+
+public:
+  using type = decltype(makeBitset(std::make_index_sequence<NumWords>()));
+};
+
+template <typename T> struct MetaBitsetNumWordsImpl;
+
+template <typename PosT, PosT Offset, uint64_t... Words>
+struct MetaBitsetNumWordsImpl<MetaBitset<PosT, Offset, Words...>>
+    : std::integral_constant<size_t, sizeof...(Words)> {};
+
+} // namespace metaset_detail
+
+/// Get the number of words used to represent a `MetaBitset`.
+template <typename MetaBitsetT>
+inline constexpr size_t MetaBitsetNumWords =
+    metaset_detail::MetaBitsetNumWordsImpl<MetaBitsetT>::value;
+
+/// Create a `MetaBitset` containing the specified values with the specified
+/// bounds.
+///
+/// \tparam PosT  Bit-position type to apply to the `MetaBitset`.
+/// \tparam MinVal  The smallest value represented in the `MetaBitset`.
+/// \tparam MaxVal  The largest value represnted in the `MetaBitset`.
+/// \tparam Values  Integral or enum values to represent in the resulting
+/// `MetaBitset`.
+///
+/// \note The specified values will be cast to `PosT`, so choose `PosT` with
+/// the potential for truncation / sign-extension in mind.
+///
+/// This type alias should be used in contexts where the min and max values are
+/// already known in the parent context to avoid unnecessary duplication of
+/// computation.
+///
+/// \warning Min and max bouds are not validated. This type alias saves
+/// compile-time in contexts where the min and max are already computed.
+template <typename PosT, PosT MinVal, PosT MaxVal, auto... Values>
+using MakeMetaBitsetDetailed = typename metaset_detail::MakeMetaBitsetImpl<
+    PosT, MinVal, MaxVal, static_cast<PosT>(Values)...>::type;
+
+namespace metaset_detail {
+
+template <typename PosT, auto... Values> struct MakeMetaBitsetFromValues;
+
+/// Emtpy value set specialization.
+template <typename PosT> struct MakeMetaBitsetFromValues<PosT> {
+  using type = MetaBitset<PosT, 0>;
+};
+
+/// At least one value specialization.
+template <typename PosT, auto Value0, auto... ValueN>
+struct MakeMetaBitsetFromValues<PosT, Value0, ValueN...> {
+private:
+  static constexpr PosT Min = ce_min<PosT>(Value0, ValueN...);
+  static constexpr PosT Max = ce_max<PosT>(Value0, ValueN...);
+
+  static_assert(MetaBitsetNumWordsDetailed<PosT, Min, Max> <
+                    MetaBitsetWordLimit,
+                "MetaBitset exceeds word limit (llvm::MetaBitsetWordLimit).");
+
+public:
+  using type = MakeMetaBitsetDetailed<PosT, Min, Max, Value0, ValueN...>;
+};
+
+} // namespace metaset_detail
+
+/// Create a `MetaBitset` containing the specified values.
+///
+/// \tparam PosT  Bit-position type to apply to the `MetaBitset`.
+/// \tparam Values  Integral or enum values to represent in the resulting
+/// `MetaBitset`.
+///
+/// \note The specified values will be cast to `PosT`, so choose this type with
+/// the potential for truncation / sign-extension in mind.
+///
+/// The max and min of the specified values will be computed in order to
+/// determine the size and offset of the resulting `MetaBitset`.  If this
+/// information is already available, use `MakeMetaBitsetDetailed`.
+template <typename PosT, auto... Values>
+using MakeMetaBitset =
+    typename metaset_detail::MakeMetaBitsetFromValues<PosT, Values...>::type;
+
+namespace metaset_detail {
+
+template <typename SeqT> class MakeMetaBitsetFromSequenceImpl {};
+
+template <typename PosT, PosT... Values>
+class MakeMetaBitsetFromSequenceImpl<std::integer_sequence<PosT, Values...>> {
+public:
+  using type = typename MakeMetaBitsetFromValues<PosT, Values...>::type;
+};
+
+template <typename SortedSeqT> class MakeMetaBitsetFromSortedSequenceImpl {};
+
+/// Empty integer sequence specialization.
+template <typename PosT>
+class MakeMetaBitsetFromSortedSequenceImpl<std::integer_sequence<PosT>> {
+public:
+  using type = MetaBitset<PosT, 0>;
+};
+
+/// One or more values in the integer sequence specialization.
+template <typename PosT, PosT Val0, PosT... ValN>
+class MakeMetaBitsetFromSortedSequenceImpl<
+    std::integer_sequence<PosT, Val0, ValN...>> {
+  // The values in the sequence are sorted, so the first and last are min
+  // and max respectively.
+  static constexpr PosT MinVal = Val0;
+  // A comma-expression evaluates to the sub-expression after the last comma.
+  static constexpr PosT MaxVal = (Val0, ..., ValN);
+
+public:
+  using type = MakeMetaBitsetDetailed<PosT, MinVal, MaxVal, Val0, ValN...>;
+};
+
+} // namespace metaset_detail
+
+/// Create a `MetaBitset` containing values taken from a
+/// `std::integer_sequence`.
+/// \relates MetaBitset
+///
+/// \tparam SeqT  `std::integer_sequence` of values to add to the generated
+/// `MetaBitset`.
+template <typename SeqT>
+using MakeMetaBitsetFromSequence =
+    typename metaset_detail::MakeMetaBitsetFromSequenceImpl<SeqT>::type;
+
+/// Create a `MetaBitset` containing values taken from a sorted
+/// `std::integer_sequence`.
+/// \relates MetaBitset
+///
+/// \tparam SortedSeqT  Pre-sorted `std::integer_sequence` of values to add to
+/// the generated `MetaBitset` type.
+template <typename SortedSeqT>
+using MakeMetaBitsetFromSortedSequence =
+    typename metaset_detail::MakeMetaBitsetFromSortedSequenceImpl<
+        SortedSeqT>::type;
+
+template <typename SeqT> class MetaSequenceSet {};
+
+/// Represent a set of values with a `std::integer_sequence`.
+///
+/// The elements of the sequence need not be sorted.
+template <typename T, T... Values>
+class MetaSequenceSet<std::integer_sequence<T, Values...>>
+    : public MetaSequenceSetTag {
+public:
+  using value_type = T;
+  using sequence_type = std::integer_sequence<T, Values...>;
+  using sorted_sequence_type = SortSequence<sequence_type>;
+
+public:
+  constexpr MetaSequenceSet() {}
+
+  /// Determine whether \p Val is equivalent to an element in the sequence.
+  ///
+  /// \return `true` if \p Val is in the set; `false` otherwise.
+  ///
+  /// \note Complexity = `O(n)` in sequence length.
+  template <typename U> static constexpr bool contains(U const &Val) {
+    if constexpr (sizeof...(Values) == 0)
+      return false;
+    else {
+      T CastVal(static_cast<T>(Val));
+      return (... || (CastVal == Values));
+    }
+  }
+
+  static constexpr std::size_t count() { return sequence_type::size(); }
+
+  static constexpr bool empty() { return sequence_type::size() == 0; }
+
+  static constexpr auto to_array() // NOLINT (readability-identifier-naming)
+  {
+    return llvm::to_array(sequence_type());
+  }
+
+  static constexpr auto
+  to_sorted_array() // NOLINT (readability-identifier-naming)
+  {
+    auto Arr = to_array();
+    ce_sort_inplace(Arr);
+    return Arr;
+  }
+};
+
+namespace metaset_detail {
+
+template <typename... MetaSetN> struct ToArrayImpl {};
+
+template <typename SeqT, typename... MetaBitsetN>
+struct ToArrayImpl<MetaSequenceSet<SeqT>, MetaBitsetN...> {};
+
+} // namespace metaset_detail
+
+/// Build a `MetaSequenceSet` from a list of integral or enum literals.
+///
+/// \tparam T  Integral or enum type of the sequence set to generate.
+/// \tparam Values  Literals to add to the sequence set.
+template <typename T, auto... Values>
+using MakeMetaSequenceSet =
+    MetaSequenceSet<std::integer_sequence<T, T(Values)...>>;
+
+/// A meta-programming set composed from some number of `MetaBitset`s and
+/// possibly one `MetaSequenceSet`.
+///
+/// The `MetaSparseSet` is created from a sequence of literal integral or enum
+/// values using the helper type aliases.  See `MakeMetaSparseSet` and
+/// `MakeMetaSparseSetFromSequence`.
+///
+/// If created using the helper type aliases, the parititoning algorithm is as
+/// follows:
+/// \code
+///  // Determine the maximal slice length of `SortedSeq` starting at
+///  // `StartIdx` which can be represented by a `MetaBitset` under the max word
+///  // length.
+///  //
+///  // If the slice length is too sort, then return 1 to indicate a singleton
+///  // value at `StartIdx`.
+///  getSliceLen(SortedSeq, StartIdx) -> Integer
+///
+///  // Parition a sequence of values into clusters / slices (MetaBitsets) and
+///  // potentially a set of singleton values (MetaSequenceSet).
+///  partition(Values...):
+///    SortedSeq = sort(Value...)
+///    ValueIndex = 0
+///    while ValueIndex < SortedSequence.size():
+///      Len = getSliceLen(SortedSeq, ValueIndex)
+///      if Len == 1:
+///        // Slice is too small, more efficiently represented as a member of a
+///        // MetaSequenceSet.
+///        Singletons.push_back(SortedSeq[])
+///      else:
+///        // Record a slice to represent the current cluster of values.
+///        Slices.push_back({ValueIndex, Len})
+///      ValueIndex += Len
+/// \endcode
+///
+/// The resulting `MetaSparseBitset` will have its slices / clusters represented
+/// as disjoint `MetaBitset`s in ascending order of start offset followed by a
+/// set of singletons represented by a single `MetaSequenceSet`.
+///
+/// Both the clusters and the trailing set of singletons are optional.  If all
+/// values are represented by slices / clusters, then the trailing singletons
+/// set will be omitted.  Conversely, if no values can be paritioned into
+/// clusters then only the `MetaSequenceSet` will be present.
+///
+/// \note Elements of the trailing sequence set will always be sorted.
+///
+/// \todo Apply binary search to elements of the sorted trailing sequence set
+/// when `contains()` is instantiated if the sequence is sufficiently large.
+template <typename... MetaSetN>
+class MetaSparseBitset : public MetaSparseBitsetTag {
+  static_assert(sizeof...(MetaSetN) != 0);
+  using MetaSet0 = PackElementT<0, MetaSetN...>;
+
+public:
+  using value_type = typename MetaSet0::value_type;
+
+public:
+  constexpr MetaSparseBitset() {}
+
+  static constexpr std::size_t count() {
+    return (MetaSetN::count() + ... + 0u);
+  }
+
+  static constexpr bool empty() {
+    if constexpr (sizeof...(MetaSetN) == 1)
+      // Expect the empty state of a MetaSparseBitset to be a single empty
+      // MetaSequenceSet.
+      return (MetaSetN::empty(), ...);
+    else
+      // The set is assumed to be non-empty if there are multiple sets in the
+      // type pack.
+      return false;
+  }
+
+  /// Determine whether \p Val is equivalent to an element in the set.
+  ///
+  /// \return `true` if \p Val is in the set; `false` otherwise.
+  ///
+  /// \note Complexity = `O(c + s)`; where c = the number of clusters and s =
+  /// singleton sequence length.
+  template <typename U> static constexpr bool contains(U const &Val) {
+    return (... || MetaSetN::contains(Val));
+  }
+
+  static constexpr auto to_array() // NOLINT (readability-identifier-naming)
+  {
+    return toArrayImpl<count()>();
+  }
+
+  static constexpr auto
+  to_sorted_array() // NOLINT (readability-identifier-naming)
+  {
+    return toSortedArrayImpl<count()>();
+  }
+
+private:
+  /// Merge the sets in the order they appear in the type pack.
+  template <std::size_t N>
+  static constexpr std::array<value_type, N> toArrayImpl() {
+    std::array<value_type, N> MergedArr{};
+
+    auto FillFrom = [](auto Set, auto MergePos) constexpr {
+      for (auto Val : Set.to_array())
+        *MergePos++ = Val;
+      return MergePos;
+    };
+
+    auto I = MergedArr.begin();
+    ((I = FillFrom(MetaSetN(), I)), ...);
+
+    return MergedArr;
+  }
+
+  template <std::size_t N>
+  static constexpr std::array<value_type, N> toSortedArrayImpl() {
+    using LastSetT = PackElementT<sizeof...(MetaSetN) - 1, MetaSetN...>;
+    if constexpr (IsMetaSequenceSet<LastSetT>) {
+      using MetaSeqT = LastSetT;
+      std::array<value_type, N> MergedArr{};
+
+      constexpr auto SeqArr = MetaSeqT::to_array();
+      auto SeqPos = SeqArr.begin();
+      auto SeqE = SeqArr.end();
+
+      auto MergeFrom = [&](auto BitSet, auto MergePos) constexpr {
+        using BitsetT =
+            std::remove_cv_t<std::remove_reference_t<decltype(BitSet)>>;
+        if constexpr (!std::is_same_v<MetaSeqT, BitsetT>) {
+          for (auto Val : BitSet.to_array()) {
+            while (SeqPos != SeqE && *SeqPos < Val)
+              *MergePos++ = *SeqPos++;
+            *MergePos++ = Val;
+          }
+        }
+        return MergePos;
+      };
+
+      auto I = MergedArr.begin();
+      ((I = MergeFrom(MetaSetN(), I)), ...);
+
+      while (SeqPos != SeqE)
+        *I++ = *SeqPos++;
+
+      return MergedArr;
+    } else {
+      // The type pack is an ordered list of MetaBitsets; each containing
+      // elements in sorted order.  A simple forward merge results in sorted
+      // elements.
+      return toArrayImpl<N>();
+    }
+  }
+
+  template <std::size_t... I>
+  static constexpr auto toSequenceImpl(std::index_sequence<I...> IdxSeq) {
+    constexpr auto SeqArr = toArrayImpl<IdxSeq.size()>();
+    return std::integer_sequence<value_type, SeqArr[I]...>();
+  }
+
+  template <std::size_t... I>
+  static constexpr auto toSortedSequenceImpl(std::index_sequence<I...> IdxSeq) {
+    constexpr auto SeqArr = toSortedArrayImpl<IdxSeq.size()>();
+    return std::integer_sequence<value_type, SeqArr[I]...>();
+  }
+
+public:
+  using sequence_type =
+      decltype(toSequenceImpl(std::make_index_sequence<count()>()));
+  using sorted_sequence_type =
+      decltype(toSortedSequenceImpl(std::make_index_sequence<count()>()));
+};
+
+namespace metaset_detail {
+
+template <typename SortedSeqT, size_t WordSize> class PartitionSparseBitset {
+  using value_type = typename SortedSeqT::value_type;
+
+  class Partitions {
+    static constexpr size_t MinSliceLen = 3u;
+    static constexpr std::array<value_type, SortedSeqT::size()> SeqArr =
+        to_array(SortedSeqT());
+
+    static constexpr size_t getSliceLen(size_t StartIdx) {
+      auto Bottom = SeqArr[StartIdx];
+      auto SeqStartPos = SeqArr.begin() + StartIdx;
+      auto SeqCheckPos = SeqStartPos + 1u;
+      auto SeqE = SeqArr.end();
+      while (SeqCheckPos != SeqE && size_t(*SeqCheckPos - Bottom) < WordSize)
+        ++SeqCheckPos;
+
+      size_t Len = size_t(SeqCheckPos - SeqStartPos);
+
+      // If slice is too short, return 1 to indicate a singleton at `StartIdx`.
+      return Len < MinSliceLen ? 1u : Len;
+    }
+
+    struct CountsT {
+      size_t NumSingles;
+      size_t NumSlices;
+    };
+
+    static constexpr CountsT buildCounts() {
+      if constexpr (SeqArr.size() == 0u)
+        return {0u, 0u};
+
+      size_t SliceStart = 0;
+      size_t Singles = 0;
+      size_t Slices = 0;
+      while (SliceStart < SeqArr.size()) {
+        size_t Len = getSliceLen(SliceStart);
+        if (Len == 1u)
+          ++Singles;
+        else
+          ++Slices;
+        SliceStart += Len;
+      }
+      return {Singles, Slices};
+    }
+
+    static constexpr auto Counts = buildCounts();
+
+  public:
+    static constexpr size_t numSeqElems() { return Counts.NumSingles; }
+    static constexpr size_t numSlices() { return Counts.NumSlices; }
+
+  private:
+    struct SliceT {
+      size_t Start;
+      size_t Length;
+    };
+
+    struct ParitionSpecT {
+      std::array<value_type, numSeqElems()> Singles;
+      std::array<SliceT, numSlices()> Slices;
+    };
+
+    static constexpr ParitionSpecT buildParitions() {
+      ParitionSpecT PS{};
+
+      size_t SliceIdx = 0u, SinglesIdx = 0u;
+      size_t SliceStart = 0u;
+      while (SliceStart < SeqArr.size()) {
+        size_t Len = getSliceLen(SliceStart);
+        if (Len == 1u)
+          PS.Singles[SinglesIdx++] = SeqArr[SliceStart];
+        else
+          PS.Slices[SliceIdx++] = {SliceStart, Len};
+        SliceStart += Len;
+      }
+      return PS;
+    }
+
+    static constexpr auto PartitionSpec = buildParitions();
+
+  public:
+    static constexpr size_t sliceStart(size_t Idx) {
+      return PartitionSpec.Slices[Idx].Start;
+    }
+
+    static constexpr size_t sliceLen(size_t Idx) {
+      return PartitionSpec.Slices[Idx].Length;
+    }
+
+    static constexpr value_type seqElem(size_t Idx) {
+      return PartitionSpec.Singles[Idx];
+    }
+  };
+
+  template <size_t SliceIdx>
+  using MakeSliceBitset = MakeMetaBitsetFromSortedSequence<
+      SliceSequence<Partitions::sliceStart(SliceIdx),
+                    Partitions::sliceLen(SliceIdx), SortedSeqT>>;
+
+  template <size_t... SeqIdx, size_t... SliceIdx>
+  static constexpr auto partition(std::index_sequence<SeqIdx...>,
+                                  std::index_sequence<SliceIdx...>) {
+
+    if constexpr (Partitions::numSeqElems() == 0u)
+      if constexpr (Partitions::numSlices() == 0u)
+        // Empty input yields and empty sequence set.
+        return MetaSparseBitset<MakeMetaSequenceSet<value_type>>();
+      else
+        // Avoid instantiating the singles sequence set if all values are
+        // represented in bitsets.
+        return MetaSparseBitset<MakeSliceBitset<SliceIdx>...>();
+    else
+      // CondGroupMetaSetParitioned looks for elements in order of the variadic
+      // types. Put the sequence set last in hopes a value hits in one of the
+      // bitsets since this would be cheaper at runtime.
+      return MetaSparseBitset<
+          MakeSliceBitset<SliceIdx>...,
+          MakeMetaSequenceSet<value_type, Partitions::seqElem(SeqIdx)...>>();
+  }
+
+public:
+  using type =
+      decltype(partition(std::make_index_sequence<Partitions::numSeqElems()>(),
+                         std::make_index_sequence<Partitions::numSlices()>()));
+};
+
+} // namespace metaset_detail
+
+/// Create a `MetaSparseBitset` containing values taken from a
+/// `std::integer_sequence`.
+///
+/// \tparam SeqT  `std::integer_sequence` of values to add to the generated
+/// `MetaBitset`.
+/// \tparam WordSize  Maximum number of bits allowed in a `MetaBitset` sub-word
+/// within the sparse bitset.
+///
+/// \p SeqT may be an unsorted sequence.  While repeat elements are tolerated,
+/// they do throw off heuristics and may result in an inefficient paritioning.
+template <typename SeqT, size_t WordSize>
+using MakeMetaSparseBitsetFromSequence =
+    typename metaset_detail::PartitionSparseBitset<SortSequence<SeqT>,
+                                                   WordSize>::type;
+
+/// Create a `MetaSparseBitset` containing the specified values.
+///
+/// \tparam PosT  Integral or enum type used to represent the position / offset
+/// in the bitset.
+/// \tparam WordSize  Maximum number of bits allowed in a `MetaBitset` sub-word
+/// within the sparse bitset.
+/// \tparam Values  Values to represent in the resulting `MetaSparseBitset`.
+///
+/// \p Values may be an unsorted sequence.  While repeat elements are tolerated,
+/// they do throw off heuristics and may result in an inefficient paritioning.
+template <typename PosT, size_t WordSize, auto... Values>
+using MakeMetaSparseBitset = typename metaset_detail::PartitionSparseBitset<
+    SortLiterals<PosT, PosT(Values)...>, WordSize>::type;
+
+/// Provide a container interface to a MetaSet type.
+///
+/// This class can be used as a mixin for any class accepting a `MetaSet` type
+/// as a template parameter. For example:
+/// \code{.cpp}
+///  template <typename FooSetT>
+///  class Foo : public MetaSetSortedContainer<FooSetT> {
+///    static_assert(IsMetaSet<FooSetT>);
+///  };
+/// \endcode
+template <typename MetaSetT> class MetaSetSortedContainer {
+  static_assert(IsMetaSet<MetaSetT>);
+
+  /// Use the instantiated type cache to ensure the array is only instantiated
+  /// if one of the container-like member functions are called.
+  template <typename T> struct ArrayInstantiator {
+    static constexpr auto Array = T::to_sorted_array();
+  };
+
+public:
+  using size_type = std::size_t;
+  using value_type = typename MetaSetT::value_type;
+
+private:
+  using ArrayT = std::array<value_type, MetaSetT::count()>;
+
+public:
+  using reference = value_type const &;
+  using const_reference = reference;
+  using iterator = typename ArrayT::const_iterator;
+  using const_iterator = iterator;
+  using reverse_iterator = typename ArrayT::const_reverse_iterator;
+  using const_reverse_iterator = reverse_iterator;
+
+public:
+  constexpr MetaSetSortedContainer() {}
+
+  constexpr const_iterator begin() const {
+    return ArrayInstantiator<MetaSetT>().Array.begin();
+  }
+
+  constexpr const_iterator end() const {
+    return ArrayInstantiator<MetaSetT>().Array.end();
+  }
+
+  constexpr const_iterator cbegin() const {
+    return ArrayInstantiator<MetaSetT>().Array.cbegin();
+  }
+
+  constexpr const_iterator cend() const {
+    return ArrayInstantiator<MetaSetT>().Array.cend();
+  }
+
+  constexpr const_reverse_iterator rbegin() const {
+    return ArrayInstantiator<MetaSetT>().Array.rbegin();
+  }
+
+  constexpr const_reverse_iterator rend() const {
+    return ArrayInstantiator<MetaSetT>().Array.rend();
+  }
+
+  constexpr const_reverse_iterator crbegin() const {
+    return ArrayInstantiator<MetaSetT>().Array.crbegin();
+  }
+
+  constexpr const_reverse_iterator crend() const {
+    return ArrayInstantiator<MetaSetT>().Array.crend();
+  }
+
+  constexpr size_type size() const {
+    return ArrayInstantiator<MetaSetT>().Array.size();
+  }
+
+  constexpr const_reference operator[](size_type Idx) const {
+    static_assert(!MetaSetT::empty());
+    return ArrayInstantiator<MetaSetT>().Array[Idx];
+  }
+
+  constexpr const_reference front() const {
+    static_assert(!MetaSetT::empty());
+    return ArrayInstantiator<MetaSetT>().Array.front();
+  }
+
+  constexpr const_reference back() const {
+    static_assert(!MetaSetT::empty());
+    return ArrayInstantiator<MetaSetT>().Array.back();
+  }
+
+  constexpr bool empty() const { return MetaSetT::empty(); }
+};
+
+namespace metaset_detail {
+
+/// Count the number of increments applied; drop any assignments to the
+/// dereferenced type.
+///
+/// This fake iterator is used by the set operation algorithms to determine the
+/// required size of the array to generate at compile-time.
+class CountIterator {
+  std::size_t Count = 0;
+
+  struct FakeReference {
+    template <typename T> constexpr FakeReference &operator=(T const &) {
+      return *this;
+    }
+  };
+
+public:
+  constexpr CountIterator() {}
+
+  constexpr FakeReference operator*() { return {}; }
+
+  constexpr CountIterator &operator++() {
+    ++Count;
+    return *this;
+  }
+
+  constexpr CountIterator operator++(int) {
+    CountIterator Ret(*this);
+    operator++();
+    return Ret;
+  }
+
+  constexpr std::size_t count() const { return Count; }
+};
+
+template <typename Algorithm, std::size_t WordSize> class SetOperation {
+  using value_type = typename Algorithm::value_type;
+  using LeftSetT = typename Algorithm::LeftSetT;
+  using RightSetT = typename Algorithm::RightSetT;
+
+  static constexpr std::size_t ResultSize =
+      Algorithm()(metaset_detail::CountIterator()).count();
+
+  static constexpr auto makeResultArr() {
+    std::array<value_type, ResultSize> ResultArr{};
+    Algorithm()(ResultArr.begin());
+    return ResultArr;
+  }
+
+  template <std::size_t... Idx>
+  static constexpr auto makeResultSequence(std::index_sequence<Idx...>) {
+    constexpr auto ResultArr = makeResultArr();
+    return std::integer_sequence<value_type, ResultArr[Idx]...>();
+  }
+
+  using ResultSequence =
+      decltype(makeResultSequence(std::make_index_sequence<ResultSize>()));
+
+public:
+  using type = typename PartitionSparseBitset<ResultSequence, WordSize>::type;
+};
+
+template <typename L, typename R> struct AlgoBase {
+  using value_type = typename L::value_type;
+  static_assert(
+      std::is_same_v<value_type, typename R::value_type>,
+      "MetaSet operations are only valid on sets with the same `value_type`.");
+
+  using LeftSetT = L;
+  using RightSetT = R;
+
+  static constexpr auto ArrL = L::to_sorted_array();
+  static constexpr auto ArrR = R::to_sorted_array();
+};
+
+template <typename L, typename R> struct UnionAlgo : AlgoBase<L, R> {
+  using UnionAlgo::AlgoBase::ArrL;
+  using UnionAlgo::AlgoBase::ArrR;
+
+  template <typename OutIt> constexpr OutIt operator()(OutIt Out) const {
+    auto I0 = ArrL.begin(), E0 = ArrL.end();
+    auto I1 = ArrR.begin(), E1 = ArrR.end();
+
+    while (I0 != E0) {
+      if (I1 != E1) {
+        if (*I0 == *I1) {
+          *Out++ = *I0++;
+          ++I1;
+        } else if (*I0 < *I1) {
+          *Out++ = *I0++;
+        } else {
+          *Out++ = *I1++;
+        }
+      } else {
+        *Out++ = *I0++;
+      }
+    }
+
+    while (I1 != E1)
+      *Out++ = *I1++;
+
+    return Out;
+  }
+};
+
+template <typename L, typename R> struct IntersectionAlgo : AlgoBase<L, R> {
+  using IntersectionAlgo::AlgoBase::ArrL;
+  using IntersectionAlgo::AlgoBase::ArrR;
+
+  template <typename OutIt> constexpr OutIt operator()(OutIt Out) const {
+    auto I0 = ArrL.begin(), E0 = ArrL.end();
+    auto I1 = ArrR.begin(), E1 = ArrR.end();
+
+    while (I0 != E0 && I1 != E1) {
+      if (*I0 == *I1) {
+        *Out++ = *I0++;
+        ++I1;
+      } else if (*I0 < *I1) {
+        ++I0;
+      } else {
+        ++I1;
+      }
+    }
+
+    return Out;
+  }
+};
+
+template <typename L, typename R> struct MinusAlgo : AlgoBase<L, R> {
+  using MinusAlgo::AlgoBase::ArrL;
+  using MinusAlgo::AlgoBase::ArrR;
+
+  template <typename OutIt> constexpr OutIt operator()(OutIt Out) const {
+    auto I0 = ArrL.begin(), E0 = ArrL.end();
+    auto I1 = ArrR.begin(), E1 = ArrR.end();
+
+    while (I0 != E0 && I1 != E1) {
+      if (*I0 == *I1) {
+        ++I0;
+        ++I1;
+      } else if (*I0 < *I1) {
+        *Out++ = *I0++;
+      } else {
+        ++I1;
+      }
+    }
+
+    // All remaining elements in I0's range do not exist in I1's range.
+    while (I0 != E0)
+      *Out++ = *I0++;
+
+    return Out;
+  }
+};
+
+inline constexpr std::size_t SetOpDefaultWordSize = 512;
+
+} // namespace metaset_detail
+
+/// Compute type resulting from a union of two meta set types.
+///
+/// The resulting type will always be expressed as a `MetaSparseBitset`.
+///
+/// \tparam L  Left-hand type to compute the union of.
+/// \tparam R  Right-hand type to compute the union of.
+/// \tparam WordSize  Sub-word size limit applied to the resulting
+/// `MetaSparseBitset`.
+///
+/// Example usage:
+/// \code{.cpp}
+///  using EvensT = MakeMetaBitset<int, 2, 4, 6, 8, 10>;
+///  using OddsT = MakeMetaBitset<int, 1, 3, 5, 7, 9>;
+///
+///  // Compute the union of `EvensT` and `OddsT`, limiting bitset word-size to
+///  // 128 bits in the output `MetaSparseBitset`.
+///  using Union = MetaSetUnion<EvensT, OddsT, 128>;
+/// \endcode
+template <typename L, typename R,
+          std::size_t WordSize = metaset_detail::SetOpDefaultWordSize>
+using MetaSetUnion =
+    typename metaset_detail::SetOperation<metaset_detail::UnionAlgo<L, R>,
+                                          WordSize>::type;
+
+/// Compute the type resulting from an intersection of two meta set types.
+///
+/// The resulting type will always be expressed as a `MetaSparseBitset`.
+///
+/// \tparam L  Left-hand type to compute the intersection of.
+/// \tparam R  Right-hand type to compute the intersection of.
+/// \tparam WordSize  Sub-word size limit applied to the resulting
+/// `MetaSparseBitset`.
+///
+/// Example usage:
+/// \code{.cpp}
+///  using EvensT = MakeMetaBitset<int, 2, 4, 6, 8, 10>;
+///  using OneToFiveT = MakeMetaBitset<int, 1, 2, 3, 4, 5>;
+///
+///  // Compute the intersection of `EvensT` and `OneToFiveT`, limiting bitset
+///  // word-size to 128 bits in the output `MetaSparseBitset`.
+///  using Intersection = MetaSetIntersection<EvensT, OneToFiveT, 128>;
+/// \endcode
+template <typename L, typename R,
+          std::size_t WordSize = metaset_detail::SetOpDefaultWordSize>
+using MetaSetIntersection = typename metaset_detail::SetOperation<
+    metaset_detail::IntersectionAlgo<L, R>, WordSize>::type;
+
+/// Compute the type resulting from a subtraction of two meta set types.
+///
+/// The resulting type will always be expressed as a `MetaSparseBitset`.
+///
+/// \tparam L  Left-hand type to subtract from.
+/// \tparam R  Right-hand type use as the subtrahend.
+/// \tparam WordSize  Sub-word size limit applied to the resulting
+/// `MetaSparseBitset`.
+///
+/// Example usage:
+/// \code{.cpp}
+///  using EvensT = MakeMetaBitset<int, 2, 4, 6, 8, 10>;
+///  using OneToFiveT = MakeMetaBitset<int, 1, 2, 3, 4, 5>;
+///
+///  // Compute the difference by subtracting  `OneToFiveT` from `EvensT` and,
+///  // limiting bitset word-size to 128 bits in the output `MetaSparseBitset`.
+///  using Difference = MetaSetMinus<EvensT, OneToFiveT, 128>;
+/// \endcode
+template <typename L, typename R,
+          std::size_t WordSize = metaset_detail::SetOpDefaultWordSize>
+using MetaSetMinus =
+    typename metaset_detail::SetOperation<metaset_detail::MinusAlgo<L, R>,
+                                          WordSize>::type;
+
+} // namespace llvm
+
+#endif // LLVM_ADT_METASET_H
diff --git a/llvm/unittests/ADT/CMakeLists.txt b/llvm/unittests/ADT/CMakeLists.txt
index af503d9b82843..be865a433b654 100644
--- a/llvm/unittests/ADT/CMakeLists.txt
+++ b/llvm/unittests/ADT/CMakeLists.txt
@@ -19,6 +19,8 @@ add_llvm_unittest(ADTTests
   CoalescingBitVectorTest.cpp
   CombinationGeneratorTest.cpp
   ConcurrentHashtableTest.cpp
+  CondGroupTest.cpp
+  ConstexprUtilsTest.cpp
   CountCopyAndMove.cpp
   DAGDeltaAlgorithmTest.cpp
   DeltaAlgorithmTest.cpp
@@ -55,6 +57,7 @@ add_llvm_unittest(ADTTests
   LazyAtomicPointerTest.cpp
   MappedIteratorTest.cpp
   MapVectorTest.cpp
+  MetaSetTest.cpp
   PackedVectorTest.cpp
   PagedVectorTest.cpp
   PointerEmbeddedIntTest.cpp
diff --git a/llvm/unittests/ADT/CondGroupTest.cpp b/llvm/unittests/ADT/CondGroupTest.cpp
new file mode 100644
index 0000000000000..ad1767cdbd7d3
--- /dev/null
+++ b/llvm/unittests/ADT/CondGroupTest.cpp
@@ -0,0 +1,947 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains testing for the condition group infrastructure.
+///
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/CondGroup.h"
+#include "gtest/gtest.h"
+
+#include <cstdlib>
+#include <optional>
+#include <type_traits>
+
+using namespace llvm;
+using namespace cgrp;
+
+namespace {
+
+template <typename Iter0, typename Iter1>
+constexpr bool ce_equal( // NOLINT (readability-identifier-naming)
+    Iter0 B0, Iter0 E0, Iter1 B1, Iter1 E1) {
+  for (; B0 != E0; ++B0, ++B1) {
+    if (B1 == E1 || *B0 != *B1)
+      return false;
+  }
+  return B1 == E1;
+}
+
+template <typename R1, typename R2>
+constexpr bool ce_equal( // NOLINT (readability-identifier-naming)
+    R1 const &L, R2 const &R) {
+  return ce_equal(adl_begin(L), adl_end(L), adl_begin(R), adl_end(R));
+}
+
+template <typename Range1, typename Range2>
+constexpr bool ce_requal( // NOLINT (readability-identifier-naming)
+    Range1 const &L, Range2 const &R) {
+  return ce_equal(adl_rbegin(L), adl_rend(L), adl_rbegin(R), adl_rend(R));
+}
+
+namespace cexpr {
+
+template <int Val, bool = (Val == anyOf(1, 2, 3))>
+class OneTwoThreeSimple : public std::false_type {};
+
+template <int Val>
+class OneTwoThreeSimple<Val, true> : public std::true_type {};
+
+template <int Val, bool = (Val == anyOf(makeGroup(1, 2), makeGroup(3)))>
+class OneTwoThreeNested : public std::false_type {};
+
+template <int Val>
+class OneTwoThreeNested<Val, true> : public std::true_type {};
+} // namespace cexpr
+
+enum class Number : int {
+  Zero,
+  One,
+  Two,
+  Three,
+  Four,
+  Five,
+  Six,
+  Seven,
+  Eight,
+  Nine,
+  Ten
+};
+
+enum class Letter : int { A, B, C, D, E, F, G, H, I, J };
+
+enum LetterNonClass : int { A, B, C, D, E, F, G, H, I, J };
+
+enum NumberNonClass : int {
+  Zero,
+  One,
+  Two,
+  Three,
+  Four,
+  Five,
+  Six,
+  Seven,
+  Eight,
+  Nine,
+  Ten
+};
+
+TEST(CondGroupTest, ConstExpr) {
+  using namespace cexpr;
+
+  // Ensure anyOf() can be called in the context of a `static_assert`
+  // condition.
+  static_assert(1 == anyOf(1, 2, 3), "1 is in group {1,2,3}");
+  static_assert(4 != anyOf(1, 2, 3), "4 is not in group {1,2,3}");
+
+  static_assert(1 == anyOf(makeGroup(1, 2, 3)), "1 is in group {1,2,3}");
+  static_assert(9 != anyOf(makeGroup(1, 2, 3)), "9 is not in group {1,2,3}");
+
+  static_assert(1 == anyOf(makeGroup(1, 2, 3)), "1 is in group {1,2,3}");
+  static_assert(9 != anyOf(makeGroup(1, 2, 3)), "9 is not in group {1,2,3}");
+
+  // Ensure that we can instantiate a template which calls anyOf() to derive
+  // the value of an anonymous template parameter.
+  static_assert(OneTwoThreeSimple<0>::value == false,
+                "0 is not in group {1,2,3}");
+  static_assert(OneTwoThreeNested<0>::value == false,
+                "0 is not in group {1,2,3}");
+  static_assert(OneTwoThreeSimple<2>::value == true, "2 is in group {1,2,3}");
+  static_assert(OneTwoThreeNested<1>::value == true, "1 is in group {1,2,3}");
+}
+
+TEST(CondGroupTest, LiteralRepresentation) {
+  // Single MetaBitset representation.
+  {
+    static constexpr auto Fives =
+        cgrp::Literals<5, 15, 25, 35, 45, 55, 65, 75, 85, 95, 10, 20, 30, 40,
+                       50, 60, 70, 80, 90, 100>;
+
+    static_assert(
+        std::is_same_v<std::remove_cv_t<decltype(Fives)>,
+                       CondGroupMetaSet<MetaBitset<int, 5, 0x1084210842108421,
+                                                   0x84210842>>>);
+    static_assert(35 == anyOf(Fives));
+  }
+
+  // Multiple MetaBitsets for clusters (no singletons).
+  {
+    static constexpr auto Clusters =
+        cgrp::Literals<10000, 10002, 10004, 10006, 10008, 1000, 1002, 1004,
+                       1006, 1008>;
+
+    static_assert(
+        std::is_same_v<
+            std::remove_cv_t<decltype(Clusters)>,
+            CondGroupMetaSet<MetaSparseBitset<MetaBitset<int, 1000, 0x155>,
+                                              MetaBitset<int, 10000, 0x155>>>>);
+  }
+
+  // Multiple MetaBitsets for clusters + MetaSequneceSet for singletons.
+  {
+    static constexpr auto ClustersAndSingletons =
+        cgrp::Literals<10000, 10002, 10004, 10006, 10008, 1000, 1002, 1004,
+                       1006, 1008, 5000, 8000>;
+
+    static_assert(
+        std::is_same_v<
+            std::remove_cv_t<decltype(ClustersAndSingletons)>,
+            CondGroupMetaSet<MetaSparseBitset<
+                MetaBitset<int, 1000, 0x155>, MetaBitset<int, 10000, 0x155>,
+                MakeMetaSequenceSet<int, 5000, 8000>>>>);
+  }
+
+  // No clusters, all singletons; MetaSequenceSet for all values.
+  {
+    static constexpr auto Singletons =
+        cgrp::Literals<10000, 5000, 8000, 20000, 90000>;
+
+    static_assert(
+        std::is_same_v<std::remove_cv_t<decltype(Singletons)>,
+                       CondGroupMetaSet<MetaSparseBitset<MakeMetaSequenceSet<
+                           int, 5000, 8000, 10000, 20000, 90000>>>>);
+  }
+
+  // Small set, no paritioning attempted; MetaSequenceSet with no sorting.
+  {
+    static constexpr auto SmallSet = cgrp::Literals<3, 1>;
+
+    static_assert(
+        std::is_same_v<std::remove_cv_t<decltype(SmallSet)>,
+                       CondGroupMetaSet<
+                           MetaSequenceSet<std::integer_sequence<int, 3, 1>>>>);
+  }
+}
+
+TEST(CondGroupTest, Integers) {
+  static constexpr auto Evens = makeGroup(2, 4, 6, 8, 10);
+  static constexpr auto Odds = makeGroup(1, 3, 5, 7, 9);
+
+  std::decay_t<decltype(Evens)> EvensC(Evens);
+
+  static constexpr auto EvensL = Literals<2, 4, 6, 8, 10>;
+  static_assert(10 == anyOf(EvensL));
+
+  static constexpr auto LargeGap = Literals<128, 129, 131, 381, 382, 383>;
+
+  static_assert(128 == anyOf(LargeGap));
+  static_assert(129 == anyOf(LargeGap));
+  static_assert(130 != anyOf(LargeGap));
+  static_assert(383 == anyOf(LargeGap));
+  static_assert(191 != anyOf(LargeGap));
+  static_assert(192 != anyOf(LargeGap));
+  static_assert(193 != anyOf(LargeGap));
+  static_assert(255 != anyOf(LargeGap));
+  static_assert(256 != anyOf(LargeGap));
+  static_assert(257 != anyOf(LargeGap));
+
+  // Empty group.
+  EXPECT_TRUE(0 != anyOf());
+  EXPECT_TRUE(anyOf() != 0);
+
+  // Mixed group + value compare.
+  EXPECT_TRUE(1 == anyOf(EvensC, 1));
+  EXPECT_TRUE(anyOf(Evens, 1) == 1);
+
+  // Multiple groups.
+  EXPECT_TRUE(1 == anyOf(Evens, Odds));
+  EXPECT_TRUE(anyOf(Evens, Odds) == 1);
+
+  // Negative comparison.
+  EXPECT_TRUE(0 != anyOf(1, 2, 3, 4, 5, 6, 7, 8, 9));
+  EXPECT_TRUE(anyOf(1, 2, 3, 4, 5, 6, 7, 8, 9) != 0);
+
+  // Negative comparison of multiple groups.
+  EXPECT_TRUE(0 != anyOf(Evens, Odds));
+  EXPECT_TRUE(anyOf(Evens, Odds) != 0);
+}
+
+TEST(CondGroupTest, OptionalIntegers) {
+  using Opt = std::optional<int>;
+
+  auto Evens = makeGroup(Opt(), Opt(2), Opt(4), Opt(6), Opt(8), Opt(10));
+
+  auto Odds = makeGroup(Opt(), Opt(1), Opt(3), Opt(5), Opt(7), Opt(9));
+
+  auto OddsNoNullopt = makeGroup(Opt(1), Opt(3), Opt(5), Opt(7), Opt(9));
+
+  // Empty group.
+  EXPECT_TRUE(Opt(0) != anyOf());
+  EXPECT_TRUE(anyOf() != Opt(0));
+
+  // Mixed group + value compare.
+  EXPECT_TRUE(1 == anyOf(Evens, 1));
+  EXPECT_TRUE(anyOf(Evens, 1) == 1);
+
+  // Multiple groups.
+  EXPECT_TRUE(1 == anyOf(Evens, Odds));
+  EXPECT_TRUE(anyOf(Evens, Odds) == 1);
+
+  // Negative comparison.
+  EXPECT_TRUE(0 != anyOf(1, 2, 3, 4, 5, 6, 7, 8, 9));
+  EXPECT_TRUE(anyOf(1, 2, 3, 4, 5, 6, 7, 8, 9) != 0);
+
+  // Negative comparison of multiple groups.
+  EXPECT_TRUE(0 != anyOf(Evens, Odds));
+  EXPECT_TRUE(anyOf(Evens, Odds) != 0);
+
+  // std::nullopt should match the default-constructed Opt() in the Evens group.
+  EXPECT_TRUE(std::nullopt == anyOf(Evens));
+
+  // Should be able to compare an Optional to a group containing a std::nullopt.
+  EXPECT_TRUE(Opt(2) == anyOf(std::nullopt, 2));
+  EXPECT_TRUE(anyOf(std::nullopt, 2) == Opt(2));
+
+  EXPECT_FALSE(std::nullopt == anyOf(OddsNoNullopt));
+  EXPECT_FALSE(anyOf(OddsNoNullopt) == std::nullopt);
+
+  EXPECT_TRUE(std::nullopt != anyOf(OddsNoNullopt));
+  EXPECT_TRUE(anyOf(OddsNoNullopt) != std::nullopt);
+}
+
+TEST(CondGroupTest, LiteralsTuple) {
+  EXPECT_TRUE(1268 == (AnyOf<1, 5, 9, 1600, 905, 1268, 2402, 5732>));
+  EXPECT_TRUE(1268 == (anyOf(Literals<1, 5, 9, 1600, 905, 1268, 2402, 5732>)));
+
+  static constexpr auto Group = Literals<1, 5, 9, 1600, 905, 1268, 2402, 5732>;
+  EXPECT_TRUE(1268 == anyOf(Group));
+  EXPECT_FALSE(12 == anyOf(Group));
+
+  {
+    constexpr auto G =
+        Literals<9971U, 9972U, 9973U, 9974U, 9975U, 9976U, 9977U, 9978U, 9985U,
+                 9986U, 9987U, 9988U, 9989U, 9990U, 9991U, 9992U, 9993U, 9994U,
+                 9995U, 10002U, 10003U, 10004U, 10005U, 10006U, 10007U, 10008U,
+                 10009U, 10010U, 10011U, 10012U, 10013U, 9133U, 9134U>;
+
+    static_assert(10009U == anyOf(G));
+  }
+}
+
+namespace implicit_conversion {
+
+struct Foo {
+  // Support implicit conversions from int, unsigned or short.
+  constexpr Foo(int V) : Val(V) {}
+  constexpr Foo(unsigned V) : Val(static_cast<int>(V)) {}
+  constexpr Foo(short V) : Val(static_cast<int>(V)) {}
+
+  constexpr operator int() const { return Val; }
+
+  int Val;
+};
+
+} // namespace implicit_conversion
+
+TEST(CondGroupTest, ImplicitConversions) {
+  using namespace implicit_conversion;
+
+  static constexpr auto Evens =
+      makeGroup(Foo(2), Foo(4U), Foo(short(6)), 8, 10);
+  static constexpr auto Odds =
+      makeGroup(Foo(1), Foo(3), Foo(5U), Foo(short(7)), 9);
+
+  auto NegGroup =
+      makeGroup(1, Foo(short(2)), 3, Foo(4U), short(5), Foo(6), 7, Foo(8), 9);
+
+  //
+  // Single value is `Foo` type.
+  //
+
+  // Empty group.
+  EXPECT_TRUE(Foo(0) != anyOf());
+  EXPECT_TRUE(anyOf() != Foo(0));
+
+  // Mixed group + value compare.
+  EXPECT_TRUE(Foo(1) == anyOf(Evens, 1));
+  EXPECT_TRUE(anyOf(Evens, 1) == Foo(1));
+
+  // Multiple groups.
+  EXPECT_TRUE(Foo(1) == anyOf(Evens, Odds));
+  EXPECT_TRUE(anyOf(Evens, Odds) == Foo(1));
+
+  // Negative comparison.
+  EXPECT_TRUE(Foo(0) != anyOf(NegGroup));
+  EXPECT_TRUE(anyOf(NegGroup) != Foo(0));
+
+  // Negative comparison of multiple groups.
+  EXPECT_TRUE(Foo(0) != anyOf(Evens, Odds));
+  EXPECT_TRUE(anyOf(Evens, Odds) != Foo(0));
+
+  //
+  // Single value is integral type.
+  //
+
+  // Empty group.
+  EXPECT_TRUE(0 != anyOf());
+  EXPECT_TRUE(anyOf() != 0);
+
+  // Mixed group + value compare.
+  EXPECT_TRUE(1 == anyOf(Evens, 1));
+  EXPECT_TRUE(anyOf(Evens, 1) == 1);
+
+  // Multiple groups.
+  EXPECT_TRUE(1 == anyOf(Evens, Odds));
+  EXPECT_TRUE(anyOf(Evens, Odds) == 1);
+
+  // Negative comparison.
+  EXPECT_TRUE(0 != anyOf(NegGroup));
+  EXPECT_TRUE(anyOf(NegGroup) != 0);
+
+  // Negative comparison of multiple groups.
+  EXPECT_TRUE(0 != anyOf(Evens, Odds));
+  EXPECT_TRUE(anyOf(Evens, Odds) != 0);
+}
+
+TEST(CondGroupTest, EnumLiterals) {
+  using namespace implicit_conversion;
+
+  //
+  // Sequence set test cases.
+  //
+
+  // All enum group ; non-class (sequence).
+  {
+    static constexpr auto Letters = Literals<A, B, C>;
+
+    static_assert(0 == anyOf(Letters));
+    static_assert(A == anyOf(Letters));
+    static_assert(1u == anyOf(Letters));
+    static_assert(B == anyOf(Letters));
+    static_assert(Foo(C) == anyOf(Letters));
+    static_assert(Letter::D != anyOf(Letters));
+    static_assert(E != anyOf(Letters));
+
+    using ExpectedT = CondGroupMetaSet<MakeMetaSequenceSet<int, A, B, C>>;
+
+    static_assert(
+        std::is_same_v<std::remove_cv_t<decltype(Letters)>, ExpectedT>);
+  }
+
+  // All enum group ; class (sequence).
+  {
+    static constexpr auto Letters = Literals<Letter::A, Letter::B, Letter::C>;
+
+    static_assert(short(0) == anyOf(Letters));
+    static_assert(Letter::A == anyOf(Letters));
+    static_assert(1ul == anyOf(Letters));
+    static_assert(Letter::B == anyOf(Letters));
+    static_assert(Foo(C) == anyOf(Letters));
+    static_assert(Letter::D != anyOf(Letters));
+    static_assert(Letter::E != anyOf(Letters));
+
+    using ExpectedT = CondGroupMetaSet<
+        MakeMetaSequenceSet<int, Letter::A, Letter::B, Letter::C>, Letter>;
+
+    static_assert(
+        std::is_same_v<std::remove_cv_t<decltype(Letters)>, ExpectedT>);
+  }
+
+  // Mixed enum + integral group ; non-class (sequence).
+  {
+    static constexpr auto Letters = Literals<0, B, Two>;
+
+    static_assert(0ull == anyOf(Letters));
+    static_assert(A == anyOf(Letters));
+    static_assert(1 == anyOf(Letters));
+    static_assert(B == anyOf(Letters));
+    static_assert(Foo(C) == anyOf(Letters));
+    static_assert(D != anyOf(Letters));
+    static_assert(E != anyOf(Letters));
+
+    using ExpectedT = CondGroupMetaSet<MakeMetaSequenceSet<int, 0, B, 2>>;
+
+    static_assert(
+        std::is_same_v<std::remove_cv_t<decltype(Letters)>, ExpectedT>);
+  }
+
+  // Mixed group ; enum class (sequence).
+  {
+    static constexpr auto Letters = Literals<A, char(1), Letter::C>;
+
+    static_assert(0 == anyOf(Letters));
+    static_assert(Letter::A == anyOf(Letters));
+    static_assert(1 == anyOf(Letters));
+    static_assert(Letter::B == anyOf(Letters));
+    static_assert(Foo(C) == anyOf(Letters));
+    static_assert(C == anyOf(Letters));
+    static_assert(Letter::D != anyOf(Letters));
+
+    using ExpectedT =
+        CondGroupMetaSet<MakeMetaSequenceSet<int, A, char(1), Letter::C>,
+                         Letter>;
+
+    static_assert(
+        std::is_same_v<std::remove_cv_t<decltype(Letters)>, ExpectedT>);
+  }
+
+  //
+  // Bitset test cases.
+  //
+
+  // All enum group ; non-class (bitset).
+  {
+    static constexpr auto Letters = Literals<A, B, C, D>;
+
+    static_assert(0 == anyOf(Letters));
+    static_assert(A == anyOf(Letters));
+    static_assert(1u == anyOf(Letters));
+    static_assert(B == anyOf(Letters));
+    static_assert(Foo(C) == anyOf(Letters));
+    static_assert(D == anyOf(Letters));
+    static_assert(E != anyOf(Letters));
+
+    using ExpectedT = CondGroupMetaSet<MakeMetaBitset<int, A, B, C, D>>;
+
+    static_assert(
+        std::is_same_v<std::remove_cv_t<decltype(Letters)>, ExpectedT>);
+  }
+
+  // All enum group ; class (bitset).
+  {
+    static constexpr auto Letters =
+        Literals<Letter::A, Letter::B, Letter::C, Letter::D>;
+
+    static_assert(short(0) == anyOf(Letters));
+    static_assert(Letter::A == anyOf(Letters));
+    static_assert(1ul == anyOf(Letters));
+    static_assert(Letter::B == anyOf(Letters));
+    static_assert(Foo(C) == anyOf(Letters));
+    static_assert(Letter::D == anyOf(Letters));
+    static_assert(Letter::E != anyOf(Letters));
+
+    using ExpectedT = CondGroupMetaSet<
+        MakeMetaBitset<int, Letter::A, Letter::B, Letter::C, Letter::D>,
+        Letter>;
+
+    static_assert(
+        std::is_same_v<std::remove_cv_t<decltype(Letters)>, ExpectedT>);
+  }
+
+  // Mixed enum + integral group ; non-class (bitset).
+  {
+    static constexpr auto Letters = Literals<0, B, 2, D>;
+
+    static_assert(0ull == anyOf(Letters));
+    static_assert(A == anyOf(Letters));
+    static_assert(1 == anyOf(Letters));
+    static_assert(B == anyOf(Letters));
+    static_assert(Foo(C) == anyOf(Letters));
+    static_assert(D == anyOf(Letters));
+    static_assert(E != anyOf(Letters));
+
+    using ExpectedT = CondGroupMetaSet<MakeMetaBitset<int, 0, B, 2, D>>;
+
+    static_assert(
+        std::is_same_v<std::remove_cv_t<decltype(Letters)>, ExpectedT>);
+  }
+
+  // Mixed group ; enum class (bitset).
+  {
+    static constexpr auto Letters = Literals<0, char(1), Letter::C, Letter::D>;
+
+    static_assert(0 == anyOf(Letters));
+    static_assert(Letter::A == anyOf(Letters));
+    static_assert(1 == anyOf(Letters));
+    static_assert(Letter::B == anyOf(Letters));
+    static_assert(Foo(C) == anyOf(Letters));
+    static_assert(Letter::D == anyOf(Letters));
+    static_assert(Letter::E != anyOf(Letters));
+
+    using ExpectedT =
+        CondGroupMetaSet<MakeMetaBitset<int, 0, char(1), Letter::C, Letter::D>,
+                         Letter>;
+
+    static_assert(
+        std::is_same_v<std::remove_cv_t<decltype(Letters)>, ExpectedT>);
+  }
+
+  //
+  // Sparse bitset test cases.
+  //
+
+  // Mixed enum + integral group ; non-class (sparse bitset).
+  {
+    static constexpr auto Letters = Literals<0, B, 2, D, 1000>;
+
+    static_assert(0ull == anyOf(Letters));
+    static_assert(A == anyOf(Letters));
+    static_assert(1 == anyOf(Letters));
+    static_assert(B == anyOf(Letters));
+    static_assert(Foo(C) == anyOf(Letters));
+    static_assert(D == anyOf(Letters));
+    static_assert(E != anyOf(Letters));
+    static_assert(1000 == anyOf(Letters));
+
+    using ExpectedT =
+        CondGroupMetaSet<MetaSparseBitset<MakeMetaBitset<int, 0, B, 2, D>,
+                                          MakeMetaSequenceSet<int, 1000>>>;
+
+    static_assert(
+        std::is_same_v<std::remove_cv_t<decltype(Letters)>, ExpectedT>);
+  }
+
+  // Mixed group ; enum class (sparse bitset).
+  {
+    static constexpr auto Letters =
+        Literals<0, char(1), Letter::C, Letter::D, 5000>;
+
+    static_assert(0 == anyOf(Letters));
+    static_assert(Letter::A == anyOf(Letters));
+    static_assert(1 == anyOf(Letters));
+    static_assert(Letter::B == anyOf(Letters));
+    static_assert(Foo(C) == anyOf(Letters));
+    static_assert(Letter::D == anyOf(Letters));
+    static_assert(Letter::E != anyOf(Letters));
+    static_assert(4999 != anyOf(Letters));
+    static_assert(5000 == anyOf(Letters));
+    static_assert(5001 != anyOf(Letters));
+    static_assert(Foo(1) == anyOf(Letters));
+
+    using ExpectedT = CondGroupMetaSet<
+        MetaSparseBitset<MakeMetaBitset<int, 0, char(1), Letter::C, Letter::D>,
+                         MakeMetaSequenceSet<int, 5000>>,
+        Letter>;
+
+    static_assert(
+        std::is_same_v<std::remove_cv_t<decltype(Letters)>, ExpectedT>);
+  }
+
+#if 0 // compile errors
+  {
+    // Mixing signedness.
+    [[maybe_unused]] static constexpr auto MixSignedness =
+        Literals<1, 2u, 3, 4u>;
+  }
+
+  {
+    // Multiple enum types: class + class
+    [[maybe_unused]] static constexpr auto MalformedLetters =
+        Literals<Number::Zero, Letter::B, Letter::C, Letter::D>;
+  }
+
+  {
+    // enum class group.
+    static constexpr auto Letters =
+        Literals<Letter::A, Letter::B, Letter::C, Letter::D>;
+
+    // Incompatible comparisons:
+    EXPECT_TRUE(Number::One == anyOf(Letters));
+  }
+#endif
+}
+
+TEST(CondGroupTest, AllEmptyTuple) {
+  auto Evens = Literals<2, 4, 6, 8, 10>;
+  auto Odds = Literals<1, 3, 5, 7, 9>;
+
+  static_assert(std::is_empty_v<decltype(Evens)> &&
+                std::is_empty_v<decltype(Odds)>);
+
+  // Create a tuple-representation group of two empty classes.
+  auto G = makeGroup(Evens, Odds);
+  EXPECT_TRUE(1 == anyOf(G));
+}
+
+TEST(CondGroupTest, LiteralsContainer) {
+
+  // Empty set.
+  {
+    static constexpr auto Test = Literals<>;
+
+    static constexpr std::array<int, 0> Expected{};
+
+    static_assert(Test.empty() == true);
+    static_assert(Test.size() == Expected.size());
+    static_assert(ce_equal(Test, Expected));
+    static_assert(ce_requal(Test, Expected));
+  }
+
+  // Single value set.
+  {
+    static constexpr auto Test = Literals<5000>;
+
+    static constexpr int Expected[]{5000};
+
+    static_assert(Test.empty() == false);
+    static_assert(Test.size() == std::size(Expected));
+    static_assert(Test.front() == Expected[0]);
+    static_assert(Test.back() == Expected[std::size(Expected) - 1]);
+    static_assert(Test[0] == Expected[0]);
+    static_assert(ce_equal(Test, Expected));
+    static_assert(ce_requal(Test, Expected));
+  }
+
+  // Single bitset.
+  {
+    static constexpr auto Test = Literals<5, 10, 15, 20, 25>;
+
+    static constexpr int Expected[]{5, 10, 15, 20, 25};
+
+    static_assert(Test.empty() == false);
+    static_assert(Test.size() == std::size(Expected));
+    static_assert(Test.front() == Expected[0]);
+    static_assert(Test.back() == Expected[std::size(Expected) - 1]);
+    static_assert(Test[1] == Expected[1]);
+    static_assert(ce_equal(Test, Expected));
+    static_assert(ce_requal(Test, Expected));
+  }
+
+  // Single sequence.
+  {
+    static constexpr auto Test = Literals<0, 10000, 50000, 100000>;
+
+    static constexpr int Expected[]{0, 10000, 50000, 100000};
+
+    static_assert(Test.empty() == false);
+    static_assert(Test.size() == std::size(Expected));
+    static_assert(Test.front() == Expected[0]);
+    static_assert(Test.back() == Expected[std::size(Expected) - 1]);
+    static_assert(Test[1] == Expected[1]);
+    static_assert(ce_equal(Test, Expected));
+    static_assert(ce_requal(Test, Expected));
+  }
+
+  // Multiple clusters.
+  {
+    static constexpr auto Test =
+        Literals<1005, 1004, 1003, 1002, 1001, 1000, 5, 4, 3, 2, 1, 0>;
+
+    static constexpr int Expected[]{0,    1,    2,    3,    4,    5,
+                                    1000, 1001, 1002, 1003, 1004, 1005};
+
+    static_assert(Test.empty() == false);
+    static_assert(Test.size() == std::size(Expected));
+    static_assert(Test.front() == Expected[0]);
+    static_assert(Test.back() == Expected[std::size(Expected) - 1]);
+    static_assert(Test[1] == Expected[1]);
+    static_assert(ce_equal(Test, Expected));
+    static_assert(ce_requal(Test, Expected));
+  }
+
+  // Cluster + sequence.
+  {
+    static constexpr auto Test = Literals<1004, 1005, 5, 4, 3, 2, 1, 0>;
+
+    static constexpr int Expected[]{0, 1, 2, 3, 4, 5, 1004, 1005};
+
+    static_assert(Test.empty() == false);
+    static_assert(Test.size() == std::size(Expected));
+    static_assert(Test.front() == Expected[0]);
+    static_assert(Test.back() == Expected[std::size(Expected) - 1]);
+    static_assert(Test[1] == Expected[1]);
+    static_assert(ce_equal(Test, Expected));
+    static_assert(ce_requal(Test, Expected));
+  }
+
+  // Duplicate values (raw sequence).
+  {
+    static constexpr auto Test = Literals<3, 2, 3>;
+
+    static constexpr int Expected[]{2, 3, 3};
+
+    static_assert(Test.empty() == false);
+    static_assert(Test.size() == std::size(Expected));
+    static_assert(Test.front() == Expected[0]);
+    static_assert(Test.back() == Expected[std::size(Expected) - 1]);
+    static_assert(Test[1] == Expected[1]);
+    static_assert(ce_equal(Test, Expected));
+    static_assert(ce_requal(Test, Expected));
+  }
+
+  // Duplicate values (bitset representation).
+  {
+    static constexpr auto Test = Literals<3, 2, 3, 11, 6>;
+
+    static constexpr int Expected[]{2, 3, 6, 11};
+
+    static_assert(Test.empty() == false);
+    static_assert(Test.size() == std::size(Expected));
+    static_assert(Test.front() == Expected[0]);
+    static_assert(Test.back() == Expected[std::size(Expected) - 1]);
+    static_assert(Test[1] == Expected[1]);
+    static_assert(ce_equal(Test, Expected));
+    static_assert(ce_requal(Test, Expected));
+  }
+}
+
+TEST(CondGroupTest, LiteralsUnion) {
+
+  // Identity
+  {
+    constexpr auto Group = cgrp::Literals<5, 10, 15, 65, 70, 75>;
+
+    static constexpr int Expected[]{5, 10, 15, 65, 70, 75};
+
+    static_assert(ce_equal(Group | Group, Expected));
+  }
+
+  // Identity (signed)
+  {
+    constexpr auto Group = cgrp::Literals<75, -70, -65, 5, 10, 64>;
+
+    static constexpr int Expected[]{-70, -65, 5, 10, 64, 75};
+
+    static_assert(ce_equal(Group | Group, Expected));
+  }
+
+  // Disjoint
+  {
+    constexpr auto L = cgrp::Literals<1, 3, 5, 7, 9>;
+    constexpr auto R = cgrp::Literals<2, 4, 6, 8, 10>;
+
+    static constexpr int Expected[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+
+    static_assert(ce_equal(L | R, Expected));
+  }
+
+  // Disjoint (signed)
+  {
+    constexpr auto L = cgrp::Literals<-5, -3, -1, 1, 3, 5>;
+    constexpr auto R = cgrp::Literals<-4, -2, 0, 2, 4>;
+
+    static constexpr int Expected[]{-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5};
+
+    static_assert(ce_equal(L | R, Expected));
+  }
+
+  // Intersecting.
+  {
+    constexpr auto L = cgrp::Literals<1, 2, 3, 4, 5, 6, 7, 8, 9, 10>;
+    constexpr auto R = cgrp::Literals<2, 4, 6, 8, 10>;
+
+    static constexpr int Expected[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+
+    static_assert(ce_equal(L | R, Expected));
+  }
+
+  // Intersecting (signed)
+  {
+    constexpr auto L = cgrp::Literals<-6, -4, -2, 0, 2, 4, 6>;
+    constexpr auto R = cgrp::Literals<-4, -3, -2, -1, 0, 1, 2, 3, 4>;
+
+    static constexpr int Expected[]{-6, -4, -3, -2, -1, 0, 1, 2, 3, 4, 6};
+
+    static_assert(ce_equal(L | R, Expected));
+  }
+
+  // Big gap.
+  {
+    constexpr auto L = cgrp::Literals<10000, 5000, 8000>;
+    constexpr auto R = cgrp::Literals<10, 20, 30, 40, 50, 60>;
+
+    static constexpr int Expected[]{10, 20, 30, 40, 50, 60, 5000, 8000, 10000};
+
+    static_assert(ce_equal(L | R, Expected));
+  }
+}
+
+TEST(CondGroupTest, LiteralsIntersection) {
+
+  // Identity
+  {
+    constexpr auto Group = cgrp::Literals<5, 10, 15, 65, 70, 75>;
+
+    static constexpr int Expected[]{5, 10, 15, 65, 70, 75};
+
+    static_assert(ce_equal(Group & Group, Expected));
+  }
+
+  // Identity (signed)
+  {
+    constexpr auto Group = cgrp::Literals<75, -70, -65, 5, 10, 64>;
+
+    static constexpr int Expected[]{-70, -65, 5, 10, 64, 75};
+
+    static_assert(ce_equal(Group & Group, Expected));
+  }
+
+  // Disjoint
+  {
+    constexpr auto L = cgrp::Literals<1, 3, 5, 7, 9>;
+    constexpr auto R = cgrp::Literals<2, 4, 6, 8, 10>;
+
+    static constexpr std::array<int, 0> Expected{};
+
+    static_assert(ce_equal(L & R, Expected));
+  }
+
+  // Disjoint (signed)
+  {
+    constexpr auto L = cgrp::Literals<-5, -3, -1, 1, 3, 5>;
+    constexpr auto R = cgrp::Literals<-4, -2, 0, 2, 4>;
+
+    static constexpr std::array<int, 0> Expected{};
+
+    static_assert(ce_equal(L & R, Expected));
+  }
+
+  // Intersecting.
+  {
+    constexpr auto L = cgrp::Literals<1, 2, 3, 4, 5, 6, 7, 8, 9, 10>;
+    constexpr auto R = cgrp::Literals<2, 4, 6, 8, 10>;
+
+    static constexpr int Expected[]{2, 4, 6, 8, 10};
+
+    static_assert(ce_equal(L & R, Expected));
+  }
+
+  // Intersecting (signed)
+  {
+    constexpr auto L = cgrp::Literals<-6, -4, -2, 0, 2, 4, 6>;
+    constexpr auto R = cgrp::Literals<-4, -3, -2, -1, 0, 1, 2, 3, 4>;
+
+    static constexpr int Expected[]{-4, -2, 0, 2, 4};
+
+    static_assert(ce_equal(L & R, Expected));
+  }
+
+  // Big gap.
+  {
+    constexpr auto L = cgrp::Literals<10000, 5000, 8000>;
+    constexpr auto R = cgrp::Literals<10, 20, 30, 40, 50, 60>;
+
+    static constexpr std::array<int, 0> Expected{};
+
+    static_assert(ce_equal(L & R, Expected));
+  }
+}
+
+TEST(CondGroupTest, LiteralsMinus) {
+
+  // Same operands
+  {
+    constexpr auto Group = cgrp::Literals<5, 10, 15, 65, 70, 75>;
+
+    static constexpr std::array<int, 0> Expected{};
+
+    static_assert(ce_equal(Group - Group, Expected));
+  }
+
+  // Same operands (signed)
+  {
+    constexpr auto Group = cgrp::Literals<75, -70, -65, 5, 10, 64>;
+
+    static constexpr std::array<int, 0> Expected{};
+
+    static_assert(ce_equal(Group - Group, Expected));
+  }
+
+  // Disjoint
+  {
+    constexpr auto L = cgrp::Literals<1, 3, 5, 7, 9>;
+    constexpr auto R = cgrp::Literals<2, 4, 6, 8, 10>;
+
+    static constexpr int Expected[]{1, 3, 5, 7, 9};
+
+    static_assert(ce_equal(L - R, Expected));
+  }
+
+  // Disjoint (signed)
+  {
+    constexpr auto L = cgrp::Literals<-5, -3, -1, 1, 3, 5>;
+    constexpr auto R = cgrp::Literals<-4, -2, 0, 2, 4>;
+
+    static constexpr int Expected[]{-5, -3, -1, 1, 3, 5};
+
+    static_assert(ce_equal(L - R, Expected));
+  }
+
+  // Intersecting.
+  {
+    constexpr auto L = cgrp::Literals<1, 2, 3, 4, 5, 6, 7, 8, 9, 10>;
+    constexpr auto R = cgrp::Literals<2, 4, 6, 8, 10>;
+
+    static constexpr int Expected[]{1, 3, 5, 7, 9};
+
+    static_assert(ce_equal(L - R, Expected));
+  }
+
+  // Intersecting (signed)
+  {
+    constexpr auto L = cgrp::Literals<-6, -4, -2, 0, 2, 4, 6>;
+    constexpr auto R = cgrp::Literals<-4, -3, -2, -1, 0, 1, 2, 3, 4>;
+
+    static constexpr int Expected[]{-6, 6};
+
+    static_assert(ce_equal(L - R, Expected));
+  }
+
+  // Big gap.
+  {
+    constexpr auto L = cgrp::Literals<10000, 5000, 8000>;
+    constexpr auto R = cgrp::Literals<10, 20, 30, 40, 50, 60>;
+
+    static constexpr int Expected[]{5000, 8000, 10000};
+
+    static_assert(ce_equal(L - R, Expected));
+  }
+}
+
+} // namespace
diff --git a/llvm/unittests/ADT/ConstexprUtilsTest.cpp b/llvm/unittests/ADT/ConstexprUtilsTest.cpp
new file mode 100644
index 0000000000000..b2ed246f58f73
--- /dev/null
+++ b/llvm/unittests/ADT/ConstexprUtilsTest.cpp
@@ -0,0 +1,973 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains testing for constexpr utilities.
+///
+/// Utilities include sorting, various other algorithms and integer sequence
+/// manipulation.
+///
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/ConstexprUtils.h"
+#include "llvm/ADT/STLExtras.h"
+#include "gtest/gtest.h"
+#include <cstdlib>
+#include <type_traits>
+#include <utility>
+
+using namespace llvm;
+
+static constexpr auto Greater = [](auto L, auto R) constexpr { return L > R; };
+
+template <typename T> struct Type {
+  using type = T;
+};
+
+// clang-format off
+using IntTypes =
+    ::testing::Types<
+      signed char,
+      short,
+      int,
+      long,
+      long long,
+      unsigned char,
+      unsigned short,
+      unsigned,
+      unsigned long,
+      unsigned long long
+    >;
+// clang-format on
+
+template <typename T> class ConstexprUtilsTest : public testing::Test {
+public:
+  using IntT = T;
+
+  enum class Number : IntT {
+    Zero,
+    One,
+    Two,
+    Three,
+    Four,
+    Five,
+    Six,
+    Seven,
+    Eight,
+    Nine,
+  };
+
+  static constexpr IntT min() { return std::numeric_limits<IntT>::min(); }
+  static constexpr IntT max() { return std::numeric_limits<IntT>::max(); }
+};
+
+#define EXPOSE_TYPE(type)                                                      \
+  using type = typename remove_cvref_t<decltype(*this)>::type
+
+TYPED_TEST_SUITE(ConstexprUtilsTest, IntTypes, );
+
+TYPED_TEST(ConstexprUtilsTest, CompileTimeSwap) {
+  EXPOSE_TYPE(IntT);
+  EXPOSE_TYPE(Number);
+
+  // Swap pair elements.
+  {
+    constexpr auto P1 = []() {
+      auto Ret = std::pair(IntT(1), IntT(2));
+      ce_swap(Ret.first, Ret.second);
+      return Ret;
+    }();
+
+    static_assert(P1.first == 2);
+    static_assert(P1.second == 1);
+
+    constexpr auto P2 = [](auto PIn) {
+      ce_swap(PIn.first, PIn.second);
+      return PIn;
+    }(P1);
+
+    static_assert(P2.first == 1);
+    static_assert(P2.second == 2);
+  }
+
+  // Swap pair elements (enum).
+  {
+    constexpr auto P1 = []() {
+      auto Ret = std::pair(Number::One, Number::Two);
+      ce_swap(Ret.first, Ret.second);
+      return Ret;
+    }();
+
+    static_assert(P1.first == Number::Two);
+    static_assert(P1.second == Number::One);
+
+    constexpr auto P2 = [](auto PIn) {
+      ce_swap(PIn.first, PIn.second);
+      return PIn;
+    }(P1);
+
+    static_assert(P2.first == Number::One);
+    static_assert(P2.second == Number::Two);
+  }
+}
+
+TYPED_TEST(ConstexprUtilsTest, MinMaxCompiletime) {
+  EXPOSE_TYPE(IntT);
+  EXPOSE_TYPE(Number);
+  using BaseT = remove_cvref_t<decltype(*this)>;
+
+  if constexpr (BaseT::max() > 4723) {
+    static_assert(ce_max<IntT>(192, 273, 4723, 20, 3709) == IntT(4723));
+    static_assert(ce_min<IntT>(192, 273, 4723, 20, 3709) == IntT(20));
+  }
+
+  static_assert(ce_max<IntT>(20, 120, 124, 57) == IntT(124));
+  static_assert(ce_min<IntT>(20, 120, 124, 57) == IntT(20));
+
+  if constexpr (std::is_signed_v<IntT>) {
+    if constexpr (BaseT::max() > 4723 && BaseT::min() < -2873) {
+      static_assert(ce_max<IntT>(192, 273, -2873, 4723, 20, 3709) ==
+                    IntT(4723));
+      static_assert(ce_min<IntT>(192, 273, -2873, 4723, 20, 3709) ==
+                    IntT(-2873));
+    }
+  }
+
+  if constexpr (std::is_signed_v<IntT>) {
+    static_assert(ce_max<IntT>(127, 0, 20, -20, -128, -90, 103) == IntT(127));
+    static_assert(ce_min<IntT>(127, 0, 20, -20, -128, -90, 103) == IntT(-128));
+  }
+
+  static_assert(ce_max<Number>(Number::Four, Number::Two, Number::Six,
+                               Number::Three, Number::Four) == Number::Six);
+
+  static_assert(ce_min<Number>(Number::Four, Number::Two, Number::Six,
+                               Number::Three, Number::Four) == Number::Two);
+}
+
+TYPED_TEST(ConstexprUtilsTest, ToArray) {
+  EXPOSE_TYPE(IntT);
+  EXPOSE_TYPE(Number);
+
+  // int array conversion.
+  {
+    static constexpr IntT CArr[]{1, 3, 5, 7, 9};
+    static constexpr auto Arr = to_array(CArr);
+
+    static_assert(
+        std::is_same_v<std::remove_cv_t<decltype(Arr)>, std::array<IntT, 5>>);
+
+    static_assert(Arr.size() == std::size(CArr));
+    static_assert(Arr[0] == IntT(1));
+    static_assert(Arr[1] == IntT(3));
+    static_assert(Arr[2] == IntT(5));
+    static_assert(Arr[3] == IntT(7));
+    static_assert(Arr[4] == IntT(9));
+
+    EXPECT_TRUE(equal(CArr, Arr));
+  }
+
+  // enum array conversion.
+  {
+    static constexpr Number CArr[]{Number::One, Number::Three, Number::Five,
+                                   Number::Seven, Number::Nine};
+    static constexpr auto Arr = to_array(CArr);
+
+    static_assert(
+        std::is_same_v<std::remove_cv_t<decltype(Arr)>, std::array<Number, 5>>);
+
+    static_assert(Arr.size() == std::size(CArr));
+    static_assert(Arr[0] == Number::One);
+    static_assert(Arr[1] == Number::Three);
+    static_assert(Arr[2] == Number::Five);
+    static_assert(Arr[3] == Number::Seven);
+    static_assert(Arr[4] == Number::Nine);
+
+    EXPECT_TRUE(equal(CArr, Arr));
+  }
+}
+
+TYPED_TEST(ConstexprUtilsTest, SliceArray) {
+  EXPOSE_TYPE(IntT);
+  EXPOSE_TYPE(Number);
+
+  // int array slice.
+  {
+    static constexpr std::array<IntT, 5> Arr{{1, 3, 5, 7, 9}};
+
+    // Front slice.
+    {
+      static constexpr auto Slice = ce_slice<0, 2>(Arr);
+
+      static_assert(std::is_same_v<std::remove_cv_t<decltype(Slice)>,
+                                   std::array<IntT, 2>>);
+
+      static_assert(Slice[0] == 1);
+      static_assert(Slice[1] == 3);
+    }
+
+    // Back slice.
+    {
+      static constexpr auto Slice = ce_slice<1, 4>(Arr);
+
+      static_assert(std::is_same_v<std::remove_cv_t<decltype(Slice)>,
+                                   std::array<IntT, 4>>);
+
+      static_assert(Slice[0] == 3);
+      static_assert(Slice[1] == 5);
+      static_assert(Slice[2] == 7);
+      static_assert(Slice[3] == 9);
+    }
+
+    // Middle slice.
+    {
+      static constexpr auto Slice = ce_slice<2, 2>(Arr);
+
+      static_assert(std::is_same_v<std::remove_cv_t<decltype(Slice)>,
+                                   std::array<IntT, 2>>);
+
+      static_assert(Slice[0] == 5);
+      static_assert(Slice[1] == 7);
+    }
+
+    // Zero-length slice (begin).
+    {
+      static constexpr auto Slice = ce_slice<0, 0>(Arr);
+
+      static_assert(std::is_same_v<std::remove_cv_t<decltype(Slice)>,
+                                   std::array<IntT, 0>>);
+    }
+
+    // Zero-length slice (last)
+    {
+      static constexpr auto Slice = ce_slice<4, 0>(Arr);
+
+      static_assert(std::is_same_v<std::remove_cv_t<decltype(Slice)>,
+                                   std::array<IntT, 0>>);
+    }
+
+    // Zero-length slice (end)
+    {
+      static constexpr auto Slice = ce_slice<5, 0>(Arr);
+
+      static_assert(std::is_same_v<std::remove_cv_t<decltype(Slice)>,
+                                   std::array<IntT, 0>>);
+    }
+
+    // Complete slice.
+    {
+      static constexpr auto Slice = ce_slice<0, 5>(Arr);
+
+      static_assert(std::is_same_v<std::remove_cv_t<decltype(Slice)>,
+                                   std::array<IntT, 5>>);
+
+      static_assert(Slice[0] == 1);
+      static_assert(Slice[1] == 3);
+      static_assert(Slice[2] == 5);
+      static_assert(Slice[3] == 7);
+      static_assert(Slice[4] == 9);
+    }
+  }
+
+  // enum array slice.
+  {
+    static constexpr std::array<Number, 5> Arr{{Number::One, Number::Three,
+                                                Number::Five, Number::Seven,
+                                                Number::Nine}};
+
+    // Front slice.
+    {
+      static constexpr auto Slice = ce_slice<0, 2>(Arr);
+
+      static_assert(std::is_same_v<std::remove_cv_t<decltype(Slice)>,
+                                   std::array<Number, 2>>);
+
+      static_assert(Slice[0] == Number::One);
+      static_assert(Slice[1] == Number::Three);
+    }
+
+    // Back slice.
+    {
+      static constexpr auto Slice = ce_slice<1, 4>(Arr);
+
+      static_assert(std::is_same_v<std::remove_cv_t<decltype(Slice)>,
+                                   std::array<Number, 4>>);
+
+      static_assert(Slice[0] == Number::Three);
+      static_assert(Slice[1] == Number::Five);
+      static_assert(Slice[2] == Number::Seven);
+      static_assert(Slice[3] == Number::Nine);
+    }
+
+    // Middle slice.
+    {
+      static constexpr auto Slice = ce_slice<2, 2>(Arr);
+
+      static_assert(std::is_same_v<std::remove_cv_t<decltype(Slice)>,
+                                   std::array<Number, 2>>);
+
+      static_assert(Slice[0] == Number::Five);
+      static_assert(Slice[1] == Number::Seven);
+    }
+
+    // Zero-length slice (begin).
+    {
+      static constexpr auto Slice = ce_slice<0, 0>(Arr);
+
+      static_assert(std::is_same_v<std::remove_cv_t<decltype(Slice)>,
+                                   std::array<Number, 0>>);
+    }
+
+    // Zero-length slice (last)
+    {
+      static constexpr auto Slice = ce_slice<4, 0>(Arr);
+
+      static_assert(std::is_same_v<std::remove_cv_t<decltype(Slice)>,
+                                   std::array<Number, 0>>);
+    }
+
+    // Zero-length slice (end)
+    {
+      static constexpr auto Slice = ce_slice<5, 0>(Arr);
+
+      static_assert(std::is_same_v<std::remove_cv_t<decltype(Slice)>,
+                                   std::array<Number, 0>>);
+    }
+
+    // Complete slice.
+    {
+      static constexpr auto Slice = ce_slice<0, 5>(Arr);
+
+      static_assert(std::is_same_v<std::remove_cv_t<decltype(Slice)>,
+                                   std::array<Number, 5>>);
+
+      static_assert(Slice[0] == Number::One);
+      static_assert(Slice[1] == Number::Three);
+      static_assert(Slice[2] == Number::Five);
+      static_assert(Slice[3] == Number::Seven);
+      static_assert(Slice[4] == Number::Nine);
+    }
+  }
+}
+
+TYPED_TEST(ConstexprUtilsTest, SliceLiterals) {
+  EXPOSE_TYPE(IntT);
+
+  // Front slice.
+  {
+    using Slice = SliceLiterals<0, 2, IntT, 5, 10, 15, 20, 25, 30, 35, 40>;
+    static_assert(std::is_same_v<Slice, std::integer_sequence<IntT, 5, 10>>);
+  }
+
+  // Back slice.
+  {
+    using Slice = SliceLiterals<5, 3, IntT, 5, 10, 15, 20, 25, 30, 35, 40>;
+    static_assert(
+        std::is_same_v<Slice, std::integer_sequence<IntT, 30, 35, 40>>);
+  }
+
+  // Middle slice.
+  {
+    using Slice = SliceLiterals<2, 4, IntT, 5, 10, 15, 20, 25, 30, 35, 40>;
+    static_assert(
+        std::is_same_v<Slice, std::integer_sequence<IntT, 15, 20, 25, 30>>);
+  }
+
+  // Zero-length slice (begin).
+  {
+    using Slice = SliceLiterals<0, 0, IntT, 5, 10, 15, 20, 25, 30, 35, 40>;
+    static_assert(std::is_same_v<Slice, std::integer_sequence<IntT>>);
+  }
+
+  // Zero-length slice (last)
+  {
+    using Slice = SliceLiterals<7, 0, IntT, 5, 10, 15, 20, 25, 30, 35, 40>;
+    static_assert(std::is_same_v<Slice, std::integer_sequence<IntT>>);
+  }
+
+  // Zero-length slice (end)
+  {
+    using Slice = SliceLiterals<8, 0, IntT, 5, 10, 15, 20, 25, 30, 35, 40>;
+    static_assert(std::is_same_v<Slice, std::integer_sequence<IntT>>);
+  }
+
+  // Complete slice.
+  {
+    using Slice = SliceLiterals<0, 8, IntT, 5, 10, 15, 20, 25, 30, 35, 40>;
+    static_assert(
+        std::is_same_v<
+            Slice, std::integer_sequence<IntT, 5, 10, 15, 20, 25, 30, 35, 40>>);
+  }
+}
+
+TYPED_TEST(ConstexprUtilsTest, SliceSequence) {
+  EXPOSE_TYPE(IntT);
+
+  using Seq = std::integer_sequence<IntT, 5, 10, 15, 20, 25, 30, 35, 40>;
+
+  // Front slice.
+  {
+    using Slice = SliceSequence<0, 2, Seq>;
+    static_assert(std::is_same_v<Slice, std::integer_sequence<IntT, 5, 10>>);
+  }
+
+  // Back slice.
+  {
+    using Slice = SliceSequence<5, 3, Seq>;
+    static_assert(
+        std::is_same_v<Slice, std::integer_sequence<IntT, 30, 35, 40>>);
+  }
+
+  // Middle slice.
+  {
+    using Slice = SliceSequence<2, 4, Seq>;
+    static_assert(
+        std::is_same_v<Slice, std::integer_sequence<IntT, 15, 20, 25, 30>>);
+  }
+
+  // Zero-length slice (begin).
+  {
+    using Slice = SliceSequence<0, 0, Seq>;
+    static_assert(std::is_same_v<Slice, std::integer_sequence<IntT>>);
+  }
+
+  // Zero-length slice (last)
+  {
+    using Slice = SliceSequence<Seq().size() - 1, 0, Seq>;
+    static_assert(std::is_same_v<Slice, std::integer_sequence<IntT>>);
+  }
+
+  // Zero-length slice (end)
+  {
+    using Slice = SliceSequence<Seq().size(), 0, Seq>;
+    static_assert(std::is_same_v<Slice, std::integer_sequence<IntT>>);
+  }
+
+  // Complete slice.
+  {
+    using Slice = SliceSequence<0, Seq().size(), Seq>;
+    static_assert(
+        std::is_same_v<
+            Slice, std::integer_sequence<IntT, 5, 10, 15, 20, 25, 30, 35, 40>>);
+  }
+}
+
+TYPED_TEST(ConstexprUtilsTest, PushBackSequence) {
+  EXPOSE_TYPE(IntT);
+
+  using Empty = std::integer_sequence<IntT>;
+
+  using Seq1 = PushBackSequence<Empty, 10>;
+  static_assert(std::is_same_v<Seq1, std::integer_sequence<IntT, 10>>);
+
+  using Seq2 = PushBackSequence<Seq1, 20>;
+  static_assert(std::is_same_v<Seq2, std::integer_sequence<IntT, 10, 20>>);
+
+  using Seq3 = PushBackSequence<Seq2, 30>;
+  static_assert(std::is_same_v<Seq3, std::integer_sequence<IntT, 10, 20, 30>>);
+
+  using Seq4 = PushBackSequence<Seq3, 30>;
+  static_assert(
+      std::is_same_v<Seq4, std::integer_sequence<IntT, 10, 20, 30, 30>>);
+
+  using Seq5 = PushBackSequence<Seq4, 20>;
+  static_assert(
+      std::is_same_v<Seq5, std::integer_sequence<IntT, 10, 20, 30, 30, 20>>);
+
+  using Seq6 = PushBackSequence<Seq5, 10>;
+  static_assert(
+      std::is_same_v<Seq6,
+                     std::integer_sequence<IntT, 10, 20, 30, 30, 20, 10>>);
+}
+
+TYPED_TEST(ConstexprUtilsTest, CompileTimeBubbleSortLiterals) {
+  EXPOSE_TYPE(IntT);
+
+  // Pre-sorted
+  {
+    using Sorted = SortLiterals<IntT, 1, 2, 3, 4, 5, 6, 7, 8>;
+
+    static_assert(
+        std::is_same_v<Sorted,
+                       std::integer_sequence<IntT, 1, 2, 3, 4, 5, 6, 7, 8>>);
+  }
+
+  // Pre-sorted + Duplicate elements
+  {
+    using Sorted = SortLiterals<IntT, 1, 1, 2, 2, 3, 3, 4, 4>;
+
+    static_assert(
+        std::is_same_v<Sorted,
+                       std::integer_sequence<IntT, 1, 1, 2, 2, 3, 3, 4, 4>>);
+  }
+
+  // Scrambled
+  {
+    using Sorted = SortLiterals<IntT, 4, 1, 3, 2>;
+
+    static_assert(
+        std::is_same_v<Sorted, std::integer_sequence<IntT, 1, 2, 3, 4>>);
+  }
+
+  // Scrambled + Duplicate elements
+  {
+    using Sorted = SortLiterals<IntT, 1, 4, 1, 2, 4, 3, 2>;
+
+    static_assert(
+        std::is_same_v<Sorted,
+                       std::integer_sequence<IntT, 1, 1, 2, 2, 3, 4, 4>>);
+  }
+
+  // Negative values.
+  if constexpr (std::is_signed_v<IntT>) {
+    using Sorted = SortLiterals<IntT, -1, 4, 1, 2, -4, 3, -2>;
+
+    static_assert(
+        std::is_same_v<Sorted,
+                       std::integer_sequence<IntT, -4, -2, -1, 1, 2, 3, 4>>);
+  }
+}
+
+TYPED_TEST(ConstexprUtilsTest, CompileTimeQuickSortLiterals) {
+  EXPOSE_TYPE(IntT);
+
+  // Quick Sort engages when the number of elements is >= 20
+
+  // Pre-sorted
+  {
+    using Sorted = SortLiterals<IntT, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
+                                14, 15, 16, 17, 18, 19, 20>;
+
+    static_assert(
+        std::is_same_v<Sorted, std::integer_sequence<IntT, 1, 2, 3, 4, 5, 6, 7,
+                                                     8, 9, 10, 11, 12, 13, 14,
+                                                     15, 16, 17, 18, 19, 20>>);
+  }
+
+  // Pre-sorted + Duplicate elements
+  {
+    using Sorted = SortLiterals<IntT, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7,
+                                8, 8, 9, 9, 10, 10>;
+
+    static_assert(
+        std::is_same_v<Sorted,
+                       std::integer_sequence<IntT, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5,
+                                             6, 6, 7, 7, 8, 8, 9, 9, 10, 10>>);
+  }
+
+  // Scrambled
+  {
+    using Sorted = SortLiterals<IntT, 1, 20, 3, 18, 5, 16, 7, 14, 9, 12, 11, 10,
+                                13, 8, 15, 6, 17, 4, 19, 2>;
+
+    static_assert(
+        std::is_same_v<Sorted, std::integer_sequence<IntT, 1, 2, 3, 4, 5, 6, 7,
+                                                     8, 9, 10, 11, 12, 13, 14,
+                                                     15, 16, 17, 18, 19, 20>>);
+  }
+
+  // Scrambled + Duplicate elements
+  {
+    using Sorted = SortLiterals<IntT, 1, 10, 2, 9, 3, 8, 4, 7, 5, 6, 6, 5, 7, 4,
+                                8, 3, 9, 2, 10, 1>;
+
+    static_assert(
+        std::is_same_v<Sorted,
+                       std::integer_sequence<IntT, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5,
+                                             6, 6, 7, 7, 8, 8, 9, 9, 10, 10>>);
+  }
+
+  // Negative values
+  if constexpr (std::is_signed_v<IntT>) {
+    using Sorted = SortLiterals<IntT, 1, -10, 2, -9, 3, -8, 4, -7, 5, -6, 6, -5,
+                                7, -4, 8, -3, 9, -2, 10, -1, 0>;
+
+    static_assert(
+        std::is_same_v<Sorted, std::integer_sequence<
+                                   IntT, -10, -9, -8, -7, -6, -5, -4, -3, -2,
+                                   -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10>>);
+  }
+}
+
+TYPED_TEST(ConstexprUtilsTest, CompileTimeBubbleSortSequences) {
+  EXPOSE_TYPE(IntT);
+
+  // Pre-sorted
+  {
+    using Seq = std::integer_sequence<IntT, 1, 2, 3, 4, 5, 6, 7, 8>;
+    using Sorted = SortSequence<Seq>;
+
+    static_assert(
+        std::is_same_v<Sorted,
+                       std::integer_sequence<IntT, 1, 2, 3, 4, 5, 6, 7, 8>>);
+  }
+
+  // Pre-sorted + Duplicate elements
+  {
+    using Seq = std::integer_sequence<IntT, 1, 1, 2, 2, 3, 3, 4, 4>;
+    using Sorted = SortSequence<Seq>;
+
+    static_assert(
+        std::is_same_v<Sorted,
+                       std::integer_sequence<IntT, 1, 1, 2, 2, 3, 3, 4, 4>>);
+  }
+
+  // Scrambled
+  {
+    using Seq = std::integer_sequence<IntT, 4, 1, 3, 2>;
+    using Sorted = SortSequence<Seq>;
+
+    static_assert(
+        std::is_same_v<Sorted, std::integer_sequence<IntT, 1, 2, 3, 4>>);
+  }
+
+  // Scrambled + Duplicate elements
+  {
+    using Seq = std::integer_sequence<IntT, 1, 4, 1, 2, 4, 3, 2>;
+    using Sorted = SortSequence<Seq>;
+
+    static_assert(
+        std::is_same_v<Sorted,
+                       std::integer_sequence<IntT, 1, 1, 2, 2, 3, 4, 4>>);
+  }
+
+  // Negative values.
+  if constexpr (std::is_signed_v<IntT>) {
+    using Seq = std::integer_sequence<IntT, -1, 4, 1, 2, -4, 3, -2>;
+    using Sorted = SortSequence<Seq>;
+
+    static_assert(
+        std::is_same_v<Sorted,
+                       std::integer_sequence<IntT, -4, -2, -1, 1, 2, 3, 4>>);
+  }
+}
+
+TYPED_TEST(ConstexprUtilsTest, BubbleSortArrays) {
+  EXPOSE_TYPE(IntT);
+  EXPOSE_TYPE(Number);
+
+  // Pre-sorted
+  {
+    static constexpr IntT Seq[]{1, 2, 3, 4, 5, 6, 7, 8};
+    static constexpr std::array<IntT, std::size(Seq)> Sorted = ce_sort(Seq);
+
+    EXPECT_TRUE(equal(Seq, Sorted));
+
+    // Run-time sort in-place.
+    std::array<IntT, std::size(Seq)> Arr = to_array(Seq);
+    ce_sort_inplace(Arr);
+
+    EXPECT_TRUE(equal(Seq, Arr));
+
+    // Custom comparison object.
+    static constexpr std::array<IntT, std::size(Seq)> SortedG =
+        ce_sort(Seq, Greater);
+    static const IntT ExpectedG[std::size(Seq)]{8, 7, 6, 5, 4, 3, 2, 1};
+
+    EXPECT_TRUE(equal(ExpectedG, SortedG));
+  }
+
+  // Pre-sorted + Duplicate elements
+  {
+    static constexpr IntT Seq[]{1, 1, 2, 2, 3, 3, 4, 4};
+    static constexpr std::array<IntT, std::size(Seq)> Sorted = ce_sort(Seq);
+
+    EXPECT_TRUE(equal(Seq, Sorted));
+
+    // Run-time sort in-place.
+    std::array<IntT, std::size(Seq)> Arr = to_array(Seq);
+    ce_sort_inplace(Arr);
+
+    EXPECT_TRUE(equal(Seq, Arr));
+
+    // Custom comparison object.
+    static constexpr std::array<IntT, std::size(Seq)> SortedG =
+        ce_sort(Seq, Greater);
+    static const IntT ExpectedG[std::size(Seq)]{4, 4, 3, 3, 2, 2, 1, 1};
+
+    EXPECT_TRUE(equal(ExpectedG, SortedG));
+  }
+
+  // Pre-sorted (enum)
+  {
+    constexpr Number Seq[]{Number::One, Number::Two, Number::Three,
+                           Number::Four};
+    static constexpr std::array<Number, std::size(Seq)> Sorted = ce_sort(Seq);
+
+    EXPECT_TRUE(equal(Seq, Sorted));
+
+    // Run-time sort in-place.
+    std::array<Number, std::size(Seq)> Arr = to_array(Seq);
+    ce_sort_inplace(Arr);
+
+    EXPECT_TRUE(equal(Seq, Arr));
+
+    // Custom comparison object.
+    static constexpr std::array<Number, std::size(Seq)> SortedG =
+        ce_sort(Seq, Greater);
+    static const Number ExpectedG[std::size(Seq)]{Number::Four, Number::Three,
+                                                  Number::Two, Number::One};
+
+    EXPECT_TRUE(equal(ExpectedG, SortedG));
+  }
+
+  // Pre-sorted (enum) + Duplicate elements
+  {
+    constexpr Number Seq[]{Number::One, Number::One,   Number::Two,
+                           Number::Two, Number::Three, Number::Four,
+                           Number::Four};
+    static constexpr std::array<Number, std::size(Seq)> Sorted = ce_sort(Seq);
+
+    EXPECT_TRUE(equal(Seq, Sorted));
+
+    // Run-time sort in-place.
+    std::array<Number, std::size(Seq)> Arr = to_array(Seq);
+    ce_sort_inplace(Arr);
+
+    EXPECT_TRUE(equal(Seq, Arr));
+
+    // Custom comparison object.
+    static constexpr std::array<Number, std::size(Seq)> SortedG =
+        ce_sort(Seq, Greater);
+    static const Number ExpectedG[std::size(Seq)]{
+        Number::Four, Number::Four, Number::Three, Number::Two,
+        Number::Two,  Number::One,  Number::One};
+
+    EXPECT_TRUE(equal(ExpectedG, SortedG));
+  }
+
+  // Scrambled
+  {
+    static constexpr IntT Seq[]{8, 4, 1, 6, 2, 3, 7, 5};
+
+    static constexpr std::array<IntT, std::size(Seq)> Sorted = ce_sort(Seq);
+    static const IntT Expected[std::size(Seq)]{1, 2, 3, 4, 5, 6, 7, 8};
+
+    EXPECT_TRUE(equal(Expected, Sorted));
+
+    // Run-time sort in-place.
+    std::array<IntT, std::size(Seq)> Arr = to_array(Seq);
+    ce_sort_inplace(Arr);
+
+    EXPECT_TRUE(equal(Expected, Arr));
+
+    // Custom comparison object.
+    static constexpr std::array<IntT, std::size(Seq)> SortedG =
+        ce_sort(Seq, Greater);
+    static const IntT ExpectedG[std::size(Seq)]{8, 7, 6, 5, 4, 3, 2, 1};
+
+    EXPECT_TRUE(equal(ExpectedG, SortedG));
+  }
+
+  // Scrambled + Duplicate elements
+  {
+    static constexpr IntT Seq[]{2, 4, 1, 3, 1, 3, 4, 2};
+    static constexpr std::array<IntT, std::size(Seq)> Sorted = ce_sort(Seq);
+    static constexpr IntT Expected[]{1, 1, 2, 2, 3, 3, 4, 4};
+
+    EXPECT_TRUE(equal(Expected, Sorted));
+
+    // Run-time sort in-place.
+    std::array<IntT, std::size(Seq)> Arr = to_array(Seq);
+    ce_sort_inplace(Arr);
+
+    EXPECT_TRUE(equal(Expected, Arr));
+
+    // Custom comparison object.
+    static constexpr std::array<IntT, std::size(Seq)> SortedG =
+        ce_sort(Seq, Greater);
+    static const IntT ExpectedG[std::size(Seq)]{4, 4, 3, 3, 2, 2, 1, 1};
+
+    EXPECT_TRUE(equal(ExpectedG, SortedG));
+  }
+
+  // Scrambled (enum)
+  {
+    static constexpr Number Seq[]{Number::Four, Number::One, Number::Three,
+                                  Number::Two};
+    static constexpr std::array<Number, std::size(Seq)> Sorted = ce_sort(Seq);
+    static const Number Expected[std::size(Seq)]{Number::One, Number::Two,
+                                                 Number::Three, Number::Four};
+
+    EXPECT_TRUE(equal(Expected, Sorted));
+
+    // Run-time sort in-place.
+    std::array<Number, std::size(Seq)> Arr = to_array(Seq);
+    ce_sort_inplace(Arr);
+
+    EXPECT_TRUE(equal(Expected, Arr));
+
+    // Custom comparison object.
+    static constexpr std::array<Number, std::size(Seq)> SortedG =
+        ce_sort(Seq, Greater);
+    static const Number ExpectedG[std::size(Seq)]{Number::Four, Number::Three,
+                                                  Number::Two, Number::One};
+
+    EXPECT_TRUE(equal(ExpectedG, SortedG));
+  }
+
+  // Scrambled (enum) + Duplicate elements
+  {
+    static constexpr Number Seq[]{Number::One, Number::Four, Number::One,
+                                  Number::Two, Number::Four, Number::Three,
+                                  Number::Two};
+    static constexpr std::array<Number, std::size(Seq)> Sorted = ce_sort(Seq);
+    static const Number Expected[std::size(Seq)]{
+        Number::One,   Number::One,  Number::Two, Number::Two,
+        Number::Three, Number::Four, Number::Four};
+
+    EXPECT_TRUE(equal(Expected, Sorted));
+
+    // Run-time sort in-place.
+    std::array<Number, std::size(Seq)> Arr = to_array(Seq);
+    ce_sort_inplace(Arr);
+
+    EXPECT_TRUE(equal(Expected, Arr));
+
+    // Custom comparison object.
+    static constexpr std::array<Number, std::size(Seq)> SortedG =
+        ce_sort(Seq, Greater);
+    static const Number ExpectedG[std::size(Seq)]{
+        Number::Four, Number::Four, Number::Three, Number::Two,
+        Number::Two,  Number::One,  Number::One};
+
+    EXPECT_TRUE(equal(ExpectedG, SortedG));
+  }
+
+  // Negative values
+  if constexpr (std::is_signed_v<IntT>) {
+    static constexpr IntT Seq[]{-2, 4, 1, -3, -1, 3, -4, 2};
+    static constexpr std::array<IntT, std::size(Seq)> Sorted = ce_sort(Seq);
+    static constexpr IntT Expected[]{-4, -3, -2, -1, 1, 2, 3, 4};
+
+    EXPECT_TRUE(equal(Expected, Sorted));
+
+    // Run-time sort in-place.
+    std::array<IntT, std::size(Seq)> Arr = to_array(Seq);
+    ce_sort_inplace(Arr);
+
+    EXPECT_TRUE(equal(Expected, Arr));
+
+    // Custom comparison object.
+    static constexpr std::array<IntT, std::size(Seq)> SortedG =
+        ce_sort(Seq, Greater);
+    static const IntT ExpectedG[std::size(Seq)]{4, 3, 2, 1, -1, -2, -3, -4};
+
+    EXPECT_TRUE(equal(ExpectedG, SortedG));
+  }
+}
+
+TYPED_TEST(ConstexprUtilsTest, QuickSortArrays) {
+  EXPOSE_TYPE(IntT);
+
+  // Quick Sort engages when the number of elements is >= 20
+
+  // Pre-sorted
+  {
+    static constexpr IntT Seq[]{1,  2,  3,  4,  5,  6,  7,  8,  9,  10,
+                                11, 12, 13, 14, 15, 16, 17, 18, 19, 20};
+
+    static constexpr std::array<IntT, std::size(Seq)> Sorted = ce_sort(Seq);
+    static const IntT Expected[std::size(Seq)]{
+        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20};
+
+    EXPECT_TRUE(equal(Expected, Sorted));
+
+    // Run-time sort in-place.
+    std::array<IntT, std::size(Seq)> Arr = to_array(Seq);
+    ce_sort_inplace(Arr);
+
+    EXPECT_TRUE(equal(Expected, Arr));
+
+    // Custom comparison object.
+    static constexpr std::array<IntT, std::size(Seq)> SortedG =
+        ce_sort(Seq, Greater);
+    static const IntT ExpectedG[std::size(Seq)]{
+        20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
+
+    EXPECT_TRUE(equal(ExpectedG, SortedG));
+  }
+
+  // Pre-sorted + Duplicate elements
+  {
+    static constexpr IntT Seq[]{1, 1, 2, 2, 3, 3, 4, 4, 5,  5,
+                                6, 6, 7, 7, 8, 8, 9, 9, 10, 10};
+
+    static constexpr std::array<IntT, std::size(Seq)> Sorted = ce_sort(Seq);
+    static const IntT Expected[std::size(Seq)]{1, 1, 2, 2, 3, 3, 4, 4, 5,  5,
+                                               6, 6, 7, 7, 8, 8, 9, 9, 10, 10};
+
+    EXPECT_TRUE(equal(Expected, Sorted));
+
+    // Run-time sort in-place.
+    std::array<IntT, std::size(Seq)> Arr = to_array(Seq);
+    ce_sort_inplace(Arr);
+
+    EXPECT_TRUE(equal(Expected, Arr));
+
+    // Custom comparison object.
+    static constexpr std::array<IntT, std::size(Seq)> SortedG =
+        ce_sort(Seq, Greater);
+    static const IntT ExpectedG[std::size(Seq)]{10, 10, 9, 9, 8, 8, 7, 7, 6, 6,
+                                                5,  5,  4, 4, 3, 3, 2, 2, 1, 1};
+
+    EXPECT_TRUE(equal(ExpectedG, SortedG));
+  }
+
+  // Scrambled
+  {
+    static constexpr IntT Seq[]{1,  20, 3,  18, 5,  16, 7,  14, 9,  12,
+                                11, 10, 13, 8,  15, 6,  17, 4,  19, 2};
+
+    static constexpr std::array<IntT, std::size(Seq)> Sorted = ce_sort(Seq);
+    static const IntT Expected[std::size(Seq)]{
+        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20};
+
+    EXPECT_TRUE(equal(Expected, Sorted));
+
+    // Run-time sort in-place.
+    std::array<IntT, std::size(Seq)> Arr = to_array(Seq);
+    ce_sort_inplace(Arr);
+
+    EXPECT_TRUE(equal(Expected, Arr));
+
+    // Custom comparison object.
+    static constexpr std::array<IntT, std::size(Seq)> SortedG =
+        ce_sort(Seq, Greater);
+    static const IntT ExpectedG[std::size(Seq)]{
+        20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
+
+    EXPECT_TRUE(equal(ExpectedG, SortedG));
+  }
+
+  // Scrambled + Duplicate elements
+  {
+    static constexpr IntT Seq[]{1, 10, 2, 9, 3, 8, 4, 7, 5,  6,
+                                6, 5,  7, 4, 8, 3, 9, 2, 10, 1};
+
+    static constexpr std::array<IntT, std::size(Seq)> Sorted = ce_sort(Seq);
+    static const IntT Expected[std::size(Seq)]{1, 1, 2, 2, 3, 3, 4, 4, 5,  5,
+                                               6, 6, 7, 7, 8, 8, 9, 9, 10, 10};
+
+    EXPECT_TRUE(equal(Expected, Sorted));
+
+    // Run-time sort in-place.
+    std::array<IntT, std::size(Seq)> Arr = to_array(Seq);
+    ce_sort_inplace(Arr);
+
+    EXPECT_TRUE(equal(Expected, Arr));
+
+    // Custom comparison object.
+    static constexpr std::array<IntT, std::size(Seq)> SortedG =
+        ce_sort(Seq, Greater);
+    static const IntT ExpectedG[std::size(Seq)]{10, 10, 9, 9, 8, 8, 7, 7, 6, 6,
+                                                5,  5,  4, 4, 3, 3, 2, 2, 1, 1};
+
+    EXPECT_TRUE(equal(ExpectedG, SortedG));
+  }
+}
\ No newline at end of file
diff --git a/llvm/unittests/ADT/MetaSetTest.cpp b/llvm/unittests/ADT/MetaSetTest.cpp
new file mode 100644
index 0000000000000..fcce5728f4562
--- /dev/null
+++ b/llvm/unittests/ADT/MetaSetTest.cpp
@@ -0,0 +1,1267 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains testing for the MetaSet infrastructure.
+///
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/MetaSet.h"
+#include "llvm/ADT/ConstexprUtils.h"
+
+#include "gtest/gtest.h"
+#include <limits>
+#include <type_traits>
+#include <utility>
+
+using namespace llvm;
+
+namespace {
+
+template <typename Iter0, typename Iter1>
+constexpr bool ce_equal( // NOLINT (readability-identifier-naming)
+    Iter0 B0, Iter0 E0, Iter1 B1, Iter1 E1) {
+  for (; B0 != E0; ++B0, ++B1) {
+    if (B1 == E1 || *B0 != *B1)
+      return false;
+  }
+  return B1 == E1;
+}
+
+template <typename R1, typename R2>
+constexpr bool ce_equal( // NOLINT (readability-identifier-naming)
+    R1 const &L, R2 const &R) {
+  return ce_equal(adl_begin(L), adl_end(L), adl_begin(R), adl_end(R));
+}
+
+template <typename Range1, typename Range2>
+constexpr bool ce_requal( // NOLINT (readability-identifier-naming)
+    Range1 const &L, Range2 const &R) {
+  return ce_equal(adl_rbegin(L), adl_rend(L), adl_rbegin(R), adl_rend(R));
+}
+
+template <typename PosT, auto... Values>
+inline constexpr size_t ComputeNumWords =
+    MetaBitsetNumWordsDetailed<PosT, ce_min<PosT>(Values...),
+                               ce_max<PosT>(Values...)>;
+
+enum class Kind { Bitset, Sequence, SparseBitset };
+template <typename T> struct BuildBitset {
+  using PosT = T;
+
+  static constexpr bool IsBitset = true;
+  static constexpr bool IsSparseBitset = false;
+
+  template <auto... Vals> static auto makeSet() {
+    return MakeMetaBitset<PosT, Vals...>();
+  }
+
+  template <Kind K, typename QueryT, typename ExpectedT> struct CheckType {
+    static_assert(K != Kind::Bitset || std::is_same_v<QueryT, ExpectedT>);
+  };
+};
+
+template <typename T> struct BuildBitsetSorted {
+  using PosT = T;
+
+  static constexpr bool IsBitset = true;
+  static constexpr bool IsSparseBitset = false;
+
+  template <auto... Vals> static auto makeSet() {
+    using Sorted = SortLiterals<PosT, PosT(Vals)...>;
+    return MakeMetaBitsetFromSortedSequence<Sorted>();
+  }
+
+  template <Kind K, typename QueryT, typename ExpectedT> struct CheckType {
+    static_assert(K != Kind::Bitset || std::is_same_v<QueryT, ExpectedT>);
+  };
+};
+
+template <typename T> struct BuildSequenceSet {
+  using PosT = T;
+
+  static constexpr bool IsBitset = false;
+  static constexpr bool IsSparseBitset = false;
+
+  template <auto... Vals> static auto makeSet() {
+    return MakeMetaSequenceSet<PosT, Vals...>();
+  }
+
+  template <Kind K, typename QueryT, typename ExpectedT> struct CheckType {
+    static_assert(K != Kind::Sequence || std::is_same_v<QueryT, ExpectedT>);
+  };
+};
+
+template <typename T> struct BuildSparseBitset {
+  using PosT = T;
+
+  static constexpr bool IsBitset = false;
+  static constexpr bool IsSparseBitset = true;
+
+  template <auto... Vals> static auto makeSet() {
+    return MakeMetaSparseBitset<PosT, 512, Vals...>();
+  }
+
+  template <Kind K, typename QueryT, typename ExpectedT> struct CheckType {
+    static_assert(K != Kind::SparseBitset || std::is_same_v<QueryT, ExpectedT>);
+  };
+};
+
+template <typename BuilderT, auto... Vals>
+using MakeMetaSet = decltype(BuilderT::template makeSet<Vals...>());
+
+template <typename T> class MetaSetTest : public testing::Test {
+protected:
+  using Builder = T;
+  using PosT = typename T::PosT;
+  using ThisT = MetaSetTest;
+
+  enum class E : PosT { A, B, C, D, E };
+
+  static constexpr int64_t min() { return std::numeric_limits<PosT>::min(); }
+  static constexpr uint64_t max() { return std::numeric_limits<PosT>::max(); }
+};
+
+#define EXPOSE_TYPE(type)                                                      \
+  using type = typename remove_cvref_t<decltype(*this)>::type
+
+// clang-format off
+using TestTypes =
+    ::testing::Types<
+      // MetaBitset
+      BuildBitset<signed char>,
+      BuildBitset<short>,
+      BuildBitset<int>,
+      BuildBitset<long>,
+      BuildBitset<long long>,
+      BuildBitset<unsigned char>,
+      BuildBitset<unsigned short>,
+      BuildBitset<unsigned>,
+      BuildBitset<unsigned long>,
+      BuildBitset<unsigned long long>,
+      // MetaBitset (sorted first)
+      BuildBitsetSorted<signed char>,
+      BuildBitsetSorted<short>,
+      BuildBitsetSorted<int>,
+      BuildBitsetSorted<long>,
+      BuildBitsetSorted<long long>,
+      BuildBitsetSorted<unsigned char>,
+      BuildBitsetSorted<unsigned short>,
+      BuildBitsetSorted<unsigned>,
+      BuildBitsetSorted<unsigned long>,
+      BuildBitsetSorted<unsigned long long>,
+      // MetaSequenceSet
+      BuildSequenceSet<signed char>,
+      BuildSequenceSet<short>,
+      BuildSequenceSet<int>,
+      BuildSequenceSet<long>,
+      BuildSequenceSet<long long>,
+      BuildSequenceSet<unsigned char>,
+      BuildSequenceSet<unsigned short>,
+      BuildSequenceSet<unsigned>,
+      BuildSequenceSet<unsigned long>,
+      BuildSequenceSet<unsigned long long>,
+      // MetaSparseBitset
+      BuildSparseBitset<signed char>,
+      BuildSparseBitset<short>,
+      BuildSparseBitset<int>,
+      BuildSparseBitset<long>,
+      BuildSparseBitset<long long>,
+      BuildSparseBitset<unsigned char>,
+      BuildSparseBitset<unsigned short>,
+      BuildSparseBitset<unsigned>,
+      BuildSparseBitset<unsigned long>,
+      BuildSparseBitset<unsigned long long>
+    >;
+// clang-format on
+
+TYPED_TEST_SUITE(MetaSetTest, TestTypes, );
+
+TYPED_TEST(MetaSetTest, BitsetBuilderCompiletime) {
+  EXPOSE_TYPE(PosT);
+  EXPOSE_TYPE(Builder);
+
+  if constexpr (!Builder::IsBitset)
+    return;
+
+  static constexpr PosT MIN = std::numeric_limits<PosT>::min();
+  static constexpr PosT MAX = std::numeric_limits<PosT>::max();
+
+  using Empty = MakeMetaBitset<PosT>;
+  static_assert(MetaBitsetNumWords<Empty> == 0);
+  static_assert(std::is_same_v<Empty, MetaBitset<PosT, 0>>);
+
+  using ZeroOffset = MakeMetaBitset<PosT, 0, 1, 2, 3>;
+  using ZeroOffsetD = MakeMetaBitsetDetailed<PosT, 0, 3, 0, 1, 2, 3>;
+  static_assert(MetaBitsetNumWords<ZeroOffset> == 1);
+  static_assert(ComputeNumWords<PosT, 0, 1, 2, 3> == 1);
+  static_assert(std::is_same_v<ZeroOffset, ZeroOffsetD>);
+  static_assert(std::is_same_v<ZeroOffset, MetaBitset<PosT, 0, 0xf>>);
+
+  using PositiveOffset = MakeMetaBitset<PosT, 6, 5, 7>;
+  using PositiveOffsetD = MakeMetaBitsetDetailed<PosT, 5, 7, 6, 5, 7>;
+  static_assert(MetaBitsetNumWords<PositiveOffset> == 1);
+  static_assert(ComputeNumWords<PosT, 6, 5, 7> == 1);
+  static_assert(std::is_same_v<PositiveOffset, PositiveOffsetD>);
+  static_assert(std::is_same_v<PositiveOffset, MetaBitset<PosT, 5, 0x7>>);
+
+  if constexpr (std::is_signed_v<PosT>) {
+    using NegativeOffset = MakeMetaBitset<PosT, 0, -1, -2, -3, 1>;
+    using NegativeOffsetD =
+        MakeMetaBitsetDetailed<PosT, -3, 1, 0, -1, -2, -3, 1>;
+    static_assert(MetaBitsetNumWords<NegativeOffset> == 1);
+    static_assert(ComputeNumWords<PosT, -3, 0> == 1);
+    static_assert(std::is_same_v<NegativeOffset, NegativeOffsetD>);
+    static_assert(std::is_same_v<NegativeOffset, MetaBitset<PosT, -3, 0x1f>>);
+
+    using NegativeOffset2 = MakeMetaBitset<PosT, -128, 127>;
+    using NegativeOffset2D = MakeMetaBitset<PosT, -128, 127, -128, 127>;
+    static_assert(MetaBitsetNumWords<NegativeOffset2> == 4);
+    static_assert(ComputeNumWords<PosT, -128, 127> == 4);
+    static_assert(std::is_same_v<NegativeOffset2, NegativeOffset2D>);
+    static_assert(
+        std::is_same_v<NegativeOffset2,
+                       MetaBitset<PosT, -128, 1ULL, 0ULL, 0ULL, (1ULL << 63)>>);
+  }
+
+  using DuplicateElements = MakeMetaBitset<PosT, 0, 1, 1, 2, 2, 3, 3>;
+  using DuplicateElementsD =
+      MakeMetaBitsetDetailed<PosT, 0, 3, 0, 1, 1, 2, 2, 3, 3>;
+  static_assert(MetaBitsetNumWords<DuplicateElements> == 1);
+  static_assert(ComputeNumWords<PosT, 0, 3> == 1);
+  static_assert(std::is_same_v<DuplicateElements, DuplicateElementsD>);
+  static_assert(std::is_same_v<DuplicateElements, MetaBitset<PosT, 0, 0xf>>);
+
+  using WordBoundary1 = MakeMetaBitset<PosT, 0, 63>;
+  using WordBoundary1D = MakeMetaBitsetDetailed<PosT, 0, 63, 0, 63>;
+  static_assert(ComputeNumWords<PosT, 0, 63> == 1);
+  static_assert(MetaBitsetNumWords<WordBoundary1> == 1);
+  static_assert(std::is_same_v<WordBoundary1, WordBoundary1D>);
+  static_assert(std::is_same_v<WordBoundary1,
+                               MetaBitset<PosT, 0, (1ULL << 0 | 1ULL << 63)>>);
+
+  using WordBoundary2 = MakeMetaBitset<PosT, 0, 64>;
+  using WordBoundary2D = MakeMetaBitsetDetailed<PosT, 0, 64, 0, 64>;
+  static_assert(ComputeNumWords<PosT, 0, 64> == 2);
+  static_assert(MetaBitsetNumWords<WordBoundary2> == 2);
+  static_assert(std::is_same_v<WordBoundary2, WordBoundary2D>);
+  static_assert(std::is_same_v<WordBoundary2, MetaBitset<PosT, 0, 1ULL, 1ULL>>);
+
+  using WordBoundary3 = MakeMetaBitset<PosT, 0, 127>;
+  using WordBoundary3D = MakeMetaBitset<PosT, 0, 127>;
+  static_assert(ComputeNumWords<PosT, 0, 127> == 2);
+  static_assert(MetaBitsetNumWords<WordBoundary3> == 2);
+  static_assert(std::is_same_v<WordBoundary3, WordBoundary3D>);
+  static_assert(
+      std::is_same_v<WordBoundary3, MetaBitset<PosT, 0, 1ULL, (1ULL << 63)>>);
+
+  if constexpr (MAX > 128) {
+    using WordBoundary4 = MakeMetaBitset<PosT, 0, 128>;
+    using WordBoundary4D = MakeMetaBitsetDetailed<PosT, 0, 128, 0, 128>;
+    static_assert(ComputeNumWords<PosT, 0, 128> == 3);
+    static_assert(MetaBitsetNumWords<WordBoundary4> == 3);
+    static_assert(std::is_same_v<WordBoundary4, WordBoundary4D>);
+    static_assert(
+        std::is_same_v<WordBoundary4, MetaBitset<PosT, 0, 1ULL, 0ULL, 1ULL>>);
+  }
+
+  if constexpr (MAX > 511) {
+    using BigGap = MakeMetaBitset<PosT, 0, 511>;
+    using BigGapD = MakeMetaBitsetDetailed<PosT, 0, 511, 0, 511>;
+    static_assert(ComputeNumWords<PosT, 0, 511> == 8);
+    static_assert(MetaBitsetNumWords<BigGap> == 8);
+    static_assert(std::is_same_v<BigGap, BigGapD>);
+    using BigGapExp = MetaBitset<PosT, 0, 1ULL, 0ULL, 0ULL, 0ULL, 0ULL, 0ULL,
+                                 0ULL, (1ULL << 63)>;
+    static_assert(std::is_same_v<BigGap, BigGapExp>);
+  } else {
+    // 8-bit integer types.
+    using BigGap = MakeMetaBitset<PosT, 0, MAX>;
+    using BigGapD = MakeMetaBitsetDetailed<PosT, 0, MAX, 0, MAX>;
+    static_assert(std::is_same_v<BigGap, BigGapD>);
+    if constexpr (std::is_unsigned_v<PosT>) {
+      static_assert(ComputeNumWords<PosT, 0, MAX> == 4);
+      static_assert(MetaBitsetNumWords<BigGap> == 4);
+      using BigGapExp = MetaBitset<PosT, 0, 1ULL, 0ULL, 0ULL, (1ULL << 63)>;
+      static_assert(std::is_same_v<BigGap, BigGapExp>);
+    } else {
+      static_assert(ComputeNumWords<PosT, 0, MAX> == 2);
+      static_assert(MetaBitsetNumWords<BigGap> == 2);
+      using BigGapExp = MetaBitset<PosT, 0, 1ULL, (1ULL << 63)>;
+      static_assert(std::is_same_v<BigGap, BigGapExp>);
+    }
+  }
+
+  using LargeOffset = MakeMetaBitset<PosT, MAX - 63, MAX>;
+  using LargeOffsetD = MakeMetaBitset<PosT, MAX - 63, MAX, (MAX - 63), MAX>;
+  static_assert(ComputeNumWords<PosT, MAX - 63, MAX> == 1);
+  static_assert(MetaBitsetNumWords<LargeOffset> == 1);
+  static_assert(std::is_same_v<LargeOffset, LargeOffsetD>);
+  static_assert(
+      std::is_same_v<LargeOffset,
+                     MetaBitset<PosT, MAX - 63, (1ULL << 0 | 1ULL << 63)>>);
+
+  if constexpr (std::is_signed_v<PosT>) {
+    if constexpr (MIN < -36729) {
+      using LargeNegativeOffset = MakeMetaBitset<PosT, -36729, -36729 + 64>;
+      using LargeNegativeOffsetD =
+          MakeMetaBitset<PosT, -36729, -36729 + 64, -36729, -36729 + 64>;
+      static_assert(ComputeNumWords<PosT, -36729, -36729 + 64> == 2);
+      static_assert(MetaBitsetNumWords<LargeNegativeOffset> == 2);
+      static_assert(std::is_same_v<LargeNegativeOffset, LargeNegativeOffsetD>);
+      static_assert(std::is_same_v<LargeNegativeOffset,
+                                   MetaBitset<PosT, -36729, 1ULL, 1ULL>>);
+    }
+  }
+}
+
+template <typename BuilderT> class CheckMetaTypes {
+public:
+  template <typename QueryT, typename ExpectedBitsetT,
+            typename ExpectedSequenceT, typename ExpectedSparseBitsetT>
+  static constexpr void check() {
+    typename BuilderT::template CheckType<Kind::Bitset, QueryT,
+                                          ExpectedBitsetT>();
+
+    typename BuilderT::template CheckType<Kind::Sequence, QueryT,
+                                          ExpectedSequenceT>();
+
+    typename BuilderT::template CheckType<Kind::SparseBitset, QueryT,
+                                          ExpectedSparseBitsetT>();
+  }
+};
+
+TYPED_TEST(MetaSetTest, BuiltTypeVerify) {
+  EXPOSE_TYPE(PosT);
+  // EXPOSE_TYPE(E);
+  EXPOSE_TYPE(Builder);
+  using ThisT = std::remove_cv_t<std::remove_reference_t<decltype(*this)>>;
+
+  using Checker = CheckMetaTypes<Builder>;
+
+  // All Set types.
+  {
+    // Sorted values, small range
+    {
+      using BS = MakeMetaSet<Builder, PosT(1), PosT(2), PosT(3), PosT(4)>;
+
+      using ExpectedBitset = MetaBitset<PosT, 1, 0xf>;
+
+      using ExpectedSequence =
+          MetaSequenceSet<std::integer_sequence<PosT, 1, 2, 3, 4>>;
+
+      using ExpectedSparseBitset = MetaSparseBitset<ExpectedBitset>;
+
+      Checker::template check<BS, ExpectedBitset, ExpectedSequence,
+                              ExpectedSparseBitset>();
+    }
+
+    // Reverse sorted values, small range.
+    {
+      using BS = MakeMetaSet<Builder, PosT(4), PosT(3), PosT(2), PosT(1)>;
+
+      using ExpectedBitset = MetaBitset<PosT, 1, 0xf>;
+
+      using ExpectedSequence =
+          MetaSequenceSet<std::integer_sequence<PosT, 4, 3, 2, 1>>;
+
+      using ExpectedSparseBitset = MetaSparseBitset<ExpectedBitset>;
+
+      Checker::template check<BS, ExpectedBitset, ExpectedSequence,
+                              ExpectedSparseBitset>();
+    }
+
+    // Duplicate values, small range.
+    {
+      using BS = MakeMetaSet<Builder, 1, 3, 5, 7, 9, 3, 5, 7>;
+
+      using ExpectedBitset = MetaBitset<PosT, 1, 0x155>;
+
+      using ExpectedSequence =
+          MetaSequenceSet<std::integer_sequence<PosT, 1, 3, 5, 7, 9, 3, 5, 7>>;
+
+      using ExpectedSparseBitset = MetaSparseBitset<ExpectedBitset>;
+
+      Checker::template check<BS, ExpectedBitset, ExpectedSequence,
+                              ExpectedSparseBitset>();
+    }
+
+    // Large offset from 0.
+    {
+      using BS = MakeMetaSet<Builder, 101, 103, 105, 107, 109>;
+
+      using ExpectedBitset = MetaBitset<PosT, 101, 0x155>;
+
+      using ExpectedSequence =
+          MetaSequenceSet<std::integer_sequence<PosT, 101, 103, 105, 107, 109>>;
+
+      using ExpectedSparseBitset = MetaSparseBitset<ExpectedBitset>;
+
+      Checker::template check<BS, ExpectedBitset, ExpectedSequence,
+                              ExpectedSparseBitset>();
+    }
+
+    // Larger offset from 0.
+    if constexpr (ThisT::max() > 10009u) {
+      using BS = MakeMetaSet<Builder, 10001, 10003, 10005, 10007, 10009>;
+
+      using ExpectedBitset = MetaBitset<PosT, 10001, 0x155>;
+
+      using ExpectedSequence = MetaSequenceSet<
+          std::integer_sequence<PosT, 10001, 10003, 10005, 10007, 10009>>;
+
+      using ExpectedSparseBitset = MetaSparseBitset<ExpectedBitset>;
+
+      Checker::template check<BS, ExpectedBitset, ExpectedSequence,
+                              ExpectedSparseBitset>();
+    }
+
+    // Negative offset.
+    if constexpr (ThisT::min() < -1) {
+      using BS = MakeMetaSet<Builder, -101, -103, -105, -107, -109>;
+
+      using ExpectedBitset = MetaBitset<PosT, -109, 0x155>;
+
+      using ExpectedSequence = MetaSequenceSet<
+          std::integer_sequence<PosT, -101, -103, -105, -107, -109>>;
+
+      using ExpectedSparseBitset = MetaSparseBitset<ExpectedBitset>;
+
+      Checker::template check<BS, ExpectedBitset, ExpectedSequence,
+                              ExpectedSparseBitset>();
+    }
+  }
+
+  // MetaSparseBitset + MetaSequenceSet validation.
+  if constexpr (!Builder::IsBitset) {
+    using ExpectedBitset = void;
+
+    // Single cluster; no singletons
+    {
+      using Fives = MakeMetaSet<Builder, 5, 15, 25, 35, 45, 55, 65, 75, 85, 95,
+                                10, 20, 30, 40, 50, 60, 70, 80, 90, 100>;
+
+      using ExpectedSequence = MetaSequenceSet<
+          std::integer_sequence<PosT, 5, 15, 25, 35, 45, 55, 65, 75, 85, 95, 10,
+                                20, 30, 40, 50, 60, 70, 80, 90, 100>>;
+      using ExpectedSparseBitset =
+          MetaSparseBitset<MetaBitset<PosT, 5, 0x1084210842108421, 0x84210842>>;
+
+      Checker::template check<Fives, ExpectedBitset, ExpectedSequence,
+                              ExpectedSparseBitset>();
+    }
+
+    // Multiple clusters; no singletons.
+    if constexpr (ThisT::max() >= 10008u) {
+      using Clusters = MakeMetaSet<Builder, 10000, 10002, 10004, 10006, 10008,
+                                   1000, 1002, 1004, 1006, 1008>;
+
+      using ExpectedSequence = MetaSequenceSet<
+          std::integer_sequence<PosT, 10000, 10002, 10004, 10006, 10008, 1000,
+                                1002, 1004, 1006, 1008>>;
+
+      using ExpectedSparseBitset =
+          MetaSparseBitset<MetaBitset<PosT, 1000, 0x155>,
+                           MetaBitset<PosT, 10000, 0x155>>;
+
+      Checker::template check<Clusters, ExpectedBitset, ExpectedSequence,
+                              ExpectedSparseBitset>();
+    }
+
+    // Multiple clusters; with singletons.
+    if constexpr (ThisT::max() >= 10008u) {
+      using ClustersAndSingletons =
+          MakeMetaSet<Builder, 10000, 10002, 10004, 10006, 10008, 1000, 1002,
+                      1004, 1006, 1008, 8000, 5000>;
+
+      using ExpectedSequence = MetaSequenceSet<
+          std::integer_sequence<PosT, 10000, 10002, 10004, 10006, 10008, 1000,
+                                1002, 1004, 1006, 1008, 8000, 5000>>;
+      using ExpectedSparseBitset =
+          MetaSparseBitset<MetaBitset<PosT, 1000, 0x155>,
+                           MetaBitset<PosT, 10000, 0x155>,
+                           MakeMetaSequenceSet<PosT, 5000, 8000>>;
+
+      Checker::template check<ClustersAndSingletons, ExpectedBitset,
+                              ExpectedSequence, ExpectedSparseBitset>();
+    }
+
+    // No clusters; all singletons.
+    if constexpr (ThisT::max() >= 90000u) {
+      using Singletons = MakeMetaSet<Builder, 10000, 5000, 8000, 20000, 90000>;
+
+      using ExpectedSequence = MetaSequenceSet<
+          std::integer_sequence<PosT, 10000, 5000, 8000, 20000, 90000>>;
+
+      using ExpectedSparseBitset = MetaSparseBitset<
+          MakeMetaSequenceSet<PosT, 5000, 8000, 10000, 20000, 90000>>;
+
+      Checker::template check<Singletons, ExpectedBitset, ExpectedSequence,
+                              ExpectedSparseBitset>();
+    }
+  }
+
+  // MetaSparseBitset validation (varied word sizes)
+  if constexpr (Builder::IsSparseBitset) {
+    // Single cluster; no singletons
+    {
+      using Fives =
+          MakeMetaSparseBitset<PosT, 128, 5, 15, 25, 35, 45, 55, 65, 75, 85, 95,
+                               10, 20, 30, 40, 50, 60, 70, 80, 90, 100>;
+
+      using Expected =
+          MetaSparseBitset<MetaBitset<PosT, 5, 0x1084210842108421, 0x84210842>>;
+
+      static_assert(std::is_same_v<Fives, Expected>);
+    }
+
+    // Multiple clusters; no singletons.
+    if constexpr (ThisT::max() >= 10008u) {
+      using Clusters =
+          MakeMetaSparseBitset<PosT, 256, 10000, 10002, 10004, 10006, 10008,
+                               1000, 1002, 1004, 1006, 1008>;
+
+      using Expected = MetaSparseBitset<MetaBitset<PosT, 1000, 0x155>,
+                                        MetaBitset<PosT, 10000, 0x155>>;
+
+      static_assert(std::is_same_v<Clusters, Expected>);
+    }
+
+    // Multiple clusters; with singletons.
+    if constexpr (ThisT::max() >= 10008u) {
+      using ClustersAndSingletons =
+          MakeMetaSparseBitset<PosT, 64, 10000, 10002, 10004, 10006, 10008,
+                               1000, 1002, 1004, 1006, 1008, 8000, 5000>;
+
+      using Expected = MetaSparseBitset<MetaBitset<PosT, 1000, 0x155>,
+                                        MetaBitset<PosT, 10000, 0x155>,
+                                        MakeMetaSequenceSet<PosT, 5000, 8000>>;
+
+      static_assert(std::is_same_v<ClustersAndSingletons, Expected>);
+    }
+
+    // No clusters; all singletons.
+    if constexpr (ThisT::max() >= 90000u) {
+      using Singletons =
+          MakeMetaSparseBitset<PosT, 64, 10000, 5000, 8000, 20000, 90000>;
+
+      using Expected = MetaSparseBitset<
+          MakeMetaSequenceSet<PosT, 5000, 8000, 10000, 20000, 90000>>;
+
+      static_assert(std::is_same_v<Singletons, Expected>);
+    }
+
+    // Word size too small resulting in singletons
+    if constexpr (ThisT::max() > 192u) {
+      using WordSizeTooSmall = MakeMetaSparseBitset<PosT, 64, 5, 4, 3, 2, 1, 0,
+                                                    64, 65, 130, 129, 128, 192>;
+
+      using Expected = MetaSparseBitset<
+          MetaBitset<PosT, 0, 0x3f>, MetaBitset<PosT, 128, 0x7>,
+          MetaSequenceSet<std::integer_sequence<PosT, 64, 65, 192>>>;
+
+      static_assert(std::is_same_v<WordSizeTooSmall, Expected>);
+    }
+
+    // Larger word size; no singletons
+    if constexpr (ThisT::max() > 192u) {
+      using WordSizeTooSmall = MakeMetaSparseBitset<PosT, 128, 5, 4, 3, 2, 1, 0,
+                                                    64, 65, 130, 129, 128, 192>;
+
+      using Expected = MetaSparseBitset<MetaBitset<PosT, 0, 0x3f, 0x3>,
+                                        MetaBitset<PosT, 128, 0x7, 0x1>>;
+
+      static_assert(std::is_same_v<WordSizeTooSmall, Expected>);
+    }
+
+    // Even larger word size; coalesce MetaBitsets
+    if constexpr (ThisT::max() > 192u) {
+      using WordSizeTooSmall = MakeMetaSparseBitset<PosT, 256, 5, 4, 3, 2, 1, 0,
+                                                    64, 65, 130, 129, 128, 192>;
+
+      using Expected =
+          MetaSparseBitset<MetaBitset<PosT, 0, 0x3f, 0x3, 0x7, 0x1>>;
+
+      static_assert(std::is_same_v<WordSizeTooSmall, Expected>);
+    }
+  }
+}
+
+TYPED_TEST(MetaSetTest, SortedSequence) {
+  EXPOSE_TYPE(PosT);
+  EXPOSE_TYPE(Builder);
+  EXPOSE_TYPE(ThisT);
+
+  {
+    using Empty = MakeMetaSet<Builder>;
+
+    using Expected = std::integer_sequence<PosT>;
+
+    static_assert(
+        std::is_same_v<typename Empty::sorted_sequence_type, Expected>);
+  }
+
+  {
+    using ZeroOffset = MakeMetaSet<Builder, 0, 1, 2, 3>;
+
+    using Expected = std::integer_sequence<PosT, 0, 1, 2, 3>;
+    static_assert(
+        std::is_same_v<typename ZeroOffset::sorted_sequence_type, Expected>);
+  }
+
+  {
+    using PositiveOffset = MakeMetaSet<Builder, 6, 5, 7>;
+
+    using Expected = std::integer_sequence<PosT, 5, 6, 7>;
+    static_assert(std::is_same_v<typename PositiveOffset::sorted_sequence_type,
+                                 Expected>);
+  }
+
+  if constexpr (std::is_signed_v<PosT>) {
+    using NegativeOffset = MakeMetaSet<Builder, 0, -1, -2, -3, 1>;
+
+    using Expected = std::integer_sequence<PosT, -3, -2, -1, 0, 1>;
+    static_assert(std::is_same_v<typename NegativeOffset::sorted_sequence_type,
+                                 Expected>);
+  }
+
+  if constexpr (std::is_signed_v<PosT>) {
+    using NegativeOffset2 = MakeMetaSet<Builder, -128, 127>;
+
+    using Expected = std::integer_sequence<PosT, -128, 127>;
+    static_assert(std::is_same_v<typename NegativeOffset2::sorted_sequence_type,
+                                 Expected>);
+  }
+
+  {
+    using DuplicateElements = MakeMetaSet<Builder, 0, 1, 1, 2, 2, 3, 3>;
+
+    using Expected =
+        std::conditional_t<IsMetaSetWithDuplicateElements<DuplicateElements>,
+                           std::integer_sequence<PosT, 0, 1, 1, 2, 2, 3, 3>,
+                           std::integer_sequence<PosT, 0, 1, 2, 3>>;
+    static_assert(
+        std::is_same_v<typename DuplicateElements::sorted_sequence_type,
+                       Expected>);
+  }
+
+  {
+    using WordBoundary1 = MakeMetaSet<Builder, 0, 63>;
+
+    using Expected = std::integer_sequence<PosT, 0, 63>;
+    static_assert(
+        std::is_same_v<typename WordBoundary1::sorted_sequence_type, Expected>);
+  }
+
+  {
+    using WordBoundary2 = MakeMetaSet<Builder, 0, 64>;
+
+    using Expected = std::integer_sequence<PosT, 0, 64>;
+    static_assert(
+        std::is_same_v<typename WordBoundary2::sorted_sequence_type, Expected>);
+  }
+
+  {
+    using WordBoundary3 = MakeMetaSet<Builder, 0, 127>;
+
+    using Expected = std::integer_sequence<PosT, 0, 127>;
+    static_assert(
+        std::is_same_v<typename WordBoundary3::sorted_sequence_type, Expected>);
+  }
+
+  if constexpr (ThisT::max() > 128) {
+    using WordBoundary4 = MakeMetaSet<Builder, 0, 128>;
+
+    using Expected = std::integer_sequence<PosT, 0, 128>;
+    static_assert(
+        std::is_same_v<typename WordBoundary4::sorted_sequence_type, Expected>);
+  }
+
+  if constexpr (ThisT::max() > 511) {
+    using BigGap = MakeMetaSet<Builder, 0, 511>;
+
+    using Expected = std::integer_sequence<PosT, 0, 511>;
+    static_assert(
+        std::is_same_v<typename BigGap::sorted_sequence_type, Expected>);
+  } else {
+    // 8-bit integer types.
+    using BigGap = MakeMetaSet<Builder, 0, ThisT::max()>;
+
+    using Expected = std::integer_sequence<PosT, 0, ThisT::max()>;
+    static_assert(
+        std::is_same_v<typename BigGap::sorted_sequence_type, Expected>);
+  }
+
+  {
+    using LargeOffset = MakeMetaSet<Builder, ThisT::max() - 63, ThisT::max()>;
+
+    using Expected =
+        std::integer_sequence<PosT, ThisT::max() - 63, ThisT::max()>;
+    static_assert(
+        std::is_same_v<typename LargeOffset::sorted_sequence_type, Expected>);
+  }
+
+  if constexpr (std::is_signed_v<PosT> && ThisT::min() < -36729) {
+    using LargeNegativeOffset = MakeMetaSet<Builder, -36729, -36729 + 64>;
+
+    using Expected = std::integer_sequence<PosT, -36729, -36729 + 64>;
+    static_assert(
+        std::is_same_v<typename LargeNegativeOffset::sorted_sequence_type,
+                       Expected>);
+  }
+}
+
+TYPED_TEST(MetaSetTest, Container) {
+  EXPOSE_TYPE(PosT);
+  EXPOSE_TYPE(Builder);
+  EXPOSE_TYPE(ThisT);
+
+  {
+    using Empty = MakeMetaSet<Builder>;
+
+    MetaSetSortedContainer<Empty> Test;
+    static_assert(Test.size() == 0);
+    static_assert(Test.empty() == true);
+  }
+
+  {
+    using OneElem = MakeMetaSet<Builder, 127>;
+
+    MetaSetSortedContainer<OneElem> Test;
+    static constexpr PosT Expected[]{127};
+
+    static_assert(Test.size() == std::size(Expected));
+    static_assert(Test.empty() == false);
+    static_assert(Test.front() == Expected[0]);
+    static_assert(Test.back() == Expected[std::size(Expected) - 1]);
+    static_assert(Test[0] == Expected[0]);
+
+    static_assert(ce_equal(Test, Expected));
+    static_assert(ce_requal(Test, Expected));
+  }
+
+  {
+    using ZeroOffset = MakeMetaSet<Builder, 0, 1, 2, 3>;
+
+    MetaSetSortedContainer<ZeroOffset> Test;
+    static constexpr PosT Expected[]{0, 1, 2, 3};
+
+    static_assert(Test.size() == std::size(Expected));
+    static_assert(Test.empty() == false);
+    static_assert(Test.front() == Expected[0]);
+    static_assert(Test.back() == Expected[std::size(Expected) - 1]);
+    static_assert(Test[1] == Expected[1]);
+
+    EXPECT_TRUE(ce_equal(Test, Expected));
+    EXPECT_TRUE(ce_requal(Test, Expected));
+  }
+
+  {
+    using PositiveOffset = MakeMetaSet<Builder, 6, 5, 7>;
+
+    MetaSetSortedContainer<PositiveOffset> Test;
+    static constexpr PosT Expected[]{5, 6, 7};
+
+    static_assert(Test.size() == std::size(Expected));
+    static_assert(Test.empty() == false);
+    static_assert(Test.front() == Expected[0]);
+    static_assert(Test.back() == Expected[std::size(Expected) - 1]);
+    static_assert(Test[1] == Expected[1]);
+
+    EXPECT_TRUE(ce_equal(Test, Expected));
+    EXPECT_TRUE(ce_requal(Test, Expected));
+  }
+
+  if constexpr (std::is_signed_v<PosT>) {
+    using NegativeOffset = MakeMetaSet<Builder, 0, -1, -2, -3, 1>;
+
+    MetaSetSortedContainer<NegativeOffset> Test;
+    static constexpr PosT Expected[]{-3, -2, -1, 0, 1};
+
+    static_assert(Test.size() == std::size(Expected));
+    static_assert(Test.empty() == false);
+    static_assert(Test.front() == Expected[0]);
+    static_assert(Test.back() == Expected[std::size(Expected) - 1]);
+    static_assert(Test[1] == Expected[1]);
+
+    EXPECT_TRUE(ce_equal(Test, Expected));
+    EXPECT_TRUE(ce_requal(Test, Expected));
+  }
+
+  if constexpr (std::is_signed_v<PosT>) {
+    using NegativeOffset2 = MakeMetaSet<Builder, -128, 127>;
+
+    MetaSetSortedContainer<NegativeOffset2> Test;
+    static constexpr PosT Expected[]{-128, 127};
+
+    static_assert(Test.size() == std::size(Expected));
+    static_assert(Test.empty() == false);
+    static_assert(Test.front() == Expected[0]);
+    static_assert(Test.back() == Expected[std::size(Expected) - 1]);
+    static_assert(Test[1] == Expected[1]);
+
+    EXPECT_TRUE(ce_equal(Test, Expected));
+    EXPECT_TRUE(ce_requal(Test, Expected));
+  }
+
+  {
+    using DuplicateElements = MakeMetaSet<Builder, 0, 1, 1, 2, 2, 3, 3>;
+
+    MetaSetSortedContainer<DuplicateElements> Test;
+
+    auto MakeExpected = []() constexpr {
+      if constexpr (IsMetaSetWithDuplicateElements<DuplicateElements>)
+        return std::integer_sequence<PosT, 0, 1, 1, 2, 2, 3, 3>();
+      else
+        return std::integer_sequence<PosT, 0, 1, 2, 3>();
+    };
+
+    static constexpr auto Expected = to_array(MakeExpected());
+
+    static_assert(Test.size() == std::size(Expected));
+    static_assert(Test.empty() == false);
+    static_assert(Test.front() == Expected[0]);
+    static_assert(Test.back() == Expected[std::size(Expected) - 1]);
+    static_assert(Test[1] == Expected[1]);
+
+    EXPECT_TRUE(ce_equal(Test, Expected));
+    EXPECT_TRUE(ce_requal(Test, Expected));
+  }
+
+  if constexpr (ThisT::max() > 511) {
+    using BigGap = MakeMetaSet<Builder, 0, 511>;
+
+    MetaSetSortedContainer<BigGap> Test;
+    static constexpr PosT Expected[]{0, 511};
+
+    static_assert(Test.size() == std::size(Expected));
+    static_assert(Test.empty() == false);
+    static_assert(Test.front() == Expected[0]);
+    static_assert(Test.back() == Expected[std::size(Expected) - 1]);
+    static_assert(Test[1] == Expected[1]);
+
+    EXPECT_TRUE(ce_equal(Test, Expected));
+    EXPECT_TRUE(ce_requal(Test, Expected));
+  } else {
+    // 8-bit integer types.
+    using BigGap = MakeMetaSet<Builder, 0, ThisT::max()>;
+
+    MetaSetSortedContainer<BigGap> Test;
+    static constexpr PosT Expected[]{0, ThisT::max()};
+
+    static_assert(Test.size() == std::size(Expected));
+    static_assert(Test.empty() == false);
+    static_assert(Test.front() == Expected[0]);
+    static_assert(Test.back() == Expected[std::size(Expected) - 1]);
+    static_assert(Test[1] == Expected[1]);
+
+    EXPECT_TRUE(ce_equal(Test, Expected));
+    EXPECT_TRUE(ce_requal(Test, Expected));
+  }
+
+  {
+    using LargeOffset = MakeMetaSet<Builder, ThisT::max() - 63, ThisT::max()>;
+
+    MetaSetSortedContainer<LargeOffset> Test;
+    static constexpr PosT Expected[]{ThisT::max() - 63, ThisT::max()};
+
+    static_assert(Test.size() == std::size(Expected));
+    static_assert(Test.empty() == false);
+    static_assert(Test.front() == Expected[0]);
+    static_assert(Test.back() == Expected[std::size(Expected) - 1]);
+    static_assert(Test[1] == Expected[1]);
+
+    EXPECT_TRUE(ce_equal(Test, Expected));
+    EXPECT_TRUE(ce_requal(Test, Expected));
+  }
+
+  if constexpr (std::is_signed_v<PosT> && ThisT::min() < -36729) {
+    using LargeNegativeOffset = MakeMetaSet<Builder, -36729, -36729 + 64>;
+
+    MetaSetSortedContainer<LargeNegativeOffset> Test;
+    static constexpr PosT Expected[]{-36729, -36729 + 64};
+
+    static_assert(Test.size() == std::size(Expected));
+    static_assert(Test.empty() == false);
+    static_assert(Test.front() == Expected[0]);
+    static_assert(Test.back() == Expected[std::size(Expected) - 1]);
+    static_assert(Test[1] == Expected[1]);
+
+    EXPECT_TRUE(ce_equal(Test, Expected));
+    EXPECT_TRUE(ce_requal(Test, Expected));
+  }
+}
+
+TYPED_TEST(MetaSetTest, ContainsCompiletime) {
+  EXPOSE_TYPE(PosT);
+  EXPOSE_TYPE(E);
+  EXPOSE_TYPE(Builder);
+
+  {
+    using BS = MakeMetaSet<Builder, PosT(1), PosT(2), PosT(3), PosT(4)>;
+    static_assert(BS::contains(1));
+    static_assert(BS::contains(2));
+    static_assert(BS::contains(3));
+    static_assert(BS::contains(4));
+
+    static_assert(!BS::contains(0));
+    static_assert(!BS::contains(-1));
+    static_assert(!BS::contains(1000000));
+  }
+
+  {
+    using BS = MakeMetaSet<Builder, E::A, E::C, E::D>;
+    static_assert(BS::contains(E::A));
+    static_assert(BS::contains(0));
+    static_assert(BS::contains(E::C));
+    static_assert(BS::contains(2));
+    static_assert(BS::contains(E::D));
+    static_assert(BS::contains(3));
+
+    static_assert(!BS::contains(E::B));
+    static_assert(!BS::contains(1));
+    static_assert(!BS::contains(E::E));
+    static_assert(!BS::contains(4));
+  }
+
+  if constexpr (std::is_signed_v<PosT>) {
+    using BS = MakeMetaSet<Builder, 0, -126, 127>;
+
+    if constexpr (Builder::IsBitset) {
+      static_assert(
+          std::is_same_v<BS, MetaBitset<PosT, -126, 1ULL, (1ULL << 62), 0ULL,
+                                        (1ULL << 61)>>);
+    }
+
+    static_assert(BS::contains(0));
+    static_assert(BS::contains(-126));
+    static_assert(BS::contains(127));
+
+    static_assert(!BS::contains(-127));
+    static_assert(!BS::contains(128));
+    static_assert(!BS::contains(-125));
+    static_assert(!BS::contains(126));
+    static_assert(!BS::contains(-1));
+    static_assert(!BS::contains(1));
+  }
+}
+
+TYPED_TEST(MetaSetTest, ContainsRuntime) {
+  EXPOSE_TYPE(PosT);
+  EXPOSE_TYPE(E);
+  EXPOSE_TYPE(Builder);
+
+  {
+    using BS = MakeMetaSet<Builder, PosT(1), PosT(2), PosT(3), PosT(4)>;
+    EXPECT_TRUE(BS::contains(1));
+    EXPECT_TRUE(BS::contains(2));
+    EXPECT_TRUE(BS::contains(3));
+    EXPECT_TRUE(BS::contains(4));
+
+    EXPECT_FALSE(BS::contains(0));
+    EXPECT_FALSE(BS::contains(-1));
+    EXPECT_FALSE(BS::contains(1000000));
+  }
+
+  {
+    using BS = MakeMetaSet<Builder, E::A, E::C, E::D>;
+    EXPECT_TRUE(BS::contains(E::A));
+    EXPECT_TRUE(BS::contains(0));
+    EXPECT_TRUE(BS::contains(E::C));
+    EXPECT_TRUE(BS::contains(2));
+    EXPECT_TRUE(BS::contains(E::D));
+    EXPECT_TRUE(BS::contains(3));
+
+    EXPECT_FALSE(BS::contains(E::B));
+    EXPECT_FALSE(BS::contains(1));
+    EXPECT_FALSE(BS::contains(E::E));
+    EXPECT_FALSE(BS::contains(4));
+  }
+
+  if constexpr (std::is_signed_v<PosT>) {
+    using BS = MakeMetaSet<Builder, 0, -126, 127>;
+    EXPECT_TRUE(BS::contains(0));
+    EXPECT_TRUE(BS::contains(-126));
+    EXPECT_TRUE(BS::contains(127));
+
+    EXPECT_FALSE(BS::contains(-127));
+    EXPECT_FALSE(BS::contains(128));
+    EXPECT_FALSE(BS::contains(-125));
+    EXPECT_FALSE(BS::contains(126));
+    EXPECT_FALSE(BS::contains(-1));
+    EXPECT_FALSE(BS::contains(1));
+  }
+}
+
+TYPED_TEST(MetaSetTest, Union) {
+  EXPOSE_TYPE(PosT);
+  EXPOSE_TYPE(Builder);
+  EXPOSE_TYPE(ThisT);
+
+  // Identity
+  {
+    using S = MakeMetaSet<Builder, 5, 10, 15, 65, 70, 75>;
+
+    using Union = MetaSetUnion<S, S, 64>;
+
+    using ExpectedT = MakeMetaSparseBitset<PosT, 64, 5, 10, 15, 65, 70, 75>;
+
+    static_assert(std::is_same_v<Union, ExpectedT>);
+  }
+
+  // Identity (signed)
+  if constexpr (std::is_signed_v<PosT>) {
+    using S = MakeMetaSet<Builder, -75, -70, -65, 5, 10>;
+
+    using Union = MetaSetUnion<S, S, 64>;
+
+    using ExpectedT = MakeMetaSparseBitset<PosT, 64, -75, -70, -65, 5, 10>;
+
+    static_assert(std::is_same_v<Union, ExpectedT>);
+  }
+
+  // Disjoint
+  {
+    using L = MakeMetaSet<Builder, 1, 3, 5, 7, 9>;
+    using R = MakeMetaSet<Builder, 2, 4, 6, 8, 10>;
+
+    using Union = MetaSetUnion<L, R, 512>;
+
+    using ExpectedT =
+        MakeMetaSparseBitset<PosT, 512, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10>;
+
+    static_assert(std::is_same_v<Union, ExpectedT>);
+  }
+
+  // Disjoint (signed)
+  if constexpr (std::is_signed_v<PosT>) {
+    using L = MakeMetaSet<Builder, -5, -3, -1, 1, 3, 5>;
+    using R = MakeMetaSet<Builder, -4, -2, 0, 2, 4>;
+
+    using Union = MetaSetUnion<L, R, 512>;
+
+    using ExpectedT =
+        MakeMetaSparseBitset<PosT, 512, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5>;
+
+    static_assert(std::is_same_v<Union, ExpectedT>);
+  }
+
+  // Intersecting.
+  {
+    using L = MakeMetaSet<Builder, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10>;
+    using R = MakeMetaSet<Builder, 2, 4, 6, 8, 10>;
+
+    using Union = MetaSetUnion<L, R, 512>;
+
+    using ExpectedT =
+        MakeMetaSparseBitset<PosT, 512, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10>;
+
+    static_assert(std::is_same_v<Union, ExpectedT>);
+  }
+
+  // Intersecting (signed)
+  if constexpr (std::is_signed_v<PosT>) {
+    using L = MakeMetaSet<Builder, -6, -4, -2, 0, 2, 4, 6>;
+    using R = MakeMetaSet<Builder, -4, -3, -2, -1, 0, 1, 2, 3, 4>;
+
+    using Union = MetaSetUnion<L, R, 512>;
+
+    using ExpectedT =
+        MakeMetaSparseBitset<PosT, 512, -6, -4, -3, -2, -1, 0, 1, 2, 3, 4, 6>;
+
+    static_assert(std::is_same_v<Union, ExpectedT>);
+  }
+
+  // Big gap.
+  if constexpr (ThisT::max() >= 10000) {
+    using Singletons = MakeMetaSet<Builder, 10000, 5000, 8000>;
+    using SmallVals = MakeMetaSet<Builder, 10, 20, 30, 40, 50, 60>;
+
+    using Union = MetaSetUnion<Singletons, SmallVals>;
+
+    using ExpectedT = MakeMetaSparseBitset<PosT, 512, 10, 20, 30, 40, 50, 60,
+                                           5000, 8000, 10000>;
+    static_assert(std::is_same_v<Union, ExpectedT>);
+  }
+}
+
+TYPED_TEST(MetaSetTest, Intersect) {
+  EXPOSE_TYPE(PosT);
+  EXPOSE_TYPE(Builder);
+  EXPOSE_TYPE(ThisT);
+
+  // Identity
+  {
+    using S = MakeMetaSet<Builder, 5, 10, 15, 65, 70, 75>;
+
+    using Intersection = MetaSetIntersection<S, S, 64>;
+
+    using ExpectedT = MakeMetaSparseBitset<PosT, 64, 5, 10, 15, 65, 70, 75>;
+
+    static_assert(std::is_same_v<Intersection, ExpectedT>);
+  }
+
+  // Identity (signed)
+  if constexpr (std::is_signed_v<PosT>) {
+    using S = MakeMetaSet<Builder, -75, -70, -65, 5, 10>;
+
+    using Intersection = MetaSetIntersection<S, S, 64>;
+
+    using ExpectedT = MakeMetaSparseBitset<PosT, 64, -75, -70, -65, 5, 10>;
+
+    static_assert(std::is_same_v<Intersection, ExpectedT>);
+  }
+
+  // Disjoint
+  {
+    using L = MakeMetaSet<Builder, 1, 3, 5, 7, 9>;
+    using R = MakeMetaSet<Builder, 2, 4, 6, 8, 10>;
+
+    using Intersection = MetaSetIntersection<L, R, 512>;
+
+    using ExpectedT = MakeMetaSparseBitset<PosT, 512>;
+
+    static_assert(std::is_same_v<Intersection, ExpectedT>);
+  }
+
+  // Disjoint (signed)
+  if constexpr (std::is_signed_v<PosT>) {
+    using L = MakeMetaSet<Builder, -5, -3, -1, 1, 3, 5>;
+    using R = MakeMetaSet<Builder, -4, -2, 0, 2, 4>;
+
+    using Intersection = MetaSetIntersection<L, R, 512>;
+
+    using ExpectedT = MakeMetaSparseBitset<PosT, 512>;
+
+    static_assert(std::is_same_v<Intersection, ExpectedT>);
+  }
+
+  // Intersecting.
+  {
+    using L = MakeMetaSet<Builder, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10>;
+    using R = MakeMetaSet<Builder, 2, 4, 6, 8, 10>;
+
+    using Intersection = MetaSetIntersection<L, R, 512>;
+
+    using ExpectedT = MakeMetaSparseBitset<PosT, 512, 2, 4, 6, 8, 10>;
+
+    static_assert(std::is_same_v<Intersection, ExpectedT>);
+  }
+
+  // Intersecting (signed)
+  if constexpr (std::is_signed_v<PosT>) {
+    using L = MakeMetaSet<Builder, -6, -4, -2, 0, 2, 4, 6>;
+    using R = MakeMetaSet<Builder, -4, -3, -2, -1, 0, 1, 2, 3, 4>;
+
+    using Intersection = MetaSetIntersection<L, R, 512>;
+
+    using ExpectedT = MakeMetaSparseBitset<PosT, 512, -4, -2, 0, 2, 4>;
+
+    static_assert(std::is_same_v<Intersection, ExpectedT>);
+  }
+
+  // Big gap.
+  if constexpr (ThisT::max() >= 10000) {
+    using Singletons = MakeMetaSet<Builder, 10000, 5000, 8000>;
+    using SmallVals = MakeMetaSet<Builder, 10, 20, 30, 40, 50, 60>;
+
+    using Intersection = MetaSetIntersection<Singletons, SmallVals>;
+
+    using ExpectedT = MakeMetaSparseBitset<PosT, 512>;
+    static_assert(std::is_same_v<Intersection, ExpectedT>);
+  }
+}
+
+TYPED_TEST(MetaSetTest, Minus) {
+  EXPOSE_TYPE(PosT);
+  EXPOSE_TYPE(Builder);
+  EXPOSE_TYPE(ThisT);
+
+  // Identity
+  {
+    using S = MakeMetaSet<Builder, 5, 10, 15, 65, 70, 75>;
+
+    using Minus = MetaSetMinus<S, S, 64>;
+
+    using ExpectedT = MakeMetaSparseBitset<PosT, 64>;
+
+    static_assert(std::is_same_v<Minus, ExpectedT>);
+  }
+
+  // Identity (signed)
+  if constexpr (std::is_signed_v<PosT>) {
+    using S = MakeMetaSet<Builder, -75, -70, -65, 5, 10>;
+
+    using Minus = MetaSetMinus<S, S, 64>;
+
+    using ExpectedT = MakeMetaSparseBitset<PosT, 64>;
+
+    static_assert(std::is_same_v<Minus, ExpectedT>);
+  }
+
+  // Disjoint
+  {
+    using L = MakeMetaSet<Builder, 1, 3, 5, 7, 9>;
+    using R = MakeMetaSet<Builder, 2, 4, 6, 8, 10>;
+
+    using Minus = MetaSetMinus<L, R, 512>;
+
+    using ExpectedT = MakeMetaSparseBitset<PosT, 512, 1, 3, 5, 7, 9>;
+
+    static_assert(std::is_same_v<Minus, ExpectedT>);
+  }
+
+  // Disjoint (signed)
+  if constexpr (std::is_signed_v<PosT>) {
+    using L = MakeMetaSet<Builder, -5, -3, -1, 1, 3, 5>;
+    using R = MakeMetaSet<Builder, -4, -2, 0, 2, 4>;
+
+    using Minus = MetaSetMinus<L, R, 512>;
+
+    using ExpectedT = MakeMetaSparseBitset<PosT, 512, -5, -3, -1, 1, 3, 5>;
+
+    static_assert(std::is_same_v<Minus, ExpectedT>);
+  }
+
+  // Intersecting.
+  {
+    using L = MakeMetaSet<Builder, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10>;
+    using R = MakeMetaSet<Builder, 2, 4, 6, 8, 10>;
+
+    using Minus = MetaSetMinus<L, R, 512>;
+
+    using ExpectedT = MakeMetaSparseBitset<PosT, 512, 1, 3, 5, 7, 9>;
+
+    static_assert(std::is_same_v<Minus, ExpectedT>);
+  }
+
+  // Intersecting (signed)
+  if constexpr (std::is_signed_v<PosT>) {
+    using L = MakeMetaSet<Builder, -6, -4, -2, 0, 2, 4, 6>;
+    using R = MakeMetaSet<Builder, -4, -3, -2, -1, 0, 1, 2, 3, 4>;
+
+    using Minus = MetaSetMinus<L, R, 512>;
+
+    using ExpectedT = MakeMetaSparseBitset<PosT, 512, -6, 6>;
+
+    static_assert(std::is_same_v<Minus, ExpectedT>);
+  }
+
+  // Big gap.
+  if constexpr (ThisT::max() >= 10000) {
+    using Singletons = MakeMetaSet<Builder, 10000, 5000, 8000>;
+    using SmallVals = MakeMetaSet<Builder, 10, 20, 30, 40, 50, 60>;
+
+    using Minus = MetaSetMinus<Singletons, SmallVals>;
+
+    using ExpectedT = MakeMetaSparseBitset<PosT, 512, 10000, 5000, 8000>;
+    static_assert(std::is_same_v<Minus, ExpectedT>);
+  }
+}
+
+} // namespace



More information about the llvm-commits mailing list