xref: /freebsd/contrib/llvm-project/llvm/lib/ExecutionEngine/Orc/SimpleRemoteEPC.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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 
9 #include "llvm/ExecutionEngine/Orc/SimpleRemoteEPC.h"
10 #include "llvm/ExecutionEngine/Orc/EPCGenericJITLinkMemoryManager.h"
11 #include "llvm/ExecutionEngine/Orc/EPCGenericMemoryAccess.h"
12 #include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h"
13 #include "llvm/Support/FormatVariadic.h"
14 
15 #define DEBUG_TYPE "orc"
16 
17 namespace llvm {
18 namespace orc {
19 
~SimpleRemoteEPC()20 SimpleRemoteEPC::~SimpleRemoteEPC() {
21 #ifndef NDEBUG
22   std::lock_guard<std::mutex> Lock(SimpleRemoteEPCMutex);
23   assert(Disconnected && "Destroyed without disconnection");
24 #endif // NDEBUG
25 }
26 
27 Expected<tpctypes::DylibHandle>
loadDylib(const char * DylibPath)28 SimpleRemoteEPC::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.
35 static void
lookupSymbolsAsyncHelper(EPCGenericDylibManager & DylibMgr,ArrayRef<SimpleRemoteEPC::LookupRequest> Request,std::vector<tpctypes::LookupResult> Result,SimpleRemoteEPC::SymbolLookupCompleteFn Complete)36 lookupSymbolsAsyncHelper(EPCGenericDylibManager &DylibMgr,
37                          ArrayRef<SimpleRemoteEPC::LookupRequest> Request,
38                          std::vector<tpctypes::LookupResult> Result,
39                          SimpleRemoteEPC::SymbolLookupCompleteFn Complete) {
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 
54                          lookupSymbolsAsyncHelper(
55                              DylibMgr, Request.drop_front(), std::move(Result),
56                              std::move(Complete));
57                        });
58 }
59 
lookupSymbolsAsync(ArrayRef<LookupRequest> Request,SymbolLookupCompleteFn Complete)60 void SimpleRemoteEPC::lookupSymbolsAsync(ArrayRef<LookupRequest> Request,
61                                          SymbolLookupCompleteFn Complete) {
62   lookupSymbolsAsyncHelper(*DylibMgr, Request, {}, std::move(Complete));
63 }
64 
runAsMain(ExecutorAddr MainFnAddr,ArrayRef<std::string> Args)65 Expected<int32_t> SimpleRemoteEPC::runAsMain(ExecutorAddr MainFnAddr,
66                                              ArrayRef<std::string> Args) {
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 
runAsVoidFunction(ExecutorAddr VoidFnAddr)74 Expected<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 
runAsIntFunction(ExecutorAddr IntFnAddr,int Arg)82 Expected<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 
callWrapperAsync(ExecutorAddr WrapperFnAddr,IncomingWFRHandler OnComplete,ArrayRef<char> ArgBuffer)91 void 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)) {
104     IncomingWFRHandler H;
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 
disconnect()127 Error 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 
135 Expected<SimpleRemoteEPCTransportClient::HandleMessageAction>
handleMessage(SimpleRemoteEPCOpcode OpC,uint64_t SeqNo,ExecutorAddr TagAddr,SimpleRemoteEPCArgBytesVector ArgBytes)136 SimpleRemoteEPC::handleMessage(SimpleRemoteEPCOpcode OpC, uint64_t SeqNo,
137                                ExecutorAddr TagAddr,
138                                SimpleRemoteEPCArgBytesVector ArgBytes) {
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",
169                                    inconvertibleErrorCode());
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 
handleDisconnect(Error Err)192 void SimpleRemoteEPC::handleDisconnect(Error Err) {
193   LLVM_DEBUG({
194     dbgs() << "SimpleRemoteEPC::handleDisconnect: "
195            << (Err ? "failure" : "success") << "\n";
196   });
197 
198   PendingCallWrapperResultsMap TmpPending;
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 
215 Expected<std::unique_ptr<jitlink::JITLinkMemoryManager>>
createDefaultMemoryManager(SimpleRemoteEPC & SREPC)216 SimpleRemoteEPC::createDefaultMemoryManager(SimpleRemoteEPC &SREPC) {
217   EPCGenericJITLinkMemoryManager::SymbolAddrs SAs;
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 
229 Expected<std::unique_ptr<ExecutorProcessControl::MemoryAccess>>
createDefaultMemoryAccess(SimpleRemoteEPC & SREPC)230 SimpleRemoteEPC::createDefaultMemoryAccess(SimpleRemoteEPC &SREPC) {
231   return nullptr;
232 }
233 
sendMessage(SimpleRemoteEPCOpcode OpC,uint64_t SeqNo,ExecutorAddr TagAddr,ArrayRef<char> ArgBytes)234 Error 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 
handleSetup(uint64_t SeqNo,ExecutorAddr TagAddr,SimpleRemoteEPCArgBytesVector ArgBytes)270 Error SimpleRemoteEPC::handleSetup(uint64_t SeqNo, ExecutorAddr TagAddr,
271                                    SimpleRemoteEPCArgBytesVector ArgBytes) {
272   if (SeqNo != 0)
273     return make_error<StringError>("Setup packet SeqNo not zero",
274                                    inconvertibleErrorCode());
275 
276   if (TagAddr)
277     return make_error<StringError>("Setup packet TagAddr not zero",
278                                    inconvertibleErrorCode());
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 
setup(Setup S)294 Error 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 
handleResult(uint64_t SeqNo,ExecutorAddr TagAddr,SimpleRemoteEPCArgBytesVector ArgBytes)387 Error 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",
393                                    inconvertibleErrorCode());
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),
401                                      inconvertibleErrorCode());
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 
handleCallWrapper(uint64_t RemoteSeqNo,ExecutorAddr TagAddr,SimpleRemoteEPCArgBytesVector ArgBytes)413 void 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 
handleHangup(SimpleRemoteEPCArgBytesVector ArgBytes)431 Error 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 
437   detail::SPSSerializableError Info;
438   SPSInputBuffer IB(WFR.data(), WFR.size());
439   if (!SPSArgList<SPSError>::deserialize(IB, Info))
440     return make_error<StringError>("Could not deserialize hangup info",
441                                    inconvertibleErrorCode());
442   return fromSPSSerializable(std::move(Info));
443 }
444 
445 } // end namespace orc
446 } // end namespace llvm
447