This PR introduces out-of-process (OOP) execution support for Clang-Repl. With this enhancement, two new flags, oop-executor and oop-executor-connect, are added to the Clang-Repl interface. These flags enable the launch of an external executor (llvm-jitlink-executor), which handles code execution in a separate process.
This commit is contained in:
parent
ac9204de7d
commit
3f531552e6
@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
#include "llvm/ADT/DenseMap.h"
|
#include "llvm/ADT/DenseMap.h"
|
||||||
#include "llvm/ExecutionEngine/JITSymbol.h"
|
#include "llvm/ExecutionEngine/JITSymbol.h"
|
||||||
|
#include "llvm/ExecutionEngine/Orc/ExecutorProcessControl.h"
|
||||||
#include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h"
|
#include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h"
|
||||||
#include "llvm/Support/Error.h"
|
#include "llvm/Support/Error.h"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@ -136,10 +137,14 @@ protected:
|
|||||||
public:
|
public:
|
||||||
virtual ~Interpreter();
|
virtual ~Interpreter();
|
||||||
static llvm::Expected<std::unique_ptr<Interpreter>>
|
static llvm::Expected<std::unique_ptr<Interpreter>>
|
||||||
create(std::unique_ptr<CompilerInstance> CI);
|
create(std::unique_ptr<CompilerInstance> CI,
|
||||||
|
std::unique_ptr<llvm::orc::LLJITBuilder> JITBuilder = nullptr);
|
||||||
static llvm::Expected<std::unique_ptr<Interpreter>>
|
static llvm::Expected<std::unique_ptr<Interpreter>>
|
||||||
createWithCUDA(std::unique_ptr<CompilerInstance> CI,
|
createWithCUDA(std::unique_ptr<CompilerInstance> CI,
|
||||||
std::unique_ptr<CompilerInstance> DCI);
|
std::unique_ptr<CompilerInstance> DCI);
|
||||||
|
static llvm::Expected<std::unique_ptr<llvm::orc::LLJITBuilder>>
|
||||||
|
createLLJITBuilder(std::unique_ptr<llvm::orc::ExecutorProcessControl> EPC,
|
||||||
|
llvm::StringRef OrcRuntimePath);
|
||||||
const ASTContext &getASTContext() const;
|
const ASTContext &getASTContext() const;
|
||||||
ASTContext &getASTContext();
|
ASTContext &getASTContext();
|
||||||
const CompilerInstance *getCompilerInstance() const;
|
const CompilerInstance *getCompilerInstance() const;
|
||||||
|
38
clang/include/clang/Interpreter/RemoteJITUtils.h
Normal file
38
clang/include/clang/Interpreter/RemoteJITUtils.h
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
//===-- RemoteJITUtils.h - Utilities for remote-JITing ----------*- C++ -*-===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// Utilities for ExecutorProcessControl-based remote JITing with Orc and
|
||||||
|
// JITLink.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef LLVM_CLANG_INTERPRETER_REMOTEJITUTILS_H
|
||||||
|
#define LLVM_CLANG_INTERPRETER_REMOTEJITUTILS_H
|
||||||
|
|
||||||
|
#include "llvm/ADT/StringRef.h"
|
||||||
|
#include "llvm/ExecutionEngine/Orc/Core.h"
|
||||||
|
#include "llvm/ExecutionEngine/Orc/Layer.h"
|
||||||
|
#include "llvm/ExecutionEngine/Orc/SimpleRemoteEPC.h"
|
||||||
|
#include "llvm/Support/Error.h"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
llvm::Expected<std::unique_ptr<llvm::orc::SimpleRemoteEPC>>
|
||||||
|
launchExecutor(llvm::StringRef ExecutablePath, bool UseSharedMemory,
|
||||||
|
llvm::StringRef SlabAllocateSizeString);
|
||||||
|
|
||||||
|
/// Create a JITLinkExecutor that connects to the given network address
|
||||||
|
/// through a TCP socket. A valid NetworkAddress provides hostname and port,
|
||||||
|
/// e.g. localhost:20000.
|
||||||
|
llvm::Expected<std::unique_ptr<llvm::orc::SimpleRemoteEPC>>
|
||||||
|
connectTCPSocket(llvm::StringRef NetworkAddress, bool UseSharedMemory,
|
||||||
|
llvm::StringRef SlabAllocateSizeString);
|
||||||
|
|
||||||
|
#endif // LLVM_CLANG_INTERPRETER_REMOTEJITUTILS_H
|
@ -27,6 +27,7 @@ add_clang_library(clangInterpreter
|
|||||||
Interpreter.cpp
|
Interpreter.cpp
|
||||||
InterpreterValuePrinter.cpp
|
InterpreterValuePrinter.cpp
|
||||||
InterpreterUtils.cpp
|
InterpreterUtils.cpp
|
||||||
|
RemoteJITUtils.cpp
|
||||||
Value.cpp
|
Value.cpp
|
||||||
${WASM_SRC}
|
${WASM_SRC}
|
||||||
PARTIAL_SOURCES_INTENDED
|
PARTIAL_SOURCES_INTENDED
|
||||||
|
@ -46,6 +46,7 @@
|
|||||||
#include "clang/Sema/Lookup.h"
|
#include "clang/Sema/Lookup.h"
|
||||||
#include "clang/Serialization/ObjectFilePCHContainerReader.h"
|
#include "clang/Serialization/ObjectFilePCHContainerReader.h"
|
||||||
#include "llvm/ExecutionEngine/JITSymbol.h"
|
#include "llvm/ExecutionEngine/JITSymbol.h"
|
||||||
|
#include "llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h"
|
||||||
#include "llvm/ExecutionEngine/Orc/LLJIT.h"
|
#include "llvm/ExecutionEngine/Orc/LLJIT.h"
|
||||||
#include "llvm/IR/Module.h"
|
#include "llvm/IR/Module.h"
|
||||||
#include "llvm/Support/Errc.h"
|
#include "llvm/Support/Errc.h"
|
||||||
@ -455,10 +456,11 @@ const char *const Runtimes = R"(
|
|||||||
)";
|
)";
|
||||||
|
|
||||||
llvm::Expected<std::unique_ptr<Interpreter>>
|
llvm::Expected<std::unique_ptr<Interpreter>>
|
||||||
Interpreter::create(std::unique_ptr<CompilerInstance> CI) {
|
Interpreter::create(std::unique_ptr<CompilerInstance> CI,
|
||||||
|
std::unique_ptr<llvm::orc::LLJITBuilder> JB) {
|
||||||
llvm::Error Err = llvm::Error::success();
|
llvm::Error Err = llvm::Error::success();
|
||||||
auto Interp =
|
auto Interp = std::unique_ptr<Interpreter>(
|
||||||
std::unique_ptr<Interpreter>(new Interpreter(std::move(CI), Err));
|
new Interpreter(std::move(CI), Err, JB ? std::move(JB) : nullptr));
|
||||||
if (Err)
|
if (Err)
|
||||||
return std::move(Err);
|
return std::move(Err);
|
||||||
|
|
||||||
@ -617,6 +619,25 @@ createJITTargetMachineBuilder(const std::string &TT) {
|
|||||||
return llvm::orc::JITTargetMachineBuilder(llvm::Triple(TT));
|
return llvm::orc::JITTargetMachineBuilder(llvm::Triple(TT));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
llvm::Expected<std::unique_ptr<llvm::orc::LLJITBuilder>>
|
||||||
|
Interpreter::createLLJITBuilder(
|
||||||
|
std::unique_ptr<llvm::orc::ExecutorProcessControl> EPC,
|
||||||
|
llvm::StringRef OrcRuntimePath) {
|
||||||
|
const std::string &TT = EPC->getTargetTriple().getTriple();
|
||||||
|
auto JTMB = createJITTargetMachineBuilder(TT);
|
||||||
|
if (!JTMB)
|
||||||
|
return JTMB.takeError();
|
||||||
|
auto JB = IncrementalExecutor::createDefaultJITBuilder(std::move(*JTMB));
|
||||||
|
if (!JB)
|
||||||
|
return JB.takeError();
|
||||||
|
|
||||||
|
(*JB)->setExecutorProcessControl(std::move(EPC));
|
||||||
|
(*JB)->setPlatformSetUp(
|
||||||
|
llvm::orc::ExecutorNativePlatform(OrcRuntimePath.str()));
|
||||||
|
|
||||||
|
return std::move(*JB);
|
||||||
|
}
|
||||||
|
|
||||||
llvm::Error Interpreter::CreateExecutor() {
|
llvm::Error Interpreter::CreateExecutor() {
|
||||||
if (IncrExecutor)
|
if (IncrExecutor)
|
||||||
return llvm::make_error<llvm::StringError>("Operation failed. "
|
return llvm::make_error<llvm::StringError>("Operation failed. "
|
||||||
@ -756,11 +777,13 @@ llvm::Error Interpreter::LoadDynamicLibrary(const char *name) {
|
|||||||
if (!EE)
|
if (!EE)
|
||||||
return EE.takeError();
|
return EE.takeError();
|
||||||
|
|
||||||
auto &DL = EE->getDataLayout();
|
if (llvm::Expected<
|
||||||
|
std::unique_ptr<llvm::orc::EPCDynamicLibrarySearchGenerator>>
|
||||||
if (auto DLSG = llvm::orc::DynamicLibrarySearchGenerator::Load(
|
DLSG = llvm::orc::EPCDynamicLibrarySearchGenerator::Load(
|
||||||
name, DL.getGlobalPrefix()))
|
EE->getExecutionSession(), name))
|
||||||
EE->getMainJITDylib().addGenerator(std::move(*DLSG));
|
// FIXME: Eventually we should put each library in its own JITDylib and
|
||||||
|
// turn off process symbols by default.
|
||||||
|
EE->getProcessSymbolsJITDylib()->addGenerator(std::move(*DLSG));
|
||||||
else
|
else
|
||||||
return DLSG.takeError();
|
return DLSG.takeError();
|
||||||
#endif
|
#endif
|
||||||
|
267
clang/lib/Interpreter/RemoteJITUtils.cpp
Normal file
267
clang/lib/Interpreter/RemoteJITUtils.cpp
Normal file
@ -0,0 +1,267 @@
|
|||||||
|
//===-- RemoteJITUtils.cpp - Utilities for remote-JITing --------*- C++ -*-===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// FIXME: Unify this code with similar functionality in llvm-jitlink.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include "clang/Interpreter/RemoteJITUtils.h"
|
||||||
|
|
||||||
|
#include "llvm/ADT/StringExtras.h"
|
||||||
|
#include "llvm/ExecutionEngine/Orc/DebugObjectManagerPlugin.h"
|
||||||
|
#include "llvm/ExecutionEngine/Orc/EPCDebugObjectRegistrar.h"
|
||||||
|
#include "llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h"
|
||||||
|
#include "llvm/ExecutionEngine/Orc/MapperJITLinkMemoryManager.h"
|
||||||
|
#include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h"
|
||||||
|
#include "llvm/ExecutionEngine/Orc/Shared/SimpleRemoteEPCUtils.h"
|
||||||
|
#include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h"
|
||||||
|
#include "llvm/Support/FileSystem.h"
|
||||||
|
#include "llvm/Support/Path.h"
|
||||||
|
|
||||||
|
#ifdef LLVM_ON_UNIX
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif // LLVM_ON_UNIX
|
||||||
|
|
||||||
|
using namespace llvm;
|
||||||
|
using namespace llvm::orc;
|
||||||
|
|
||||||
|
Expected<uint64_t> getSlabAllocSize(StringRef SizeString) {
|
||||||
|
SizeString = SizeString.trim();
|
||||||
|
|
||||||
|
uint64_t Units = 1024;
|
||||||
|
|
||||||
|
if (SizeString.ends_with_insensitive("kb"))
|
||||||
|
SizeString = SizeString.drop_back(2).rtrim();
|
||||||
|
else if (SizeString.ends_with_insensitive("mb")) {
|
||||||
|
Units = 1024 * 1024;
|
||||||
|
SizeString = SizeString.drop_back(2).rtrim();
|
||||||
|
} else if (SizeString.ends_with_insensitive("gb")) {
|
||||||
|
Units = 1024 * 1024 * 1024;
|
||||||
|
SizeString = SizeString.drop_back(2).rtrim();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t SlabSize = 0;
|
||||||
|
if (SizeString.getAsInteger(10, SlabSize))
|
||||||
|
return make_error<StringError>("Invalid numeric format for slab size",
|
||||||
|
inconvertibleErrorCode());
|
||||||
|
|
||||||
|
return SlabSize * Units;
|
||||||
|
}
|
||||||
|
|
||||||
|
Expected<std::unique_ptr<jitlink::JITLinkMemoryManager>>
|
||||||
|
createSharedMemoryManager(SimpleRemoteEPC &SREPC,
|
||||||
|
StringRef SlabAllocateSizeString) {
|
||||||
|
SharedMemoryMapper::SymbolAddrs SAs;
|
||||||
|
if (auto Err = SREPC.getBootstrapSymbols(
|
||||||
|
{{SAs.Instance, rt::ExecutorSharedMemoryMapperServiceInstanceName},
|
||||||
|
{SAs.Reserve,
|
||||||
|
rt::ExecutorSharedMemoryMapperServiceReserveWrapperName},
|
||||||
|
{SAs.Initialize,
|
||||||
|
rt::ExecutorSharedMemoryMapperServiceInitializeWrapperName},
|
||||||
|
{SAs.Deinitialize,
|
||||||
|
rt::ExecutorSharedMemoryMapperServiceDeinitializeWrapperName},
|
||||||
|
{SAs.Release,
|
||||||
|
rt::ExecutorSharedMemoryMapperServiceReleaseWrapperName}}))
|
||||||
|
return std::move(Err);
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
size_t SlabSize = 1024 * 1024;
|
||||||
|
#else
|
||||||
|
size_t SlabSize = 1024 * 1024 * 1024;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!SlabAllocateSizeString.empty()) {
|
||||||
|
if (Expected<uint64_t> S = getSlabAllocSize(SlabAllocateSizeString))
|
||||||
|
SlabSize = *S;
|
||||||
|
else
|
||||||
|
return S.takeError();
|
||||||
|
}
|
||||||
|
|
||||||
|
return MapperJITLinkMemoryManager::CreateWithMapper<SharedMemoryMapper>(
|
||||||
|
SlabSize, SREPC, SAs);
|
||||||
|
}
|
||||||
|
|
||||||
|
Expected<std::unique_ptr<SimpleRemoteEPC>>
|
||||||
|
launchExecutor(StringRef ExecutablePath, bool UseSharedMemory,
|
||||||
|
llvm::StringRef SlabAllocateSizeString) {
|
||||||
|
#ifndef LLVM_ON_UNIX
|
||||||
|
// FIXME: Add support for Windows.
|
||||||
|
return make_error<StringError>("-" + ExecutablePath +
|
||||||
|
" not supported on non-unix platforms",
|
||||||
|
inconvertibleErrorCode());
|
||||||
|
#elif !LLVM_ENABLE_THREADS
|
||||||
|
// Out of process mode using SimpleRemoteEPC depends on threads.
|
||||||
|
return make_error<StringError>(
|
||||||
|
"-" + ExecutablePath +
|
||||||
|
" requires threads, but LLVM was built with "
|
||||||
|
"LLVM_ENABLE_THREADS=Off",
|
||||||
|
inconvertibleErrorCode());
|
||||||
|
#else
|
||||||
|
|
||||||
|
if (!sys::fs::can_execute(ExecutablePath))
|
||||||
|
return make_error<StringError>(
|
||||||
|
formatv("Specified executor invalid: {0}", ExecutablePath),
|
||||||
|
inconvertibleErrorCode());
|
||||||
|
|
||||||
|
constexpr int ReadEnd = 0;
|
||||||
|
constexpr int WriteEnd = 1;
|
||||||
|
|
||||||
|
// Pipe FDs.
|
||||||
|
int ToExecutor[2];
|
||||||
|
int FromExecutor[2];
|
||||||
|
|
||||||
|
pid_t ChildPID;
|
||||||
|
|
||||||
|
// Create pipes to/from the executor..
|
||||||
|
if (pipe(ToExecutor) != 0 || pipe(FromExecutor) != 0)
|
||||||
|
return make_error<StringError>("Unable to create pipe for executor",
|
||||||
|
inconvertibleErrorCode());
|
||||||
|
|
||||||
|
ChildPID = fork();
|
||||||
|
|
||||||
|
if (ChildPID == 0) {
|
||||||
|
// In the child...
|
||||||
|
|
||||||
|
// Close the parent ends of the pipes
|
||||||
|
close(ToExecutor[WriteEnd]);
|
||||||
|
close(FromExecutor[ReadEnd]);
|
||||||
|
|
||||||
|
// Execute the child process.
|
||||||
|
std::unique_ptr<char[]> ExecutorPath, FDSpecifier;
|
||||||
|
{
|
||||||
|
ExecutorPath = std::make_unique<char[]>(ExecutablePath.size() + 1);
|
||||||
|
strcpy(ExecutorPath.get(), ExecutablePath.data());
|
||||||
|
|
||||||
|
std::string FDSpecifierStr("filedescs=");
|
||||||
|
FDSpecifierStr += utostr(ToExecutor[ReadEnd]);
|
||||||
|
FDSpecifierStr += ',';
|
||||||
|
FDSpecifierStr += utostr(FromExecutor[WriteEnd]);
|
||||||
|
FDSpecifier = std::make_unique<char[]>(FDSpecifierStr.size() + 1);
|
||||||
|
strcpy(FDSpecifier.get(), FDSpecifierStr.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
char *const Args[] = {ExecutorPath.get(), FDSpecifier.get(), nullptr};
|
||||||
|
int RC = execvp(ExecutorPath.get(), Args);
|
||||||
|
if (RC != 0) {
|
||||||
|
errs() << "unable to launch out-of-process executor \""
|
||||||
|
<< ExecutorPath.get() << "\"\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// else we're the parent...
|
||||||
|
|
||||||
|
// Close the child ends of the pipes
|
||||||
|
close(ToExecutor[ReadEnd]);
|
||||||
|
close(FromExecutor[WriteEnd]);
|
||||||
|
|
||||||
|
SimpleRemoteEPC::Setup S = SimpleRemoteEPC::Setup();
|
||||||
|
if (UseSharedMemory)
|
||||||
|
S.CreateMemoryManager = [SlabAllocateSizeString](SimpleRemoteEPC &EPC) {
|
||||||
|
return createSharedMemoryManager(EPC, SlabAllocateSizeString);
|
||||||
|
};
|
||||||
|
|
||||||
|
return SimpleRemoteEPC::Create<FDSimpleRemoteEPCTransport>(
|
||||||
|
std::make_unique<DynamicThreadPoolTaskDispatcher>(std::nullopt),
|
||||||
|
std::move(S), FromExecutor[ReadEnd], ToExecutor[WriteEnd]);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#if LLVM_ON_UNIX && LLVM_ENABLE_THREADS
|
||||||
|
|
||||||
|
static Expected<int> connectTCPSocketImpl(std::string Host,
|
||||||
|
std::string PortStr) {
|
||||||
|
addrinfo *AI;
|
||||||
|
addrinfo Hints{};
|
||||||
|
Hints.ai_family = AF_INET;
|
||||||
|
Hints.ai_socktype = SOCK_STREAM;
|
||||||
|
Hints.ai_flags = AI_NUMERICSERV;
|
||||||
|
|
||||||
|
if (int EC = getaddrinfo(Host.c_str(), PortStr.c_str(), &Hints, &AI))
|
||||||
|
return make_error<StringError>(
|
||||||
|
formatv("address resolution failed ({0})", gai_strerror(EC)),
|
||||||
|
inconvertibleErrorCode());
|
||||||
|
// Cycle through the returned addrinfo structures and connect to the first
|
||||||
|
// reachable endpoint.
|
||||||
|
int SockFD;
|
||||||
|
addrinfo *Server;
|
||||||
|
for (Server = AI; Server != nullptr; Server = Server->ai_next) {
|
||||||
|
// socket might fail, e.g. if the address family is not supported. Skip to
|
||||||
|
// the next addrinfo structure in such a case.
|
||||||
|
if ((SockFD = socket(AI->ai_family, AI->ai_socktype, AI->ai_protocol)) < 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// If connect returns null, we exit the loop with a working socket.
|
||||||
|
if (connect(SockFD, Server->ai_addr, Server->ai_addrlen) == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
close(SockFD);
|
||||||
|
}
|
||||||
|
freeaddrinfo(AI);
|
||||||
|
|
||||||
|
// If we reached the end of the loop without connecting to a valid endpoint,
|
||||||
|
// dump the last error that was logged in socket() or connect().
|
||||||
|
if (Server == nullptr)
|
||||||
|
return make_error<StringError>("invalid hostname",
|
||||||
|
inconvertibleErrorCode());
|
||||||
|
|
||||||
|
return SockFD;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Expected<std::unique_ptr<SimpleRemoteEPC>>
|
||||||
|
connectTCPSocket(StringRef NetworkAddress, bool UseSharedMemory,
|
||||||
|
llvm::StringRef SlabAllocateSizeString) {
|
||||||
|
#ifndef LLVM_ON_UNIX
|
||||||
|
// FIXME: Add TCP support for Windows.
|
||||||
|
return make_error<StringError>("-" + NetworkAddress +
|
||||||
|
" not supported on non-unix platforms",
|
||||||
|
inconvertibleErrorCode());
|
||||||
|
#elif !LLVM_ENABLE_THREADS
|
||||||
|
// Out of process mode using SimpleRemoteEPC depends on threads.
|
||||||
|
return make_error<StringError>(
|
||||||
|
"-" + NetworkAddress +
|
||||||
|
" requires threads, but LLVM was built with "
|
||||||
|
"LLVM_ENABLE_THREADS=Off",
|
||||||
|
inconvertibleErrorCode());
|
||||||
|
#else
|
||||||
|
|
||||||
|
auto CreateErr = [NetworkAddress](Twine Details) {
|
||||||
|
return make_error<StringError>(
|
||||||
|
formatv("Failed to connect TCP socket '{0}': {1}", NetworkAddress,
|
||||||
|
Details),
|
||||||
|
inconvertibleErrorCode());
|
||||||
|
};
|
||||||
|
|
||||||
|
StringRef Host, PortStr;
|
||||||
|
std::tie(Host, PortStr) = NetworkAddress.split(':');
|
||||||
|
if (Host.empty())
|
||||||
|
return CreateErr("Host name for -" + NetworkAddress + " can not be empty");
|
||||||
|
if (PortStr.empty())
|
||||||
|
return CreateErr("Port number in -" + NetworkAddress + " can not be empty");
|
||||||
|
int Port = 0;
|
||||||
|
if (PortStr.getAsInteger(10, Port))
|
||||||
|
return CreateErr("Port number '" + PortStr + "' is not a valid integer");
|
||||||
|
|
||||||
|
Expected<int> SockFD = connectTCPSocketImpl(Host.str(), PortStr.str());
|
||||||
|
if (!SockFD)
|
||||||
|
return SockFD.takeError();
|
||||||
|
|
||||||
|
SimpleRemoteEPC::Setup S = SimpleRemoteEPC::Setup();
|
||||||
|
if (UseSharedMemory)
|
||||||
|
S.CreateMemoryManager = [SlabAllocateSizeString](SimpleRemoteEPC &EPC) {
|
||||||
|
return createSharedMemoryManager(EPC, SlabAllocateSizeString);
|
||||||
|
};
|
||||||
|
|
||||||
|
return SimpleRemoteEPC::Create<FDSimpleRemoteEPCTransport>(
|
||||||
|
std::make_unique<DynamicThreadPoolTaskDispatcher>(std::nullopt),
|
||||||
|
std::move(S), *SockFD, *SockFD);
|
||||||
|
#endif
|
||||||
|
}
|
88
clang/test/Interpreter/out-of-process.cpp
Normal file
88
clang/test/Interpreter/out-of-process.cpp
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
// REQUIRES: host-supports-jit, host-supports-out-of-process-jit, x86_64-linux
|
||||||
|
|
||||||
|
// RUN: cat %s | clang-repl -oop-executor -orc-runtime | FileCheck %s
|
||||||
|
|
||||||
|
extern "C" int printf(const char *, ...);
|
||||||
|
|
||||||
|
int intVar = 0;
|
||||||
|
double doubleVar = 3.14;
|
||||||
|
%undo
|
||||||
|
double doubleVar = 2.71;
|
||||||
|
|
||||||
|
auto r1 = printf("intVar = %d\n", intVar);
|
||||||
|
// CHECK: intVar = 0
|
||||||
|
auto r2 = printf("doubleVar = %.2f\n", doubleVar);
|
||||||
|
// CHECK: doubleVar = 2.71
|
||||||
|
|
||||||
|
// Test redefinition with inline and static functions.
|
||||||
|
int add(int a, int b, int c) { return a + b + c; }
|
||||||
|
%undo // Revert to the initial version of add
|
||||||
|
inline int add(int a, int b) { return a + b; }
|
||||||
|
|
||||||
|
auto r3 = printf("add(1, 2) = %d\n", add(1, 2));
|
||||||
|
// CHECK-NEXT: add(1, 2) = 3
|
||||||
|
|
||||||
|
// Test inline and lambda functions with variations.
|
||||||
|
inline int square(int x) { return x * x; }
|
||||||
|
auto lambdaSquare = [](int x) { return x * x; };
|
||||||
|
auto lambdaMult = [](int a, int b) { return a * b; };
|
||||||
|
|
||||||
|
auto r4 = printf("square(4) = %d\n", square(4));
|
||||||
|
// CHECK-NEXT: square(4) = 16
|
||||||
|
auto lambda_r1 = printf("lambdaSquare(5) = %d\n", lambdaSquare(5));
|
||||||
|
// CHECK-NEXT: lambdaSquare(5) = 25
|
||||||
|
auto lambda_r2 = printf("lambdaMult(2, 3) = %d\n", lambdaMult(2, 3));
|
||||||
|
// CHECK-NEXT: lambdaMult(2, 3) = 6
|
||||||
|
|
||||||
|
%undo // Undo previous lambda assignments
|
||||||
|
auto lambda_r3 = lambdaMult(3, 4); // Should fail or revert to the original lambda
|
||||||
|
|
||||||
|
// Test weak and strong symbol linkage.
|
||||||
|
int __attribute__((weak)) weakFunc() { return 42; }
|
||||||
|
int strongFunc() { return 100; }
|
||||||
|
%undo // Revert the weak function
|
||||||
|
|
||||||
|
auto r5 = printf("weakFunc() = %d\n", weakFunc());
|
||||||
|
// CHECK: weakFunc() = 42
|
||||||
|
auto r6 = printf("strongFunc() = %d\n", strongFunc());
|
||||||
|
// CHECK-NEXT: strongFunc() = 100
|
||||||
|
|
||||||
|
// Weak variable linkage with different types.
|
||||||
|
int varA = 20;
|
||||||
|
static __typeof(varA) weakVarA __attribute__((__weakref__("varA")));
|
||||||
|
char charVar = 'c';
|
||||||
|
static __typeof(charVar) weakCharVar __attribute__((__weakref__("charVar")));
|
||||||
|
auto r7 = printf("weakVarA = %d\n", weakVarA);
|
||||||
|
// CHECK: weakVarA = 20
|
||||||
|
auto r8 = printf("weakCharVar = %c\n", weakCharVar);
|
||||||
|
// CHECK-NEXT: weakCharVar = c
|
||||||
|
|
||||||
|
// Test complex lambdas with captures.
|
||||||
|
int captureVar = 5;
|
||||||
|
auto captureLambda = [](int x) { return x + captureVar; };
|
||||||
|
int result1 = captureLambda(10);
|
||||||
|
%undo // Undo capture lambda
|
||||||
|
|
||||||
|
auto r9 = printf("captureLambda(10) = %d\n", result1);
|
||||||
|
// CHECK: captureLambda(10) = 15
|
||||||
|
|
||||||
|
// Multiline statement test with arithmetic operations.
|
||||||
|
int sum = \
|
||||||
|
5 + \
|
||||||
|
10;
|
||||||
|
int prod = sum * 2;
|
||||||
|
auto r10 = printf("sum = %d, prod = %d\n", sum, prod);
|
||||||
|
// CHECK: sum = 15, prod = 30
|
||||||
|
|
||||||
|
// Test multiline functions and macro behavior.
|
||||||
|
#define MULTIPLY(a, b) ((a) * (b))
|
||||||
|
|
||||||
|
int complexFunc(int x) \
|
||||||
|
{ \
|
||||||
|
return MULTIPLY(x, 2) + x; \
|
||||||
|
}
|
||||||
|
|
||||||
|
auto r11 = printf("complexFunc(5) = %d\n", complexFunc(5));
|
||||||
|
// CHECK: complexFunc(5) = 15
|
||||||
|
|
||||||
|
%quit
|
@ -117,6 +117,29 @@ if config.clang_examples:
|
|||||||
config.available_features.add("examples")
|
config.available_features.add("examples")
|
||||||
|
|
||||||
|
|
||||||
|
def have_host_out_of_process_jit_feature_support():
|
||||||
|
clang_repl_exe = lit.util.which("clang-repl", config.clang_tools_dir)
|
||||||
|
|
||||||
|
if not clang_repl_exe:
|
||||||
|
return False
|
||||||
|
|
||||||
|
testcode = b"\n".join([b"int i = 0;", b"%quit"])
|
||||||
|
|
||||||
|
try:
|
||||||
|
clang_repl_cmd = subprocess.run(
|
||||||
|
[clang_repl_exe, "-orc-runtime", "-oop-executor"],
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE,
|
||||||
|
input=testcode,
|
||||||
|
)
|
||||||
|
except OSError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if clang_repl_cmd.returncode == 0:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
def have_host_jit_feature_support(feature_name):
|
def have_host_jit_feature_support(feature_name):
|
||||||
clang_repl_exe = lit.util.which("clang-repl", config.clang_tools_dir)
|
clang_repl_exe = lit.util.which("clang-repl", config.clang_tools_dir)
|
||||||
|
|
||||||
@ -169,6 +192,9 @@ if have_host_jit_feature_support('jit'):
|
|||||||
if have_host_clang_repl_cuda():
|
if have_host_clang_repl_cuda():
|
||||||
config.available_features.add('host-supports-cuda')
|
config.available_features.add('host-supports-cuda')
|
||||||
|
|
||||||
|
if have_host_out_of_process_jit_feature_support():
|
||||||
|
config.available_features.add("host-supports-out-of-process-jit")
|
||||||
|
|
||||||
if config.clang_staticanalyzer:
|
if config.clang_staticanalyzer:
|
||||||
config.available_features.add("staticanalyzer")
|
config.available_features.add("staticanalyzer")
|
||||||
tools.append("clang-check")
|
tools.append("clang-check")
|
||||||
|
@ -4,7 +4,9 @@ set( LLVM_LINK_COMPONENTS
|
|||||||
LineEditor
|
LineEditor
|
||||||
Option
|
Option
|
||||||
OrcJIT
|
OrcJIT
|
||||||
|
OrcShared
|
||||||
Support
|
Support
|
||||||
|
TargetParser
|
||||||
)
|
)
|
||||||
|
|
||||||
add_clang_tool(clang-repl
|
add_clang_tool(clang-repl
|
||||||
|
@ -10,7 +10,11 @@
|
|||||||
//
|
//
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include "clang/Interpreter/RemoteJITUtils.h"
|
||||||
|
|
||||||
#include "clang/Basic/Diagnostic.h"
|
#include "clang/Basic/Diagnostic.h"
|
||||||
|
#include "clang/Basic/Version.h"
|
||||||
|
#include "clang/Config/config.h"
|
||||||
#include "clang/Frontend/CompilerInstance.h"
|
#include "clang/Frontend/CompilerInstance.h"
|
||||||
#include "clang/Frontend/FrontendDiagnostic.h"
|
#include "clang/Frontend/FrontendDiagnostic.h"
|
||||||
#include "clang/Interpreter/CodeCompletion.h"
|
#include "clang/Interpreter/CodeCompletion.h"
|
||||||
@ -24,8 +28,11 @@
|
|||||||
#include "llvm/Support/ManagedStatic.h" // llvm_shutdown
|
#include "llvm/Support/ManagedStatic.h" // llvm_shutdown
|
||||||
#include "llvm/Support/Signals.h"
|
#include "llvm/Support/Signals.h"
|
||||||
#include "llvm/Support/TargetSelect.h"
|
#include "llvm/Support/TargetSelect.h"
|
||||||
|
#include "llvm/TargetParser/Host.h"
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
|
#include "llvm/ExecutionEngine/Orc/Debugging/DebuggerSupport.h"
|
||||||
|
|
||||||
// Disable LSan for this test.
|
// Disable LSan for this test.
|
||||||
// FIXME: Re-enable once we can assume GCC 13.2 or higher.
|
// FIXME: Re-enable once we can assume GCC 13.2 or higher.
|
||||||
// https://llvm.org/github.com/llvm/llvm-project/issues/67586.
|
// https://llvm.org/github.com/llvm/llvm-project/issues/67586.
|
||||||
@ -34,10 +41,36 @@
|
|||||||
LLVM_ATTRIBUTE_USED int __lsan_is_turned_off() { return 1; }
|
LLVM_ATTRIBUTE_USED int __lsan_is_turned_off() { return 1; }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define DEBUG_TYPE "clang-repl"
|
||||||
|
|
||||||
static llvm::cl::opt<bool> CudaEnabled("cuda", llvm::cl::Hidden);
|
static llvm::cl::opt<bool> CudaEnabled("cuda", llvm::cl::Hidden);
|
||||||
static llvm::cl::opt<std::string> CudaPath("cuda-path", llvm::cl::Hidden);
|
static llvm::cl::opt<std::string> CudaPath("cuda-path", llvm::cl::Hidden);
|
||||||
static llvm::cl::opt<std::string> OffloadArch("offload-arch", llvm::cl::Hidden);
|
static llvm::cl::opt<std::string> OffloadArch("offload-arch", llvm::cl::Hidden);
|
||||||
|
static llvm::cl::OptionCategory OOPCategory("Out-of-process Execution Options");
|
||||||
|
static llvm::cl::opt<std::string> SlabAllocateSizeString(
|
||||||
|
"slab-allocate",
|
||||||
|
llvm::cl::desc("Allocate from a slab of the given size "
|
||||||
|
"(allowable suffixes: Kb, Mb, Gb. default = "
|
||||||
|
"Kb)"),
|
||||||
|
llvm::cl::init(""), llvm::cl::cat(OOPCategory));
|
||||||
|
static llvm::cl::opt<std::string>
|
||||||
|
OOPExecutor("oop-executor",
|
||||||
|
llvm::cl::desc("Launch an out-of-process executor to run code"),
|
||||||
|
llvm::cl::init(""), llvm::cl::ValueOptional,
|
||||||
|
llvm::cl::cat(OOPCategory));
|
||||||
|
static llvm::cl::opt<std::string> OOPExecutorConnect(
|
||||||
|
"oop-executor-connect",
|
||||||
|
llvm::cl::desc(
|
||||||
|
"Connect to an out-of-process executor through a TCP socket"),
|
||||||
|
llvm::cl::value_desc("<hostname>:<port>"));
|
||||||
|
static llvm::cl::opt<std::string>
|
||||||
|
OrcRuntimePath("orc-runtime", llvm::cl::desc("Path to the ORC runtime"),
|
||||||
|
llvm::cl::init(""), llvm::cl::ValueOptional,
|
||||||
|
llvm::cl::cat(OOPCategory));
|
||||||
|
static llvm::cl::opt<bool> UseSharedMemory(
|
||||||
|
"use-shared-memory",
|
||||||
|
llvm::cl::desc("Use shared memory to transfer generated code and data"),
|
||||||
|
llvm::cl::init(false), llvm::cl::cat(OOPCategory));
|
||||||
static llvm::cl::list<std::string>
|
static llvm::cl::list<std::string>
|
||||||
ClangArgs("Xcc",
|
ClangArgs("Xcc",
|
||||||
llvm::cl::desc("Argument to pass to the CompilerInvocation"),
|
llvm::cl::desc("Argument to pass to the CompilerInvocation"),
|
||||||
@ -47,6 +80,72 @@ static llvm::cl::opt<bool> OptHostSupportsJit("host-supports-jit",
|
|||||||
static llvm::cl::list<std::string> OptInputs(llvm::cl::Positional,
|
static llvm::cl::list<std::string> OptInputs(llvm::cl::Positional,
|
||||||
llvm::cl::desc("[code to run]"));
|
llvm::cl::desc("[code to run]"));
|
||||||
|
|
||||||
|
static llvm::Error sanitizeOopArguments(const char *ArgV0) {
|
||||||
|
// Only one of -oop-executor and -oop-executor-connect can be used.
|
||||||
|
if (!!OOPExecutor.getNumOccurrences() &&
|
||||||
|
!!OOPExecutorConnect.getNumOccurrences())
|
||||||
|
return llvm::make_error<llvm::StringError>(
|
||||||
|
"Only one of -" + OOPExecutor.ArgStr + " and -" +
|
||||||
|
OOPExecutorConnect.ArgStr + " can be specified",
|
||||||
|
llvm::inconvertibleErrorCode());
|
||||||
|
|
||||||
|
llvm::Triple SystemTriple(llvm::sys::getProcessTriple());
|
||||||
|
// TODO: Remove once out-of-process execution support is implemented for
|
||||||
|
// non-Unix platforms.
|
||||||
|
if ((!SystemTriple.isOSBinFormatELF() &&
|
||||||
|
!SystemTriple.isOSBinFormatMachO()) &&
|
||||||
|
(OOPExecutor.getNumOccurrences() ||
|
||||||
|
OOPExecutorConnect.getNumOccurrences()))
|
||||||
|
return llvm::make_error<llvm::StringError>(
|
||||||
|
"Out-of-process execution is only supported on Unix platforms",
|
||||||
|
llvm::inconvertibleErrorCode());
|
||||||
|
|
||||||
|
// If -slab-allocate is passed, check that we're not trying to use it in
|
||||||
|
// -oop-executor or -oop-executor-connect mode.
|
||||||
|
//
|
||||||
|
// FIXME: Remove once we enable remote slab allocation.
|
||||||
|
if (SlabAllocateSizeString != "") {
|
||||||
|
if (OOPExecutor.getNumOccurrences() ||
|
||||||
|
OOPExecutorConnect.getNumOccurrences())
|
||||||
|
return llvm::make_error<llvm::StringError>(
|
||||||
|
"-slab-allocate cannot be used with -oop-executor or "
|
||||||
|
"-oop-executor-connect",
|
||||||
|
llvm::inconvertibleErrorCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Out-of-process executors require the ORC runtime.
|
||||||
|
if (OrcRuntimePath.empty() && (OOPExecutor.getNumOccurrences() ||
|
||||||
|
OOPExecutorConnect.getNumOccurrences())) {
|
||||||
|
llvm::SmallString<256> BasePath(llvm::sys::fs::getMainExecutable(
|
||||||
|
ArgV0, reinterpret_cast<void *>(&sanitizeOopArguments)));
|
||||||
|
llvm::sys::path::remove_filename(BasePath); // Remove clang-repl filename.
|
||||||
|
llvm::sys::path::remove_filename(BasePath); // Remove ./bin directory.
|
||||||
|
llvm::sys::path::append(BasePath, CLANG_INSTALL_LIBDIR_BASENAME, "clang",
|
||||||
|
CLANG_VERSION_MAJOR_STRING);
|
||||||
|
if (SystemTriple.isOSBinFormatELF())
|
||||||
|
OrcRuntimePath =
|
||||||
|
BasePath.str().str() + "/lib/x86_64-unknown-linux-gnu/liborc_rt.a";
|
||||||
|
else if (SystemTriple.isOSBinFormatMachO())
|
||||||
|
OrcRuntimePath = BasePath.str().str() + "/lib/darwin/liborc_rt_osx.a";
|
||||||
|
else
|
||||||
|
return llvm::make_error<llvm::StringError>(
|
||||||
|
"Out-of-process execution is not supported on non-unix platforms",
|
||||||
|
llvm::inconvertibleErrorCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
// If -oop-executor was used but no value was specified then use a sensible
|
||||||
|
// default.
|
||||||
|
if (!!OOPExecutor.getNumOccurrences() && OOPExecutor.empty()) {
|
||||||
|
llvm::SmallString<256> OOPExecutorPath(llvm::sys::fs::getMainExecutable(
|
||||||
|
ArgV0, reinterpret_cast<void *>(&sanitizeOopArguments)));
|
||||||
|
llvm::sys::path::remove_filename(OOPExecutorPath);
|
||||||
|
llvm::sys::path::append(OOPExecutorPath, "llvm-jitlink-executor");
|
||||||
|
OOPExecutor = OOPExecutorPath.str().str();
|
||||||
|
}
|
||||||
|
|
||||||
|
return llvm::Error::success();
|
||||||
|
}
|
||||||
|
|
||||||
static void LLVMErrorHandler(void *UserData, const char *Message,
|
static void LLVMErrorHandler(void *UserData, const char *Message,
|
||||||
bool GenCrashDiag) {
|
bool GenCrashDiag) {
|
||||||
auto &Diags = *static_cast<clang::DiagnosticsEngine *>(UserData);
|
auto &Diags = *static_cast<clang::DiagnosticsEngine *>(UserData);
|
||||||
@ -183,6 +282,25 @@ int main(int argc, const char **argv) {
|
|||||||
DeviceCI = ExitOnErr(CB.CreateCudaDevice());
|
DeviceCI = ExitOnErr(CB.CreateCudaDevice());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ExitOnErr(sanitizeOopArguments(argv[0]));
|
||||||
|
|
||||||
|
std::unique_ptr<llvm::orc::ExecutorProcessControl> EPC;
|
||||||
|
if (OOPExecutor.getNumOccurrences()) {
|
||||||
|
// Launch an out-of-process executor locally in a child process.
|
||||||
|
EPC = ExitOnErr(
|
||||||
|
launchExecutor(OOPExecutor, UseSharedMemory, SlabAllocateSizeString));
|
||||||
|
} else if (OOPExecutorConnect.getNumOccurrences()) {
|
||||||
|
EPC = ExitOnErr(connectTCPSocket(OOPExecutorConnect, UseSharedMemory,
|
||||||
|
SlabAllocateSizeString));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<llvm::orc::LLJITBuilder> JB;
|
||||||
|
if (EPC) {
|
||||||
|
CB.SetTargetTriple(EPC->getTargetTriple().getTriple());
|
||||||
|
JB = ExitOnErr(
|
||||||
|
clang::Interpreter::createLLJITBuilder(std::move(EPC), OrcRuntimePath));
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME: Investigate if we could use runToolOnCodeWithArgs from tooling. It
|
// FIXME: Investigate if we could use runToolOnCodeWithArgs from tooling. It
|
||||||
// can replace the boilerplate code for creation of the compiler instance.
|
// can replace the boilerplate code for creation of the compiler instance.
|
||||||
std::unique_ptr<clang::CompilerInstance> CI;
|
std::unique_ptr<clang::CompilerInstance> CI;
|
||||||
@ -214,6 +332,9 @@ int main(int argc, const char **argv) {
|
|||||||
auto CudaRuntimeLibPath = CudaPath + "/lib/libcudart.so";
|
auto CudaRuntimeLibPath = CudaPath + "/lib/libcudart.so";
|
||||||
ExitOnErr(Interp->LoadDynamicLibrary(CudaRuntimeLibPath.c_str()));
|
ExitOnErr(Interp->LoadDynamicLibrary(CudaRuntimeLibPath.c_str()));
|
||||||
}
|
}
|
||||||
|
} else if (JB) {
|
||||||
|
Interp =
|
||||||
|
ExitOnErr(clang::Interpreter::create(std::move(CI), std::move(JB)));
|
||||||
} else
|
} else
|
||||||
Interp = ExitOnErr(clang::Interpreter::create(std::move(CI)));
|
Interp = ExitOnErr(clang::Interpreter::create(std::move(CI)));
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user