LLVM 24.0.0git
InProcessEPC.cpp
Go to the documentation of this file.
1//===---------- InProcessEPC.cpp -- In-process EPC for new ORC runtime ----===//
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
18
19#define DEBUG_TYPE "orc"
20
21namespace llvm::orc {
22
23Expected<std::unique_ptr<InProcessEPC>>
25 std::shared_ptr<SymbolStringPool> SSP,
26 std::unique_ptr<TaskDispatcher> D) {
27 assert(C && "C must not be null");
28 assert(BIA && "BIA must not be null");
29
30 // Lifecycle and IPCA-side fields must be populated by the controller side
31 // before OnConnect is invoked.
32 assert(C->Retain && "C->Retain not set by controller");
33 assert(C->Release && "C->Release not set by controller");
34 assert(C->Disconnect && "C->Disconnect not set by controller");
35 assert(C->EnterMessageScope && "C->EnterMessageScope not set by controller");
36 assert(C->LeaveMessageScope && "C->LeaveMessageScope not set by controller");
37 assert(C->IPCA && "C->IPCA not set by controller");
38 assert(C->CallWrapper && "C->CallWrapper not set by controller");
39 assert(C->ReturnJITDispatchResult &&
40 "C->ReturnJITDispatchResult not set by controller");
41
42 if (!SSP)
43 SSP = std::make_shared<SymbolStringPool>();
44
45 if (!D)
46 D = std::make_unique<InPlaceTaskDispatcher>();
47
48 std::unique_ptr<InProcessEPC> IPEPC(
49 new InProcessEPC(C, std::move(SSP), std::move(D)));
50
51 // First set values in C.
52 C->IPEPC = IPEPC.get();
53 C->CallJITDispatch = callJITDispatchEntry;
54 C->ReturnWrapperResult = returnWrapperResultEntry;
55
56 // Then grab bootstrap values.
57 if (auto PageSize = BIA->GetPageSize(BIA))
58 IPEPC->PageSize = PageSize;
59 else
61 "Cannot create InProcessEPC with page-size = 0",
63
64 if (auto TT = BIA->GetTargetTriple(BIA)) {
65 IPEPC->TargetTriple = llvm::Triple(TT);
66 } else
68 "Cannot create InProcessEPC with target-triple = \"\"",
70
71 {
72 const char *Name;
73 const char *ValBytes;
74 uint64_t ValSize;
75 int RC;
76 while ((RC = BIA->GetNextValue(BIA, &Name, &ValBytes, &ValSize)) == 1) {
77 if (!IPEPC->BootstrapMap
78 .try_emplace(Name,
79 std::vector<char>(ValBytes, ValBytes + ValSize))
80 .second)
82 ("Cannot create InProcessEPC: bootstrap-value map contains "
83 "duplicate key \"" +
84 StringRef(Name) + "\""),
86 }
87 if (RC < 0)
89 "Cannot create InProcessEPC: bootstrap-value map corrupted",
91 }
92
93 {
94 const char *SymName;
95 uint64_t SymAddr;
96 int RC;
97 while ((RC = BIA->GetNextSymbol(BIA, &SymName, &SymAddr)) == 1) {
98 if (!IPEPC->BootstrapSymbols.try_emplace(SymName, ExecutorAddr(SymAddr))
99 .second)
101 ("Cannot create InProcessEPC: bootstrap-symbol map contains "
102 "duplicate symbol \"" +
103 StringRef(SymName) + "\""),
105 }
106 if (RC < 0)
108 "Cannot create InProcessEPC: bootstrap-symbol map corrupted",
110 }
111
112 return std::move(IPEPC);
113}
114
116 // Guarantee that a discarded InProcessEPC initiates disconnect, even if it
117 // was never attached to an ExecutionSession (e.g. Create failed partway
118 // through, or the caller dropped the returned object without handing it to
119 // a session). When the InProcessEPC *is* attached, the ExecutionSession is
120 // guaranteed to call disconnect() during shutdown, and this call becomes a
121 // no-op via the idempotency of C->Disconnect.
122 doDisconnect();
123
124 // Shut down the dispatcher.
125 D->shutdown();
126
127 // Release the connection object.
128 C->Release(C);
129}
130
133 using MainTy = int (*)(int, char *[]);
134 return orc::runAsMain(MainFnAddr.toPtr<MainTy>(), Args);
135}
136
138 IncomingWFRHandler OnComplete,
139 ArrayRef<char> ArgBuffer) {
140 if (C->EnterMessageScope(C)) {
141 auto CallId = registerPendingCallWrapperResult(std::move(OnComplete));
142 auto ArgBytes = shared::WrapperFunctionBuffer::copyFrom(ArgBuffer.data(),
143 ArgBuffer.size());
144
145 LLVM_DEBUG(dbgs() << "InProcessEPC: callWrapperAsync call id " << CallId
146 << " to " << WrapperFnAddr << "\n");
147
148 C->CallWrapper(C->IPCA, CallId, WrapperFnAddr.toPtr<void *>(),
149 ArgBytes.release());
150 C->LeaveMessageScope(C);
151 } else
153 "connection closed"));
154}
155
158 // FIXME: Should actually use InProcessMemoryManager for this.
160}
161
163 // FIXME: Should actually use in-process for this.
165 if (!DM)
166 return DM.takeError();
167 return std::make_unique<EPCGenericDylibManager>(std::move(*DM));
168}
169
191
193 doDisconnect();
194 return Error::success();
195}
196
197uint64_t InProcessEPC::registerPendingCallWrapperResult(IncomingWFRHandler H) {
198 std::scoped_lock<std::mutex> Lock(M);
199 assert(!PendingCallWrapperResults.count(NextCallId) &&
200 "CallId already in use");
201 PendingCallWrapperResults[NextCallId] = std::move(H);
202 return NextCallId++;
203}
204
205void InProcessEPC::doDisconnect() {
206 // Disconnect from InProcessControllerAccess. This should prevent any further
207 // incoming or outgoing calls.
208 C->Disconnect(C);
209
210 // Drain any pending handlers.
212 {
213 std::scoped_lock<std::mutex> Lock(M);
214 HandlersToDrain = std::move(PendingCallWrapperResults);
215 }
216
217 for (auto &[_, H] : HandlersToDrain)
219}
220
221void InProcessEPC::callJITDispatch(uint64_t CallId, void *HandlerTag,
223 assert(C->ReturnJITDispatchResult && "ReturnJITDispatchResult not set");
224
225 LLVM_DEBUG(dbgs() << "InProcessEPC: JIT-dispatch call id " << CallId << " to "
226 << HandlerTag << "\n");
227
229 [this, CallId](shared::WrapperFunctionBuffer ResultBytes) {
230 LLVM_DEBUG(dbgs() << "InProcessEPC: Returning JIT-dispatch result for "
231 "call id "
232 << CallId << "\n");
233 if (C->EnterMessageScope(C)) {
234 C->ReturnJITDispatchResult(C->IPCA, CallId, ResultBytes.release());
235 C->LeaveMessageScope(C);
236 }
237 },
238 ExecutorAddr::fromPtr(HandlerTag),
239 shared::WrapperFunctionBuffer(ArgBytes));
240}
241
242void InProcessEPC::callJITDispatchEntry(
243 void *IPEPC, uint64_t CallId, void *HandlerTag,
245 static_cast<InProcessEPC *>(IPEPC)->callJITDispatch(CallId, HandlerTag,
246 ArgBytes);
247}
248
249void InProcessEPC::returnWrapperResult(
250 uint64_t CallId, shared::CWrapperFunctionBuffer ResultBytes) {
251
252 LLVM_DEBUG(dbgs() << "InProcessEPC: Wrapper result for call id " << CallId
253 << "\n");
254
256 {
257 std::scoped_lock<std::mutex> Lock(M);
258 auto I = PendingCallWrapperResults.find(CallId);
259 if (I != PendingCallWrapperResults.end()) {
260 H = std::move(I->second);
261 PendingCallWrapperResults.erase(I);
262 }
263 }
264
265 if (!H) {
267 "InProcessEPC received result for invalid call id " + Twine(CallId),
269 return;
270 }
271
272 H(shared::WrapperFunctionBuffer(ResultBytes));
273}
274
275void InProcessEPC::returnWrapperResultEntry(
276 void *IPEPC, uint64_t CallId, shared::CWrapperFunctionBuffer ResultBytes) {
277 static_cast<InProcessEPC *>(IPEPC)->returnWrapperResult(CallId, ResultBytes);
278}
279
280} // namespace llvm::orc
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
static GCRegistry::Add< ShadowStackGC > C("shadow-stack", "Very portable GC for uncooperative code generators")
static RegisterPass< DebugifyModulePass > DM("debugify", "Attach debug info to everything")
#define _
#define I(x, y, z)
Definition MD5.cpp:57
#define H(x, y, z)
Definition MD5.cpp:56
Provides a library for accessing information about this process and other processes on the operating ...
#define LLVM_DEBUG(...)
Definition Debug.h:119
Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition ArrayRef.h:40
size_t size() const
Get the array size.
Definition ArrayRef.h:141
const T * data() const
Definition ArrayRef.h:138
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:219
Lightweight error class with error context and mandatory checking.
Definition Error.h:159
static ErrorSuccess success()
Create a success value.
Definition Error.h:336
Tagged union holding either a T or a Error.
Definition Error.h:485
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
Triple - Helper class for working with autoconf configuration names.
Definition Triple.h:48
static Expected< EPCGenericDylibManager > Create(JITDylib &JD, rt::SimpleExecutorDylibManagerSymbolNames SNs=rt::orc_rt_NativeDylibManagerSPSSymbols)
Create an EPCGenericDylibManager using the given implementation symbol names.
static Expected< std::unique_ptr< EPCGenericJITLinkMemoryManager > > Create(JITDylib &JD, rt::SimpleExecutorMemoryManagerSymbolNames SNs=rt::orc_rt_SimpleNativeMemoryMapSPSSymbols)
Create an EPCGenericJITLinkMemoryManager using the given implementation symbol names.
LLVM_ABI void runJITDispatchHandler(SendResultFunction SendResult, ExecutorAddr HandlerFnTagAddr, shared::WrapperFunctionBuffer ArgBytes)
Run a registered jit-side wrapper function.
Definition Core.cpp:1898
void reportError(Error Err)
Report a error for this execution session.
Definition Core.h:1262
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 handler or incoming WrapperFunctionBuffers – either return values from callWrapper* calls,...
std::unique_ptr< TaskDispatcher > D
std::shared_ptr< SymbolStringPool > SSP
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...
ExecutionSession & getExecutionSession()
Return the ExecutionSession associated with this instance.
Expected< std::unique_ptr< jitlink::JITLinkMemoryManager > > createDefaultMemoryManager() override
Create a default JITLinkMemoryManager for the target process.
Expected< std::unique_ptr< DylibManager > > createDefaultDylibMgr() override
Create a default DylibManager for the target process.
void callWrapperAsync(ExecutorAddr WrapperFnAddr, IncomingWFRHandler OnComplete, ArrayRef< char > ArgBuffer) override
Run a wrapper function in the executor.
static Expected< std::unique_ptr< InProcessEPC > > Create(Connection *C, BootstrapInfoAccess *BIA, std::shared_ptr< SymbolStringPool > SSP=nullptr, std::unique_ptr< TaskDispatcher > D=nullptr)
Create a new InProcessEPC.
Error disconnect() override
Disconnect from the target process.
Expected< std::unique_ptr< MemoryAccess > > createDefaultMemoryAccess() override
Create a default MemoryAccess for the target process.
Expected< int32_t > runAsMain(ExecutorAddr MainFnAddr, ArrayRef< std::string > Args) override
Run function with a main-like signature.
static WrapperFunctionBuffer copyFrom(const char *Source, size_t Size)
Copy from the given char range.
static WrapperFunctionBuffer createOutOfBandError(const char *Msg)
Create an out-of-band error by copying the given string.
LLVM_ABI const char * MemoryReadUInt64sWrapperName
LLVM_ABI const char * MemoryWriteUInt16sWrapperName
LLVM_ABI const char * MemoryReadStringsWrapperName
LLVM_ABI const char * MemoryReadUInt16sWrapperName
LLVM_ABI const char * MemoryReadUInt32sWrapperName
LLVM_ABI const char * MemoryWriteUInt64sWrapperName
LLVM_ABI const char * MemoryWriteUInt8sWrapperName
LLVM_ABI const char * MemoryWritePointersWrapperName
LLVM_ABI const char * MemoryWriteUInt32sWrapperName
LLVM_ABI const char * MemoryWriteBuffersWrapperName
LLVM_ABI const char * MemoryReadBuffersWrapperName
LLVM_ABI const char * MemoryReadUInt8sWrapperName
LLVM_ABI int runAsMain(int(*Main)(int, char *[]), ArrayRef< std::string > Args, std::optional< StringRef > ProgramName=std::nullopt)
Run a main function, returning the result.
LLVM_ABI std::error_code inconvertibleErrorCode()
The value returned by this function can be returned from convertToErrorCode for Error values where no...
Definition Error.cpp:94
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:209
Error make_error(ArgTs &&... Args)
Make a Error instance representing failure using the given error info type.
Definition Error.h:340
Function addresses for memory access.
Provides access to bootstrap info.
const char *(* GetTargetTriple)(void *BIA)
int(* GetNextSymbol)(void *BIA, const char **Name, uint64_t *Addr)
int(* GetNextValue)(void *BIA, const char **Name, const char **ValueBytes, uint64_t *ValueSize)
Pseudo-connection C struct.