[orc-rt] Add ExecutorAddress.h: ExecutorAddr and ExecutorAddrRange. (#155066)

These types are used to represent addresses and address ranges within
the executing JIT'd code in a way that can be communicated to an ORC
controller.
diff --git a/orc-rt/include/CMakeLists.txt b/orc-rt/include/CMakeLists.txt
index e58a70f..dc8b7cf 100644
--- a/orc-rt/include/CMakeLists.txt
+++ b/orc-rt/include/CMakeLists.txt
@@ -5,6 +5,7 @@
     orc-rt/BitmaskEnum.h
     orc-rt/Compiler.h
     orc-rt/Error.h
+    orc-rt/ExecutorAddr.h
     orc-rt/Math.h
     orc-rt/RTTI.h
     orc-rt/WrapperFunctionResult.h
diff --git a/orc-rt/include/orc-rt/ExecutorAddress.h b/orc-rt/include/orc-rt/ExecutorAddress.h
new file mode 100644
index 0000000..cc7bbf5
--- /dev/null
+++ b/orc-rt/include/orc-rt/ExecutorAddress.h
@@ -0,0 +1,230 @@
+//===------ ExecutorAddress.h - Executing process address -------*- 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Utilites for representing addresses and address ranges in the executing
+// program that can be shared with an ORC controller.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef ORC_RT_EXECUTORADDRESS_H
+#define ORC_RT_EXECUTORADDRESS_H
+
+#include "span.h"
+
+#include <cassert>
+#include <cstdint>
+#include <functional>
+#include <type_traits>
+
+namespace orc_rt {
+
+using ExecutorAddrDiff = uint64_t;
+
+/// Represents an address in the executor process.
+class ExecutorAddr {
+public:
+  /// Return pointer unmodified.
+  template <typename T> struct rawPtr {
+    T *operator()(T *p) const { return p; }
+  };
+
+  /// Default wrap function to use on this host.
+  template <typename T> using defaultWrap = rawPtr<T>;
+
+  /// Default unwrap function to use on this host.
+  template <typename T> using defaultUnwrap = rawPtr<T>;
+
+  /// Merges a tag into the raw address value:
+  ///   P' = P | (TagValue << TagOffset).
+  class Tag {
+  public:
+    constexpr Tag(uintptr_t TagValue, uintptr_t TagOffset)
+        : TagMask(TagValue << TagOffset) {}
+
+    template <typename T> constexpr T *operator()(T *P) {
+      return reinterpret_cast<T *>(reinterpret_cast<uintptr_t>(P) | TagMask);
+    }
+
+  private:
+    uintptr_t TagMask;
+  };
+
+  /// Strips a tag of the given length from the given offset within the pointer:
+  /// P' = P & ~(((1 << TagLen) -1) << TagOffset)
+  class Untag {
+  public:
+    constexpr Untag(uintptr_t TagLen, uintptr_t TagOffset)
+        : UntagMask(~(((uintptr_t(1) << TagLen) - 1) << TagOffset)) {}
+
+    template <typename T> constexpr T *operator()(T *P) {
+      return reinterpret_cast<T *>(reinterpret_cast<uintptr_t>(P) & UntagMask);
+    }
+
+  private:
+    uintptr_t UntagMask;
+  };
+
+  constexpr ExecutorAddr() noexcept = default;
+  explicit constexpr ExecutorAddr(uint64_t Addr) noexcept : Addr(Addr) {}
+
+  /// Create an ExecutorAddr from the given pointer.
+  template <typename T, typename UnwrapFn = defaultUnwrap<T>>
+  static constexpr ExecutorAddr fromPtr(T *Ptr,
+                                        UnwrapFn &&Unwrap = UnwrapFn()) {
+    return ExecutorAddr(
+        static_cast<uint64_t>(reinterpret_cast<uintptr_t>(Unwrap(Ptr))));
+  }
+
+  /// Cast this ExecutorAddr to a pointer of the given type.
+  template <typename T, typename WrapFn = defaultWrap<std::remove_pointer_t<T>>>
+  constexpr std::enable_if_t<std::is_pointer<T>::value, T>
+  toPtr(WrapFn &&Wrap = WrapFn()) const {
+    uintptr_t IntPtr = static_cast<uintptr_t>(Addr);
+    assert(IntPtr == Addr && "ExecutorAddr value out of range for uintptr_t");
+    return Wrap(reinterpret_cast<T>(IntPtr));
+  }
+
+  /// Cast this ExecutorAddr to a pointer of the given function type.
+  template <typename T, typename WrapFn = defaultWrap<T>>
+  constexpr std::enable_if_t<std::is_function<T>::value, T *>
+  toPtr(WrapFn &&Wrap = WrapFn()) const {
+    uintptr_t IntPtr = static_cast<uintptr_t>(Addr);
+    assert(IntPtr == Addr && "ExecutorAddr value out of range for uintptr_t");
+    return Wrap(reinterpret_cast<T *>(IntPtr));
+  }
+
+  constexpr uint64_t getValue() const noexcept { return Addr; }
+  constexpr void setValue(uint64_t Addr) noexcept { this->Addr = Addr; }
+  constexpr bool isNull() const noexcept { return Addr == 0; }
+
+  constexpr explicit operator bool() const noexcept { return Addr != 0; }
+
+  friend constexpr bool operator==(const ExecutorAddr &LHS,
+                                   const ExecutorAddr &RHS) noexcept {
+    return LHS.Addr == RHS.Addr;
+  }
+
+  friend constexpr bool operator!=(const ExecutorAddr &LHS,
+                                   const ExecutorAddr &RHS) noexcept {
+    return LHS.Addr != RHS.Addr;
+  }
+
+  friend constexpr bool operator<(const ExecutorAddr &LHS,
+                                  const ExecutorAddr &RHS) noexcept {
+    return LHS.Addr < RHS.Addr;
+  }
+
+  friend constexpr bool operator<=(const ExecutorAddr &LHS,
+                                   const ExecutorAddr &RHS) noexcept {
+    return LHS.Addr <= RHS.Addr;
+  }
+
+  friend constexpr bool operator>(const ExecutorAddr &LHS,
+                                  const ExecutorAddr &RHS) noexcept {
+    return LHS.Addr > RHS.Addr;
+  }
+
+  friend constexpr bool operator>=(const ExecutorAddr &LHS,
+                                   const ExecutorAddr &RHS) noexcept {
+    return LHS.Addr >= RHS.Addr;
+  }
+
+  constexpr ExecutorAddr &operator++() noexcept {
+    ++Addr;
+    return *this;
+  }
+  constexpr ExecutorAddr &operator--() noexcept {
+    --Addr;
+    return *this;
+  }
+  constexpr ExecutorAddr operator++(int) noexcept {
+    return ExecutorAddr(Addr++);
+  }
+  constexpr ExecutorAddr operator--(int) noexcept {
+    return ExecutorAddr(Addr++);
+  }
+
+  constexpr ExecutorAddr &operator+=(const ExecutorAddrDiff Delta) noexcept {
+    Addr += Delta;
+    return *this;
+  }
+
+  constexpr ExecutorAddr &operator-=(const ExecutorAddrDiff Delta) noexcept {
+    Addr -= Delta;
+    return *this;
+  }
+
+private:
+  uint64_t Addr = 0;
+};
+
+/// Subtracting two addresses yields an offset.
+inline constexpr ExecutorAddrDiff operator-(const ExecutorAddr &LHS,
+                                            const ExecutorAddr &RHS) noexcept {
+  return ExecutorAddrDiff(LHS.getValue() - RHS.getValue());
+}
+
+/// Adding an offset and an address yields an address.
+inline constexpr ExecutorAddr operator+(const ExecutorAddr &LHS,
+                                        const ExecutorAddrDiff &RHS) noexcept {
+  return ExecutorAddr(LHS.getValue() + RHS);
+}
+
+/// Adding an address and an offset yields an address.
+inline constexpr ExecutorAddr operator+(const ExecutorAddrDiff &LHS,
+                                        const ExecutorAddr &RHS) noexcept {
+  return ExecutorAddr(LHS + RHS.getValue());
+}
+
+/// Represents an address range in the exceutor process.
+struct ExecutorAddrRange {
+  constexpr ExecutorAddrRange() noexcept = default;
+  constexpr ExecutorAddrRange(ExecutorAddr Start, ExecutorAddr End) noexcept
+      : Start(Start), End(End) {}
+  constexpr ExecutorAddrRange(ExecutorAddr Start,
+                              ExecutorAddrDiff Size) noexcept
+      : Start(Start), End(Start + Size) {}
+
+  constexpr bool empty() const noexcept { return Start == End; }
+  constexpr ExecutorAddrDiff size() const noexcept { return End - Start; }
+
+  friend constexpr bool operator==(const ExecutorAddrRange &LHS,
+                                   const ExecutorAddrRange &RHS) noexcept {
+    return LHS.Start == RHS.Start && LHS.End == RHS.End;
+  }
+  friend constexpr bool operator!=(const ExecutorAddrRange &LHS,
+                                   const ExecutorAddrRange &RHS) noexcept {
+    return !(LHS == RHS);
+  }
+  constexpr bool contains(ExecutorAddr Addr) const noexcept {
+    return Start <= Addr && Addr < End;
+  }
+  constexpr bool overlaps(const ExecutorAddrRange &Other) const noexcept {
+    return !(Other.End <= Start || End <= Other.Start);
+  }
+
+  template <typename T> constexpr span<T> toSpan() const noexcept {
+    assert(size() % sizeof(T) == 0 &&
+           "AddressRange is not a multiple of sizeof(T)");
+    return span<T>(Start.toPtr<T *>(), size() / sizeof(T));
+  }
+
+  ExecutorAddr Start;
+  ExecutorAddr End;
+};
+
+} // namespace orc_rt
+
+// Make ExecutorAddr hashable.
+template <> struct std::hash<orc_rt::ExecutorAddr> {
+  constexpr size_t operator()(const orc_rt::ExecutorAddr &A) const noexcept {
+    return std::hash<uint64_t>()(A.getValue());
+  }
+};
+
+#endif // ORC_RT_EXECUTORADDRESS_H
diff --git a/orc-rt/unittests/CMakeLists.txt b/orc-rt/unittests/CMakeLists.txt
index 2abbdd7..af1f961 100644
--- a/orc-rt/unittests/CMakeLists.txt
+++ b/orc-rt/unittests/CMakeLists.txt
@@ -14,6 +14,7 @@
 add_orc_rt_unittest(CoreTests
   BitmaskEnumTest.cpp
   ErrorTest.cpp
+  ExecutorAddressTest.cpp
   MathTest.cpp
   RTTITest.cpp
   WrapperFunctionResultTest.cpp
diff --git a/orc-rt/unittests/ExecutorAddressTest.cpp b/orc-rt/unittests/ExecutorAddressTest.cpp
new file mode 100644
index 0000000..98074a7
--- /dev/null
+++ b/orc-rt/unittests/ExecutorAddressTest.cpp
@@ -0,0 +1,111 @@
+//===- executorAddressTest.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
+//
+//===----------------------------------------------------------------------===//
+//
+// Test ExecutorAddress.h APIs.
+//
+//===----------------------------------------------------------------------===//
+
+#include "orc-rt/ExecutorAddress.h"
+#include "gtest/gtest.h"
+
+using namespace orc_rt;
+
+TEST(ExecutorAddrTest, DefaultAndNull) {
+  // Check that default constructed values and isNull behave as expected.
+
+  ExecutorAddr Default;
+  ExecutorAddr Null(0);
+  ExecutorAddr NonNull(1);
+
+  EXPECT_TRUE(Null.isNull());
+  EXPECT_EQ(Default, Null);
+
+  EXPECT_FALSE(NonNull.isNull());
+  EXPECT_NE(Default, NonNull);
+}
+
+TEST(ExecutorAddrTest, Ordering) {
+  // Check that ordering operations.
+  ExecutorAddr A1(1), A2(2);
+
+  EXPECT_LE(A1, A1);
+  EXPECT_LT(A1, A2);
+  EXPECT_GT(A2, A1);
+  EXPECT_GE(A2, A2);
+}
+
+TEST(ExecutorAddrTest, PtrConversion) {
+  // Test toPtr / fromPtr round-tripping.
+  int X = 0;
+  auto XAddr = ExecutorAddr::fromPtr(&X);
+  int *XPtr = XAddr.toPtr<int *>();
+
+  EXPECT_EQ(XPtr, &X);
+}
+
+static void F() {}
+
+TEST(ExecutorAddrTest, PtrConversionWithFunctionType) {
+  // Test that function types (as opposed to function pointer types) can be
+  // used with toPtr.
+  auto FAddr = ExecutorAddr::fromPtr(F);
+  void (*FPtr)() = FAddr.toPtr<void()>();
+
+  EXPECT_EQ(FPtr, &F);
+}
+
+TEST(ExecutorAddrTest, WrappingAndUnwrapping) {
+  constexpr uintptr_t RawAddr = 0x123456;
+  int *RawPtr = (int *)RawAddr;
+
+  constexpr uintptr_t TagOffset = 8 * (sizeof(uintptr_t) - 1);
+  uintptr_t TagVal = 0xA5;
+  uintptr_t TagBits = TagVal << TagOffset;
+  void *TaggedPtr = (void *)((uintptr_t)RawPtr | TagBits);
+
+  ExecutorAddr EA =
+      ExecutorAddr::fromPtr(TaggedPtr, ExecutorAddr::Untag(8, TagOffset));
+
+  EXPECT_EQ(EA.getValue(), RawAddr);
+
+  void *ReconstitutedTaggedPtr =
+      EA.toPtr<void *>(ExecutorAddr::Tag(TagVal, TagOffset));
+
+  EXPECT_EQ(TaggedPtr, ReconstitutedTaggedPtr);
+}
+
+TEST(ExecutorAddrTest, AddrRanges) {
+  ExecutorAddr A0(0), A1(1), A2(2), A3(3);
+  ExecutorAddrRange R0(A0, A1), R1(A1, A2), R2(A2, A3), R3(A0, A2), R4(A1, A3);
+  //     012
+  // R0: #      -- Before R1
+  // R1:  #     --
+  // R2:   #    -- After R1
+  // R3: ##     -- Overlaps R1 start
+  // R4:  ##    -- Overlaps R1 end
+
+  EXPECT_EQ(R1, ExecutorAddrRange(A1, A2));
+  EXPECT_EQ(R1, ExecutorAddrRange(A1, ExecutorAddrDiff(1)));
+  EXPECT_NE(R1, R2);
+
+  EXPECT_TRUE(R1.contains(A1));
+  EXPECT_FALSE(R1.contains(A0));
+  EXPECT_FALSE(R1.contains(A2));
+
+  EXPECT_FALSE(R1.overlaps(R0));
+  EXPECT_FALSE(R1.overlaps(R2));
+  EXPECT_TRUE(R1.overlaps(R3));
+  EXPECT_TRUE(R1.overlaps(R4));
+}
+
+TEST(ExecutorAddrTest, Hashable) {
+  uint64_t RawAddr = 0x1234567890ABCDEF;
+  ExecutorAddr Addr(RawAddr);
+
+  EXPECT_EQ(std::hash<uint64_t>()(RawAddr), std::hash<ExecutorAddr>()(Addr));
+}