LLVM 20.0.0git
SimpleRemoteEPC.cpp
Go to the documentation of this file.
1//===------- SimpleRemoteEPC.cpp -- Simple remote executor control --------===//
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
14
15#define DEBUG_TYPE "orc"
16
17namespace llvm {
18namespace orc {
19
21#ifndef NDEBUG
22 std::lock_guard<std::mutex> Lock(SimpleRemoteEPCMutex);
23 assert(Disconnected && "Destroyed without disconnection");
24#endif // NDEBUG
25}
26
28SimpleRemoteEPC::loadDylib(const char *DylibPath) {
29 return DylibMgr->open(DylibPath, 0);
30}
31
32/// Async helper to chain together calls to DylibMgr::lookupAsync to fulfill all
33/// all the requests.
34/// FIXME: The dylib manager should support multiple LookupRequests natively.
35static void
38 std::vector<tpctypes::LookupResult> Result,
40 if (Request.empty())
41 return Complete(std::move(Result));
42
43 auto &Element = Request.front();
44 DylibMgr.lookupAsync(Element.Handle, Element.Symbols,
45 [&DylibMgr, Request, Complete = std::move(Complete),
46 Result = std::move(Result)](auto R) mutable {
47 if (!R)
48 return Complete(R.takeError());
49 Result.push_back({});
50 Result.back().reserve(R->size());
51 for (auto Addr : *R)
52 Result.back().push_back(Addr);
53
55 DylibMgr, Request.drop_front(), std::move(Result),
56 std::move(Complete));
57 });
58}
59
60void SimpleRemoteEPC::lookupSymbolsAsync(ArrayRef<LookupRequest> Request,
61 SymbolLookupCompleteFn Complete) {
62 lookupSymbolsAsyncHelper(*DylibMgr, Request, {}, std::move(Complete));
63}
64
65Expected<int32_t> SimpleRemoteEPC::runAsMain(ExecutorAddr MainFnAddr,
67 int64_t Result = 0;
68 if (auto Err = callSPSWrapper<rt::SPSRunAsMainSignature>(
69 RunAsMainAddr, Result, MainFnAddr, Args))
70 return std::move(Err);
71 return Result;
72}
73
74Expected<int32_t> SimpleRemoteEPC::runAsVoidFunction(ExecutorAddr VoidFnAddr) {
75 int32_t Result = 0;
76 if (auto Err = callSPSWrapper<rt::SPSRunAsVoidFunctionSignature>(
77 RunAsVoidFunctionAddr, Result, VoidFnAddr))
78 return std::move(Err);
79 return Result;
80}
81
82Expected<int32_t> SimpleRemoteEPC::runAsIntFunction(ExecutorAddr IntFnAddr,
83 int Arg) {
84 int32_t Result = 0;
85 if (auto Err = callSPSWrapper<rt::SPSRunAsIntFunctionSignature>(
86 RunAsIntFunctionAddr, Result, IntFnAddr, Arg))
87 return std::move(Err);
88 return Result;
89}
90
91void SimpleRemoteEPC::callWrapperAsync(ExecutorAddr WrapperFnAddr,
92 IncomingWFRHandler OnComplete,
93 ArrayRef<char> ArgBuffer) {
94 uint64_t SeqNo;
95 {
96 std::lock_guard<std::mutex> Lock(SimpleRemoteEPCMutex);
97 SeqNo = getNextSeqNo();
98 assert(!PendingCallWrapperResults.count(SeqNo) && "SeqNo already in use");
99 PendingCallWrapperResults[SeqNo] = std::move(OnComplete);
100 }
101
102 if (auto Err = sendMessage(SimpleRemoteEPCOpcode::CallWrapper, SeqNo,
103 WrapperFnAddr, ArgBuffer)) {
105
106 // We just registered OnComplete, but there may be a race between this
107 // thread returning from sendMessage and handleDisconnect being called from
108 // the transport's listener thread. If handleDisconnect gets there first
109 // then it will have failed 'H' for us. If we get there first (or if
110 // handleDisconnect already ran) then we need to take care of it.
111 {
112 std::lock_guard<std::mutex> Lock(SimpleRemoteEPCMutex);
113 auto I = PendingCallWrapperResults.find(SeqNo);
114 if (I != PendingCallWrapperResults.end()) {
115 H = std::move(I->second);
116 PendingCallWrapperResults.erase(I);
117 }
118 }
119
120 if (H)
121 H(shared::WrapperFunctionResult::createOutOfBandError("disconnecting"));
122
123 getExecutionSession().reportError(std::move(Err));
124 }
125}
126
127Error SimpleRemoteEPC::disconnect() {
128 T->disconnect();
129 D->shutdown();
130 std::unique_lock<std::mutex> Lock(SimpleRemoteEPCMutex);
131 DisconnectCV.wait(Lock, [this] { return Disconnected; });
132 return std::move(DisconnectErr);
133}
134
136SimpleRemoteEPC::handleMessage(SimpleRemoteEPCOpcode OpC, uint64_t SeqNo,
137 ExecutorAddr TagAddr,
139
140 LLVM_DEBUG({
141 dbgs() << "SimpleRemoteEPC::handleMessage: opc = ";
142 switch (OpC) {
143 case SimpleRemoteEPCOpcode::Setup:
144 dbgs() << "Setup";
145 assert(SeqNo == 0 && "Non-zero SeqNo for Setup?");
146 assert(!TagAddr && "Non-zero TagAddr for Setup?");
147 break;
148 case SimpleRemoteEPCOpcode::Hangup:
149 dbgs() << "Hangup";
150 assert(SeqNo == 0 && "Non-zero SeqNo for Hangup?");
151 assert(!TagAddr && "Non-zero TagAddr for Hangup?");
152 break;
153 case SimpleRemoteEPCOpcode::Result:
154 dbgs() << "Result";
155 assert(!TagAddr && "Non-zero TagAddr for Result?");
156 break;
157 case SimpleRemoteEPCOpcode::CallWrapper:
158 dbgs() << "CallWrapper";
159 break;
160 }
161 dbgs() << ", seqno = " << SeqNo << ", tag-addr = " << TagAddr
162 << ", arg-buffer = " << formatv("{0:x}", ArgBytes.size())
163 << " bytes\n";
164 });
165
166 using UT = std::underlying_type_t<SimpleRemoteEPCOpcode>;
167 if (static_cast<UT>(OpC) > static_cast<UT>(SimpleRemoteEPCOpcode::LastOpC))
168 return make_error<StringError>("Unexpected opcode",
170
171 switch (OpC) {
172 case SimpleRemoteEPCOpcode::Setup:
173 if (auto Err = handleSetup(SeqNo, TagAddr, std::move(ArgBytes)))
174 return std::move(Err);
175 break;
176 case SimpleRemoteEPCOpcode::Hangup:
177 T->disconnect();
178 if (auto Err = handleHangup(std::move(ArgBytes)))
179 return std::move(Err);
180 return EndSession;
181 case SimpleRemoteEPCOpcode::Result:
182 if (auto Err = handleResult(SeqNo, TagAddr, std::move(ArgBytes)))
183 return std::move(Err);
184 break;
185 case SimpleRemoteEPCOpcode::CallWrapper:
186 handleCallWrapper(SeqNo, TagAddr, std::move(ArgBytes));
187 break;
188 }
189 return ContinueSession;
190}
191
192void SimpleRemoteEPC::handleDisconnect(Error Err) {
193 LLVM_DEBUG({
194 dbgs() << "SimpleRemoteEPC::handleDisconnect: "
195 << (Err ? "failure" : "success") << "\n";
196 });
197
199
200 {
201 std::lock_guard<std::mutex> Lock(SimpleRemoteEPCMutex);
202 std::swap(TmpPending, PendingCallWrapperResults);
203 }
204
205 for (auto &KV : TmpPending)
206 KV.second(
207 shared::WrapperFunctionResult::createOutOfBandError("disconnecting"));
208
209 std::lock_guard<std::mutex> Lock(SimpleRemoteEPCMutex);
210 DisconnectErr = joinErrors(std::move(DisconnectErr), std::move(Err));
211 Disconnected = true;
212 DisconnectCV.notify_all();
213}
214
216SimpleRemoteEPC::createDefaultMemoryManager(SimpleRemoteEPC &SREPC) {
218 if (auto Err = SREPC.getBootstrapSymbols(
219 {{SAs.Allocator, rt::SimpleExecutorMemoryManagerInstanceName},
220 {SAs.Reserve, rt::SimpleExecutorMemoryManagerReserveWrapperName},
221 {SAs.Finalize, rt::SimpleExecutorMemoryManagerFinalizeWrapperName},
222 {SAs.Deallocate,
223 rt::SimpleExecutorMemoryManagerDeallocateWrapperName}}))
224 return std::move(Err);
225
226 return std::make_unique<EPCGenericJITLinkMemoryManager>(SREPC, SAs);
227}
228
229Expected<std::unique_ptr<ExecutorProcessControl::MemoryAccess>>
230SimpleRemoteEPC::createDefaultMemoryAccess(SimpleRemoteEPC &SREPC) {
231 return nullptr;
232}
233
234Error SimpleRemoteEPC::sendMessage(SimpleRemoteEPCOpcode OpC, uint64_t SeqNo,
235 ExecutorAddr TagAddr,
236 ArrayRef<char> ArgBytes) {
237 assert(OpC != SimpleRemoteEPCOpcode::Setup &&
238 "SimpleRemoteEPC sending Setup message? That's the wrong direction.");
239
240 LLVM_DEBUG({
241 dbgs() << "SimpleRemoteEPC::sendMessage: opc = ";
242 switch (OpC) {
243 case SimpleRemoteEPCOpcode::Hangup:
244 dbgs() << "Hangup";
245 assert(SeqNo == 0 && "Non-zero SeqNo for Hangup?");
246 assert(!TagAddr && "Non-zero TagAddr for Hangup?");
247 break;
248 case SimpleRemoteEPCOpcode::Result:
249 dbgs() << "Result";
250 assert(!TagAddr && "Non-zero TagAddr for Result?");
251 break;
252 case SimpleRemoteEPCOpcode::CallWrapper:
253 dbgs() << "CallWrapper";
254 break;
255 default:
256 llvm_unreachable("Invalid opcode");
257 }
258 dbgs() << ", seqno = " << SeqNo << ", tag-addr = " << TagAddr
259 << ", arg-buffer = " << formatv("{0:x}", ArgBytes.size())
260 << " bytes\n";
261 });
262 auto Err = T->sendMessage(OpC, SeqNo, TagAddr, ArgBytes);
263 LLVM_DEBUG({
264 if (Err)
265 dbgs() << " \\--> SimpleRemoteEPC::sendMessage failed\n";
266 });
267 return Err;
268}
269
270Error SimpleRemoteEPC::handleSetup(uint64_t SeqNo, ExecutorAddr TagAddr,
271 SimpleRemoteEPCArgBytesVector ArgBytes) {
272 if (SeqNo != 0)
273 return make_error<StringError>("Setup packet SeqNo not zero",
275
276 if (TagAddr)
277 return make_error<StringError>("Setup packet TagAddr not zero",
279
280 std::lock_guard<std::mutex> Lock(SimpleRemoteEPCMutex);
281 auto I = PendingCallWrapperResults.find(0);
282 assert(PendingCallWrapperResults.size() == 1 &&
283 I != PendingCallWrapperResults.end() &&
284 "Setup message handler not connectly set up");
285 auto SetupMsgHandler = std::move(I->second);
286 PendingCallWrapperResults.erase(I);
287
288 auto WFR =
289 shared::WrapperFunctionResult::copyFrom(ArgBytes.data(), ArgBytes.size());
290 SetupMsgHandler(std::move(WFR));
291 return Error::success();
292}
293
294Error SimpleRemoteEPC::setup(Setup S) {
295 using namespace SimpleRemoteEPCDefaultBootstrapSymbolNames;
296
297 std::promise<MSVCPExpected<SimpleRemoteEPCExecutorInfo>> EIP;
298 auto EIF = EIP.get_future();
299
300 // Prepare a handler for the setup packet.
301 PendingCallWrapperResults[0] =
302 RunInPlace()(
303 [&](shared::WrapperFunctionResult SetupMsgBytes) {
304 if (const char *ErrMsg = SetupMsgBytes.getOutOfBandError()) {
305 EIP.set_value(
306 make_error<StringError>(ErrMsg, inconvertibleErrorCode()));
307 return;
308 }
309 using SPSSerialize =
310 shared::SPSArgList<shared::SPSSimpleRemoteEPCExecutorInfo>;
311 shared::SPSInputBuffer IB(SetupMsgBytes.data(), SetupMsgBytes.size());
312 SimpleRemoteEPCExecutorInfo EI;
313 if (SPSSerialize::deserialize(IB, EI))
314 EIP.set_value(EI);
315 else
316 EIP.set_value(make_error<StringError>(
317 "Could not deserialize setup message", inconvertibleErrorCode()));
318 });
319
320 // Start the transport.
321 if (auto Err = T->start())
322 return Err;
323
324 // Wait for setup packet to arrive.
325 auto EI = EIF.get();
326 if (!EI) {
327 T->disconnect();
328 return EI.takeError();
329 }
330
331 LLVM_DEBUG({
332 dbgs() << "SimpleRemoteEPC received setup message:\n"
333 << " Triple: " << EI->TargetTriple << "\n"
334 << " Page size: " << EI->PageSize << "\n"
335 << " Bootstrap map" << (EI->BootstrapMap.empty() ? " empty" : ":")
336 << "\n";
337 for (const auto &KV : EI->BootstrapMap)
338 dbgs() << " " << KV.first() << ": " << KV.second.size()
339 << "-byte SPS encoded buffer\n";
340 dbgs() << " Bootstrap symbols"
341 << (EI->BootstrapSymbols.empty() ? " empty" : ":") << "\n";
342 for (const auto &KV : EI->BootstrapSymbols)
343 dbgs() << " " << KV.first() << ": " << KV.second << "\n";
344 });
345 TargetTriple = Triple(EI->TargetTriple);
346 PageSize = EI->PageSize;
347 BootstrapMap = std::move(EI->BootstrapMap);
348 BootstrapSymbols = std::move(EI->BootstrapSymbols);
349
350 if (auto Err = getBootstrapSymbols(
351 {{JDI.JITDispatchContext, ExecutorSessionObjectName},
352 {JDI.JITDispatchFunction, DispatchFnName},
353 {RunAsMainAddr, rt::RunAsMainWrapperName},
354 {RunAsVoidFunctionAddr, rt::RunAsVoidFunctionWrapperName},
355 {RunAsIntFunctionAddr, rt::RunAsIntFunctionWrapperName}}))
356 return Err;
357
358 if (auto DM =
359 EPCGenericDylibManager::CreateWithDefaultBootstrapSymbols(*this))
360 DylibMgr = std::make_unique<EPCGenericDylibManager>(std::move(*DM));
361 else
362 return DM.takeError();
363
364 // Set a default CreateMemoryManager if none is specified.
365 if (!S.CreateMemoryManager)
366 S.CreateMemoryManager = createDefaultMemoryManager;
367
368 if (auto MemMgr = S.CreateMemoryManager(*this)) {
369 OwnedMemMgr = std::move(*MemMgr);
370 this->MemMgr = OwnedMemMgr.get();
371 } else
372 return MemMgr.takeError();
373
374 // Set a default CreateMemoryAccess if none is specified.
375 if (!S.CreateMemoryAccess)
376 S.CreateMemoryAccess = createDefaultMemoryAccess;
377
378 if (auto MemAccess = S.CreateMemoryAccess(*this)) {
379 OwnedMemAccess = std::move(*MemAccess);
380 this->MemAccess = OwnedMemAccess.get();
381 } else
382 return MemAccess.takeError();
383
384 return Error::success();
385}
386
387Error SimpleRemoteEPC::handleResult(uint64_t SeqNo, ExecutorAddr TagAddr,
388 SimpleRemoteEPCArgBytesVector ArgBytes) {
389 IncomingWFRHandler SendResult;
390
391 if (TagAddr)
392 return make_error<StringError>("Unexpected TagAddr in result message",
394
395 {
396 std::lock_guard<std::mutex> Lock(SimpleRemoteEPCMutex);
397 auto I = PendingCallWrapperResults.find(SeqNo);
398 if (I == PendingCallWrapperResults.end())
399 return make_error<StringError>("No call for sequence number " +
400 Twine(SeqNo),
402 SendResult = std::move(I->second);
403 PendingCallWrapperResults.erase(I);
404 releaseSeqNo(SeqNo);
405 }
406
407 auto WFR =
408 shared::WrapperFunctionResult::copyFrom(ArgBytes.data(), ArgBytes.size());
409 SendResult(std::move(WFR));
410 return Error::success();
411}
412
413void SimpleRemoteEPC::handleCallWrapper(
414 uint64_t RemoteSeqNo, ExecutorAddr TagAddr,
415 SimpleRemoteEPCArgBytesVector ArgBytes) {
416 assert(ES && "No ExecutionSession attached");
417 D->dispatch(makeGenericNamedTask(
418 [this, RemoteSeqNo, TagAddr, ArgBytes = std::move(ArgBytes)]() {
419 ES->runJITDispatchHandler(
420 [this, RemoteSeqNo](shared::WrapperFunctionResult WFR) {
421 if (auto Err =
422 sendMessage(SimpleRemoteEPCOpcode::Result, RemoteSeqNo,
423 ExecutorAddr(), {WFR.data(), WFR.size()}))
424 getExecutionSession().reportError(std::move(Err));
425 },
426 TagAddr, ArgBytes);
427 },
428 "callWrapper task"));
429}
430
431Error SimpleRemoteEPC::handleHangup(SimpleRemoteEPCArgBytesVector ArgBytes) {
432 using namespace llvm::orc::shared;
433 auto WFR = WrapperFunctionResult::copyFrom(ArgBytes.data(), ArgBytes.size());
434 if (const char *ErrMsg = WFR.getOutOfBandError())
435 return make_error<StringError>(ErrMsg, inconvertibleErrorCode());
436
438 SPSInputBuffer IB(WFR.data(), WFR.size());
440 return make_error<StringError>("Could not deserialize hangup info",
442 return fromSPSSerializable(std::move(Info));
443}
444
445} // end namespace orc
446} // end namespace llvm
static Error reportError(StringRef Message)
static GCRegistry::Add< StatepointGC > D("statepoint-example", "an example strategy for statepoint")
Analysis containing CSE Info
Definition: CSEInfo.cpp:27
#define LLVM_DEBUG(X)
Definition: Debug.h:101
static RegisterPass< DebugifyModulePass > DM("debugify", "Attach debug info to everything")
uint64_t Addr
static cl::opt< int > PageSize("imp-null-check-page-size", cl::desc("The page size of the target in bytes"), cl::init(4096), cl::Hidden)
#define I(x, y, z)
Definition: MD5.cpp:58
#define H(x, y, z)
Definition: MD5.cpp:57
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition: ArrayRef.h:41
ArrayRef< T > drop_front(size_t N=1) const
Drop the first N elements of the array.
Definition: ArrayRef.h:204
const T & front() const
front - Get the first element.
Definition: ArrayRef.h:168
bool empty() const
empty - Check if the array is empty.
Definition: ArrayRef.h:160
Lightweight error class with error context and mandatory checking.
Definition: Error.h:160
Tagged union holding either a T or a Error.
Definition: Error.h:481
size_t size() const
Definition: SmallVector.h:91
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Definition: SmallVector.h:1209
void lookupAsync(tpctypes::DylibHandle H, const SymbolLookupSet &Lookup, SymbolLookupCompleteFn Complete)
Looks up symbols within the given dylib.
Represents an address in the executor process.
A handler or incoming WrapperFunctionResults – either return values from callWrapper* calls,...
Error getBootstrapSymbols(ArrayRef< std::pair< ExecutorAddr &, StringRef > > Pairs) const
For each (ExecutorAddr&, StringRef) pair, looks up the string in the bootstrap symbols map and writes...
Expected< tpctypes::DylibHandle > loadDylib(const char *DylibPath) override
Load the dynamic library at the given path and return a handle to it.
A utility class for serializing to a blob from a variadic list.
Input char buffer with underflow check.
unique_function is a type-erasing functor similar to std::function.
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
Error fromSPSSerializable(SPSSerializableError BSE)
std::unique_ptr< GenericNamedTask > makeGenericNamedTask(FnT &&Fn, std::string Desc)
Create a generic named task from a std::string description.
Definition: TaskDispatch.h:79
static void lookupSymbolsAsyncHelper(EPCGenericDylibManager &DylibMgr, ArrayRef< SimpleRemoteEPC::LookupRequest > Request, std::vector< tpctypes::LookupResult > Result, SimpleRemoteEPC::SymbolLookupCompleteFn Complete)
Async helper to chain together calls to DylibMgr::lookupAsync to fulfill all all the requests.
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
std::error_code inconvertibleErrorCode()
The value returned by this function can be returned from convertToErrorCode for Error values where no...
Definition: Error.cpp:98
auto formatv(const char *Fmt, Ts &&...Vals) -> formatv_object< decltype(std::make_tuple(support::detail::build_format_adapter(std::forward< Ts >(Vals))...))>
Error joinErrors(Error E1, Error E2)
Concatenate errors.
Definition: Error.h:438
raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition: Debug.cpp:163
void swap(llvm::BitVector &LHS, llvm::BitVector &RHS)
Implement std::swap in terms of BitVector swap.
Definition: BitVector.h:860