LLVM 18.0.0git
SimpleRemoteEPCServer.cpp
Go to the documentation of this file.
1//===------- SimpleEPCServer.cpp - EPC over simple abstract channel -------===//
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
10
17
18#include "OrcRTBootstrap.h"
19
20#define DEBUG_TYPE "orc"
21
22using namespace llvm::orc::shared;
23
24namespace llvm {
25namespace orc {
26
28
30
31#if LLVM_ENABLE_THREADS
32void SimpleRemoteEPCServer::ThreadDispatcher::dispatch(
33 unique_function<void()> Work) {
34 {
35 std::lock_guard<std::mutex> Lock(DispatchMutex);
36 if (!Running)
37 return;
38 ++Outstanding;
39 }
40
41 std::thread([this, Work = std::move(Work)]() mutable {
42 Work();
43 std::lock_guard<std::mutex> Lock(DispatchMutex);
44 --Outstanding;
45 OutstandingCV.notify_all();
46 }).detach();
47}
48
49void SimpleRemoteEPCServer::ThreadDispatcher::shutdown() {
50 std::unique_lock<std::mutex> Lock(DispatchMutex);
51 Running = false;
52 OutstandingCV.wait(Lock, [this]() { return Outstanding == 0; });
53}
54#endif
55
59 return DBS;
60}
61
64 ExecutorAddr TagAddr,
66
68 dbgs() << "SimpleRemoteEPCServer::handleMessage: opc = ";
69 switch (OpC) {
71 dbgs() << "Setup";
72 assert(SeqNo == 0 && "Non-zero SeqNo for Setup?");
73 assert(!TagAddr && "Non-zero TagAddr for Setup?");
74 break;
76 dbgs() << "Hangup";
77 assert(SeqNo == 0 && "Non-zero SeqNo for Hangup?");
78 assert(!TagAddr && "Non-zero TagAddr for Hangup?");
79 break;
81 dbgs() << "Result";
82 assert(!TagAddr && "Non-zero TagAddr for Result?");
83 break;
85 dbgs() << "CallWrapper";
86 break;
87 }
88 dbgs() << ", seqno = " << SeqNo << ", tag-addr = " << TagAddr
89 << ", arg-buffer = " << formatv("{0:x}", ArgBytes.size())
90 << " bytes\n";
91 });
92
93 using UT = std::underlying_type_t<SimpleRemoteEPCOpcode>;
94 if (static_cast<UT>(OpC) > static_cast<UT>(SimpleRemoteEPCOpcode::LastOpC))
95 return make_error<StringError>("Unexpected opcode",
97
98 // TODO: Clean detach message?
99 switch (OpC) {
101 return make_error<StringError>("Unexpected Setup opcode",
106 if (auto Err = handleResult(SeqNo, TagAddr, std::move(ArgBytes)))
107 return std::move(Err);
108 break;
110 handleCallWrapper(SeqNo, TagAddr, std::move(ArgBytes));
111 break;
112 }
113 return ContinueSession;
114}
115
117 std::unique_lock<std::mutex> Lock(ServerStateMutex);
118 ShutdownCV.wait(Lock, [this]() { return RunState == ServerShutDown; });
119 return std::move(ShutdownErr);
120}
121
124
125 {
126 std::lock_guard<std::mutex> Lock(ServerStateMutex);
127 std::swap(TmpPending, PendingJITDispatchResults);
128 RunState = ServerShuttingDown;
129 }
130
131 // Send out-of-band errors to any waiting threads.
132 for (auto &KV : TmpPending)
133 KV.second->set_value(
135
136 // Wait for dispatcher to clear.
137 D->shutdown();
138
139 // Shut down services.
140 while (!Services.empty()) {
141 ShutdownErr =
142 joinErrors(std::move(ShutdownErr), Services.back()->shutdown());
143 Services.pop_back();
144 }
145
146 std::lock_guard<std::mutex> Lock(ServerStateMutex);
147 ShutdownErr = joinErrors(std::move(ShutdownErr), std::move(Err));
148 RunState = ServerShutDown;
149 ShutdownCV.notify_all();
150}
151
152Error SimpleRemoteEPCServer::sendMessage(SimpleRemoteEPCOpcode OpC,
153 uint64_t SeqNo, ExecutorAddr TagAddr,
154 ArrayRef<char> ArgBytes) {
155
156 LLVM_DEBUG({
157 dbgs() << "SimpleRemoteEPCServer::sendMessage: opc = ";
158 switch (OpC) {
160 dbgs() << "Setup";
161 assert(SeqNo == 0 && "Non-zero SeqNo for Setup?");
162 assert(!TagAddr && "Non-zero TagAddr for Setup?");
163 break;
165 dbgs() << "Hangup";
166 assert(SeqNo == 0 && "Non-zero SeqNo for Hangup?");
167 assert(!TagAddr && "Non-zero TagAddr for Hangup?");
168 break;
170 dbgs() << "Result";
171 assert(!TagAddr && "Non-zero TagAddr for Result?");
172 break;
174 dbgs() << "CallWrapper";
175 break;
176 }
177 dbgs() << ", seqno = " << SeqNo << ", tag-addr = " << TagAddr
178 << ", arg-buffer = " << formatv("{0:x}", ArgBytes.size())
179 << " bytes\n";
180 });
181 auto Err = T->sendMessage(OpC, SeqNo, TagAddr, ArgBytes);
182 LLVM_DEBUG({
183 if (Err)
184 dbgs() << " \\--> SimpleRemoteEPC::sendMessage failed\n";
185 });
186 return Err;
187}
188
189Error SimpleRemoteEPCServer::sendSetupMessage(
190 StringMap<std::vector<char>> BootstrapMap,
191 StringMap<ExecutorAddr> BootstrapSymbols) {
192
193 using namespace SimpleRemoteEPCDefaultBootstrapSymbolNames;
194
195 std::vector<char> SetupPacket;
199 EI.PageSize = *PageSize;
200 else
201 return PageSize.takeError();
202 EI.BootstrapMap = std::move(BootstrapMap);
203 EI.BootstrapSymbols = std::move(BootstrapSymbols);
204
205 assert(!EI.BootstrapSymbols.count(ExecutorSessionObjectName) &&
206 "Dispatch context name should not be set");
207 assert(!EI.BootstrapSymbols.count(DispatchFnName) &&
208 "Dispatch function name should not be set");
215
216 using SPSSerialize =
218 auto SetupPacketBytes =
219 shared::WrapperFunctionResult::allocate(SPSSerialize::size(EI));
220 shared::SPSOutputBuffer OB(SetupPacketBytes.data(), SetupPacketBytes.size());
221 if (!SPSSerialize::serialize(OB, EI))
222 return make_error<StringError>("Could not send setup packet",
224
225 return sendMessage(SimpleRemoteEPCOpcode::Setup, 0, ExecutorAddr(),
226 {SetupPacketBytes.data(), SetupPacketBytes.size()});
227}
228
229Error SimpleRemoteEPCServer::handleResult(
230 uint64_t SeqNo, ExecutorAddr TagAddr,
232 std::promise<shared::WrapperFunctionResult> *P = nullptr;
233 {
234 std::lock_guard<std::mutex> Lock(ServerStateMutex);
235 auto I = PendingJITDispatchResults.find(SeqNo);
236 if (I == PendingJITDispatchResults.end())
237 return make_error<StringError>("No call for sequence number " +
238 Twine(SeqNo),
240 P = I->second;
241 PendingJITDispatchResults.erase(I);
242 releaseSeqNo(SeqNo);
243 }
245 memcpy(R.data(), ArgBytes.data(), ArgBytes.size());
246 P->set_value(std::move(R));
247 return Error::success();
248}
249
250void SimpleRemoteEPCServer::handleCallWrapper(
251 uint64_t RemoteSeqNo, ExecutorAddr TagAddr,
253 D->dispatch([this, RemoteSeqNo, TagAddr, ArgBytes = std::move(ArgBytes)]() {
254 using WrapperFnTy =
255 shared::CWrapperFunctionResult (*)(const char *, size_t);
256 auto *Fn = TagAddr.toPtr<WrapperFnTy>();
258 Fn(ArgBytes.data(), ArgBytes.size()));
259 if (auto Err = sendMessage(SimpleRemoteEPCOpcode::Result, RemoteSeqNo,
260 ExecutorAddr(),
261 {ResultBytes.data(), ResultBytes.size()}))
262 ReportError(std::move(Err));
263 });
264}
265
267SimpleRemoteEPCServer::doJITDispatch(const void *FnTag, const char *ArgData,
268 size_t ArgSize) {
269 uint64_t SeqNo;
270 std::promise<shared::WrapperFunctionResult> ResultP;
271 auto ResultF = ResultP.get_future();
272 {
273 std::lock_guard<std::mutex> Lock(ServerStateMutex);
274 if (RunState != ServerRunning)
276 "jit_dispatch not available (EPC server shut down)");
277
278 SeqNo = getNextSeqNo();
279 assert(!PendingJITDispatchResults.count(SeqNo) && "SeqNo already in use");
280 PendingJITDispatchResults[SeqNo] = &ResultP;
281 }
282
283 if (auto Err = sendMessage(SimpleRemoteEPCOpcode::CallWrapper, SeqNo,
284 ExecutorAddr::fromPtr(FnTag), {ArgData, ArgSize}))
285 ReportError(std::move(Err));
286
287 return ResultF.get();
288}
289
291SimpleRemoteEPCServer::jitDispatchEntry(void *DispatchCtx, const void *FnTag,
292 const char *ArgData, size_t ArgSize) {
293 return reinterpret_cast<SimpleRemoteEPCServer *>(DispatchCtx)
294 ->doJITDispatch(FnTag, ArgData, ArgSize)
295 .release();
296}
297
298} // end namespace orc
299} // end namespace llvm
#define LLVM_DEBUG(X)
Definition: Debug.h:101
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 P(N)
Provides a library for accessing information about this process and other processes on the operating ...
llvm::orc::shared::CWrapperFunctionResult llvm_orc_deregisterEHFrameSectionWrapper(const char *Data, uint64_t Size)
llvm::orc::shared::CWrapperFunctionResult llvm_orc_registerEHFrameSectionWrapper(const char *Data, uint64_t Size)
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
size_t size() const
size - Get the array size.
Definition: ArrayRef.h:165
iterator find(const_arg_type_t< KeyT > Val)
Definition: DenseMap.h:155
bool erase(const KeyT &Val)
Definition: DenseMap.h:329
size_type count(const_arg_type_t< KeyT > Val) const
Return 1 if the specified key is in the map, 0 otherwise.
Definition: DenseMap.h:151
iterator end()
Definition: DenseMap.h:84
Lightweight error class with error context and mandatory checking.
Definition: Error.h:160
static ErrorSuccess success()
Create a success value.
Definition: Error.h:334
Tagged union holding either a T or a Error.
Definition: Error.h:474
size_t size() const
Definition: SmallVector.h:91
pointer data()
Return a pointer to the vector's buffer, even if empty().
Definition: SmallVector.h:289
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Definition: SmallVector.h:1200
StringMap - This is an unconventional map that is specialized for handling keys that are "strings",...
Definition: StringMap.h:112
Twine - A lightweight data structure for efficiently representing the concatenation of temporary valu...
Definition: Twine.h:81
Represents an address in the executor process.
static ExecutorAddr fromPtr(T *Ptr, UnwrapFn &&Unwrap=UnwrapFn())
Create an ExecutorAddr from the given pointer.
std::enable_if_t< std::is_pointer< T >::value, T > toPtr(WrapFn &&Wrap=WrapFn()) const
Cast this ExecutorAddr to a pointer of the given type.
A simple EPC server implementation.
static StringMap< ExecutorAddr > defaultBootstrapSymbols()
Expected< HandleMessageAction > handleMessage(SimpleRemoteEPCOpcode OpC, uint64_t SeqNo, ExecutorAddr TagAddr, SimpleRemoteEPCArgBytesVector ArgBytes) override
Call to handle an incoming message.
void handleDisconnect(Error Err) override
Handle a disconnection from the underlying transport.
A utility class for serializing to a blob from a variadic list.
Output char buffer with overflow check.
C++ wrapper function result: Same as CWrapperFunctionResult but auto-releases memory.
static WrapperFunctionResult allocate(size_t Size)
Create a WrapperFunctionResult with the given size and return a pointer to the underlying memory.
CWrapperFunctionResult release()
Release ownership of the contained CWrapperFunctionResult.
static WrapperFunctionResult createOutOfBandError(const char *Msg)
Create an out-of-band error by copying the given string.
static Expected< unsigned > getPageSize()
Get the process's page size.
unique_function is a type-erasing functor similar to std::function.
void addTo(StringMap< ExecutorAddr > &M)
const char * DeregisterEHFrameSectionWrapperName
Definition: OrcRTBridge.cpp:55
const char * RegisterEHFrameSectionWrapperName
Definition: OrcRTBridge.cpp:53
std::string getProcessTriple()
getProcessTriple() - Return an appropriate target triple for generating code to be loaded into the cu...
Definition: Host.cpp:1945
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
auto formatv(const char *Fmt, Ts &&... Vals) -> formatv_object< decltype(std::make_tuple(detail::build_format_adapter(std::forward< Ts >(Vals))...))>
std::error_code inconvertibleErrorCode()
The value returned by this function can be returned from convertToErrorCode for Error values where no...
Definition: Error.cpp:90
Error joinErrors(Error E1, Error E2)
Concatenate errors.
Definition: Error.h:431
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
StringMap< ExecutorAddr > BootstrapSymbols
StringMap< std::vector< char > > BootstrapMap