Skip to content

Commit 7d069b1

Browse files
committed
[ORC] Add Auto-Loading DyLib Feature with Symbol Resolution
1 parent 9ed46fb commit 7d069b1

17 files changed

+2598
-6
lines changed

llvm/include/llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#define LLVM_EXECUTIONENGINE_ORC_EPCDYNAMICLIBRARYSEARCHGENERATOR_H
1616

1717
#include "llvm/ADT/FunctionExtras.h"
18+
#include "llvm/ADT/StringSet.h"
1819
#include "llvm/ExecutionEngine/Orc/Core.h"
1920

2021
namespace llvm {
@@ -71,6 +72,47 @@ class EPCDynamicLibrarySearchGenerator : public DefinitionGenerator {
7172
AddAbsoluteSymbolsFn AddAbsoluteSymbols;
7273
};
7374

75+
class AutoLoadDynamicLibrarySearchGenerator : public DefinitionGenerator {
76+
public:
77+
using AddAbsoluteSymbolsFn = unique_function<Error(JITDylib &, SymbolMap)>;
78+
79+
/// Creates an AutoLoadDynamicLibrarySearchGenerator that searches for symbols
80+
/// across all currently loaded libraries. If a symbol is not found, it scans
81+
/// all potential dynamic libraries (dylibs), and if the symbol is located,
82+
/// the corresponding library is loaded, and the symbol's definition is
83+
/// returned.
84+
///
85+
/// If \p AddAbsoluteSymbols is provided, it is used to add the symbols to the
86+
/// \c JITDylib; otherwise it uses JD.define(absoluteSymbols(...)).
87+
AutoLoadDynamicLibrarySearchGenerator(
88+
ExecutionSession &ES, AddAbsoluteSymbolsFn AddAbsoluteSymbols = nullptr)
89+
: EPC(ES.getExecutorProcessControl()),
90+
AddAbsoluteSymbols(std::move(AddAbsoluteSymbols)) {}
91+
92+
/// Creates a AutoLoadDynamicLibrarySearchGenerator that searches for symbols
93+
/// in the target process.
94+
static Expected<std::unique_ptr<AutoLoadDynamicLibrarySearchGenerator>>
95+
GetForTargetProcess(ExecutionSession &ES,
96+
AddAbsoluteSymbolsFn AddAbsoluteSymbols = nullptr) {
97+
return std::make_unique<AutoLoadDynamicLibrarySearchGenerator>(
98+
ES, std::move(AddAbsoluteSymbols));
99+
}
100+
101+
Error tryToGenerate(LookupState &LS, LookupKind K, JITDylib &JD,
102+
JITDylibLookupFlags JDLookupFlags,
103+
const SymbolLookupSet &Symbols) override;
104+
105+
Error
106+
tryToResolve(SymbolNameSet CandidateSyms,
107+
ExecutorProcessControl::ResolveSymbolsCompleteFn OnCompleteFn);
108+
109+
private:
110+
ExecutorProcessControl &EPC;
111+
BloomFilter GlobalFilter;
112+
StringSet<> ExcludedSymbols;
113+
AddAbsoluteSymbolsFn AddAbsoluteSymbols;
114+
};
115+
74116
} // end namespace orc
75117
} // end namespace llvm
76118

llvm/include/llvm/ExecutionEngine/Orc/EPCGenericDylibManager.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class EPCGenericDylibManager {
3434
ExecutorAddr Instance;
3535
ExecutorAddr Open;
3636
ExecutorAddr Lookup;
37+
ExecutorAddr Resolve;
3738
};
3839

3940
/// Create an EPCGenericMemoryAccess instance from a given set of
@@ -70,6 +71,9 @@ class EPCGenericDylibManager {
7071
using SymbolLookupCompleteFn =
7172
unique_function<void(Expected<std::vector<ExecutorSymbolDef>>)>;
7273

74+
using ResolveSymbolsCompleteFn =
75+
unique_function<void(Expected<ResolveResult>)>;
76+
7377
/// Looks up symbols within the given dylib.
7478
void lookupAsync(tpctypes::DylibHandle H, const SymbolLookupSet &Lookup,
7579
SymbolLookupCompleteFn Complete);
@@ -78,6 +82,14 @@ class EPCGenericDylibManager {
7882
void lookupAsync(tpctypes::DylibHandle H, const RemoteSymbolLookupSet &Lookup,
7983
SymbolLookupCompleteFn Complete);
8084

85+
/// Looks up symbols within the given dylib.
86+
void resolveAsync(const SymbolLookupSet &Lookup,
87+
ResolveSymbolsCompleteFn Complete);
88+
89+
/// Looks up symbols within the given dylib.
90+
void resolveAsync(const RemoteSymbolLookupSet &Lookup,
91+
ResolveSymbolsCompleteFn Complete);
92+
8193
private:
8294
ExecutorProcessControl &EPC;
8395
SymbolAddrs SAs;

llvm/include/llvm/ExecutionEngine/Orc/ExecutorProcessControl.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
#include "llvm/ADT/StringRef.h"
1717
#include "llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h"
18+
#include "llvm/ExecutionEngine/Orc/Shared/AutoLoadDylibUtils.h"
1819
#include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h"
1920
#include "llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h"
2021
#include "llvm/ExecutionEngine/Orc/Shared/WrapperFunctionUtils.h"
@@ -302,6 +303,9 @@ class ExecutorProcessControl {
302303
using SymbolLookupCompleteFn =
303304
unique_function<void(Expected<std::vector<tpctypes::LookupResult>>)>;
304305

306+
using ResolveSymbolsCompleteFn = unique_function<void(
307+
Expected<std::vector<ResolveResult>>)>;
308+
305309
/// Search for symbols in the target process.
306310
///
307311
/// The result of the lookup is a 2-dimensional array of target addresses
@@ -311,6 +315,9 @@ class ExecutorProcessControl {
311315
virtual void lookupSymbolsAsync(ArrayRef<LookupRequest> Request,
312316
SymbolLookupCompleteFn F) = 0;
313317

318+
virtual void resolveSymbolsAsync(ArrayRef<SymbolLookupSet> Request,
319+
ResolveSymbolsCompleteFn F) = 0;
320+
314321
/// Run function with a main-like signature.
315322
virtual Expected<int32_t> runAsMain(ExecutorAddr MainFnAddr,
316323
ArrayRef<std::string> Args) = 0;
@@ -485,6 +492,11 @@ class UnsupportedExecutorProcessControl : public ExecutorProcessControl,
485492
llvm_unreachable("Unsupported");
486493
}
487494

495+
void resolveSymbolsAsync(ArrayRef<SymbolLookupSet> Request,
496+
ResolveSymbolsCompleteFn F) override {
497+
llvm_unreachable("Unsupported");
498+
}
499+
488500
Expected<int32_t> runAsMain(ExecutorAddr MainFnAddr,
489501
ArrayRef<std::string> Args) override {
490502
llvm_unreachable("Unsupported");
@@ -531,6 +543,9 @@ class SelfExecutorProcessControl : public ExecutorProcessControl,
531543
void lookupSymbolsAsync(ArrayRef<LookupRequest> Request,
532544
SymbolLookupCompleteFn F) override;
533545

546+
void resolveSymbolsAsync(ArrayRef<SymbolLookupSet> Request,
547+
ResolveSymbolsCompleteFn F) override;
548+
534549
Expected<int32_t> runAsMain(ExecutorAddr MainFnAddr,
535550
ArrayRef<std::string> Args) override;
536551

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
//===------ AutoLoadDylibUtils.h - Auto-Loading Dynamic Library -------*- C++
2+
//-*-===//
3+
//
4+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5+
// See https://llvm.org/LICENSE.txt for license information.
6+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7+
//
8+
//===----------------------------------------------------------------------===//
9+
10+
#ifndef LLVM_EXECUTIONENGINE_ORC_SHARED_AUTOLOADDYLIBUTILS_H
11+
#define LLVM_EXECUTIONENGINE_ORC_SHARED_AUTOLOADDYLIBUTILS_H
12+
13+
#include "llvm/ExecutionEngine/Orc/Shared/ExecutorSymbolDef.h"
14+
#include "llvm/ExecutionEngine/Orc/Shared/SimplePackedSerialization.h"
15+
16+
#include <math.h>
17+
#include <type_traits>
18+
#include <vector>
19+
20+
namespace llvm {
21+
namespace orc {
22+
23+
namespace shared {
24+
using SPSBloomFilter =
25+
SPSTuple<bool, uint32_t, uint32_t, uint32_t, SPSSequence<uint64_t>>;
26+
}
27+
28+
constexpr uint32_t log2u(std::uint32_t n) {
29+
return (n > 1) ? 1 + log2u(n >> 1) : 0;
30+
}
31+
32+
class BloomFilter {
33+
private:
34+
static constexpr int Bits = 8 * sizeof(uint64_t);
35+
static constexpr float P = 0.02f;
36+
37+
bool Initialized = false;
38+
uint32_t SymbolsCount = 0;
39+
uint32_t BloomSize = 0;
40+
uint32_t BloomShift = 0;
41+
std::vector<uint64_t> BloomTable;
42+
43+
// This is a GNU implementation of hash used in bloom filter!
44+
static uint32_t GNUHash(StringRef S) {
45+
uint32_t H = 5381;
46+
for (uint8_t C : S)
47+
H = (H << 5) + H + C;
48+
return H;
49+
}
50+
// Helper method for hash testing
51+
bool TestHash(uint32_t hash) const {
52+
assert(IsInitialized && "Bloom filter is not initialized!");
53+
uint32_t hash2 = hash >> BloomShift;
54+
uint32_t n = (hash >> log2u(Bits)) % BloomSize;
55+
uint64_t mask = ((1ULL << (hash % Bits)) | (1ULL << (hash2 % Bits)));
56+
return (mask & BloomTable[n]) == mask;
57+
}
58+
59+
// Helper method to add a hash
60+
void AddHash(uint32_t hash) {
61+
assert(IsInitialized && "Bloom filter is not initialized!");
62+
uint32_t hash2 = hash >> BloomShift;
63+
uint32_t n = (hash >> log2u(Bits)) % BloomSize;
64+
uint64_t mask = ((1ULL << (hash % Bits)) | (1ULL << (hash2 % Bits)));
65+
BloomTable[n] |= mask;
66+
}
67+
68+
// Resizes the Bloom filter table based on symbol count
69+
void ResizeTable(uint32_t newSymbolsCount) {
70+
assert(SymbolsCount == 0 && "Resize not supported after initialization!");
71+
SymbolsCount = newSymbolsCount;
72+
BloomSize =
73+
static_cast<uint32_t>(ceil((-1.44f * SymbolsCount * log2f(P)) / Bits));
74+
BloomShift = std::min(6u, log2u(SymbolsCount));
75+
BloomTable.resize(BloomSize, 0);
76+
}
77+
78+
friend class shared::SPSSerializationTraits<shared::SPSBloomFilter,
79+
BloomFilter>;
80+
81+
public:
82+
BloomFilter() = default;
83+
BloomFilter(const BloomFilter &other) noexcept
84+
: Initialized(other.Initialized), SymbolsCount(other.SymbolsCount),
85+
BloomSize(other.BloomSize), BloomShift(other.BloomShift),
86+
BloomTable(other.BloomTable) {
87+
}
88+
BloomFilter &operator=(const BloomFilter &other) = delete;
89+
90+
BloomFilter(BloomFilter &&other) noexcept
91+
: Initialized(other.Initialized), SymbolsCount(other.SymbolsCount),
92+
BloomSize(other.BloomSize), BloomShift(other.BloomShift),
93+
BloomTable(std::move(other.BloomTable)) {
94+
other.Initialized = false;
95+
other.SymbolsCount = 0;
96+
other.BloomSize = 0;
97+
other.BloomShift = 0;
98+
}
99+
100+
BloomFilter &operator=(BloomFilter &&other) noexcept {
101+
if (this != &other) {
102+
Initialized = other.Initialized;
103+
SymbolsCount = other.SymbolsCount;
104+
BloomSize = other.BloomSize;
105+
BloomShift = other.BloomShift;
106+
BloomTable = std::move(other.BloomTable);
107+
108+
other.Initialized = false;
109+
other.SymbolsCount = 0;
110+
other.BloomSize = 0;
111+
other.BloomShift = 0;
112+
}
113+
return *this;
114+
}
115+
116+
void swap(BloomFilter &other) noexcept {
117+
std::swap(Initialized, other.Initialized);
118+
std::swap(SymbolsCount, other.SymbolsCount);
119+
std::swap(BloomSize, other.BloomSize);
120+
std::swap(BloomShift, other.BloomShift);
121+
std::swap(BloomTable, other.BloomTable);
122+
}
123+
124+
void Initialize(uint32_t newSymbolsCount) {
125+
assert(!Initialized && "Cannot reinitialize the Bloom filter!");
126+
Initialized = true;
127+
ResizeTable(newSymbolsCount);
128+
}
129+
130+
bool IsEmpty() const { return SymbolsCount == 0; }
131+
132+
uint32_t getSymCount() const { return SymbolsCount; }
133+
134+
bool IsInitialized() const { return Initialized; }
135+
136+
bool MayContain(uint32_t hash) const {
137+
if (IsEmpty())
138+
return false;
139+
return TestHash(hash);
140+
}
141+
142+
bool MayContain(StringRef symbol) const {
143+
return MayContain(GNUHash(symbol));
144+
}
145+
146+
void AddSymbol(StringRef symbol) { AddHash(GNUHash(symbol)); }
147+
};
148+
149+
struct ResolveResult {
150+
std::optional<BloomFilter> Filter;
151+
std::vector<ExecutorSymbolDef> SymbolDef;
152+
};
153+
154+
namespace shared {
155+
156+
template <> class SPSSerializationTraits<SPSBloomFilter, BloomFilter> {
157+
public:
158+
static size_t size(const BloomFilter &Filter) {
159+
return SPSBloomFilter::AsArgList::size(
160+
Filter.Initialized, Filter.SymbolsCount, Filter.BloomSize,
161+
Filter.BloomShift, Filter.BloomTable);
162+
}
163+
164+
static bool serialize(SPSOutputBuffer &OB, const BloomFilter &Filter) {
165+
return SPSBloomFilter::AsArgList::serialize(
166+
OB, Filter.Initialized, Filter.SymbolsCount, Filter.BloomSize,
167+
Filter.BloomShift, Filter.BloomTable);
168+
}
169+
170+
static bool deserialize(SPSInputBuffer &IB, BloomFilter &Filter) {
171+
bool IsInitialized;
172+
uint32_t SymbolsCount = 0, BloomSize = 0, BloomShift = 0;
173+
std::vector<uint64_t> BloomTable;
174+
175+
if (!SPSBloomFilter::AsArgList::deserialize(
176+
IB, IsInitialized, SymbolsCount, BloomSize, BloomShift, BloomTable))
177+
return false;
178+
179+
Filter.Initialized = IsInitialized;
180+
Filter.SymbolsCount = SymbolsCount;
181+
Filter.BloomSize = BloomSize;
182+
Filter.BloomShift = BloomShift;
183+
Filter.BloomTable = std::move(BloomTable);
184+
185+
return true;
186+
}
187+
};
188+
189+
using SPSResolveResult =
190+
SPSTuple<SPSOptional<SPSBloomFilter>, SPSSequence<SPSExecutorSymbolDef>>;
191+
template <> class SPSSerializationTraits<SPSResolveResult, ResolveResult> {
192+
public:
193+
static size_t size(const ResolveResult &Result) {
194+
return SPSResolveResult::AsArgList::size(Result.Filter, Result.SymbolDef);
195+
}
196+
197+
static bool serialize(SPSOutputBuffer &OB, const ResolveResult &Result) {
198+
return SPSResolveResult::AsArgList::serialize(OB, Result.Filter,
199+
Result.SymbolDef);
200+
}
201+
202+
static bool deserialize(SPSInputBuffer &IB, ResolveResult &Result) {
203+
return SPSResolveResult::AsArgList::deserialize(IB, Result.Filter,
204+
Result.SymbolDef);
205+
}
206+
};
207+
208+
} // end namespace shared
209+
} // end namespace orc
210+
} // end namespace llvm
211+
212+
#endif // LLVM_EXECUTIONENGINE_ORC_SHARED_AUTOLOADDYLIBUTILS_H

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#ifndef LLVM_EXECUTIONENGINE_ORC_SHARED_ORCRTBRIDGE_H
1414
#define LLVM_EXECUTIONENGINE_ORC_SHARED_ORCRTBRIDGE_H
1515

16+
#include "llvm/ExecutionEngine/Orc/Shared/AutoLoadDylibUtils.h"
1617
#include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h"
1718
#include "llvm/ExecutionEngine/Orc/Shared/ExecutorSymbolDef.h"
1819
#include "llvm/ExecutionEngine/Orc/Shared/SimpleRemoteEPCUtils.h"
@@ -25,6 +26,7 @@ namespace rt {
2526
extern const char *SimpleExecutorDylibManagerInstanceName;
2627
extern const char *SimpleExecutorDylibManagerOpenWrapperName;
2728
extern const char *SimpleExecutorDylibManagerLookupWrapperName;
29+
extern const char *SimpleExecutorDylibManagerResolveWrapperName;
2830

2931
extern const char *SimpleExecutorMemoryManagerInstanceName;
3032
extern const char *SimpleExecutorMemoryManagerReserveWrapperName;
@@ -59,6 +61,11 @@ using SPSSimpleExecutorDylibManagerLookupSignature =
5961
shared::SPSExecutorAddr, shared::SPSExecutorAddr,
6062
shared::SPSRemoteSymbolLookupSet);
6163

64+
using SPSSimpleExecutorDylibManagerResolveSignature = shared::SPSExpected<
65+
shared::SPSTuple<shared::SPSOptional<shared::SPSBloomFilter>,
66+
shared::SPSSequence<shared::SPSExecutorSymbolDef>>>(
67+
shared::SPSExecutorAddr, shared::SPSRemoteSymbolLookupSet);
68+
6269
using SPSSimpleExecutorMemoryManagerReserveSignature =
6370
shared::SPSExpected<shared::SPSExecutorAddr>(shared::SPSExecutorAddr,
6471
uint64_t);

llvm/include/llvm/ExecutionEngine/Orc/SimpleRemoteEPC.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ class SimpleRemoteEPC : public ExecutorProcessControl,
7474
void lookupSymbolsAsync(ArrayRef<LookupRequest> Request,
7575
SymbolLookupCompleteFn F) override;
7676

77+
void resolveSymbolsAsync(ArrayRef<SymbolLookupSet> Request,
78+
ResolveSymbolsCompleteFn F) override;
79+
7780
Expected<int32_t> runAsMain(ExecutorAddr MainFnAddr,
7881
ArrayRef<std::string> Args) override;
7982

0 commit comments

Comments
 (0)