<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
Hi David, thanks for working on it!<br>
<br>
I ran the test suite on Linux and macOS with examples enabled and it
worked well. Also, I didn't get a message from buildmaster for this.
Do we have a bot for your setup and is it public?<br>
<br>
<div class="moz-cite-prefix">On 30/03/2021 22:05, David Blaikie
wrote:<br>
</div>
<blockquote type="cite"
cite="mid:CAENS6EsASEwSKe1xtz92bEiB=bcKBx=c_mvRqRRG7qkGNsQopQ@mail.gmail.com">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<div dir="ltr">This broke the build with examples enabled
(LLVM_BUILD_EXAMPLES=ON) I think. My second attempt at fixing
this is in ae217bf1f3277b8c14590a130ee5e63cc1664443</div>
<br>
<div class="gmail_quote">
<div dir="ltr" class="gmail_attr">On Sun, Mar 28, 2021 at 8:25
AM Stefan Gränitz via llvm-commits <<a
href="mailto:llvm-commits@lists.llvm.org"
moz-do-not-send="true">llvm-commits@lists.llvm.org</a>>
wrote:<br>
</div>
<blockquote class="gmail_quote" style="margin:0px 0px 0px
0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><br>
Author: Stefan Gränitz<br>
Date: 2021-03-28T17:25:09+02:00<br>
New Revision: 258f055ed93661900bc568350e09f467c0950486<br>
<br>
URL: <a
href="https://github.com/llvm/llvm-project/commit/258f055ed93661900bc568350e09f467c0950486"
rel="noreferrer" target="_blank" moz-do-not-send="true">https://github.com/llvm/llvm-project/commit/258f055ed93661900bc568350e09f467c0950486</a><br>
DIFF: <a
href="https://github.com/llvm/llvm-project/commit/258f055ed93661900bc568350e09f467c0950486.diff"
rel="noreferrer" target="_blank" moz-do-not-send="true">https://github.com/llvm/llvm-project/commit/258f055ed93661900bc568350e09f467c0950486.diff</a><br>
<br>
LOG: [Orc][examples] Add LLJITWithRemoteDebugging example<br>
<br>
Added: <br>
llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/CMakeLists.txt<br>
llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/LLJITWithRemoteDebugging.cpp<br>
llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/RemoteJITUtils.cpp<br>
llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/RemoteJITUtils.h<br>
llvm/test/Examples/OrcV2Examples/Inputs/argc_sub1.c<br>
llvm/test/Examples/OrcV2Examples/Inputs/argc_sub1_elf.ll<br>
llvm/test/Examples/OrcV2Examples/lljit-with-remote-debugging.test<br>
<br>
Modified: <br>
llvm/examples/OrcV2Examples/CMakeLists.txt<br>
llvm/examples/OrcV2Examples/ExampleModules.h<br>
<br>
Removed: <br>
<br>
<br>
<br>
################################################################################<br>
diff --git a/llvm/examples/OrcV2Examples/CMakeLists.txt
b/llvm/examples/OrcV2Examples/CMakeLists.txt<br>
index bed277e59e0b..f6a11d6f0ef2 100644<br>
--- a/llvm/examples/OrcV2Examples/CMakeLists.txt<br>
+++ b/llvm/examples/OrcV2Examples/CMakeLists.txt<br>
@@ -12,3 +12,7 @@
add_subdirectory(OrcV2CBindingsAddObjectFile)<br>
add_subdirectory(OrcV2CBindingsBasicUsage)<br>
add_subdirectory(OrcV2CBindingsReflectProcessSymbols)<br>
add_subdirectory(OrcV2CBindingsRemovableCode)<br>
+<br>
+if(CMAKE_HOST_UNIX)<br>
+ add_subdirectory(LLJITWithRemoteDebugging)<br>
+endif()<br>
<br>
diff --git a/llvm/examples/OrcV2Examples/ExampleModules.h
b/llvm/examples/OrcV2Examples/ExampleModules.h<br>
index c88609fae769..53da756e15f7 100644<br>
--- a/llvm/examples/OrcV2Examples/ExampleModules.h<br>
+++ b/llvm/examples/OrcV2Examples/ExampleModules.h<br>
@@ -52,4 +52,16 @@ parseExampleModule(llvm::StringRef Source,
llvm::StringRef Name) {<br>
return createSMDiagnosticError(Err);<br>
}<br>
<br>
+inline llvm::Expected<llvm::orc::ThreadSafeModule><br>
+parseExampleModuleFromFile(llvm::StringRef FileName) {<br>
+ using namespace llvm;<br>
+ auto Ctx = std::make_unique<LLVMContext>();<br>
+ SMDiagnostic Err;<br>
+<br>
+ if (auto M = parseIRFile(FileName, Err, *Ctx))<br>
+ return orc::ThreadSafeModule(std::move(M),
std::move(Ctx));<br>
+<br>
+ return createSMDiagnosticError(Err);<br>
+}<br>
+<br>
#endif // LLVM_EXAMPLES_ORCV2EXAMPLES_EXAMPLEMODULES_H<br>
<br>
diff --git
a/llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/CMakeLists.txt
b/llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/CMakeLists.txt<br>
new file mode 100644<br>
index 000000000000..b93dd4924b05<br>
--- /dev/null<br>
+++
b/llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/CMakeLists.txt<br>
@@ -0,0 +1,18 @@<br>
+set(LLVM_LINK_COMPONENTS<br>
+ Core<br>
+ ExecutionEngine<br>
+ IRReader<br>
+ JITLink<br>
+ OrcJIT<br>
+ OrcTargetProcess<br>
+ Support<br>
+ nativecodegen<br>
+ )<br>
+<br>
+add_llvm_example(LLJITWithRemoteDebugging<br>
+ LLJITWithRemoteDebugging.cpp<br>
+ RemoteJITUtils.cpp<br>
+<br>
+ DEPENDS<br>
+ llvm-jitlink-executor<br>
+ )<br>
<br>
diff --git
a/llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/LLJITWithRemoteDebugging.cpp
b/llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/LLJITWithRemoteDebugging.cpp<br>
new file mode 100644<br>
index 000000000000..6da30f608206<br>
--- /dev/null<br>
+++
b/llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/LLJITWithRemoteDebugging.cpp<br>
@@ -0,0 +1,258 @@<br>
+//===--- LLJITWithRemoteDebugging.cpp - LLJIT targeting a
child process ---===//<br>
+//<br>
+// Part of the LLVM Project, under the Apache License v2.0
with LLVM Exceptions.<br>
+// See <a href="https://llvm.org/LICENSE.txt"
rel="noreferrer" target="_blank" moz-do-not-send="true">https://llvm.org/LICENSE.txt</a>
for license information.<br>
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception<br>
+//<br>
+//===----------------------------------------------------------------------===//<br>
+//<br>
+// This example shows how to use LLJIT and JITLink for
out-of-process execution<br>
+// with debug support. A few notes beforehand:<br>
+//<br>
+// * Debuggers must implement the GDB JIT interface (gdb,
udb, lldb 12+).<br>
+// * Debug support is currently limited to ELF on x86-64
platforms that run<br>
+// Unix-like systems.<br>
+// * There is a test for this example and it ships an IR
file that is prepared<br>
+// for the instructions below.<br>
+//<br>
+//<br>
+// The following command line session provides a complete
walkthrough of the<br>
+// feature using LLDB 12:<br>
+//<br>
+// [Terminal 1] Prepare a debuggable out-of-process JIT
session:<br>
+//<br>
+// > cd llvm-project/build<br>
+// > ninja LLJITWithRemoteDebugging
llvm-jitlink-executor<br>
+// > cp
../llvm/test/Examples/OrcV2Examples/Inputs/argc_sub1_elf.ll .<br>
+// > bin/LLJITWithRemoteDebugging --wait-for-debugger
argc_sub1_elf.ll<br>
+// Found out-of-process executor:
bin/llvm-jitlink-executor<br>
+// Launched executor in subprocess: 65535<br>
+// Attach a debugger and press any key to continue.<br>
+//<br>
+//<br>
+// [Terminal 2] Attach a debugger to the child process:<br>
+//<br>
+// (lldb) log enable lldb jit<br>
+// (lldb) settings set plugin.jit-loader.gdb.enable on<br>
+// (lldb) settings set target.source-map Inputs/ \<br>
+//
/path/to/llvm-project/llvm/test/Examples/OrcV2Examples/Inputs/<br>
+// (lldb) attach -p 65535<br>
+// JITLoaderGDB::SetJITBreakpoint looking for JIT
register hook<br>
+// JITLoaderGDB::SetJITBreakpoint setting JIT breakpoint<br>
+// Process 65535 stopped<br>
+// (lldb) b sub1<br>
+// Breakpoint 1: no locations (pending).<br>
+// WARNING: Unable to resolve breakpoint to any actual
locations.<br>
+// (lldb) c<br>
+// Process 65535 resuming<br>
+//<br>
+//<br>
+// [Terminal 1] Press a key to start code generation and
execution:<br>
+//<br>
+// Parsed input IR code from: argc_sub1_elf.ll<br>
+// Initialized LLJIT for remote executor<br>
+// Running: argc_sub1_elf.ll<br>
+//<br>
+//<br>
+// [Terminal 2] Breakpoint hits; we change the argc value
from 1 to 42:<br>
+//<br>
+// (lldb) JITLoaderGDB::JITDebugBreakpointHit hit JIT
breakpoint<br>
+// JITLoaderGDB::ReadJITDescriptorImpl registering JIT
entry at 0x106b34000<br>
+// 1 location added to breakpoint 1<br>
+// Process 65535 stopped<br>
+// * thread #1, queue = 'com.apple.main-thread', stop
reason = breakpoint 1.1<br>
+// frame #0: JIT(0x106b34000)`sub1(x=1) at
argc_sub1.c:1:28<br>
+// -> 1 int sub1(int x) { return x - 1; }<br>
+// 2 int main(int argc, char **argv) { return
sub1(argc); }<br>
+// (lldb) p x<br>
+// (int) $0 = 1<br>
+// (lldb) expr x = 42<br>
+// (int) $1 = 42<br>
+// (lldb) c<br>
+//<br>
+//<br>
+// [Terminal 1] Example output reflects the modified value:<br>
+//<br>
+// Exit code: 41<br>
+//<br>
+//===----------------------------------------------------------------------===//<br>
+<br>
+#include "llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h"<br>
+#include "llvm/ExecutionEngine/Orc/LLJIT.h"<br>
+#include "llvm/ExecutionEngine/Orc/ThreadSafeModule.h"<br>
+#include "llvm/Support/CommandLine.h"<br>
+#include "llvm/Support/Error.h"<br>
+#include "llvm/Support/FormatVariadic.h"<br>
+#include "llvm/Support/InitLLVM.h"<br>
+#include "llvm/Support/TargetSelect.h"<br>
+#include "llvm/Support/raw_ostream.h"<br>
+<br>
+#include "../ExampleModules.h"<br>
+#include "RemoteJITUtils.h"<br>
+<br>
+#include <memory><br>
+#include <string><br>
+<br>
+using namespace llvm;<br>
+using namespace llvm::orc;<br>
+<br>
+// The LLVM IR file to run.<br>
+static cl::list<std::string> InputFiles(cl::Positional,
cl::OneOrMore,<br>
+ cl::desc("<input
files>"));<br>
+<br>
+// Command line arguments to pass to the JITed main function.<br>
+static cl::list<std::string> InputArgv("args",
cl::Positional,<br>
+ cl::desc("<program
arguments>..."),<br>
+ cl::ZeroOrMore,
cl::PositionalEatsArgs);<br>
+<br>
+// Given paths must exist on the remote target.<br>
+static cl::list<std::string><br>
+ Dylibs("dlopen", cl::desc("Dynamic libraries to load
before linking"),<br>
+ cl::value_desc("filename"), cl::ZeroOrMore);<br>
+<br>
+// File path of the executable to launch for execution in a
child process.<br>
+// Inter-process communication will go through stdin/stdout
pipes.<br>
+static cl::opt<std::string><br>
+ OOPExecutor("executor", cl::desc("Set the out-of-process
executor"),<br>
+ cl::value_desc("filename"));<br>
+<br>
+// Network address of a running executor process that we can
connected through a<br>
+// TCP socket. It may run locally or on a remote machine.<br>
+static cl::opt<std::string> OOPExecutorConnect(<br>
+ "connect",<br>
+ cl::desc("Connect to an out-of-process executor through a
TCP socket"),<br>
+ cl::value_desc("<hostname>:<port>"));<br>
+<br>
+// Give the user a chance to connect a debugger. Once we
connected the executor<br>
+// process, wait for the user to press a key (and print out
its PID if it's a<br>
+// child process).<br>
+static cl::opt<bool><br>
+ WaitForDebugger("wait-for-debugger",<br>
+ cl::desc("Wait for user input before
entering JITed code"),<br>
+ cl::init(false));<br>
+<br>
+ExitOnError ExitOnErr;<br>
+<br>
+static std::unique_ptr<JITLinkExecutor>
connectExecutor(const char *Argv0,<br>
+
ExecutionSession &ES) {<br>
+ // Connect to a running out-of-process executor through a
TCP socket.<br>
+ if (!OOPExecutorConnect.empty()) {<br>
+ std::unique_ptr<TCPSocketJITLinkExecutor> Exec =<br>
+
ExitOnErr(JITLinkExecutor::ConnectTCPSocket(OOPExecutorConnect,
ES));<br>
+<br>
+ outs() << "Connected to executor at " <<
OOPExecutorConnect << "\n";<br>
+ if (WaitForDebugger) {<br>
+ outs() << "Attach a debugger and press any key to
continue.\n";<br>
+ fflush(stdin);<br>
+ getchar();<br>
+ }<br>
+<br>
+ return std::move(Exec);<br>
+ }<br>
+<br>
+ // Launch a out-of-process executor locally in a child
process.<br>
+ std::unique_ptr<ChildProcessJITLinkExecutor> Exec =
ExitOnErr(<br>
+ OOPExecutor.empty() ? JITLinkExecutor::FindLocal(Argv0)<br>
+ :
JITLinkExecutor::CreateLocal(OOPExecutor));<br>
+<br>
+ outs() << "Found out-of-process executor: " <<
Exec->getPath() << "\n";<br>
+<br>
+ ExitOnErr(Exec->launch(ES));<br>
+ if (WaitForDebugger) {<br>
+ outs() << "Launched executor in subprocess: "
<< Exec->getPID() << "\n"<br>
+ << "Attach a debugger and press any key to
continue.\n";<br>
+ fflush(stdin);<br>
+ getchar();<br>
+ }<br>
+<br>
+ return std::move(Exec);<br>
+}<br>
+<br>
+int main(int argc, char *argv[]) {<br>
+ InitLLVM X(argc, argv);<br>
+<br>
+ InitializeNativeTarget();<br>
+ InitializeNativeTargetAsmPrinter();<br>
+<br>
+ ExitOnErr.setBanner(std::string(argv[0]) + ": ");<br>
+ cl::ParseCommandLineOptions(argc, argv,
"LLJITWithRemoteDebugging");<br>
+<br>
+ auto ES = std::make_unique<ExecutionSession>();<br>
+ ES->setErrorReporter([&](Error Err) {
ExitOnErr(std::move(Err)); });<br>
+<br>
+ // Launch/connect the out-of-process executor.<br>
+ std::unique_ptr<JITLinkExecutor> Executor =
connectExecutor(argv[0], *ES);<br>
+<br>
+ // Load the given IR files.<br>
+ std::vector<ThreadSafeModule> TSMs;<br>
+ for (const std::string &Path : InputFiles) {<br>
+ outs() << "Parsing input IR code from: " <<
Path << "\n";<br>
+
TSMs.push_back(ExitOnErr(parseExampleModuleFromFile(Path)));<br>
+ }<br>
+<br>
+ StringRef TT;<br>
+ StringRef MainModuleName;<br>
+ TSMs.front().withModuleDo([&MainModuleName,
&TT](Module &M) {<br>
+ MainModuleName = M.getName();<br>
+ TT = M.getTargetTriple();<br>
+ });<br>
+<br>
+ for (const ThreadSafeModule &TSM : TSMs)<br>
+ ExitOnErr(TSM.withModuleDo([TT, MainModuleName](Module
&M) -> Error {<br>
+ if (M.getTargetTriple() != TT)<br>
+ return make_error<StringError>(<br>
+ formatv("Different target triples in input
files:\n"<br>
+ " '{0}' in '{1}'\n '{2}' in '{3}'",<br>
+ TT, MainModuleName, M.getTargetTriple(),
M.getName()),<br>
+ inconvertibleErrorCode());<br>
+ return Error::success();<br>
+ }));<br>
+<br>
+ // Create a target machine that matches the input triple.<br>
+ JITTargetMachineBuilder JTMB((Triple(TT)));<br>
+ JTMB.setCodeModel(CodeModel::Small);<br>
+ JTMB.setRelocationModel(Reloc::PIC_);<br>
+<br>
+ // Create LLJIT and destroy it before disconnecting the
target process.<br>
+ {<br>
+ outs() << "Initializing LLJIT for remote
executor\n";<br>
+ auto J = ExitOnErr(LLJITBuilder()<br>
+
.setExecutionSession(std::move(ES))<br>
+
.setJITTargetMachineBuilder(std::move(JTMB))<br>
+
.setObjectLinkingLayerCreator(std::ref(*Executor))<br>
+ .create());<br>
+<br>
+ // Add plugin for debug support.<br>
+
ExitOnErr(Executor->addDebugSupport(J->getObjLinkingLayer()));<br>
+<br>
+ // Load required shared libraries on the remote target
and add a generator<br>
+ // for each of it, so the compiler can lookup their
symbols.<br>
+ for (const std::string &Path : Dylibs)<br>
+
J->getMainJITDylib().addGenerator(ExitOnErr(Executor->loadDylib(Path)));<br>
+<br>
+ // Add the loaded IR module to the JIT. This will set up
symbol tables and<br>
+ // prepare for materialization.<br>
+ for (ThreadSafeModule &TSM : TSMs)<br>
+ ExitOnErr(J->addIRModule(std::move(TSM)));<br>
+<br>
+ // The example uses a non-lazy JIT for simplicity. Thus,
looking up the main<br>
+ // function will materialize all reachable code. It also
triggers debug<br>
+ // registration in the remote target process.<br>
+ JITEvaluatedSymbol MainFn =
ExitOnErr(J->lookup("main"));<br>
+<br>
+ outs() << "Running: main(";<br>
+ int Pos = 0;<br>
+ for (const std::string &Arg : InputArgv)<br>
+ outs() << (Pos++ == 0 ? "" : ", ") << Arg;<br>
+ outs() << ")\n";<br>
+<br>
+ // Execute the code in the remote target process and dump
the result. With<br>
+ // the debugger attached to the target, it should be
possible to inspect the<br>
+ // JITed code as if it was compiled statically.<br>
+ int Result = ExitOnErr(Executor->runAsMain(MainFn,
InputArgv));<br>
+ outs() << "Exit code: " << Result <<
"\n";<br>
+ }<br>
+<br>
+ ExitOnErr(Executor->disconnect());<br>
+ return 0;<br>
+}<br>
<br>
diff --git
a/llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/RemoteJITUtils.cpp
b/llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/RemoteJITUtils.cpp<br>
new file mode 100644<br>
index 000000000000..abce14de0fe0<br>
--- /dev/null<br>
+++
b/llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/RemoteJITUtils.cpp<br>
@@ -0,0 +1,347 @@<br>
+//===-- RemoteJITUtils.cpp - Utilities for remote-JITing
--------*- C++ -*-===//<br>
+//<br>
+// Part of the LLVM Project, under the Apache License v2.0
with LLVM Exceptions.<br>
+// See <a href="https://llvm.org/LICENSE.txt"
rel="noreferrer" target="_blank" moz-do-not-send="true">https://llvm.org/LICENSE.txt</a>
for license information.<br>
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception<br>
+//<br>
+//===----------------------------------------------------------------------===//<br>
+<br>
+#include "RemoteJITUtils.h"<br>
+<br>
+#include
"llvm/ExecutionEngine/Orc/DebugObjectManagerPlugin.h"<br>
+#include
"llvm/ExecutionEngine/Orc/OrcRPCTargetProcessControl.h"<br>
+#include "llvm/ExecutionEngine/Orc/Shared/RPCUtils.h"<br>
+#include "llvm/ExecutionEngine/Orc/TPCDebugObjectRegistrar.h"<br>
+#include
"llvm/ExecutionEngine/Orc/TPCDynamicLibrarySearchGenerator.h"<br>
+#include
"llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h"<br>
+#include "llvm/Support/FileSystem.h"<br>
+#include "llvm/Support/Path.h"<br>
+#include "llvm/Support/ToolOutputFile.h"<br>
+<br>
+#ifdef LLVM_ON_UNIX<br>
+#include <netdb.h><br>
+#include <netinet/in.h><br>
+#include <sys/socket.h><br>
+#include <unistd.h><br>
+#endif // LLVM_ON_UNIX<br>
+<br>
+using namespace llvm;<br>
+using namespace llvm::orc;<br>
+<br>
+namespace llvm {<br>
+namespace orc {<br>
+<br>
+class RemoteTargetProcessControl<br>
+ : public OrcRPCTargetProcessControlBase<<br>
+
shared::MultiThreadedRPCEndpoint<JITLinkExecutor::RPCChannel>>
{<br>
+public:<br>
+ using RPCChannel = JITLinkExecutor::RPCChannel;<br>
+ using RPCEndpoint =
shared::MultiThreadedRPCEndpoint<RPCChannel>;<br>
+<br>
+private:<br>
+ using ThisT = RemoteTargetProcessControl;<br>
+ using BaseT =
OrcRPCTargetProcessControlBase<RPCEndpoint>;<br>
+ using MemoryAccess = OrcRPCTPCMemoryAccess<ThisT>;<br>
+ using MemoryManager =
OrcRPCTPCJITLinkMemoryManager<ThisT>;<br>
+<br>
+public:<br>
+ using BaseT::initializeORCRPCTPCBase;<br>
+<br>
+ RemoteTargetProcessControl(ExecutionSession &ES,<br>
+
std::unique_ptr<RPCChannel> Channel,<br>
+
std::unique_ptr<RPCEndpoint> Endpoint);<br>
+<br>
+ void initializeMemoryManagement();<br>
+ Error disconnect() override;<br>
+<br>
+private:<br>
+ std::unique_ptr<RPCChannel> Channel;<br>
+ std::unique_ptr<RPCEndpoint> Endpoint;<br>
+ std::unique_ptr<MemoryAccess> OwnedMemAccess;<br>
+ std::unique_ptr<MemoryManager> OwnedMemMgr;<br>
+ std::atomic<bool> Finished{false};<br>
+ std::thread ListenerThread;<br>
+};<br>
+<br>
+RemoteTargetProcessControl::RemoteTargetProcessControl(<br>
+ ExecutionSession &ES,
std::unique_ptr<RPCChannel> Channel,<br>
+ std::unique_ptr<RPCEndpoint> Endpoint)<br>
+ : BaseT(ES.getSymbolStringPool(), *Endpoint,<br>
+ [&ES](Error Err) {
ES.reportError(std::move(Err)); }),<br>
+ Channel(std::move(Channel)),
Endpoint(std::move(Endpoint)) {<br>
+<br>
+ ListenerThread = std::thread([&]() {<br>
+ while (!Finished) {<br>
+ if (auto Err = this->Endpoint->handleOne()) {<br>
+ reportError(std::move(Err));<br>
+ return;<br>
+ }<br>
+ }<br>
+ });<br>
+}<br>
+<br>
+void RemoteTargetProcessControl::initializeMemoryManagement()
{<br>
+ OwnedMemAccess =
std::make_unique<MemoryAccess>(*this);<br>
+ OwnedMemMgr = std::make_unique<MemoryManager>(*this);<br>
+<br>
+ // Base class needs non-owning access.<br>
+ MemAccess = OwnedMemAccess.get();<br>
+ MemMgr = OwnedMemMgr.get();<br>
+}<br>
+<br>
+Error RemoteTargetProcessControl::disconnect() {<br>
+ std::promise<MSVCPError> P;<br>
+ auto F = P.get_future();<br>
+ auto Err = closeConnection([&](Error Err) -> Error {<br>
+ P.set_value(std::move(Err));<br>
+ Finished = true;<br>
+ return Error::success();<br>
+ });<br>
+ ListenerThread.join();<br>
+ return joinErrors(std::move(Err), F.get());<br>
+}<br>
+<br>
+} // namespace orc<br>
+} // namespace llvm<br>
+<br>
+JITLinkExecutor::JITLinkExecutor() = default;<br>
+JITLinkExecutor::~JITLinkExecutor() = default;<br>
+<br>
+Expected<std::unique_ptr<ObjectLayer>><br>
+JITLinkExecutor::operator()(ExecutionSession &ES, const
Triple &TT) {<br>
+ return std::make_unique<ObjectLinkingLayer>(ES,
TPC->getMemMgr());<br>
+}<br>
+<br>
+Error JITLinkExecutor::addDebugSupport(ObjectLayer
&ObjLayer) {<br>
+ auto Registrar = createJITLoaderGDBRegistrar(*TPC);<br>
+ if (!Registrar)<br>
+ return Registrar.takeError();<br>
+<br>
+
cast<ObjectLinkingLayer>(&ObjLayer)->addPlugin(<br>
+
std::make_unique<DebugObjectManagerPlugin>(ObjLayer.getExecutionSession(),<br>
+
std::move(*Registrar)));<br>
+<br>
+ return Error::success();<br>
+}<br>
+<br>
+Expected<std::unique_ptr<DefinitionGenerator>><br>
+JITLinkExecutor::loadDylib(StringRef RemotePath) {<br>
+ if (auto Handle = TPC->loadDylib(RemotePath.data()))<br>
+ return
std::make_unique<TPCDynamicLibrarySearchGenerator>(*TPC,
*Handle);<br>
+ else<br>
+ return Handle.takeError();<br>
+}<br>
+<br>
+Expected<int>
JITLinkExecutor::runAsMain(JITEvaluatedSymbol MainSym,<br>
+
ArrayRef<std::string> Args) {<br>
+ return TPC->runAsMain(MainSym.getAddress(), Args);<br>
+}<br>
+<br>
+Error JITLinkExecutor::disconnect() { return
TPC->disconnect(); }<br>
+<br>
+static std::string defaultPath(const char *HostArgv0,
StringRef ExecutorName) {<br>
+ // This just needs to be some symbol in the binary; C++
doesn't<br>
+ // allow taking the address of ::main however.<br>
+ void *P = (void *)(intptr_t)defaultPath;<br>
+ SmallString<256>
FullName(sys::fs::getMainExecutable(HostArgv0, P));<br>
+ sys::path::remove_filename(FullName);<br>
+ sys::path::append(FullName, ExecutorName);<br>
+ return FullName.str().str();<br>
+}<br>
+<br>
+Expected<std::unique_ptr<ChildProcessJITLinkExecutor>><br>
+JITLinkExecutor::FindLocal(const char *HostArgv) {<br>
+ std::string BestGuess = defaultPath(HostArgv,
"llvm-jitlink-executor");<br>
+ auto Executor = CreateLocal(BestGuess);<br>
+ if (!Executor) {<br>
+ consumeError(Executor.takeError());<br>
+ return make_error<StringError>(<br>
+ formatv("Unable to find usable executor: {0}",
BestGuess),<br>
+ inconvertibleErrorCode());<br>
+ }<br>
+ return Executor;<br>
+}<br>
+<br>
+Expected<std::unique_ptr<ChildProcessJITLinkExecutor>><br>
+JITLinkExecutor::CreateLocal(std::string ExecutablePath) {<br>
+ if (!sys::fs::can_execute(ExecutablePath))<br>
+ return make_error<StringError>(<br>
+ formatv("Specified executor invalid: {0}",
ExecutablePath),<br>
+ inconvertibleErrorCode());<br>
+ return std::unique_ptr<ChildProcessJITLinkExecutor>(<br>
+ new
ChildProcessJITLinkExecutor(std::move(ExecutablePath)));<br>
+}<br>
+<br>
+TCPSocketJITLinkExecutor::TCPSocketJITLinkExecutor(<br>
+ std::unique_ptr<RemoteTargetProcessControl> TPC) {<br>
+ this->TPC = std::move(TPC);<br>
+}<br>
+<br>
+#ifndef LLVM_ON_UNIX<br>
+<br>
+// FIXME: Add support for Windows.<br>
+Error ChildProcessJITLinkExecutor::launch(ExecutionSession
&ES) {<br>
+ return make_error<StringError>(<br>
+ "Remote JITing not yet supported on non-unix
platforms",<br>
+ inconvertibleErrorCode());<br>
+}<br>
+<br>
+// FIXME: Add support for Windows.<br>
+Expected<std::unique_ptr<TCPSocketJITLinkExecutor>><br>
+JITLinkExecutor::ConnectTCPSocket(StringRef NetworkAddress,<br>
+ ExecutionSession &ES) {<br>
+ return make_error<StringError>(<br>
+ "Remote JITing not yet supported on non-unix
platforms",<br>
+ inconvertibleErrorCode());<br>
+}<br>
+<br>
+#else<br>
+<br>
+Error ChildProcessJITLinkExecutor::launch(ExecutionSession
&ES) {<br>
+ constexpr int ReadEnd = 0;<br>
+ constexpr int WriteEnd = 1;<br>
+<br>
+ // Pipe FDs.<br>
+ int ToExecutor[2];<br>
+ int FromExecutor[2];<br>
+<br>
+ // Create pipes to/from the executor..<br>
+ if (pipe(ToExecutor) != 0 || pipe(FromExecutor) != 0)<br>
+ return make_error<StringError>("Unable to create
pipe for executor",<br>
+ inconvertibleErrorCode());<br>
+<br>
+ ProcessID = fork();<br>
+ if (ProcessID == 0) {<br>
+ // In the child...<br>
+<br>
+ // Close the parent ends of the pipes<br>
+ close(ToExecutor[WriteEnd]);<br>
+ close(FromExecutor[ReadEnd]);<br>
+<br>
+ // Execute the child process.<br>
+ std::unique_ptr<char[]> ExecPath, FDSpecifier;<br>
+ {<br>
+ ExecPath =
std::make_unique<char[]>(ExecutablePath.size() + 1);<br>
+ strcpy(ExecPath.get(), ExecutablePath.data());<br>
+<br>
+ std::string FDSpecifierStr("filedescs=");<br>
+ FDSpecifierStr += utostr(ToExecutor[ReadEnd]);<br>
+ FDSpecifierStr += ',';<br>
+ FDSpecifierStr += utostr(FromExecutor[WriteEnd]);<br>
+ FDSpecifier =
std::make_unique<char[]>(FDSpecifierStr.size() + 1);<br>
+ strcpy(FDSpecifier.get(), FDSpecifierStr.c_str());<br>
+ }<br>
+<br>
+ char *const Args[] = {ExecPath.get(), FDSpecifier.get(),
nullptr};<br>
+ int RC = execvp(ExecPath.get(), Args);<br>
+ if (RC != 0)<br>
+ return make_error<StringError>(<br>
+ "Unable to launch out-of-process executor '" +
ExecutablePath + "'\n",<br>
+ inconvertibleErrorCode());<br>
+<br>
+ llvm_unreachable("Fork won't return in success case");<br>
+ }<br>
+ // else we're the parent...<br>
+<br>
+ // Close the child ends of the pipes<br>
+ close(ToExecutor[ReadEnd]);<br>
+ close(FromExecutor[WriteEnd]);<br>
+<br>
+ auto Channel =<br>
+
std::make_unique<RPCChannel>(FromExecutor[ReadEnd],
ToExecutor[WriteEnd]);<br>
+ auto Endpoint =<br>
+
std::make_unique<RemoteTargetProcessControl::RPCEndpoint>(*Channel,
true);<br>
+<br>
+ TPC =
std::make_unique<RemoteTargetProcessControl>(ES,
std::move(Channel),<br>
+
std::move(Endpoint));<br>
+<br>
+ if (auto Err = TPC->initializeORCRPCTPCBase())<br>
+ return joinErrors(std::move(Err), TPC->disconnect());<br>
+<br>
+ TPC->initializeMemoryManagement();<br>
+<br>
+ shared::registerStringError<RPCChannel>();<br>
+ return Error::success();<br>
+}<br>
+<br>
+static Expected<int> connectTCPSocketImpl(std::string
Host,<br>
+ std::string
PortStr) {<br>
+ addrinfo *AI;<br>
+ addrinfo Hints{};<br>
+ Hints.ai_family = AF_INET;<br>
+ Hints.ai_socktype = SOCK_STREAM;<br>
+ Hints.ai_flags = AI_NUMERICSERV;<br>
+<br>
+ if (int EC = getaddrinfo(Host.c_str(), PortStr.c_str(),
&Hints, &AI))<br>
+ return make_error<StringError>(<br>
+ formatv("address resolution failed ({0})",
gai_strerror(EC)),<br>
+ inconvertibleErrorCode());<br>
+<br>
+ // Cycle through the returned addrinfo structures and
connect to the first<br>
+ // reachable endpoint.<br>
+ int SockFD;<br>
+ addrinfo *Server;<br>
+ for (Server = AI; Server != nullptr; Server =
Server->ai_next) {<br>
+ // If socket fails, maybe it's because the address family
is not supported.<br>
+ // Skip to the next addrinfo structure.<br>
+ if ((SockFD = socket(AI->ai_family,
AI->ai_socktype, AI->ai_protocol)) < 0)<br>
+ continue;<br>
+<br>
+ // If connect works, we exit the loop with a working
socket.<br>
+ if (connect(SockFD, Server->ai_addr,
Server->ai_addrlen) == 0)<br>
+ break;<br>
+<br>
+ close(SockFD);<br>
+ }<br>
+ freeaddrinfo(AI);<br>
+<br>
+ // Did we reach the end of the loop without connecting to a
valid endpoint?<br>
+ if (Server == nullptr)<br>
+ return make_error<StringError>("invalid hostname",<br>
+ inconvertibleErrorCode());<br>
+<br>
+ return SockFD;<br>
+}<br>
+<br>
+Expected<std::unique_ptr<TCPSocketJITLinkExecutor>><br>
+JITLinkExecutor::ConnectTCPSocket(StringRef NetworkAddress,<br>
+ ExecutionSession &ES) {<br>
+ auto CreateErr = [NetworkAddress](StringRef Details) {<br>
+ return make_error<StringError>(<br>
+ formatv("Failed to connect TCP socket '{0}': {1}",
NetworkAddress,<br>
+ Details),<br>
+ inconvertibleErrorCode());<br>
+ };<br>
+<br>
+ StringRef Host, PortStr;<br>
+ std::tie(Host, PortStr) = NetworkAddress.split(':');<br>
+ if (Host.empty())<br>
+ return CreateErr("host name cannot be empty");<br>
+ if (PortStr.empty())<br>
+ return CreateErr("port cannot be empty");<br>
+ int Port = 0;<br>
+ if (PortStr.getAsInteger(10, Port))<br>
+ return CreateErr("port number is not a valid integer");<br>
+<br>
+ Expected<int> SockFD =
connectTCPSocketImpl(Host.str(), PortStr.str());<br>
+ if (!SockFD)<br>
+ return CreateErr(toString(SockFD.takeError()));<br>
+<br>
+ auto Channel = std::make_unique<RPCChannel>(*SockFD,
*SockFD);<br>
+ auto Endpoint =<br>
+
std::make_unique<RemoteTargetProcessControl::RPCEndpoint>(*Channel,
true);<br>
+<br>
+ auto TPC =
std::make_unique<RemoteTargetProcessControl>(<br>
+ ES, std::move(Channel), std::move(Endpoint));<br>
+<br>
+ if (auto Err = TPC->initializeORCRPCTPCBase())<br>
+ return joinErrors(std::move(Err), TPC->disconnect());<br>
+<br>
+ TPC->initializeMemoryManagement();<br>
+ shared::registerStringError<RPCChannel>();<br>
+<br>
+ return std::unique_ptr<TCPSocketJITLinkExecutor>(<br>
+ new TCPSocketJITLinkExecutor(std::move(TPC)));<br>
+}<br>
+<br>
+#endif<br>
<br>
diff --git
a/llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/RemoteJITUtils.h
b/llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/RemoteJITUtils.h<br>
new file mode 100644<br>
index 000000000000..e629c0e036f5<br>
--- /dev/null<br>
+++
b/llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/RemoteJITUtils.h<br>
@@ -0,0 +1,111 @@<br>
+//===-- RemoteJITUtils.h - Utilities for remote-JITing
----------*- C++ -*-===//<br>
+//<br>
+// Part of the LLVM Project, under the Apache License v2.0
with LLVM Exceptions.<br>
+// See <a href="https://llvm.org/LICENSE.txt"
rel="noreferrer" target="_blank" moz-do-not-send="true">https://llvm.org/LICENSE.txt</a>
for license information.<br>
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception<br>
+//<br>
+//===----------------------------------------------------------------------===//<br>
+//<br>
+// Utilities for TargetProcessControl-based remote JITing
with Orc and JITLink.<br>
+//<br>
+//===----------------------------------------------------------------------===//<br>
+<br>
+#ifndef
LLVM_EXAMPLES_ORCV2EXAMPLES_LLJITWITHREMOTEDEBUGGING_REMOTEJITUTILS_H<br>
+#define
LLVM_EXAMPLES_ORCV2EXAMPLES_LLJITWITHREMOTEDEBUGGING_REMOTEJITUTILS_H<br>
+<br>
+#include "llvm/ADT/ArrayRef.h"<br>
+#include "llvm/ADT/StringRef.h"<br>
+#include "llvm/ADT/Triple.h"<br>
+#include "llvm/ExecutionEngine/JITSymbol.h"<br>
+#include "llvm/ExecutionEngine/Orc/Core.h"<br>
+#include "llvm/ExecutionEngine/Orc/Layer.h"<br>
+#include "llvm/ExecutionEngine/Orc/ObjectTransformLayer.h"<br>
+#include "llvm/ExecutionEngine/Orc/Shared/FDRawByteChannel.h"<br>
+#include "llvm/Support/Error.h"<br>
+<br>
+#include <memory><br>
+#include <string><br>
+<br>
+#if !defined(_MSC_VER) && !defined(__MINGW32__)<br>
+#include <unistd.h><br>
+#else<br>
+#include <io.h><br>
+#endif<br>
+<br>
+namespace llvm {<br>
+namespace orc {<br>
+<br>
+class ChildProcessJITLinkExecutor;<br>
+class RemoteTargetProcessControl;<br>
+class TCPSocketJITLinkExecutor;<br>
+<br>
+class JITLinkExecutor {<br>
+public:<br>
+ using RPCChannel = shared::FDRawByteChannel;<br>
+<br>
+ /// Create a JITLinkExecutor for the given exectuable on
disk.<br>
+ static
Expected<std::unique_ptr<ChildProcessJITLinkExecutor>><br>
+ CreateLocal(std::string ExecutablePath);<br>
+<br>
+ /// Find the default exectuable on disk and create a
JITLinkExecutor for it.<br>
+ static
Expected<std::unique_ptr<ChildProcessJITLinkExecutor>><br>
+ FindLocal(const char *JITArgv0);<br>
+<br>
+ /// Create a JITLinkExecutor that connects to the given
network address<br>
+ /// through a TCP socket. A valid NetworkAddress provides
hostname and port,<br>
+ /// e.g. localhost:20000.<br>
+ static
Expected<std::unique_ptr<TCPSocketJITLinkExecutor>><br>
+ ConnectTCPSocket(StringRef NetworkAddress, ExecutionSession
&ES);<br>
+<br>
+ // Implement ObjectLinkingLayerCreator<br>
+ Expected<std::unique_ptr<ObjectLayer>>
operator()(ExecutionSession &,<br>
+ const
Triple &);<br>
+<br>
+ Error addDebugSupport(ObjectLayer &ObjLayer);<br>
+<br>
+ Expected<std::unique_ptr<DefinitionGenerator>><br>
+ loadDylib(StringRef RemotePath);<br>
+<br>
+ Expected<int> runAsMain(JITEvaluatedSymbol MainSym,<br>
+ ArrayRef<std::string> Args);<br>
+ Error disconnect();<br>
+<br>
+ virtual ~JITLinkExecutor();<br>
+<br>
+protected:<br>
+ std::unique_ptr<RemoteTargetProcessControl> TPC;<br>
+<br>
+ JITLinkExecutor();<br>
+};<br>
+<br>
+/// JITLinkExecutor that runs in a child process on the local
machine.<br>
+class ChildProcessJITLinkExecutor : public JITLinkExecutor {<br>
+public:<br>
+ Error launch(ExecutionSession &ES);<br>
+<br>
+ pid_t getPID() const { return ProcessID; }<br>
+ StringRef getPath() const { return ExecutablePath; }<br>
+<br>
+private:<br>
+ std::string ExecutablePath;<br>
+ pid_t ProcessID;<br>
+<br>
+ ChildProcessJITLinkExecutor(std::string ExecutablePath)<br>
+ : ExecutablePath(std::move(ExecutablePath)) {}<br>
+<br>
+ static std::string defaultPath(const char *HostArgv0,
StringRef ExecutorName);<br>
+ friend class JITLinkExecutor;<br>
+};<br>
+<br>
+/// JITLinkExecutor connected through a TCP socket.<br>
+class TCPSocketJITLinkExecutor : public JITLinkExecutor {<br>
+private:<br>
+
TCPSocketJITLinkExecutor(std::unique_ptr<RemoteTargetProcessControl>
TPC);<br>
+<br>
+ friend class JITLinkExecutor;<br>
+};<br>
+<br>
+} // namespace orc<br>
+} // namespace llvm<br>
+<br>
+#endif<br>
<br>
diff --git
a/llvm/test/Examples/OrcV2Examples/Inputs/argc_sub1.c
b/llvm/test/Examples/OrcV2Examples/Inputs/argc_sub1.c<br>
new file mode 100644<br>
index 000000000000..3a5c2bcefee1<br>
--- /dev/null<br>
+++ b/llvm/test/Examples/OrcV2Examples/Inputs/argc_sub1.c<br>
@@ -0,0 +1,2 @@<br>
+int sub1(int x) { return x - 1; }<br>
+int main(int argc, char **argv) { return sub1(argc); }<br>
<br>
diff --git
a/llvm/test/Examples/OrcV2Examples/Inputs/argc_sub1_elf.ll
b/llvm/test/Examples/OrcV2Examples/Inputs/argc_sub1_elf.ll<br>
new file mode 100644<br>
index 000000000000..659dbe109ec6<br>
--- /dev/null<br>
+++ b/llvm/test/Examples/OrcV2Examples/Inputs/argc_sub1_elf.ll<br>
@@ -0,0 +1,52 @@<br>
+; ModuleID = 'argc_sub1.c'<br>
+target triple = "x86_64-unknown-unknown-elf"<br>
+<br>
+define i32 @sub1(i32) !dbg !8 {<br>
+ call void @llvm.dbg.value(metadata i32 %0, metadata !13,
metadata !DIExpression()), !dbg !14<br>
+ %2 = add nsw i32 %0, -1, !dbg !15<br>
+ ret i32 %2, !dbg !16<br>
+}<br>
+<br>
+define i32 @main(i32, i8** nocapture readnone) !dbg !17 {<br>
+ call void @llvm.dbg.value(metadata i32 %0, metadata !24,
metadata !DIExpression()), !dbg !26<br>
+ call void @llvm.dbg.value(metadata i8** %1, metadata !25,
metadata !DIExpression()), !dbg !27<br>
+ %3 = tail call i32 @sub1(i32 %0), !dbg !28<br>
+ ret i32 %3, !dbg !29<br>
+}<br>
+<br>
+declare void @llvm.dbg.value(metadata, metadata, metadata)<br>
+<br>
+!<a href="http://llvm.dbg.cu" rel="noreferrer"
target="_blank" moz-do-not-send="true">llvm.dbg.cu</a> =
!{!0}<br>
+!llvm.module.flags = !{!3, !4, !5, !6}<br>
+!llvm.ident = !{!7}<br>
+<br>
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1,
producer: "clang version 7.0.1-8+deb10u2
(tags/RELEASE_701/final)", isOptimized: true, runtimeVersion:
0, emissionKind: FullDebug, enums: !2)<br>
+!1 = !DIFile(filename: "argc_sub1.c", directory: "Inputs/")<br>
+!2 = !{}<br>
+!3 = !{i32 2, !"Dwarf Version", i32 4}<br>
+!4 = !{i32 2, !"Debug Info Version", i32 3}<br>
+!5 = !{i32 1, !"wchar_size", i32 4}<br>
+!6 = !{i32 7, !"PIC Level", i32 2}<br>
+!7 = !{!"clang version 7.0.1-8+deb10u2
(tags/RELEASE_701/final)"}<br>
+!8 = distinct !DISubprogram(name: "sub1", scope: !1, file:
!1, line: 1, type: !9, isLocal: false, isDefinition: true,
scopeLine: 1, flags: DIFlagPrototyped, isOptimized: true,
unit: !0, retainedNodes: !12)<br>
+!9 = !DISubroutineType(types: !10)<br>
+!10 = !{!11, !11}<br>
+!11 = !DIBasicType(name: "int", size: 32, encoding:
DW_ATE_signed)<br>
+!12 = !{!13}<br>
+!13 = !DILocalVariable(name: "x", arg: 1, scope: !8, file:
!1, line: 1, type: !11)<br>
+!14 = !DILocation(line: 1, column: 14, scope: !8)<br>
+!15 = !DILocation(line: 1, column: 28, scope: !8)<br>
+!16 = !DILocation(line: 1, column: 19, scope: !8)<br>
+!17 = distinct !DISubprogram(name: "main", scope: !1, file:
!1, line: 2, type: !18, isLocal: false, isDefinition: true,
scopeLine: 2, flags: DIFlagPrototyped, isOptimized: true,
unit: !0, retainedNodes: !23)<br>
+!18 = !DISubroutineType(types: !19)<br>
+!19 = !{!11, !11, !20}<br>
+!20 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !21,
size: 64)<br>
+!21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22,
size: 64)<br>
+!22 = !DIBasicType(name: "char", size: 8, encoding:
DW_ATE_signed_char)<br>
+!23 = !{!24, !25}<br>
+!24 = !DILocalVariable(name: "argc", arg: 1, scope: !17,
file: !1, line: 2, type: !11)<br>
+!25 = !DILocalVariable(name: "argv", arg: 2, scope: !17,
file: !1, line: 2, type: !20)<br>
+!26 = !DILocation(line: 2, column: 14, scope: !17)<br>
+!27 = !DILocation(line: 2, column: 27, scope: !17)<br>
+!28 = !DILocation(line: 2, column: 42, scope: !17)<br>
+!29 = !DILocation(line: 2, column: 35, scope: !17)<br>
<br>
diff --git
a/llvm/test/Examples/OrcV2Examples/lljit-with-remote-debugging.test
b/llvm/test/Examples/OrcV2Examples/lljit-with-remote-debugging.test<br>
new file mode 100644<br>
index 000000000000..a9c3946b55e2<br>
--- /dev/null<br>
+++
b/llvm/test/Examples/OrcV2Examples/lljit-with-remote-debugging.test<br>
@@ -0,0 +1,10 @@<br>
+# This test makes sure that the example builds and executes
as expected.<br>
+# Instructions for debugging can be found in
LLJITWithRemoteDebugging.cpp<br>
+<br>
+# RUN: LLJITWithRemoteDebugging %p/Inputs/argc_sub1_elf.ll<br>
+# CHECK: Running: {{.*}}/Inputs/argc_sub1_elf.ll<br>
+# CHECK: Exit code: 0<br>
+<br>
+# RUN: LLJITWithRemoteDebugging %p/Inputs/argc_sub1_elf.ll
--args 2nd 3rd 4th<br>
+# CHECK: Running: {{.*}}/Inputs/argc_sub1_elf.ll 2nd 3rd 4th<br>
+# CHECK: Exit code: 3<br>
<br>
<br>
<br>
_______________________________________________<br>
llvm-commits mailing list<br>
<a href="mailto:llvm-commits@lists.llvm.org" target="_blank"
moz-do-not-send="true">llvm-commits@lists.llvm.org</a><br>
<a
href="https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits"
rel="noreferrer" target="_blank" moz-do-not-send="true">https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits</a><br>
</blockquote>
</div>
</blockquote>
<pre class="moz-signature" cols="72">--
<a class="moz-txt-link-freetext" href="https://flowcrypt.com/pub/stefan.graenitz@gmail.com">https://flowcrypt.com/pub/stefan.graenitz@gmail.com</a></pre>
</body>
</html>