Skip to content

Commit da99853

Browse files
authored
[orc-rt] Add ExecutorAddress.h: ExecutorAddr and ExecutorAddrRange. (llvm#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.
1 parent 68964f5 commit da99853

File tree

4 files changed

+343
-0
lines changed

4 files changed

+343
-0
lines changed

orc-rt/include/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ set(ORC_RT_HEADERS
55
orc-rt/BitmaskEnum.h
66
orc-rt/Compiler.h
77
orc-rt/Error.h
8+
orc-rt/ExecutorAddr.h
89
orc-rt/Math.h
910
orc-rt/RTTI.h
1011
orc-rt/WrapperFunctionResult.h
Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
//===------ ExecutorAddress.h - Executing process address -------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// Utilites for representing addresses and address ranges in the executing
10+
// program that can be shared with an ORC controller.
11+
//
12+
//===----------------------------------------------------------------------===//
13+
14+
#ifndef ORC_RT_EXECUTORADDRESS_H
15+
#define ORC_RT_EXECUTORADDRESS_H
16+
17+
#include "span.h"
18+
19+
#include <cassert>
20+
#include <cstdint>
21+
#include <functional>
22+
#include <type_traits>
23+
24+
namespace orc_rt {
25+
26+
using ExecutorAddrDiff = uint64_t;
27+
28+
/// Represents an address in the executor process.
29+
class ExecutorAddr {
30+
public:
31+
/// Return pointer unmodified.
32+
template <typename T> struct rawPtr {
33+
T *operator()(T *p) const { return p; }
34+
};
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(~(((uintptr_t(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+
72+
constexpr ExecutorAddr() noexcept = default;
73+
explicit constexpr ExecutorAddr(uint64_t Addr) noexcept : Addr(Addr) {}
74+
75+
/// Create an ExecutorAddr from the given pointer.
76+
template <typename T, typename UnwrapFn = defaultUnwrap<T>>
77+
static constexpr ExecutorAddr fromPtr(T *Ptr,
78+
UnwrapFn &&Unwrap = UnwrapFn()) {
79+
return ExecutorAddr(
80+
static_cast<uint64_t>(reinterpret_cast<uintptr_t>(Unwrap(Ptr))));
81+
}
82+
83+
/// Cast this ExecutorAddr to a pointer of the given type.
84+
template <typename T, typename WrapFn = defaultWrap<std::remove_pointer_t<T>>>
85+
constexpr std::enable_if_t<std::is_pointer<T>::value, T>
86+
toPtr(WrapFn &&Wrap = WrapFn()) const {
87+
uintptr_t IntPtr = static_cast<uintptr_t>(Addr);
88+
assert(IntPtr == Addr && "ExecutorAddr value out of range for uintptr_t");
89+
return Wrap(reinterpret_cast<T>(IntPtr));
90+
}
91+
92+
/// Cast this ExecutorAddr to a pointer of the given function type.
93+
template <typename T, typename WrapFn = defaultWrap<T>>
94+
constexpr std::enable_if_t<std::is_function<T>::value, T *>
95+
toPtr(WrapFn &&Wrap = WrapFn()) const {
96+
uintptr_t IntPtr = static_cast<uintptr_t>(Addr);
97+
assert(IntPtr == Addr && "ExecutorAddr value out of range for uintptr_t");
98+
return Wrap(reinterpret_cast<T *>(IntPtr));
99+
}
100+
101+
constexpr uint64_t getValue() const noexcept { return Addr; }
102+
constexpr void setValue(uint64_t Addr) noexcept { this->Addr = Addr; }
103+
constexpr bool isNull() const noexcept { return Addr == 0; }
104+
105+
constexpr explicit operator bool() const noexcept { return Addr != 0; }
106+
107+
friend constexpr bool operator==(const ExecutorAddr &LHS,
108+
const ExecutorAddr &RHS) noexcept {
109+
return LHS.Addr == RHS.Addr;
110+
}
111+
112+
friend constexpr bool operator!=(const ExecutorAddr &LHS,
113+
const ExecutorAddr &RHS) noexcept {
114+
return LHS.Addr != RHS.Addr;
115+
}
116+
117+
friend constexpr bool operator<(const ExecutorAddr &LHS,
118+
const ExecutorAddr &RHS) noexcept {
119+
return LHS.Addr < RHS.Addr;
120+
}
121+
122+
friend constexpr bool operator<=(const ExecutorAddr &LHS,
123+
const ExecutorAddr &RHS) noexcept {
124+
return LHS.Addr <= RHS.Addr;
125+
}
126+
127+
friend constexpr bool operator>(const ExecutorAddr &LHS,
128+
const ExecutorAddr &RHS) noexcept {
129+
return LHS.Addr > RHS.Addr;
130+
}
131+
132+
friend constexpr bool operator>=(const ExecutorAddr &LHS,
133+
const ExecutorAddr &RHS) noexcept {
134+
return LHS.Addr >= RHS.Addr;
135+
}
136+
137+
constexpr ExecutorAddr &operator++() noexcept {
138+
++Addr;
139+
return *this;
140+
}
141+
constexpr ExecutorAddr &operator--() noexcept {
142+
--Addr;
143+
return *this;
144+
}
145+
constexpr ExecutorAddr operator++(int) noexcept {
146+
return ExecutorAddr(Addr++);
147+
}
148+
constexpr ExecutorAddr operator--(int) noexcept {
149+
return ExecutorAddr(Addr++);
150+
}
151+
152+
constexpr ExecutorAddr &operator+=(const ExecutorAddrDiff Delta) noexcept {
153+
Addr += Delta;
154+
return *this;
155+
}
156+
157+
constexpr ExecutorAddr &operator-=(const ExecutorAddrDiff Delta) noexcept {
158+
Addr -= Delta;
159+
return *this;
160+
}
161+
162+
private:
163+
uint64_t Addr = 0;
164+
};
165+
166+
/// Subtracting two addresses yields an offset.
167+
inline constexpr ExecutorAddrDiff operator-(const ExecutorAddr &LHS,
168+
const ExecutorAddr &RHS) noexcept {
169+
return ExecutorAddrDiff(LHS.getValue() - RHS.getValue());
170+
}
171+
172+
/// Adding an offset and an address yields an address.
173+
inline constexpr ExecutorAddr operator+(const ExecutorAddr &LHS,
174+
const ExecutorAddrDiff &RHS) noexcept {
175+
return ExecutorAddr(LHS.getValue() + RHS);
176+
}
177+
178+
/// Adding an address and an offset yields an address.
179+
inline constexpr ExecutorAddr operator+(const ExecutorAddrDiff &LHS,
180+
const ExecutorAddr &RHS) noexcept {
181+
return ExecutorAddr(LHS + RHS.getValue());
182+
}
183+
184+
/// Represents an address range in the exceutor process.
185+
struct ExecutorAddrRange {
186+
constexpr ExecutorAddrRange() noexcept = default;
187+
constexpr ExecutorAddrRange(ExecutorAddr Start, ExecutorAddr End) noexcept
188+
: Start(Start), End(End) {}
189+
constexpr ExecutorAddrRange(ExecutorAddr Start,
190+
ExecutorAddrDiff Size) noexcept
191+
: Start(Start), End(Start + Size) {}
192+
193+
constexpr bool empty() const noexcept { return Start == End; }
194+
constexpr ExecutorAddrDiff size() const noexcept { return End - Start; }
195+
196+
friend constexpr bool operator==(const ExecutorAddrRange &LHS,
197+
const ExecutorAddrRange &RHS) noexcept {
198+
return LHS.Start == RHS.Start && LHS.End == RHS.End;
199+
}
200+
friend constexpr bool operator!=(const ExecutorAddrRange &LHS,
201+
const ExecutorAddrRange &RHS) noexcept {
202+
return !(LHS == RHS);
203+
}
204+
constexpr bool contains(ExecutorAddr Addr) const noexcept {
205+
return Start <= Addr && Addr < End;
206+
}
207+
constexpr bool overlaps(const ExecutorAddrRange &Other) const noexcept {
208+
return !(Other.End <= Start || End <= Other.Start);
209+
}
210+
211+
template <typename T> constexpr span<T> toSpan() const noexcept {
212+
assert(size() % sizeof(T) == 0 &&
213+
"AddressRange is not a multiple of sizeof(T)");
214+
return span<T>(Start.toPtr<T *>(), size() / sizeof(T));
215+
}
216+
217+
ExecutorAddr Start;
218+
ExecutorAddr End;
219+
};
220+
221+
} // namespace orc_rt
222+
223+
// Make ExecutorAddr hashable.
224+
template <> struct std::hash<orc_rt::ExecutorAddr> {
225+
constexpr size_t operator()(const orc_rt::ExecutorAddr &A) const noexcept {
226+
return std::hash<uint64_t>()(A.getValue());
227+
}
228+
};
229+
230+
#endif // ORC_RT_EXECUTORADDRESS_H

orc-rt/unittests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ endfunction()
1414
add_orc_rt_unittest(CoreTests
1515
BitmaskEnumTest.cpp
1616
ErrorTest.cpp
17+
ExecutorAddressTest.cpp
1718
MathTest.cpp
1819
RTTITest.cpp
1920
WrapperFunctionResultTest.cpp
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
//===- executorAddressTest.cpp --------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// Test ExecutorAddress.h APIs.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "orc-rt/ExecutorAddress.h"
14+
#include "gtest/gtest.h"
15+
16+
using namespace orc_rt;
17+
18+
TEST(ExecutorAddrTest, DefaultAndNull) {
19+
// Check that default constructed values and isNull behave as expected.
20+
21+
ExecutorAddr Default;
22+
ExecutorAddr Null(0);
23+
ExecutorAddr NonNull(1);
24+
25+
EXPECT_TRUE(Null.isNull());
26+
EXPECT_EQ(Default, Null);
27+
28+
EXPECT_FALSE(NonNull.isNull());
29+
EXPECT_NE(Default, NonNull);
30+
}
31+
32+
TEST(ExecutorAddrTest, Ordering) {
33+
// Check that ordering operations.
34+
ExecutorAddr A1(1), A2(2);
35+
36+
EXPECT_LE(A1, A1);
37+
EXPECT_LT(A1, A2);
38+
EXPECT_GT(A2, A1);
39+
EXPECT_GE(A2, A2);
40+
}
41+
42+
TEST(ExecutorAddrTest, PtrConversion) {
43+
// Test toPtr / fromPtr round-tripping.
44+
int X = 0;
45+
auto XAddr = ExecutorAddr::fromPtr(&X);
46+
int *XPtr = XAddr.toPtr<int *>();
47+
48+
EXPECT_EQ(XPtr, &X);
49+
}
50+
51+
static void F() {}
52+
53+
TEST(ExecutorAddrTest, PtrConversionWithFunctionType) {
54+
// Test that function types (as opposed to function pointer types) can be
55+
// used with toPtr.
56+
auto FAddr = ExecutorAddr::fromPtr(F);
57+
void (*FPtr)() = FAddr.toPtr<void()>();
58+
59+
EXPECT_EQ(FPtr, &F);
60+
}
61+
62+
TEST(ExecutorAddrTest, WrappingAndUnwrapping) {
63+
constexpr uintptr_t RawAddr = 0x123456;
64+
int *RawPtr = (int *)RawAddr;
65+
66+
constexpr uintptr_t TagOffset = 8 * (sizeof(uintptr_t) - 1);
67+
uintptr_t TagVal = 0xA5;
68+
uintptr_t TagBits = TagVal << TagOffset;
69+
void *TaggedPtr = (void *)((uintptr_t)RawPtr | TagBits);
70+
71+
ExecutorAddr EA =
72+
ExecutorAddr::fromPtr(TaggedPtr, ExecutorAddr::Untag(8, TagOffset));
73+
74+
EXPECT_EQ(EA.getValue(), RawAddr);
75+
76+
void *ReconstitutedTaggedPtr =
77+
EA.toPtr<void *>(ExecutorAddr::Tag(TagVal, TagOffset));
78+
79+
EXPECT_EQ(TaggedPtr, ReconstitutedTaggedPtr);
80+
}
81+
82+
TEST(ExecutorAddrTest, AddrRanges) {
83+
ExecutorAddr A0(0), A1(1), A2(2), A3(3);
84+
ExecutorAddrRange R0(A0, A1), R1(A1, A2), R2(A2, A3), R3(A0, A2), R4(A1, A3);
85+
// 012
86+
// R0: # -- Before R1
87+
// R1: # --
88+
// R2: # -- After R1
89+
// R3: ## -- Overlaps R1 start
90+
// R4: ## -- Overlaps R1 end
91+
92+
EXPECT_EQ(R1, ExecutorAddrRange(A1, A2));
93+
EXPECT_EQ(R1, ExecutorAddrRange(A1, ExecutorAddrDiff(1)));
94+
EXPECT_NE(R1, R2);
95+
96+
EXPECT_TRUE(R1.contains(A1));
97+
EXPECT_FALSE(R1.contains(A0));
98+
EXPECT_FALSE(R1.contains(A2));
99+
100+
EXPECT_FALSE(R1.overlaps(R0));
101+
EXPECT_FALSE(R1.overlaps(R2));
102+
EXPECT_TRUE(R1.overlaps(R3));
103+
EXPECT_TRUE(R1.overlaps(R4));
104+
}
105+
106+
TEST(ExecutorAddrTest, Hashable) {
107+
uint64_t RawAddr = 0x1234567890ABCDEF;
108+
ExecutorAddr Addr(RawAddr);
109+
110+
EXPECT_EQ(std::hash<uint64_t>()(RawAddr), std::hash<ExecutorAddr>()(Addr));
111+
}

0 commit comments

Comments
 (0)