362 lines · cpp
1//===--- IncrementalExecutor.cpp - Incremental Execution --------*- C++ -*-===//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-exception6//7//===----------------------------------------------------------------------===//8//9// This file implements the class which performs incremental code execution.10//11//===----------------------------------------------------------------------===//12 13#include "IncrementalExecutor.h"14 15#include "clang/Basic/TargetInfo.h"16#include "clang/Basic/TargetOptions.h"17#include "clang/Interpreter/PartialTranslationUnit.h"18#include "llvm/ADT/StringExtras.h"19#include "llvm/ExecutionEngine/ExecutionEngine.h"20#include "llvm/ExecutionEngine/Orc/CompileUtils.h"21#include "llvm/ExecutionEngine/Orc/Debugging/DebuggerSupport.h"22#include "llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h"23#include "llvm/ExecutionEngine/Orc/ExecutionUtils.h"24#include "llvm/ExecutionEngine/Orc/IRCompileLayer.h"25#include "llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h"26#include "llvm/ExecutionEngine/Orc/LLJIT.h"27#include "llvm/ExecutionEngine/Orc/MapperJITLinkMemoryManager.h"28#include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h"29#include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h"30#include "llvm/ExecutionEngine/Orc/Shared/SimpleRemoteEPCUtils.h"31#include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h"32#include "llvm/ExecutionEngine/SectionMemoryManager.h"33#include "llvm/IR/Module.h"34#include "llvm/Support/FileSystem.h"35#include "llvm/Support/ManagedStatic.h"36#include "llvm/Support/Path.h"37#include "llvm/Support/TargetSelect.h"38#include "llvm/TargetParser/Host.h"39 40#ifdef LLVM_ON_UNIX41#include <netdb.h>42#include <netinet/in.h>43#include <sys/socket.h>44#include <unistd.h>45#endif // LLVM_ON_UNIX46 47// Force linking some of the runtimes that helps attaching to a debugger.48LLVM_ATTRIBUTE_USED void linkComponents() {49 llvm::errs() << (void *)&llvm_orc_registerJITLoaderGDBAllocAction;50}51 52namespace clang {53IncrementalExecutor::IncrementalExecutor(llvm::orc::ThreadSafeContext &TSC)54 : TSCtx(TSC) {}55 56llvm::Expected<std::unique_ptr<llvm::orc::LLJITBuilder>>57IncrementalExecutor::createDefaultJITBuilder(58 llvm::orc::JITTargetMachineBuilder JTMB) {59 auto JITBuilder = std::make_unique<llvm::orc::LLJITBuilder>();60 JITBuilder->setJITTargetMachineBuilder(std::move(JTMB));61 JITBuilder->setPrePlatformSetup([](llvm::orc::LLJIT &J) {62 // Try to enable debugging of JIT'd code (only works with JITLink for63 // ELF and MachO).64 consumeError(llvm::orc::enableDebuggerSupport(J));65 return llvm::Error::success();66 });67 return std::move(JITBuilder);68}69 70IncrementalExecutor::IncrementalExecutor(llvm::orc::ThreadSafeContext &TSC,71 llvm::orc::LLJITBuilder &JITBuilder,72 Interpreter::JITConfig Config,73 llvm::Error &Err)74 : TSCtx(TSC), OutOfProcessChildPid(Config.ExecutorPID) {75 using namespace llvm::orc;76 llvm::ErrorAsOutParameter EAO(&Err);77 78 if (auto JitOrErr = JITBuilder.create())79 Jit = std::move(*JitOrErr);80 else {81 Err = JitOrErr.takeError();82 return;83 }84}85 86IncrementalExecutor::~IncrementalExecutor() {}87 88llvm::Error IncrementalExecutor::addModule(PartialTranslationUnit &PTU) {89 llvm::orc::ResourceTrackerSP RT =90 Jit->getMainJITDylib().createResourceTracker();91 ResourceTrackers[&PTU] = RT;92 93 return Jit->addIRModule(RT, {std::move(PTU.TheModule), TSCtx});94}95 96llvm::Error IncrementalExecutor::removeModule(PartialTranslationUnit &PTU) {97 98 llvm::orc::ResourceTrackerSP RT = std::move(ResourceTrackers[&PTU]);99 if (!RT)100 return llvm::Error::success();101 102 ResourceTrackers.erase(&PTU);103 if (llvm::Error Err = RT->remove())104 return Err;105 return llvm::Error::success();106}107 108// Clean up the JIT instance.109llvm::Error IncrementalExecutor::cleanUp() {110 // This calls the global dtors of registered modules.111 return Jit->deinitialize(Jit->getMainJITDylib());112}113 114llvm::Error IncrementalExecutor::runCtors() const {115 return Jit->initialize(Jit->getMainJITDylib());116}117 118llvm::Expected<llvm::orc::ExecutorAddr>119IncrementalExecutor::getSymbolAddress(llvm::StringRef Name,120 SymbolNameKind NameKind) const {121 using namespace llvm::orc;122 auto SO = makeJITDylibSearchOrder({&Jit->getMainJITDylib(),123 Jit->getPlatformJITDylib().get(),124 Jit->getProcessSymbolsJITDylib().get()});125 126 ExecutionSession &ES = Jit->getExecutionSession();127 128 auto SymOrErr =129 ES.lookup(SO, (NameKind == LinkerName) ? ES.intern(Name)130 : Jit->mangleAndIntern(Name));131 if (auto Err = SymOrErr.takeError())132 return std::move(Err);133 return SymOrErr->getAddress();134}135 136Expected<std::unique_ptr<llvm::jitlink::JITLinkMemoryManager>>137createSharedMemoryManager(llvm::orc::SimpleRemoteEPC &SREPC,138 unsigned SlabAllocateSize) {139 llvm::orc::SharedMemoryMapper::SymbolAddrs SAs;140 if (auto Err = SREPC.getBootstrapSymbols(141 {{SAs.Instance,142 llvm::orc::rt::ExecutorSharedMemoryMapperServiceInstanceName},143 {SAs.Reserve,144 llvm::orc::rt::ExecutorSharedMemoryMapperServiceReserveWrapperName},145 {SAs.Initialize,146 llvm::orc::rt::147 ExecutorSharedMemoryMapperServiceInitializeWrapperName},148 {SAs.Deinitialize,149 llvm::orc::rt::150 ExecutorSharedMemoryMapperServiceDeinitializeWrapperName},151 {SAs.Release,152 llvm::orc::rt::153 ExecutorSharedMemoryMapperServiceReleaseWrapperName}}))154 return std::move(Err);155 156 size_t SlabSize;157 if (llvm::Triple(llvm::sys::getProcessTriple()).isOSWindows())158 SlabSize = 1024 * 1024;159 else160 SlabSize = 1024 * 1024 * 1024;161 162 if (SlabAllocateSize > 0)163 SlabSize = SlabAllocateSize;164 165 return llvm::orc::MapperJITLinkMemoryManager::CreateWithMapper<166 llvm::orc::SharedMemoryMapper>(SlabSize, SREPC, SAs);167}168 169llvm::Expected<std::pair<std::unique_ptr<llvm::orc::SimpleRemoteEPC>, uint32_t>>170IncrementalExecutor::launchExecutor(llvm::StringRef ExecutablePath,171 bool UseSharedMemory,172 unsigned SlabAllocateSize,173 std::function<void()> CustomizeFork) {174#ifndef LLVM_ON_UNIX175 // FIXME: Add support for Windows.176 return llvm::make_error<llvm::StringError>(177 "-" + ExecutablePath + " not supported on non-unix platforms",178 llvm::inconvertibleErrorCode());179#elif !LLVM_ENABLE_THREADS180 // Out of process mode using SimpleRemoteEPC depends on threads.181 return llvm::make_error<llvm::StringError>(182 "-" + ExecutablePath +183 " requires threads, but LLVM was built with "184 "LLVM_ENABLE_THREADS=Off",185 llvm::inconvertibleErrorCode());186#else187 188 if (!llvm::sys::fs::can_execute(ExecutablePath))189 return llvm::make_error<llvm::StringError>(190 llvm::formatv("Specified executor invalid: {0}", ExecutablePath),191 llvm::inconvertibleErrorCode());192 193 constexpr int ReadEnd = 0;194 constexpr int WriteEnd = 1;195 196 // Pipe FDs.197 int ToExecutor[2];198 int FromExecutor[2];199 200 uint32_t ChildPID;201 202 // Create pipes to/from the executor..203 if (pipe(ToExecutor) != 0 || pipe(FromExecutor) != 0)204 return llvm::make_error<llvm::StringError>(205 "Unable to create pipe for executor", llvm::inconvertibleErrorCode());206 207 ChildPID = fork();208 209 if (ChildPID == 0) {210 // In the child...211 212 // Close the parent ends of the pipes213 close(ToExecutor[WriteEnd]);214 close(FromExecutor[ReadEnd]);215 216 if (CustomizeFork)217 CustomizeFork();218 219 // Execute the child process.220 std::unique_ptr<char[]> ExecutorPath, FDSpecifier;221 {222 ExecutorPath = std::make_unique<char[]>(ExecutablePath.size() + 1);223 strcpy(ExecutorPath.get(), ExecutablePath.data());224 225 std::string FDSpecifierStr("filedescs=");226 FDSpecifierStr += llvm::utostr(ToExecutor[ReadEnd]);227 FDSpecifierStr += ',';228 FDSpecifierStr += llvm::utostr(FromExecutor[WriteEnd]);229 FDSpecifier = std::make_unique<char[]>(FDSpecifierStr.size() + 1);230 strcpy(FDSpecifier.get(), FDSpecifierStr.c_str());231 }232 233 char *const Args[] = {ExecutorPath.get(), FDSpecifier.get(), nullptr};234 int RC = execvp(ExecutorPath.get(), Args);235 if (RC != 0) {236 llvm::errs() << "unable to launch out-of-process executor \""237 << ExecutorPath.get() << "\"\n";238 exit(1);239 }240 }241 // else we're the parent...242 243 // Close the child ends of the pipes244 close(ToExecutor[ReadEnd]);245 close(FromExecutor[WriteEnd]);246 247 llvm::orc::SimpleRemoteEPC::Setup S = llvm::orc::SimpleRemoteEPC::Setup();248 if (UseSharedMemory)249 S.CreateMemoryManager =250 [SlabAllocateSize](llvm::orc::SimpleRemoteEPC &EPC) {251 return createSharedMemoryManager(EPC, SlabAllocateSize);252 };253 254 auto EPCOrErr =255 llvm::orc::SimpleRemoteEPC::Create<llvm::orc::FDSimpleRemoteEPCTransport>(256 std::make_unique<llvm::orc::DynamicThreadPoolTaskDispatcher>(257 std::nullopt),258 std::move(S), FromExecutor[ReadEnd], ToExecutor[WriteEnd]);259 if (!EPCOrErr)260 return EPCOrErr.takeError();261 return std::make_pair(std::move(*EPCOrErr), ChildPID);262#endif263}264 265#if LLVM_ON_UNIX && LLVM_ENABLE_THREADS266 267static Expected<int> connectTCPSocketImpl(std::string Host,268 std::string PortStr) {269 addrinfo *AI;270 addrinfo Hints{};271 Hints.ai_family = AF_INET;272 Hints.ai_socktype = SOCK_STREAM;273 Hints.ai_flags = AI_NUMERICSERV;274 275 if (int EC = getaddrinfo(Host.c_str(), PortStr.c_str(), &Hints, &AI))276 return llvm::make_error<llvm::StringError>(277 llvm::formatv("address resolution failed ({0})", strerror(EC)),278 llvm::inconvertibleErrorCode());279 // Cycle through the returned addrinfo structures and connect to the first280 // reachable endpoint.281 int SockFD;282 addrinfo *Server;283 for (Server = AI; Server != nullptr; Server = Server->ai_next) {284 // socket might fail, e.g. if the address family is not supported. Skip to285 // the next addrinfo structure in such a case.286 if ((SockFD = socket(AI->ai_family, AI->ai_socktype, AI->ai_protocol)) < 0)287 continue;288 289 // If connect returns null, we exit the loop with a working socket.290 if (connect(SockFD, Server->ai_addr, Server->ai_addrlen) == 0)291 break;292 293 close(SockFD);294 }295 freeaddrinfo(AI);296 297 // If we reached the end of the loop without connecting to a valid endpoint,298 // dump the last error that was logged in socket() or connect().299 if (Server == nullptr)300 return llvm::make_error<llvm::StringError>("invalid hostname",301 llvm::inconvertibleErrorCode());302 303 return SockFD;304}305 306llvm::Expected<std::unique_ptr<llvm::orc::SimpleRemoteEPC>>307IncrementalExecutor::connectTCPSocket(llvm::StringRef NetworkAddress,308 bool UseSharedMemory,309 unsigned SlabAllocateSize) {310#ifndef LLVM_ON_UNIX311 // FIXME: Add TCP support for Windows.312 return llvm::make_error<llvm::StringError>(313 "-" + NetworkAddress + " not supported on non-unix platforms",314 llvm::inconvertibleErrorCode());315#elif !LLVM_ENABLE_THREADS316 // Out of process mode using SimpleRemoteEPC depends on threads.317 return llvm::make_error<llvm::StringError>(318 "-" + NetworkAddress +319 " requires threads, but LLVM was built with "320 "LLVM_ENABLE_THREADS=Off",321 llvm::inconvertibleErrorCode());322#else323 324 auto CreateErr = [NetworkAddress](Twine Details) {325 return llvm::make_error<llvm::StringError>(326 formatv("Failed to connect TCP socket '{0}': {1}", NetworkAddress,327 Details),328 llvm::inconvertibleErrorCode());329 };330 331 StringRef Host, PortStr;332 std::tie(Host, PortStr) = NetworkAddress.split(':');333 if (Host.empty())334 return CreateErr("Host name for -" + NetworkAddress + " can not be empty");335 if (PortStr.empty())336 return CreateErr("Port number in -" + NetworkAddress + " can not be empty");337 int Port = 0;338 if (PortStr.getAsInteger(10, Port))339 return CreateErr("Port number '" + PortStr + "' is not a valid integer");340 341 Expected<int> SockFD = connectTCPSocketImpl(Host.str(), PortStr.str());342 if (!SockFD)343 return SockFD.takeError();344 345 llvm::orc::SimpleRemoteEPC::Setup S = llvm::orc::SimpleRemoteEPC::Setup();346 if (UseSharedMemory)347 S.CreateMemoryManager =348 [SlabAllocateSize](llvm::orc::SimpleRemoteEPC &EPC) {349 return createSharedMemoryManager(EPC, SlabAllocateSize);350 };351 352 return llvm::orc::SimpleRemoteEPC::Create<353 llvm::orc::FDSimpleRemoteEPCTransport>(354 std::make_unique<llvm::orc::DynamicThreadPoolTaskDispatcher>(355 std::nullopt),356 std::move(S), *SockFD, *SockFD);357#endif358}359#endif // _WIN32360 361} // namespace clang362