79
79
80
80
#include " llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h"
81
81
#include " llvm/ExecutionEngine/Orc/LLJIT.h"
82
+ #include " llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h"
83
+ #include " llvm/ExecutionEngine/Orc/SimpleRemoteEPC.h"
82
84
#include " llvm/ExecutionEngine/Orc/ThreadSafeModule.h"
83
85
#include " llvm/Support/CommandLine.h"
84
86
#include " llvm/Support/Error.h"
@@ -116,9 +118,9 @@ static cl::opt<std::string>
116
118
OOPExecutor (" executor" , cl::desc(" Set the out-of-process executor" ),
117
119
cl::value_desc(" filename" ));
118
120
119
- // Network address of a running executor process that we can connected through a
120
- // TCP socket. It may run locally or on a remote machine.
121
- static cl::opt<std::string> OOPExecutorConnect (
121
+ // Network address of a running executor process that we can connect via TCP. It
122
+ // may run locally or on a remote machine.
123
+ static cl::opt<std::string> OOPExecutorConnectTCP (
122
124
" connect" ,
123
125
cl::desc (" Connect to an out-of-process executor through a TCP socket" ),
124
126
cl::value_desc(" <hostname>:<port>" ));
@@ -133,41 +135,6 @@ static cl::opt<bool>
133
135
134
136
ExitOnError ExitOnErr;
135
137
136
- static std::unique_ptr<JITLinkExecutor> connectExecutor (const char *Argv0) {
137
- // Connect to a running out-of-process executor through a TCP socket.
138
- if (!OOPExecutorConnect.empty ()) {
139
- std::unique_ptr<TCPSocketJITLinkExecutor> Exec =
140
- ExitOnErr (JITLinkExecutor::ConnectTCPSocket (OOPExecutorConnect,
141
- std::ref (ExitOnErr)));
142
-
143
- outs () << " Connected to executor at " << OOPExecutorConnect << " \n " ;
144
- if (WaitForDebugger) {
145
- outs () << " Attach a debugger and press any key to continue.\n " ;
146
- fflush (stdin);
147
- getchar ();
148
- }
149
-
150
- return std::move (Exec);
151
- }
152
-
153
- // Launch a out-of-process executor locally in a child process.
154
- std::unique_ptr<ChildProcessJITLinkExecutor> Exec = ExitOnErr (
155
- OOPExecutor.empty () ? JITLinkExecutor::FindLocal (Argv0)
156
- : JITLinkExecutor::CreateLocal (OOPExecutor));
157
-
158
- outs () << " Found out-of-process executor: " << Exec->getPath () << " \n " ;
159
-
160
- ExitOnErr (Exec->launch (std::ref (ExitOnErr)));
161
- if (WaitForDebugger) {
162
- outs () << " Launched executor in subprocess: " << Exec->getPID () << " \n "
163
- << " Attach a debugger and press any key to continue.\n " ;
164
- fflush (stdin);
165
- getchar ();
166
- }
167
-
168
- return std::move (Exec);
169
- }
170
-
171
138
int main (int argc, char *argv[]) {
172
139
InitLLVM X (argc, argv);
173
140
@@ -177,8 +144,27 @@ int main(int argc, char *argv[]) {
177
144
ExitOnErr.setBanner (std::string (argv[0 ]) + " : " );
178
145
cl::ParseCommandLineOptions (argc, argv, " LLJITWithRemoteDebugging" );
179
146
180
- // Launch/connect the out-of-process executor.
181
- std::unique_ptr<JITLinkExecutor> Executor = connectExecutor (argv[0 ]);
147
+ std::unique_ptr<SimpleRemoteEPC> EPC;
148
+ if (OOPExecutorConnectTCP.getNumOccurrences () > 0 ) {
149
+ // Connect to a running out-of-process executor through a TCP socket.
150
+ EPC = ExitOnErr (connectTCPSocket (OOPExecutorConnectTCP));
151
+ outs () << " Connected to executor at " << OOPExecutorConnectTCP << " \n " ;
152
+ } else {
153
+ // Launch an out-of-process executor locally in a child process.
154
+ std::string Path =
155
+ OOPExecutor.empty () ? findLocalExecutor (argv[0 ]) : OOPExecutor;
156
+ outs () << " Found out-of-process executor: " << Path << " \n " ;
157
+
158
+ uint64_t PID;
159
+ std::tie (EPC, PID) = ExitOnErr (launchLocalExecutor (Path));
160
+ outs () << " Launched executor in subprocess: " << PID << " \n " ;
161
+ }
162
+
163
+ if (WaitForDebugger) {
164
+ outs () << " Attach a debugger and press any key to continue.\n " ;
165
+ fflush (stdin);
166
+ getchar ();
167
+ }
182
168
183
169
// Load the given IR files.
184
170
std::vector<ThreadSafeModule> TSMs;
@@ -211,44 +197,51 @@ int main(int argc, char *argv[]) {
211
197
JTMB.setRelocationModel (Reloc::PIC_);
212
198
213
199
// Create LLJIT and destroy it before disconnecting the target process.
214
- {
215
- std::unique_ptr<ExecutionSession> ES = Executor->startSession ();
216
-
217
- outs () << " Initializing LLJIT for remote executor\n " ;
218
- auto J = ExitOnErr (LLJITBuilder ()
219
- .setExecutionSession (std::move (ES))
220
- .setJITTargetMachineBuilder (std::move (JTMB))
221
- .setObjectLinkingLayerCreator (std::ref (*Executor))
222
- .create ());
223
-
224
- // Add plugin for debug support.
225
- ExitOnErr (Executor->addDebugSupport (J->getObjLinkingLayer ()));
226
-
227
- // Load required shared libraries on the remote target and add a generator
228
- // for each of it, so the compiler can lookup their symbols.
229
- for (const std::string &Path : Dylibs)
230
- J->getMainJITDylib ().addGenerator (ExitOnErr (Executor->loadDylib (Path)));
231
-
232
- // Add the loaded IR module to the JIT. This will set up symbol tables and
233
- // prepare for materialization.
234
- for (ThreadSafeModule &TSM : TSMs)
235
- ExitOnErr (J->addIRModule (std::move (TSM)));
236
-
237
- // The example uses a non-lazy JIT for simplicity. Thus, looking up the main
238
- // function will materialize all reachable code. It also triggers debug
239
- // registration in the remote target process.
240
- JITEvaluatedSymbol MainFn = ExitOnErr (J->lookup (" main" ));
241
-
242
- outs () << " Running: main(" ;
243
- int Pos = 0 ;
244
- for (const std::string &Arg : InputArgv)
245
- outs () << (Pos++ == 0 ? " " : " , " ) << " \" " << Arg << " \" " ;
246
- outs () << " )\n " ;
200
+ outs () << " Initializing LLJIT for remote executor\n " ;
201
+ auto J = ExitOnErr (LLJITBuilder ()
202
+ .setExecutorProcessControl (std::move (EPC))
203
+ .setJITTargetMachineBuilder (std::move (JTMB))
204
+ .setObjectLinkingLayerCreator ([&](auto &ES, const auto &TT) {
205
+ return std::make_unique<ObjectLinkingLayer>(ES);
206
+ })
207
+ .create ());
208
+
209
+ // Add plugin for debug support.
210
+ ExitOnErr (addDebugSupport (J->getObjLinkingLayer ()));
211
+
212
+ // Load required shared libraries on the remote target and add a generator
213
+ // for each of it, so the compiler can lookup their symbols.
214
+ for (const std::string &Path : Dylibs)
215
+ J->getMainJITDylib ().addGenerator (
216
+ ExitOnErr (loadDylib (J->getExecutionSession (), Path)));
217
+
218
+ // Add the loaded IR module to the JIT. This will set up symbol tables and
219
+ // prepare for materialization.
220
+ for (ThreadSafeModule &TSM : TSMs)
221
+ ExitOnErr (J->addIRModule (std::move (TSM)));
222
+
223
+ // The example uses a non-lazy JIT for simplicity. Thus, looking up the main
224
+ // function will materialize all reachable code. It also triggers debug
225
+ // registration in the remote target process.
226
+ JITEvaluatedSymbol MainFn = ExitOnErr (J->lookup (" main" ));
227
+
228
+ outs () << " Running: main(" ;
229
+ int Pos = 0 ;
230
+ std::vector<std::string> ActualArgv{" LLJITWithRemoteDebugging" };
231
+ for (const std::string &Arg : InputArgv) {
232
+ outs () << (Pos++ == 0 ? " " : " , " ) << " \" " << Arg << " \" " ;
233
+ ActualArgv.push_back (Arg);
234
+ }
235
+ outs () << " )\n " ;
247
236
248
- // Execute the code in the remote target process and dump the result. With
249
- // the debugger attached to the target, it should be possible to inspect the
250
- // JITed code as if it was compiled statically.
251
- int Result = ExitOnErr (Executor->runAsMain (MainFn, InputArgv));
237
+ // Execute the code in the remote target process and dump the result. With
238
+ // the debugger attached to the target, it should be possible to inspect the
239
+ // JITed code as if it was compiled statically.
240
+ {
241
+ JITTargetAddress MainFnAddr = MainFn.getAddress ();
242
+ ExecutorProcessControl &EPC =
243
+ J->getExecutionSession ().getExecutorProcessControl ();
244
+ int Result = ExitOnErr (EPC.runAsMain (ExecutorAddr (MainFnAddr), ActualArgv));
252
245
outs () << " Exit code: " << Result << " \n " ;
253
246
}
254
247
0 commit comments