Skip to content

Commit f14cb49

Browse files
committed
[ORC] Add "wrap" and "unwrap" steps to ExecutorAddr toPtr/fromPtr.
The wrap/unwrap operations are applied to pointers after/before conversion to/from raw addresses. They can be used to tag, untag, sign, or strip signing from pointers. They currently default to 'rawPtr' (identity) on all platforms, but it is expected that the default will be set based on the host architecture, e.g. they would default to signing/stripping for arm64e.
1 parent fb0f44b commit f14cb49

File tree

2 files changed

+71
-8
lines changed

2 files changed

+71
-8
lines changed

llvm/include/llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#define LLVM_EXECUTIONENGINE_ORC_SHARED_EXECUTORADDRESS_H
1515

1616
#include "llvm/ADT/DenseMapInfo.h"
17+
#include "llvm/ADT/identity.h"
1718
#include "llvm/ExecutionEngine/Orc/Shared/SimplePackedSerialization.h"
1819
#include "llvm/Support/FormatVariadic.h"
1920
#include "llvm/Support/raw_ostream.h"
@@ -29,34 +30,76 @@ using ExecutorAddrDiff = uint64_t;
2930
/// Represents an address in the executor process.
3031
class ExecutorAddr {
3132
public:
33+
/// A wrap/unwrap function that leaves pointers unmodified.
34+
template <typename T> using rawPtr = llvm::identity<T>;
35+
36+
/// Default wrap function to use on this host.
37+
template <typename T> using defaultWrap = rawPtr<T>;
38+
39+
/// Default unwrap function to use on this host.
40+
template <typename T> using defaultUnwrap = rawPtr<T>;
41+
42+
/// Merges a tag into the raw address value:
43+
/// P' = P | (TagValue << TagOffset).
44+
class Tag {
45+
public:
46+
constexpr Tag(uintptr_t TagValue, uintptr_t TagOffset)
47+
: TagMask(TagValue << TagOffset) {}
48+
49+
template <typename T> constexpr T *operator()(T *P) {
50+
return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(P) | TagMask);
51+
}
52+
53+
private:
54+
uintptr_t TagMask;
55+
};
56+
57+
/// Strips a tag of the given length from the given offset within the pointer:
58+
/// P' = P & ~(((1 << TagLen) -1) << TagOffset)
59+
class Untag {
60+
public:
61+
constexpr Untag(uintptr_t TagLen, uintptr_t TagOffset)
62+
: UntagMask(~(((1 << TagLen) - 1) << TagOffset)) {}
63+
64+
template <typename T> constexpr T *operator()(T *P) {
65+
return reinterpret_cast<T *>(reinterpret_cast<uintptr_t>(P) & UntagMask);
66+
}
67+
68+
private:
69+
uintptr_t UntagMask;
70+
};
71+
3272
ExecutorAddr() = default;
3373

3474
/// Create an ExecutorAddr from the given value.
3575
explicit constexpr ExecutorAddr(uint64_t Addr) : Addr(Addr) {}
3676

3777
/// Create an ExecutorAddr from the given pointer.
3878
/// Warning: This should only be used when JITing in-process.
39-
template <typename T> static ExecutorAddr fromPtr(T *Value) {
79+
template <typename T, typename UnwrapFn = defaultUnwrap<T *>>
80+
static ExecutorAddr fromPtr(T *Ptr, UnwrapFn &&Unwrap = UnwrapFn()) {
4081
return ExecutorAddr(
41-
static_cast<uint64_t>(reinterpret_cast<uintptr_t>(Value)));
82+
static_cast<uint64_t>(reinterpret_cast<uintptr_t>(Unwrap(Ptr))));
4283
}
4384

4485
/// Cast this ExecutorAddr to a pointer of the given type.
4586
/// Warning: This should only be used when JITing in-process.
46-
template <typename T>
47-
std::enable_if_t<std::is_pointer<T>::value, T> toPtr() const {
87+
template <typename T, typename WrapFn = defaultWrap<T>>
88+
std::enable_if_t<std::is_pointer<T>::value, T>
89+
toPtr(WrapFn &&Wrap = WrapFn()) const {
4890
uintptr_t IntPtr = static_cast<uintptr_t>(Addr);
4991
assert(IntPtr == Addr && "ExecutorAddr value out of range for uintptr_t");
50-
return reinterpret_cast<T>(IntPtr);
92+
return Wrap(reinterpret_cast<T>(IntPtr));
5193
}
5294

5395
/// Cast this ExecutorAddr to a pointer of the given function type.
5496
/// Warning: This should only be used when JITing in-process.
55-
template <typename T>
56-
std::enable_if_t<std::is_function<T>::value, T *> toPtr() const {
97+
template <typename T, typename WrapFn = defaultWrap<T *>>
98+
std::enable_if_t<std::is_function<T>::value, T *>
99+
toPtr(WrapFn &&Wrap = WrapFn()) const {
57100
uintptr_t IntPtr = static_cast<uintptr_t>(Addr);
58101
assert(IntPtr == Addr && "ExecutorAddr value out of range for uintptr_t");
59-
return reinterpret_cast<T *>(IntPtr);
102+
return Wrap(reinterpret_cast<T *>(IntPtr));
60103
}
61104

62105
uint64_t getValue() const { return Addr; }

llvm/unittests/ExecutionEngine/Orc/ExecutorAddressTest.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,26 @@ TEST(ExecutorAddrTest, PtrConversionWithFunctionType) {
5858
EXPECT_EQ(FPtr, &F);
5959
}
6060

61+
TEST(ExecutorAddrTest, WrappingAndUnwrapping) {
62+
constexpr uintptr_t RawAddr = 0x123456;
63+
int *RawPtr = (int *)RawAddr;
64+
65+
constexpr uintptr_t TagOffset = 8 * (sizeof(uintptr_t) - 1);
66+
uintptr_t TagVal = 0xA5;
67+
uintptr_t TagBits = TagVal << TagOffset;
68+
void *TaggedPtr = (void *)((uintptr_t)RawPtr | TagBits);
69+
70+
ExecutorAddr EA =
71+
ExecutorAddr::fromPtr(TaggedPtr, ExecutorAddr::Untag(8, TagOffset));
72+
73+
EXPECT_EQ(EA.getValue(), RawAddr);
74+
75+
void *ReconstitutedTaggedPtr =
76+
EA.toPtr<void *>(ExecutorAddr::Tag(TagVal, TagOffset));
77+
78+
EXPECT_EQ(TaggedPtr, ReconstitutedTaggedPtr);
79+
}
80+
6181
TEST(ExecutorAddrTest, AddrRanges) {
6282
ExecutorAddr A0(0), A1(1), A2(2), A3(3);
6383
ExecutorAddrRange R0(A0, A1), R1(A1, A2), R2(A2, A3), R3(A0, A2), R4(A1, A3);

0 commit comments

Comments
 (0)