[llvm] Orc rt updates (PR #154693)

Lang Hames via llvm-commits llvm-commits at lists.llvm.org
Thu Aug 21 01:49:34 PDT 2025


https://github.com/lhames updated https://github.com/llvm/llvm-project/pull/154693

>From f972d05385d14c5b060229b0a30e1a68119e0e5f Mon Sep 17 00:00:00 2001
From: Lang Hames <lhames at gmail.com>
Date: Wed, 20 Aug 2025 17:29:31 +1000
Subject: [PATCH 1/4] [orc-rt] Expand span.h file comment. NFC.

---
 orc-rt/include/orc-rt/span.h | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/orc-rt/include/orc-rt/span.h b/orc-rt/include/orc-rt/span.h
index 7584a4508b749..aa0b3cc66a628 100644
--- a/orc-rt/include/orc-rt/span.h
+++ b/orc-rt/include/orc-rt/span.h
@@ -6,7 +6,10 @@
 //
 //===----------------------------------------------------------------------===//
 //
-// TODO: Replace all uses with std::span once we can use C++20.
+// A substitute for std::span that can be used until the ORC runtime is allowed
+// to assume c++-20.
+//
+// TODO: Replace all uses with std::span once we can assume c++20.
 //
 //===----------------------------------------------------------------------===//
 

>From 09961a9e02c7736e467b3db1d6b3fbb5bede1db1 Mon Sep 17 00:00:00 2001
From: Lang Hames <lhames at gmail.com>
Date: Thu, 21 Aug 2025 17:07:34 +1000
Subject: [PATCH 2/4] [orc-rt] Add preliminary math.h header and basic
 operations.

The initial operations, isPowerOf2 and nextPowerOf2 will be used in an upcoming
patch to add support for bitmask-enums.
---
 orc-rt/include/CMakeLists.txt   |  1 +
 orc-rt/include/orc-rt/math.h    | 35 +++++++++++++++
 orc-rt/unittests/CMakeLists.txt |  1 +
 orc-rt/unittests/math-test.cpp  | 78 +++++++++++++++++++++++++++++++++
 4 files changed, 115 insertions(+)
 create mode 100644 orc-rt/include/orc-rt/math.h
 create mode 100644 orc-rt/unittests/math-test.cpp

diff --git a/orc-rt/include/CMakeLists.txt b/orc-rt/include/CMakeLists.txt
index eb24f29b14695..e2096a80404b4 100644
--- a/orc-rt/include/CMakeLists.txt
+++ b/orc-rt/include/CMakeLists.txt
@@ -1,5 +1,6 @@
 set(ORC_RT_HEADERS
     orc-rt-c/orc-rt.h
+    orc-rt/math.h
     orc-rt/span.h
 )
 
diff --git a/orc-rt/include/orc-rt/math.h b/orc-rt/include/orc-rt/math.h
new file mode 100644
index 0000000000000..4dd6ddd9e506c
--- /dev/null
+++ b/orc-rt/include/orc-rt/math.h
@@ -0,0 +1,35 @@
+//===--------- math.h - Math helpers for the ORC runtime --------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Math helper functions for the ORC runtime.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef ORC_RT_MATH_H
+#define ORC_RT_MATH_H
+
+#include <cstdint>
+#include <limits>
+
+namespace orc_rt {
+
+/// Test whether the given value is a power of 2.
+template <typename T> constexpr bool isPowerOf2(T Val) noexcept {
+  return Val != 0 && (Val & (Val - 1)) == 0;
+}
+
+/// Calculates the next power of 2.
+template <typename T> constexpr T nextPowerOf2(T Val) noexcept {
+  for (size_t I = 1; I < std::numeric_limits<T>::digits; I <<= 1)
+    Val |= (Val >> I);
+  return Val + 1;
+}
+
+} // namespace orc_rt
+
+#endif // ORC_RT_MATH_H
diff --git a/orc-rt/unittests/CMakeLists.txt b/orc-rt/unittests/CMakeLists.txt
index 98c28c9664c2a..51cd23304fc63 100644
--- a/orc-rt/unittests/CMakeLists.txt
+++ b/orc-rt/unittests/CMakeLists.txt
@@ -12,6 +12,7 @@ function(add_orc_rt_unittest test_dirname)
 endfunction()
 
 add_orc_rt_unittest(CoreTests
+  math-test.cpp
   span-test.cpp
   DISABLE_LLVM_LINK_LLVM_DYLIB
   )
diff --git a/orc-rt/unittests/math-test.cpp b/orc-rt/unittests/math-test.cpp
new file mode 100644
index 0000000000000..491bbd8baa6ee
--- /dev/null
+++ b/orc-rt/unittests/math-test.cpp
@@ -0,0 +1,78 @@
+//===- math-test.cpp ------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Tests for orc-rt's math.h APIs.
+//
+//===----------------------------------------------------------------------===//
+
+#include "orc-rt/math.h"
+#include "gtest/gtest.h"
+
+using namespace orc_rt;
+
+TEST(STLExtrasTest, isPowerOf2) {
+  // Test [0..16]
+  EXPECT_FALSE(isPowerOf2(0x00));
+  EXPECT_TRUE(isPowerOf2(0x01));
+  EXPECT_TRUE(isPowerOf2(0x02));
+  EXPECT_FALSE(isPowerOf2(0x03));
+  EXPECT_TRUE(isPowerOf2(0x04));
+  EXPECT_FALSE(isPowerOf2(0x05));
+  EXPECT_FALSE(isPowerOf2(0x06));
+  EXPECT_FALSE(isPowerOf2(0x07));
+  EXPECT_TRUE(isPowerOf2(0x08));
+  EXPECT_FALSE(isPowerOf2(0x09));
+  EXPECT_FALSE(isPowerOf2(0x0A));
+  EXPECT_FALSE(isPowerOf2(0x0B));
+  EXPECT_FALSE(isPowerOf2(0x0C));
+  EXPECT_FALSE(isPowerOf2(0x0D));
+  EXPECT_FALSE(isPowerOf2(0x0E));
+  EXPECT_FALSE(isPowerOf2(0x0F));
+  EXPECT_TRUE(isPowerOf2(0x10));
+
+  // Test some higher powers of two and their adjacent values.
+  EXPECT_FALSE(isPowerOf2(0x1F));
+  EXPECT_TRUE(isPowerOf2(0x20));
+  EXPECT_FALSE(isPowerOf2(0x21));
+
+  EXPECT_FALSE(isPowerOf2(0x3F));
+  EXPECT_TRUE(isPowerOf2(0x40));
+  EXPECT_FALSE(isPowerOf2(0x41));
+
+  EXPECT_FALSE(isPowerOf2(0x7F));
+  EXPECT_TRUE(isPowerOf2(0x80));
+  EXPECT_FALSE(isPowerOf2(0x81));
+
+  // Test larger values.
+  EXPECT_FALSE(isPowerOf2(0x3fffffff));
+  EXPECT_TRUE(isPowerOf2(0x40000000));
+  EXPECT_FALSE(isPowerOf2(0x40000001));
+
+  // Test negatives.
+  EXPECT_FALSE(isPowerOf2(-1));
+}
+
+TEST(STLExtrasTest, nextPowerOf2) {
+  EXPECT_EQ(nextPowerOf2(0x00), (1 << 0));
+  EXPECT_EQ(nextPowerOf2(0x01), (1 << 1));
+  EXPECT_EQ(nextPowerOf2(0x02), (1 << 2));
+  EXPECT_EQ(nextPowerOf2(0x03), (1 << 2));
+  EXPECT_EQ(nextPowerOf2(0x04), (1 << 3));
+  EXPECT_EQ(nextPowerOf2(0x05), (1 << 3));
+  EXPECT_EQ(nextPowerOf2(0x06), (1 << 3));
+  EXPECT_EQ(nextPowerOf2(0x07), (1 << 3));
+  EXPECT_EQ(nextPowerOf2(0x08), (1 << 4));
+  EXPECT_EQ(nextPowerOf2(0x09), (1 << 4));
+  EXPECT_EQ(nextPowerOf2(0x0a), (1 << 4));
+  EXPECT_EQ(nextPowerOf2(0x0b), (1 << 4));
+  EXPECT_EQ(nextPowerOf2(0x0c), (1 << 4));
+  EXPECT_EQ(nextPowerOf2(0x0d), (1 << 4));
+  EXPECT_EQ(nextPowerOf2(0x0e), (1 << 4));
+  EXPECT_EQ(nextPowerOf2(0x0f), (1 << 4));
+  EXPECT_EQ(nextPowerOf2(0x10), (1 << 5));
+}

>From 3fb39468a93f887481e077b8ae47d7e32b2027f1 Mon Sep 17 00:00:00 2001
From: Lang Hames <lhames at gmail.com>
Date: Thu, 21 Aug 2025 17:38:27 +1000
Subject: [PATCH 3/4] [orc-rt] Add bitmask-enum helper utilities.

ORC_RT_MARK_AS_BITMASK_ENUM and ORC_RT_DECLARE_ENUM_AS_BITMASK can be used to
easily add support for bitmask operators (&, |, ^, ~) to enum types.

This code was derived from LLVM's include/llvm/ADT/BitmaskEnum.h header.
---
 orc-rt/include/CMakeLists.txt          |   1 +
 orc-rt/include/orc-rt/bitmask-enum.h   | 161 +++++++++++++++++++++++++
 orc-rt/unittests/CMakeLists.txt        |   1 +
 orc-rt/unittests/bitmask-enum-test.cpp | 143 ++++++++++++++++++++++
 4 files changed, 306 insertions(+)
 create mode 100644 orc-rt/include/orc-rt/bitmask-enum.h
 create mode 100644 orc-rt/unittests/bitmask-enum-test.cpp

diff --git a/orc-rt/include/CMakeLists.txt b/orc-rt/include/CMakeLists.txt
index e2096a80404b4..f753de56c1b33 100644
--- a/orc-rt/include/CMakeLists.txt
+++ b/orc-rt/include/CMakeLists.txt
@@ -1,5 +1,6 @@
 set(ORC_RT_HEADERS
     orc-rt-c/orc-rt.h
+    orc-rt/bitmask-enum.h
     orc-rt/math.h
     orc-rt/span.h
 )
diff --git a/orc-rt/include/orc-rt/bitmask-enum.h b/orc-rt/include/orc-rt/bitmask-enum.h
new file mode 100644
index 0000000000000..adc95424b83a8
--- /dev/null
+++ b/orc-rt/include/orc-rt/bitmask-enum.h
@@ -0,0 +1,161 @@
+//===---- bitmask-enum.h - Enable bitmask operations on enums ---*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Provides utilities for easily adding bitmask operation support to enums.
+//
+// This code was derived from LLVM's include/llvm/ADT/BitmaskEnum.h header, and
+// adapted for the ORC runtime.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef ORC_RT_BITMASK_ENUM_H
+#define ORC_RT_BITMASK_ENUM_H
+
+#include "math.h"
+
+#include <cassert>
+#include <type_traits>
+
+namespace orc_rt {
+
+/// ORC_RT_MARK_AS_BITMASK_ENUM lets you opt in an individual enum type so you
+/// can perform bitwise operations on it without putting static_cast everywhere.
+///
+/// \code
+///   enum MyEnum {
+///     E1 = 1, E2 = 2, E3 = 4, E4 = 8,
+///     ORC_RT_MARK_AS_BITMASK_ENUM(/* LargestValue = */ E4)
+///   };
+///
+///   void Foo() {
+///     MyEnum A = (E1 | E2) & E3 ^ ~E4; // Look, ma: No static_cast!
+///   }
+/// \endcode
+///
+/// Normally when you do a bitwise operation on an enum value, you get back an
+/// instance of the underlying type (e.g. int).  But using this macro, bitwise
+/// ops on your enum will return you back instances of the enum.  This is
+/// particularly useful for enums which represent a combination of flags.
+///
+/// The parameter to ORC_RT_MARK_AS_BITMASK_ENUM should be the largest
+/// individual value in your enum.
+///
+/// All of the enum's values must be non-negative.
+#define ORC_RT_MARK_AS_BITMASK_ENUM(LargestValue)                              \
+  ORC_RT_BITMASK_LARGEST_ENUMERATOR = LargestValue
+
+/// ORC_RT_DECLARE_ENUM_AS_BITMASK can be used to declare an enum type as a bit
+/// set, so that bitwise operation on such enum does not require static_cast.
+///
+/// \code
+///   enum MyEnum { E1 = 1, E2 = 2, E3 = 4, E4 = 8 };
+///   ORC_RT_DECLARE_ENUM_AS_BITMASK(MyEnum, E4);
+///
+///   void Foo() {
+///     MyEnum A = (E1 | E2) & E3 ^ ~E4; // No static_cast
+///   }
+/// \endcode
+///
+/// The second parameter to ORC_RT_DECLARE_ENUM_AS_BITMASK specifies the largest
+/// bit value of the enum type.
+///
+/// ORC_RT_DECLARE_ENUM_AS_BITMASK should be used in __orc_rt namespace.
+///
+/// This a non-intrusive alternative for ORC_RT_MARK_AS_BITMASK_ENUM. It allows
+/// declaring more than one non-scoped enumerations as bitmask types in the same
+/// scope. Otherwise it provides the same functionality as
+/// ORC_RT_MARK_AS_BITMASK_ENUM.
+#define ORC_RT_DECLARE_ENUM_AS_BITMASK(Enum, LargestValue)                     \
+  template <> struct is_bitmask_enum<Enum> : std::true_type {};                \
+  template <> struct largest_bitmask_enum_bit<Enum> {                          \
+    static constexpr std::underlying_type_t<Enum> value = LargestValue;        \
+  }
+
+/// Traits class to determine whether an enum has been declared as a bitwise
+/// enum via ORC_RT_DECLARE_ENUM_AS_BITMASK.
+template <typename E, typename Enable = void>
+struct is_bitmask_enum : std::false_type {};
+
+template <typename E>
+struct is_bitmask_enum<
+    E, std::enable_if_t<sizeof(E::ORC_RT_BITMASK_LARGEST_ENUMERATOR) >= 0>>
+    : std::true_type {};
+
+template <typename E>
+inline constexpr bool is_bitmask_enum_v = is_bitmask_enum<E>::value;
+
+/// Traits class to deermine bitmask enum largest bit.
+template <typename E, typename Enable = void> struct largest_bitmask_enum_bit;
+
+template <typename E>
+struct largest_bitmask_enum_bit<
+    E, std::enable_if_t<sizeof(E::ORC_RT_BITMASK_LARGEST_ENUMERATOR) >= 0>> {
+  using UnderlyingTy = std::underlying_type_t<E>;
+  static constexpr UnderlyingTy value =
+      static_cast<UnderlyingTy>(E::ORC_RT_BITMASK_LARGEST_ENUMERATOR);
+};
+
+template <typename E>
+constexpr std::underlying_type_t<E> bitmask_enum_mask() noexcept {
+  return nextPowerOf2(largest_bitmask_enum_bit<E>::value) - 1;
+}
+
+template <typename E>
+constexpr std::underlying_type_t<E> bitmask_enum_to_underlying(E Val) noexcept {
+  auto U = static_cast<std::underlying_type_t<E>>(Val);
+  assert(U >= 0 && "Negative enum values are not allowed");
+  assert(U <= bitmask_enum_mask<E>() &&
+         "Enum value too large (or langest val too small");
+  return U;
+}
+
+template <typename E, typename = std::enable_if_t<is_bitmask_enum_v<E>>>
+constexpr E operator~(E Val) noexcept {
+  return static_cast<E>(~bitmask_enum_to_underlying(Val) &
+                        bitmask_enum_mask<E>());
+}
+
+template <typename E, typename = std::enable_if_t<is_bitmask_enum_v<E>>>
+constexpr E operator|(E LHS, E RHS) noexcept {
+  return static_cast<E>(bitmask_enum_to_underlying(LHS) |
+                        bitmask_enum_to_underlying(RHS));
+}
+
+template <typename E, typename = std::enable_if_t<is_bitmask_enum_v<E>>>
+constexpr E operator&(E LHS, E RHS) noexcept {
+  return static_cast<E>(bitmask_enum_to_underlying(LHS) &
+                        bitmask_enum_to_underlying(RHS));
+}
+
+template <typename E, typename = std::enable_if_t<is_bitmask_enum_v<E>>>
+constexpr E operator^(E LHS, E RHS) noexcept {
+  return static_cast<E>(bitmask_enum_to_underlying(LHS) ^
+                        bitmask_enum_to_underlying(RHS));
+}
+
+template <typename E, typename = std::enable_if_t<is_bitmask_enum_v<E>>>
+constexpr E &operator|=(E &LHS, E RHS) noexcept {
+  LHS = LHS | RHS;
+  return LHS;
+}
+
+template <typename E, typename = std::enable_if_t<is_bitmask_enum_v<E>>>
+constexpr E &operator&=(E &LHS, E RHS) noexcept {
+  LHS = LHS & RHS;
+  return LHS;
+}
+
+template <typename E, typename = std::enable_if_t<is_bitmask_enum_v<E>>>
+constexpr E &operator^=(E &LHS, E RHS) noexcept {
+  LHS = LHS ^ RHS;
+  return LHS;
+}
+
+} // namespace orc_rt
+
+#endif // ORC_RT_BITMASK_ENUM_H
diff --git a/orc-rt/unittests/CMakeLists.txt b/orc-rt/unittests/CMakeLists.txt
index 51cd23304fc63..4aaf2caf0cd7d 100644
--- a/orc-rt/unittests/CMakeLists.txt
+++ b/orc-rt/unittests/CMakeLists.txt
@@ -12,6 +12,7 @@ function(add_orc_rt_unittest test_dirname)
 endfunction()
 
 add_orc_rt_unittest(CoreTests
+  bitmask-enum-test.cpp
   math-test.cpp
   span-test.cpp
   DISABLE_LLVM_LINK_LLVM_DYLIB
diff --git a/orc-rt/unittests/bitmask-enum-test.cpp b/orc-rt/unittests/bitmask-enum-test.cpp
new file mode 100644
index 0000000000000..3367a8d4b040e
--- /dev/null
+++ b/orc-rt/unittests/bitmask-enum-test.cpp
@@ -0,0 +1,143 @@
+//===-- bitmask-enum-test.cpp ---------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of the ORC runtime.
+//
+//===----------------------------------------------------------------------===//
+
+#include "orc-rt/bitmask-enum.h"
+#include "gtest/gtest.h"
+
+#include <sstream>
+#include <string>
+
+using namespace orc_rt;
+
+namespace {
+
+enum Flags { F0 = 0, F1 = 1, F2 = 2, F3 = 4, F4 = 8 };
+
+} // namespace
+
+namespace orc_rt {
+ORC_RT_DECLARE_ENUM_AS_BITMASK(Flags, F4);
+} // namespace orc_rt
+
+static_assert(is_bitmask_enum<Flags>::value != 0);
+static_assert(largest_bitmask_enum_bit<Flags>::value == Flags::F4);
+
+namespace {
+
+static_assert(is_bitmask_enum<Flags>::value != 0);
+static_assert(largest_bitmask_enum_bit<Flags>::value == Flags::F4);
+
+TEST(BitmaskEnumTest, BitwiseOr) {
+  Flags f = F1 | F2;
+  EXPECT_EQ(3, f);
+
+  f = f | F3;
+  EXPECT_EQ(7, f);
+}
+
+TEST(BitmaskEnumTest, BitwiseOrEquals) {
+  Flags f = F1;
+  f |= F3;
+  EXPECT_EQ(5, f);
+
+  // |= should return a reference to the LHS.
+  f = F2;
+  (f |= F3) = F1;
+  EXPECT_EQ(F1, f);
+}
+
+TEST(BitmaskEnumTest, BitwiseAnd) {
+  Flags f = static_cast<Flags>(3) & F2;
+  EXPECT_EQ(F2, f);
+
+  f = (f | F3) & (F1 | F2 | F3);
+  EXPECT_EQ(6, f);
+}
+
+TEST(BitmaskEnumTest, BitwiseAndEquals) {
+  Flags f = F1 | F2 | F3;
+  f &= F1 | F2;
+  EXPECT_EQ(3, f);
+
+  // &= should return a reference to the LHS.
+  (f &= F1) = F3;
+  EXPECT_EQ(F3, f);
+}
+
+TEST(BitmaskEnumTest, BitwiseXor) {
+  Flags f = (F1 | F2) ^ (F2 | F3);
+  EXPECT_EQ(5, f);
+
+  f = f ^ F1;
+  EXPECT_EQ(4, f);
+}
+
+TEST(BitmaskEnumTest, BitwiseXorEquals) {
+  Flags f = (F1 | F2);
+  f ^= (F2 | F4);
+  EXPECT_EQ(9, f);
+
+  // ^= should return a reference to the LHS.
+  (f ^= F4) = F3;
+  EXPECT_EQ(F3, f);
+}
+
+TEST(BitmaskEnumTest, ConstantExpression) {
+  constexpr Flags f1 = ~F1;
+  constexpr Flags f2 = F1 | F2;
+  constexpr Flags f3 = F1 & F2;
+  constexpr Flags f4 = F1 ^ F2;
+  EXPECT_EQ(f1, ~F1);
+  EXPECT_EQ(f2, F1 | F2);
+  EXPECT_EQ(f3, F1 & F2);
+  EXPECT_EQ(f4, F1 ^ F2);
+}
+
+TEST(BitmaskEnumTest, BitwiseNot) {
+  Flags f = ~F1;
+  EXPECT_EQ(14, f); // Largest value for f is 15.
+  EXPECT_EQ(15, ~F0);
+}
+
+enum class FlagsClass {
+  F0 = 0,
+  F1 = 1,
+  F2 = 2,
+  F3 = 4,
+  ORC_RT_MARK_AS_BITMASK_ENUM(F3)
+};
+
+TEST(BitmaskEnumTest, ScopedEnum) {
+  FlagsClass f = (FlagsClass::F1 & ~FlagsClass::F0) | FlagsClass::F2;
+  f |= FlagsClass::F3;
+  EXPECT_EQ(7, static_cast<int>(f));
+}
+
+struct Container {
+  enum Flags {
+    F0 = 0,
+    F1 = 1,
+    F2 = 2,
+    F3 = 4,
+    ORC_RT_MARK_AS_BITMASK_ENUM(F3)
+  };
+
+  static Flags getFlags() {
+    Flags f = F0 | F1;
+    f |= F2;
+    return f;
+  }
+};
+
+TEST(BitmaskEnumTest, EnumInStruct) { EXPECT_EQ(3, Container::getFlags()); }
+
+} // namespace

>From bb6dd1e45d94b13f964133b8aece4bee4f765ce5 Mon Sep 17 00:00:00 2001
From: Lang Hames <lhames at gmail.com>
Date: Thu, 21 Aug 2025 18:32:40 +1000
Subject: [PATCH 4/4] [orc-rt] Add rtti header and unit tests.

The orc-rt extensible RTTI mechanism is used to provide simple dynamic RTTI
checks for orc-rt types that do not depend on standard C++ RTTI (meaning that
they will work equally well for programs compiled with -fno-rtti).
---
 orc-rt/include/CMakeLists.txt      |   1 +
 orc-rt/include/orc-rt/rtti.h       | 138 +++++++++++++++++++++++++++++
 orc-rt/lib/executor/CMakeLists.txt |   1 +
 orc-rt/lib/executor/rtti.cpp       |  24 +++++
 orc-rt/unittests/CMakeLists.txt    |   1 +
 orc-rt/unittests/rtti-test.cpp     |  52 +++++++++++
 6 files changed, 217 insertions(+)
 create mode 100644 orc-rt/include/orc-rt/rtti.h
 create mode 100644 orc-rt/lib/executor/rtti.cpp
 create mode 100644 orc-rt/unittests/rtti-test.cpp

diff --git a/orc-rt/include/CMakeLists.txt b/orc-rt/include/CMakeLists.txt
index f753de56c1b33..717b75290e052 100644
--- a/orc-rt/include/CMakeLists.txt
+++ b/orc-rt/include/CMakeLists.txt
@@ -2,6 +2,7 @@ set(ORC_RT_HEADERS
     orc-rt-c/orc-rt.h
     orc-rt/bitmask-enum.h
     orc-rt/math.h
+    orc-rt/rtti.h
     orc-rt/span.h
 )
 
diff --git a/orc-rt/include/orc-rt/rtti.h b/orc-rt/include/orc-rt/rtti.h
new file mode 100644
index 0000000000000..3097edf68d7b2
--- /dev/null
+++ b/orc-rt/include/orc-rt/rtti.h
@@ -0,0 +1,138 @@
+//===------------- rtti.h - RTTI support for ORC RT -------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// \file
+//
+// Provides an extensible RTTI mechanism, that can be used regardless of whether
+// the runtime is built with -frtti or not. This is predominantly used to
+// support error handling.
+//
+// The RTTIRoot class defines methods for comparing type ids. Implementations
+// of these methods can be injected into new classes using the RTTIExtends
+// class template.
+//
+// E.g.
+//
+//   @code{.cpp}
+//   class MyBaseClass : public RTTIExtends<MyBaseClass, RTTIRoot> {
+//   public:
+//     virtual void foo() = 0;
+//   };
+//
+//   class MyDerivedClass1 : public RTTIExtends<MyDerivedClass1, MyBaseClass> {
+//   public:
+//     void foo() override {}
+//   };
+//
+//   class MyDerivedClass2 : public RTTIExtends<MyDerivedClass2, MyBaseClass> {
+//   public:
+//     void foo() override {}
+//   };
+//
+//   void fn() {
+//     std::unique_ptr<MyBaseClass> B = std::make_unique<MyDerivedClass1>();
+//     outs() << isa<MyBaseClass>(B) << "\n"; // Outputs "1".
+//     outs() << isa<MyDerivedClass1>(B) << "\n"; // Outputs "1".
+//     outs() << isa<MyDerivedClass2>(B) << "\n"; // Outputs "0'.
+//   }
+//
+//   @endcode
+//
+// Note:
+//   This header was adapted from llvm/Support/ExtensibleRTTI.h, however the
+// data structures are not shared and the code need not be kept in sync.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef ORC_RT_RTTI_H
+#define ORC_RT_RTTI_H
+
+namespace orc_rt {
+
+template <typename ThisT, typename ParentT> class RTTIExtends;
+
+/// Base class for the extensible RTTI hierarchy.
+///
+/// This class defines virtual methods, dynamicClassID and isA, that enable
+/// type comparisons.
+class RTTIRoot {
+public:
+  virtual ~RTTIRoot() = default;
+
+  /// Returns the class ID for this type.
+  static const void *classID() noexcept { return &ID; }
+
+  /// Returns the class ID for the dynamic type of this RTTIRoot instance.
+  virtual const void *dynamicClassID() const noexcept = 0;
+
+  /// Returns true if this class's ID matches the given class ID.
+  virtual bool isA(const void *const ClassID) const noexcept {
+    return ClassID == classID();
+  }
+
+  /// Check whether this instance is a subclass of QueryT.
+  template <typename QueryT> bool isA() const noexcept {
+    return isA(QueryT::classID());
+  }
+
+  static bool classof(const RTTIRoot *R) noexcept { return R->isA<RTTIRoot>(); }
+
+private:
+  virtual void anchor();
+
+  static char ID;
+};
+
+/// Inheritance utility for extensible RTTI.
+///
+/// Supports single inheritance only: A class can only have one
+/// ExtensibleRTTI-parent (i.e. a parent for which the isa<> test will work),
+/// though it can have many non-ExtensibleRTTI parents.
+///
+/// RTTIExtents uses CRTP so the first template argument to RTTIExtends is the
+/// newly introduced type, and the *second* argument is the parent class.
+///
+/// class MyType : public RTTIExtends<MyType, RTTIRoot> {
+///   ...
+/// };
+///
+/// class MyDerivedType : public RTTIExtends<MyDerivedType, MyType> {
+///   ...
+/// };
+///
+template <typename ThisT, typename ParentT> class RTTIExtends : public ParentT {
+public:
+  // Inherit constructors and isA methods from ParentT.
+  using ParentT::isA;
+  using ParentT::ParentT;
+
+  static char ID;
+
+  static const void *classID() noexcept { return &ThisT::ID; }
+
+  const void *dynamicClassID() const noexcept override { return &ThisT::ID; }
+
+  bool isA(const void *const ClassID) const noexcept override {
+    return ClassID == classID() || ParentT::isA(ClassID);
+  }
+
+  static bool classof(const RTTIRoot *R) { return R->isA<ThisT>(); }
+};
+
+template <typename ThisT, typename ParentT>
+char RTTIExtends<ThisT, ParentT>::ID = 0;
+
+/// Returns true if the given value is an instance of the template type
+/// parameter.
+template <typename To, typename From> bool isa(const From &Value) noexcept {
+  return To::classof(&Value);
+}
+
+} // namespace orc_rt
+
+#endif // ORC_RT_RTTI_H
diff --git a/orc-rt/lib/executor/CMakeLists.txt b/orc-rt/lib/executor/CMakeLists.txt
index 82d29455eb6df..40035bb4dcffb 100644
--- a/orc-rt/lib/executor/CMakeLists.txt
+++ b/orc-rt/lib/executor/CMakeLists.txt
@@ -1,5 +1,6 @@
 set(files
   orc-rt-executor.cpp
+  rtti.cpp
   )
 
 add_library(orc-rt-executor STATIC ${files})
diff --git a/orc-rt/lib/executor/rtti.cpp b/orc-rt/lib/executor/rtti.cpp
new file mode 100644
index 0000000000000..8c6d27fb25c73
--- /dev/null
+++ b/orc-rt/lib/executor/rtti.cpp
@@ -0,0 +1,24 @@
+//===- rtti.cpp -----------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of the ORC runtime support library.
+//
+// Note:
+//   This source file was adapted from lib/Support/ExtensibleRTTI.cpp, however
+// the data structures are not shared and the code need not be kept in sync.
+//
+//===----------------------------------------------------------------------===//
+
+#include "orc-rt/rtti.h"
+
+namespace orc_rt {
+
+char RTTIRoot::ID = 0;
+void RTTIRoot::anchor() {}
+
+} // namespace orc_rt
diff --git a/orc-rt/unittests/CMakeLists.txt b/orc-rt/unittests/CMakeLists.txt
index 4aaf2caf0cd7d..00751e07742c6 100644
--- a/orc-rt/unittests/CMakeLists.txt
+++ b/orc-rt/unittests/CMakeLists.txt
@@ -14,6 +14,7 @@ endfunction()
 add_orc_rt_unittest(CoreTests
   bitmask-enum-test.cpp
   math-test.cpp
+  rtti-test.cpp
   span-test.cpp
   DISABLE_LLVM_LINK_LLVM_DYLIB
   )
diff --git a/orc-rt/unittests/rtti-test.cpp b/orc-rt/unittests/rtti-test.cpp
new file mode 100644
index 0000000000000..bb9fac6447010
--- /dev/null
+++ b/orc-rt/unittests/rtti-test.cpp
@@ -0,0 +1,52 @@
+//===-- rtti_test.cpp -----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Note:
+//   This unit test was adapted from
+//   llvm/unittests/Support/ExtensibleRTTITest.cpp
+//
+//===----------------------------------------------------------------------===//
+
+#include "orc-rt/rtti.h"
+#include "gtest/gtest.h"
+
+using namespace orc_rt;
+
+namespace {
+
+class MyBase : public RTTIExtends<MyBase, RTTIRoot> {};
+
+class MyDerivedA : public RTTIExtends<MyDerivedA, MyBase> {};
+
+class MyDerivedB : public RTTIExtends<MyDerivedB, MyBase> {};
+
+} // end anonymous namespace
+
+TEST(ExtensibleRTTITest, BaseCheck) {
+  MyBase MB;
+  MyDerivedA MDA;
+  MyDerivedB MDB;
+
+  // Check MB properties.
+  EXPECT_TRUE(isa<RTTIRoot>(MB));
+  EXPECT_TRUE(isa<MyBase>(MB));
+  EXPECT_FALSE(isa<MyDerivedA>(MB));
+  EXPECT_FALSE(isa<MyDerivedB>(MB));
+
+  // Check MDA properties.
+  EXPECT_TRUE(isa<RTTIRoot>(MDA));
+  EXPECT_TRUE(isa<MyBase>(MDA));
+  EXPECT_TRUE(isa<MyDerivedA>(MDA));
+  EXPECT_FALSE(isa<MyDerivedB>(MDA));
+
+  // Check MDB properties.
+  EXPECT_TRUE(isa<RTTIRoot>(MDB));
+  EXPECT_TRUE(isa<MyBase>(MDB));
+  EXPECT_FALSE(isa<MyDerivedA>(MDB));
+  EXPECT_TRUE(isa<MyDerivedB>(MDB));
+}



More information about the llvm-commits mailing list