clang 23.0.0git
IncrementalExecutor.cpp
Go to the documentation of this file.
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-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This has the implementation of the base facilities for incremental execution.
10//
11//===----------------------------------------------------------------------===//
12
15#ifdef __EMSCRIPTEN__
16#include "Wasm.h"
17#endif // __EMSCRIPTEN__
18
21#include "clang/Driver/Driver.h"
23
24#include "llvm/ADT/SmallString.h"
25#include "llvm/ADT/SmallVector.h"
26#include "llvm/ADT/StringRef.h"
27#include "llvm/ADT/Twine.h"
28
29#include "llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h"
30#include "llvm/ExecutionEngine/Orc/Debugging/DebuggerSupport.h"
31#include "llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h"
32#include "llvm/ExecutionEngine/Orc/ExecutionUtils.h"
33#include "llvm/ExecutionEngine/Orc/ExecutorProcessControl.h"
34#include "llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h"
35#include "llvm/ExecutionEngine/Orc/LLJIT.h"
36#include "llvm/ExecutionEngine/Orc/MapperJITLinkMemoryManager.h"
37#include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h"
38#include "llvm/ExecutionEngine/Orc/Shared/SimpleRemoteEPCUtils.h"
39#include "llvm/ExecutionEngine/Orc/SimpleRemoteEPC.h"
40
41#include "llvm/Support/Error.h"
42#include "llvm/Support/FileSystem.h"
43#include "llvm/Support/FormatVariadic.h"
44#include "llvm/Support/Path.h"
45#include "llvm/Support/raw_ostream.h"
46
47#include "llvm/TargetParser/Host.h"
48
49#include <array>
50#include <functional>
51#include <memory>
52#include <optional>
53#include <string>
54#include <utility>
55
56#ifdef LLVM_ON_UNIX
57#include <netdb.h>
58#include <netinet/in.h>
59#include <sys/socket.h>
60#include <unistd.h>
61#endif
62
63namespace clang {
65
67createJITTargetMachineBuilder(const llvm::Triple &TT) {
68 if (TT.getTriple() == llvm::sys::getProcessTriple())
69 // This fails immediately if the target backend is not registered
70 return llvm::orc::JITTargetMachineBuilder::detectHost();
71
72 // If the target backend is not registered, LLJITBuilder::create() will fail
73 return llvm::orc::JITTargetMachineBuilder(TT);
74}
75
77createDefaultJITBuilder(llvm::orc::JITTargetMachineBuilder JTMB) {
78 auto JITBuilder = std::make_unique<llvm::orc::LLJITBuilder>();
79 JITBuilder->setJITTargetMachineBuilder(std::move(JTMB));
80 JITBuilder->setPrePlatformSetup([](llvm::orc::LLJIT &J) {
81 // Try to enable debugging of JIT'd code (only works with JITLink for
82 // ELF and MachO).
83 consumeError(llvm::orc::enableDebuggerSupport(J));
84 return llvm::Error::success();
85 });
86 return std::move(JITBuilder);
87}
88
90createSharedMemoryManager(llvm::orc::SimpleRemoteEPC &SREPC,
91 unsigned SlabAllocateSize) {
92 llvm::orc::SharedMemoryMapper::SymbolAddrs SAs;
93 if (auto Err = SREPC.getBootstrapSymbols(
94 {{SAs.Instance,
95 llvm::orc::rt::ExecutorSharedMemoryMapperServiceInstanceName},
96 {SAs.Reserve,
97 llvm::orc::rt::ExecutorSharedMemoryMapperServiceReserveWrapperName},
98 {SAs.Initialize,
99 llvm::orc::rt::
100 ExecutorSharedMemoryMapperServiceInitializeWrapperName},
101 {SAs.Deinitialize,
102 llvm::orc::rt::
103 ExecutorSharedMemoryMapperServiceDeinitializeWrapperName},
104 {SAs.Release,
105 llvm::orc::rt::
106 ExecutorSharedMemoryMapperServiceReleaseWrapperName}}))
107 return std::move(Err);
108
109 size_t SlabSize;
110 if (llvm::Triple(llvm::sys::getProcessTriple()).isOSWindows())
111 SlabSize = 1024 * 1024;
112 else
113 SlabSize = 1024 * 1024 * 1024;
114
115 if (SlabAllocateSize > 0)
116 SlabSize = SlabAllocateSize;
117
118 return llvm::orc::MapperJITLinkMemoryManager::CreateWithMapper<
119 llvm::orc::SharedMemoryMapper>(SlabSize, SREPC, SAs);
120}
121
122static llvm::Expected<
123 std::pair<std::unique_ptr<llvm::orc::SimpleRemoteEPC>, uint32_t>>
124launchExecutor(llvm::StringRef ExecutablePath, bool UseSharedMemory,
125 unsigned SlabAllocateSize, std::function<void()> CustomizeFork) {
126#ifndef LLVM_ON_UNIX
127 // FIXME: Add support for Windows.
128 return llvm::make_error<llvm::StringError>(
129 "-" + ExecutablePath + " not supported on non-unix platforms",
130 llvm::inconvertibleErrorCode());
131#elif !LLVM_ENABLE_THREADS
132 // Out of process mode using SimpleRemoteEPC depends on threads.
133 return llvm::make_error<llvm::StringError>(
134 "-" + ExecutablePath +
135 " requires threads, but LLVM was built with "
136 "LLVM_ENABLE_THREADS=Off",
137 llvm::inconvertibleErrorCode());
138#else
139
140 if (!llvm::sys::fs::can_execute(ExecutablePath))
141 return llvm::make_error<llvm::StringError>(
142 llvm::formatv("Specified executor invalid: {0}", ExecutablePath),
143 llvm::inconvertibleErrorCode());
144
145 constexpr int ReadEnd = 0;
146 constexpr int WriteEnd = 1;
147
148 // Pipe FDs.
149 int ToExecutor[2];
150 int FromExecutor[2];
151
152 uint32_t ChildPID;
153
154 // Create pipes to/from the executor..
155 if (pipe(ToExecutor) != 0 || pipe(FromExecutor) != 0)
156 return llvm::make_error<llvm::StringError>(
157 "Unable to create pipe for executor", llvm::inconvertibleErrorCode());
158
159 ChildPID = fork();
160
161 if (ChildPID == 0) {
162 // In the child...
163
164 // Close the parent ends of the pipes
165 close(ToExecutor[WriteEnd]);
166 close(FromExecutor[ReadEnd]);
167
168 if (CustomizeFork)
169 CustomizeFork();
170
171 // Execute the child process.
172 std::unique_ptr<char[]> ExecutorPath, FDSpecifier;
173 {
174 ExecutorPath = std::make_unique<char[]>(ExecutablePath.size() + 1);
175 strcpy(ExecutorPath.get(), ExecutablePath.data());
176
177 std::string FDSpecifierStr("filedescs=");
178 FDSpecifierStr += llvm::utostr(ToExecutor[ReadEnd]);
179 FDSpecifierStr += ',';
180 FDSpecifierStr += llvm::utostr(FromExecutor[WriteEnd]);
181 FDSpecifier = std::make_unique<char[]>(FDSpecifierStr.size() + 1);
182 strcpy(FDSpecifier.get(), FDSpecifierStr.c_str());
183 }
184
185 char *const Args[] = {ExecutorPath.get(), FDSpecifier.get(), nullptr};
186 int RC = execvp(ExecutorPath.get(), Args);
187 if (RC != 0) {
188 llvm::errs() << "unable to launch out-of-process executor \""
189 << ExecutorPath.get() << "\"\n";
190 exit(1);
191 }
192 }
193 // else we're the parent...
194
195 // Close the child ends of the pipes
196 close(ToExecutor[ReadEnd]);
197 close(FromExecutor[WriteEnd]);
198
199 llvm::orc::SimpleRemoteEPC::Setup S = llvm::orc::SimpleRemoteEPC::Setup();
200 if (UseSharedMemory)
201 S.CreateMemoryManager =
202 [SlabAllocateSize](llvm::orc::SimpleRemoteEPC &EPC) {
203 return createSharedMemoryManager(EPC, SlabAllocateSize);
204 };
205
206 auto EPCOrErr =
207 llvm::orc::SimpleRemoteEPC::Create<llvm::orc::FDSimpleRemoteEPCTransport>(
208 std::make_unique<llvm::orc::DynamicThreadPoolTaskDispatcher>(
209 std::nullopt),
210 std::move(S), FromExecutor[ReadEnd], ToExecutor[WriteEnd]);
211 if (!EPCOrErr)
212 return EPCOrErr.takeError();
213 return std::make_pair(std::move(*EPCOrErr), ChildPID);
214#endif
215}
216
217#if LLVM_ON_UNIX && LLVM_ENABLE_THREADS
218
219static Expected<int> connectTCPSocketImpl(std::string Host,
220 std::string PortStr) {
221 addrinfo *AI;
222 addrinfo Hints{};
223 Hints.ai_family = AF_INET;
224 Hints.ai_socktype = SOCK_STREAM;
225 Hints.ai_flags = AI_NUMERICSERV;
226
227 if (int EC = getaddrinfo(Host.c_str(), PortStr.c_str(), &Hints, &AI))
228 return llvm::make_error<llvm::StringError>(
229 llvm::formatv("address resolution failed ({0})", strerror(EC)),
230 llvm::inconvertibleErrorCode());
231 // Cycle through the returned addrinfo structures and connect to the first
232 // reachable endpoint.
233 int SockFD;
234 addrinfo *Server;
235 for (Server = AI; Server != nullptr; Server = Server->ai_next) {
236 // socket might fail, e.g. if the address family is not supported. Skip to
237 // the next addrinfo structure in such a case.
238 if ((SockFD = socket(AI->ai_family, AI->ai_socktype, AI->ai_protocol)) < 0)
239 continue;
240
241 // If connect returns null, we exit the loop with a working socket.
242 if (connect(SockFD, Server->ai_addr, Server->ai_addrlen) == 0)
243 break;
244
245 close(SockFD);
246 }
247 freeaddrinfo(AI);
248
249 // If we reached the end of the loop without connecting to a valid endpoint,
250 // dump the last error that was logged in socket() or connect().
251 if (Server == nullptr)
252 return llvm::make_error<llvm::StringError>("invalid hostname",
253 llvm::inconvertibleErrorCode());
254
255 return SockFD;
256}
257
259connectTCPSocket(llvm::StringRef NetworkAddress, bool UseSharedMemory,
260 unsigned SlabAllocateSize) {
261#ifndef LLVM_ON_UNIX
262 // FIXME: Add TCP support for Windows.
263 return llvm::make_error<llvm::StringError>(
264 "-" + NetworkAddress + " not supported on non-unix platforms",
265 llvm::inconvertibleErrorCode());
266#elif !LLVM_ENABLE_THREADS
267 // Out of process mode using SimpleRemoteEPC depends on threads.
268 return llvm::make_error<llvm::StringError>(
269 "-" + NetworkAddress +
270 " requires threads, but LLVM was built with "
271 "LLVM_ENABLE_THREADS=Off",
272 llvm::inconvertibleErrorCode());
273#else
274
275 auto CreateErr = [NetworkAddress](Twine Details) {
276 return llvm::make_error<llvm::StringError>(
277 formatv("Failed to connect TCP socket '{0}': {1}", NetworkAddress,
278 Details),
279 llvm::inconvertibleErrorCode());
280 };
281
282 StringRef Host, PortStr;
283 std::tie(Host, PortStr) = NetworkAddress.split(':');
284 if (Host.empty())
285 return CreateErr("Host name for -" + NetworkAddress + " can not be empty");
286 if (PortStr.empty())
287 return CreateErr("Port number in -" + NetworkAddress + " can not be empty");
288 int Port = 0;
289 if (PortStr.getAsInteger(10, Port))
290 return CreateErr("Port number '" + PortStr + "' is not a valid integer");
291
292 Expected<int> SockFD = connectTCPSocketImpl(Host.str(), PortStr.str());
293 if (!SockFD)
294 return SockFD.takeError();
295
296 llvm::orc::SimpleRemoteEPC::Setup S = llvm::orc::SimpleRemoteEPC::Setup();
297 if (UseSharedMemory)
298 S.CreateMemoryManager =
299 [SlabAllocateSize](llvm::orc::SimpleRemoteEPC &EPC) {
300 return createSharedMemoryManager(EPC, SlabAllocateSize);
301 };
302
303 return llvm::orc::SimpleRemoteEPC::Create<
304 llvm::orc::FDSimpleRemoteEPCTransport>(
305 std::make_unique<llvm::orc::DynamicThreadPoolTaskDispatcher>(
306 std::nullopt),
307 std::move(S), *SockFD, *SockFD);
308#endif
309}
310#endif // _WIN32
311
313createLLJITBuilder(std::unique_ptr<llvm::orc::ExecutorProcessControl> EPC,
314 llvm::StringRef OrcRuntimePath) {
315 auto JTMB = createJITTargetMachineBuilder(EPC->getTargetTriple());
316 if (!JTMB)
317 return JTMB.takeError();
318 auto JB = createDefaultJITBuilder(std::move(*JTMB));
319 if (!JB)
320 return JB.takeError();
321
322 (*JB)->setExecutorProcessControl(std::move(EPC));
323 (*JB)->setPlatformSetUp(
324 llvm::orc::ExecutorNativePlatform(OrcRuntimePath.str()));
325
326 return std::move(*JB);
327}
328
329static llvm::Expected<
330 std::pair<std::unique_ptr<llvm::orc::LLJITBuilder>, uint32_t>>
332 std::unique_ptr<llvm::orc::ExecutorProcessControl> EPC;
333 uint32_t childPid = -1;
334 if (!IncrExecutorBuilder.OOPExecutor.empty()) {
335 // Launch an out-of-process executor locally in a child process.
336 auto ResultOrErr = launchExecutor(IncrExecutorBuilder.OOPExecutor,
337 IncrExecutorBuilder.UseSharedMemory,
338 IncrExecutorBuilder.SlabAllocateSize,
339 IncrExecutorBuilder.CustomizeFork);
340 if (!ResultOrErr)
341 return ResultOrErr.takeError();
342 childPid = ResultOrErr->second;
343 auto EPCOrErr = std::move(ResultOrErr->first);
344 EPC = std::move(EPCOrErr);
345 } else if (IncrExecutorBuilder.OOPExecutorConnect != "") {
346#if LLVM_ON_UNIX && LLVM_ENABLE_THREADS
347 auto EPCOrErr = connectTCPSocket(IncrExecutorBuilder.OOPExecutorConnect,
348 IncrExecutorBuilder.UseSharedMemory,
349 IncrExecutorBuilder.SlabAllocateSize);
350 if (!EPCOrErr)
351 return EPCOrErr.takeError();
352 EPC = std::move(*EPCOrErr);
353#else
354 return llvm::make_error<llvm::StringError>(
355 "Out-of-process JIT over TCP is not supported on this platform",
356 std::error_code());
357#endif
358 }
359
360 std::unique_ptr<llvm::orc::LLJITBuilder> JB;
361 if (EPC) {
362 auto JBOrErr =
363 createLLJITBuilder(std::move(EPC), IncrExecutorBuilder.OrcRuntimePath);
364 if (!JBOrErr)
365 return JBOrErr.takeError();
366 JB = std::move(*JBOrErr);
367 }
368
369 return std::make_pair(std::move(JB), childPid);
370}
371
373IncrementalExecutorBuilder::create(llvm::orc::ThreadSafeContext &TSC,
374 const clang::TargetInfo &TI) {
375 if (IE)
376 return std::move(IE);
377 llvm::Triple TT = TI.getTriple();
378 if (!TT.isOSWindows() && IsOutOfProcess) {
379 if (!JITBuilder) {
380 auto ResOrErr = outOfProcessJITBuilder(*this);
381 if (!ResOrErr)
382 return ResOrErr.takeError();
383 JITBuilder = std::move(ResOrErr->first);
384 ExecutorPID = ResOrErr->second;
385 }
386 if (!JITBuilder)
387 return llvm::make_error<llvm::StringError>(
388 "Operation failed. No LLJITBuilder for out-of-process JIT",
389 std::error_code());
390 }
391
392 if (!JITBuilder) {
393 auto JTMB = createJITTargetMachineBuilder(TT);
394 if (!JTMB)
395 return JTMB.takeError();
396 if (CM)
397 JTMB->setCodeModel(CM);
398 auto JB = createDefaultJITBuilder(std::move(*JTMB));
399 if (!JB)
400 return JB.takeError();
401 JITBuilder = std::move(*JB);
402 }
403
404 llvm::Error Err = llvm::Error::success();
405 std::unique_ptr<IncrementalExecutor> Executor;
406#ifdef __EMSCRIPTEN__
407 Executor = std::make_unique<WasmIncrementalExecutor>(Err);
408#else
409 Executor = std::make_unique<OrcIncrementalExecutor>(TSC, *JITBuilder, Err);
410#endif
411
412 if (Err)
413 return std::move(Err);
414
415 return std::move(Executor);
416}
417
418llvm::Error IncrementalExecutorBuilder::UpdateOrcRuntimePath(
420 if (!IsOutOfProcess)
421 return llvm::Error::success();
422
423 const clang::driver::Driver &D = C.getDriver();
424 const clang::driver::ToolChain &TC = C.getDefaultToolChain();
425
427
428 // Get canonical compiler-rt path
429 std::string CompilerRTPath = TC.getCompilerRT(C.getArgs(), "orc_rt");
430 llvm::StringRef CanonicalFilename = llvm::sys::path::filename(CompilerRTPath);
431
432 if (CanonicalFilename.empty()) {
433 return llvm::make_error<llvm::StringError>(
434 "Could not determine OrcRuntime filename from ToolChain",
435 llvm::inconvertibleErrorCode());
436 }
437
438 OrcRTLibNames.push_back(CanonicalFilename.str());
439
440 // Derive legacy spelling (libclang_rt.orc_rt -> orc_rt)
441 llvm::StringRef LegacySuffix = CanonicalFilename;
442 if (LegacySuffix.consume_front("libclang_rt.")) {
443 OrcRTLibNames.push_back(("lib" + LegacySuffix).str());
444 }
445
446 // Extract directory
447 llvm::SmallString<256> OrcRTDir(CompilerRTPath);
448 llvm::sys::path::remove_filename(OrcRTDir);
449
451
452 auto findInDir = [&](llvm::StringRef Dir) -> std::optional<std::string> {
453 for (const auto &LibName : OrcRTLibNames) {
454 llvm::SmallString<256> FullPath = Dir;
455 llvm::sys::path::append(FullPath, LibName);
456 if (llvm::sys::fs::exists(FullPath))
457 return std::string(FullPath.str());
458 triedPaths.push_back(std::string(FullPath.str()));
459 }
460 return std::nullopt;
461 };
462
463 // Try the primary directory first
464 if (auto Found = findInDir(OrcRTDir)) {
465 OrcRuntimePath = *Found;
466 return llvm::Error::success();
467 }
468
469 // We want to find the relative path from the Driver to the OrcRTDir
470 // to replicate that structure elsewhere if needed.
471 llvm::StringRef Rel = OrcRTDir.str();
472 if (!Rel.consume_front(llvm::sys::path::parent_path(D.Dir))) {
473 return llvm::make_error<llvm::StringError>(
474 llvm::formatv("OrcRuntime library path ({0}) is not located within the "
475 "Clang resource directory ({1}). Check your installation "
476 "or provide an explicit path via -resource-dir.",
477 OrcRTDir, D.Dir)
478 .str(),
479 llvm::inconvertibleErrorCode());
480 }
481
482 // Generic Backward Search (Climbing the tree)
483 // This is useful for unit tests or relocated toolchains
484 llvm::SmallString<256> Cursor(D.Dir); // Start from the driver directory
485 while (llvm::sys::path::has_parent_path(Cursor)) {
486 Cursor = llvm::sys::path::parent_path(Cursor).str();
487 llvm::SmallString<256> Candidate = Cursor;
488 llvm::sys::path::append(Candidate, Rel);
489
490 if (auto Found = findInDir(Candidate)) {
491 OrcRuntimePath = *Found;
492 return llvm::Error::success();
493 }
494
495 // Safety check
496 if (triedPaths.size() > 32)
497 break;
498 }
499
500 // Build a helpful error string
501 std::string Joined;
502 for (size_t i = 0; i < triedPaths.size(); ++i) {
503 if (i > 0)
504 Joined += "\n ";
505 Joined += triedPaths[i];
506 }
507
508 return llvm::make_error<llvm::StringError>(
509 llvm::formatv("OrcRuntime library not found. Checked: {0}",
510 Joined.empty() ? "<none>" : Joined)
511 .str(),
512 std::make_error_code(std::errc::no_such_file_or_directory));
513}
514
515} // end namespace clang
std::optional< llvm::CodeModel::Model > CM
An optional code model to provide to the JITTargetMachineBuilder.
bool IsOutOfProcess
Indicates whether out-of-process JIT execution is enabled.
std::unique_ptr< IncrementalExecutor > IE
An optional external IncrementalExecutor.
std::function< void()> CustomizeFork
Custom lambda to be executed inside child process/executor.
std::string OOPExecutor
Path to the out-of-process JIT executor.
uint32_t ExecutorPID
PID of the out-of-process JIT executor.
bool UseSharedMemory
Indicates whether to use shared memory for communication.
llvm::Expected< std::unique_ptr< IncrementalExecutor > > create(llvm::orc::ThreadSafeContext &TSC, const clang::TargetInfo &TI)
std::string OrcRuntimePath
Path to the ORC runtime library.
std::unique_ptr< llvm::orc::LLJITBuilder > JITBuilder
An optional external orc jit builder.
unsigned SlabAllocateSize
Representing the slab allocation size for memory management in kb.
Exposes information about the current target.
Definition TargetInfo.h:226
const llvm::Triple & getTriple() const
Returns the target triple of the primary target.
Compilation - A set of tasks to perform for a single driver invocation.
Definition Compilation.h:45
Driver - Encapsulate logic for constructing compilation processes from a set of gcc-driver-like comma...
Definition Driver.h:99
std::string Dir
The path the driver executable was in, as invoked from the command line.
Definition Driver.h:180
ToolChain - Access to tools for a single platform.
Definition ToolChain.h:92
virtual std::string getCompilerRT(const llvm::opt::ArgList &Args, StringRef Component, FileType Type=ToolChain::FT_Static, bool IsFortran=false) const
Defines the clang::TargetInfo interface.
The JSON file list parser is used to communicate input to InstallAPI.
static llvm::Expected< std::unique_ptr< llvm::orc::LLJITBuilder > > createLLJITBuilder(std::unique_ptr< llvm::orc::ExecutorProcessControl > EPC, llvm::StringRef OrcRuntimePath)
static llvm::Expected< std::unique_ptr< llvm::orc::LLJITBuilder > > createDefaultJITBuilder(llvm::orc::JITTargetMachineBuilder JTMB)
static llvm::Expected< std::pair< std::unique_ptr< llvm::orc::LLJITBuilder >, uint32_t > > outOfProcessJITBuilder(const IncrementalExecutorBuilder &IncrExecutorBuilder)
Expected< std::unique_ptr< llvm::jitlink::JITLinkMemoryManager > > createSharedMemoryManager(llvm::orc::SimpleRemoteEPC &SREPC, unsigned SlabAllocateSize)
static llvm::Expected< llvm::orc::JITTargetMachineBuilder > createJITTargetMachineBuilder(const llvm::Triple &TT)
static llvm::Expected< std::pair< std::unique_ptr< llvm::orc::SimpleRemoteEPC >, uint32_t > > launchExecutor(llvm::StringRef ExecutablePath, bool UseSharedMemory, unsigned SlabAllocateSize, std::function< void()> CustomizeFork)
int const char * function
Definition c++config.h:31