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
13#include "clang/Interpreter/IncrementalExecutor.h"
14#include "OrcIncrementalExecutor.h"
15#ifdef __EMSCRIPTEN__
16#include "Wasm.h"
17#endif // __EMSCRIPTEN__
18
19#include "clang/Basic/TargetInfo.h"
20#include "clang/Driver/Compilation.h"
21#include "clang/Driver/Driver.h"
22#include "clang/Driver/ToolChain.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 {
64IncrementalExecutorBuilder::~IncrementalExecutorBuilder() = default;
65
66static llvm::Expected<llvm::orc::JITTargetMachineBuilder>
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
76static llvm::Expected<std::unique_ptr<llvm::orc::LLJITBuilder>>
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(Err: llvm::orc::enableDebuggerSupport(J));
84 return llvm::Error::success();
85 });
86 return std::move(JITBuilder);
87}
88
89Expected<std::unique_ptr<llvm::jitlink::JITLinkMemoryManager>>
90createSharedMemoryManager(llvm::orc::ExecutorProcessControl &EPC,
91 unsigned SlabAllocateSize) {
92 llvm::orc::SharedMemoryMapper::SymbolAddrs SAs;
93 if (auto Err = EPC.getBootstrapSymbols(
94 Pairs: {{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>(ReservationGranularity: SlabSize, A&: EPC, A&: SAs);
120}
121
122static llvm::Expected<
123 std::pair<std::unique_ptr<llvm::orc::SimpleRemoteEPC>, uint32_t>>
124launchExecutor(llvm::StringRef ExecutablePath,
125 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(Path: ExecutablePath))
141 return llvm::make_error<llvm::StringError>(
142 Args: llvm::formatv(Fmt: "Specified executor invalid: {0}", Vals&: ExecutablePath),
143 Args: 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(pipedes: ToExecutor) != 0 || pipe(pipedes: FromExecutor) != 0)
156 return llvm::make_error<llvm::StringError>(
157 Args: "Unable to create pipe for executor", Args: 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(fd: ToExecutor[WriteEnd]);
166 close(fd: 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[]>(num: ExecutablePath.size() + 1);
175 strcpy(dest: ExecutorPath.get(), src: ExecutablePath.data());
176
177 std::string FDSpecifierStr("filedescs=");
178 FDSpecifierStr += llvm::utostr(X: ToExecutor[ReadEnd]);
179 FDSpecifierStr += ',';
180 FDSpecifierStr += llvm::utostr(X: FromExecutor[WriteEnd]);
181 FDSpecifier = std::make_unique<char[]>(num: FDSpecifierStr.size() + 1);
182 strcpy(dest: FDSpecifier.get(), src: FDSpecifierStr.c_str());
183 }
184
185 char *const Args[] = {ExecutorPath.get(), FDSpecifier.get(), nullptr};
186 int RC = execvp(file: ExecutorPath.get(), argv: Args);
187 if (RC != 0) {
188 llvm::errs() << "unable to launch out-of-process executor \""
189 << ExecutorPath.get() << "\"\n";
190 exit(status: 1);
191 }
192 }
193 // else we're the parent...
194
195 // Close the child ends of the pipes
196 close(fd: ToExecutor[ReadEnd]);
197 close(fd: FromExecutor[WriteEnd]);
198
199 auto EPCOrErr =
200 llvm::orc::SimpleRemoteEPC::Create<llvm::orc::FDSimpleRemoteEPCTransport>(
201 D: std::make_unique<llvm::orc::DynamicThreadPoolTaskDispatcher>(
202 args: std::nullopt),
203 TransportTCtorArgs&: FromExecutor[ReadEnd], TransportTCtorArgs&: ToExecutor[WriteEnd]);
204 if (!EPCOrErr)
205 return EPCOrErr.takeError();
206 return std::make_pair(x: std::move(*EPCOrErr), y&: ChildPID);
207#endif
208}
209
210#if LLVM_ON_UNIX && LLVM_ENABLE_THREADS
211
212static Expected<int> connectTCPSocketImpl(std::string Host,
213 std::string PortStr) {
214 addrinfo *AI;
215 addrinfo Hints{};
216 Hints.ai_family = AF_INET;
217 Hints.ai_socktype = SOCK_STREAM;
218 Hints.ai_flags = AI_NUMERICSERV;
219
220 if (int EC = getaddrinfo(name: Host.c_str(), service: PortStr.c_str(), req: &Hints, pai: &AI))
221 return llvm::make_error<llvm::StringError>(
222 Args: llvm::formatv(Fmt: "address resolution failed ({0})", Vals: strerror(errnum: EC)),
223 Args: llvm::inconvertibleErrorCode());
224 // Cycle through the returned addrinfo structures and connect to the first
225 // reachable endpoint.
226 int SockFD;
227 addrinfo *Server;
228 for (Server = AI; Server != nullptr; Server = Server->ai_next) {
229 // socket might fail, e.g. if the address family is not supported. Skip to
230 // the next addrinfo structure in such a case.
231 if ((SockFD = socket(domain: AI->ai_family, type: AI->ai_socktype, protocol: AI->ai_protocol)) < 0)
232 continue;
233
234 // If connect returns null, we exit the loop with a working socket.
235 if (connect(fd: SockFD, addr: Server->ai_addr, len: Server->ai_addrlen) == 0)
236 break;
237
238 close(fd: SockFD);
239 }
240 freeaddrinfo(ai: AI);
241
242 // If we reached the end of the loop without connecting to a valid endpoint,
243 // dump the last error that was logged in socket() or connect().
244 if (Server == nullptr)
245 return llvm::make_error<llvm::StringError>(Args: "invalid hostname",
246 Args: llvm::inconvertibleErrorCode());
247
248 return SockFD;
249}
250
251static llvm::Expected<std::unique_ptr<llvm::orc::SimpleRemoteEPC>>
252connectTCPSocket(llvm::StringRef NetworkAddress) {
253#ifndef LLVM_ON_UNIX
254 // FIXME: Add TCP support for Windows.
255 return llvm::make_error<llvm::StringError>(
256 "-" + NetworkAddress + " not supported on non-unix platforms",
257 llvm::inconvertibleErrorCode());
258#elif !LLVM_ENABLE_THREADS
259 // Out of process mode using SimpleRemoteEPC depends on threads.
260 return llvm::make_error<llvm::StringError>(
261 "-" + NetworkAddress +
262 " requires threads, but LLVM was built with "
263 "LLVM_ENABLE_THREADS=Off",
264 llvm::inconvertibleErrorCode());
265#else
266
267 auto CreateErr = [NetworkAddress](Twine Details) {
268 return llvm::make_error<llvm::StringError>(
269 Args: formatv(Fmt: "Failed to connect TCP socket '{0}': {1}", Vals: NetworkAddress,
270 Vals&: Details),
271 Args: llvm::inconvertibleErrorCode());
272 };
273
274 StringRef Host, PortStr;
275 std::tie(args&: Host, args&: PortStr) = NetworkAddress.split(Separator: ':');
276 if (Host.empty())
277 return CreateErr("Host name for -" + NetworkAddress + " can not be empty");
278 if (PortStr.empty())
279 return CreateErr("Port number in -" + NetworkAddress + " can not be empty");
280 int Port = 0;
281 if (PortStr.getAsInteger(Radix: 10, Result&: Port))
282 return CreateErr("Port number '" + PortStr + "' is not a valid integer");
283
284 Expected<int> SockFD = connectTCPSocketImpl(Host: Host.str(), PortStr: PortStr.str());
285 if (!SockFD)
286 return SockFD.takeError();
287
288 return llvm::orc::SimpleRemoteEPC::Create<
289 llvm::orc::FDSimpleRemoteEPCTransport>(
290 D: std::make_unique<llvm::orc::DynamicThreadPoolTaskDispatcher>(
291 args: std::nullopt),
292 TransportTCtorArgs&: *SockFD, TransportTCtorArgs&: *SockFD);
293#endif
294}
295#endif // _WIN32
296
297static llvm::Expected<std::unique_ptr<llvm::orc::LLJITBuilder>>
298createLLJITBuilder(std::unique_ptr<llvm::orc::ExecutorProcessControl> EPC,
299 llvm::StringRef OrcRuntimePath) {
300 auto JTMB = createJITTargetMachineBuilder(TT: EPC->getTargetTriple());
301 if (!JTMB)
302 return JTMB.takeError();
303 auto JB = createDefaultJITBuilder(JTMB: std::move(*JTMB));
304 if (!JB)
305 return JB.takeError();
306
307 (*JB)->setExecutorProcessControl(std::move(EPC));
308 (*JB)->setPlatformSetUp(
309 llvm::orc::ExecutorNativePlatform(OrcRuntimePath.str()));
310
311 return std::move(*JB);
312}
313
314static llvm::Expected<
315 std::pair<std::unique_ptr<llvm::orc::LLJITBuilder>, uint32_t>>
316outOfProcessJITBuilder(const IncrementalExecutorBuilder &IncrExecutorBuilder) {
317 std::unique_ptr<llvm::orc::ExecutorProcessControl> EPC;
318 uint32_t childPid = -1;
319 if (!IncrExecutorBuilder.OOPExecutor.empty()) {
320 // Launch an out-of-process executor locally in a child process.
321 auto ResultOrErr = launchExecutor(ExecutablePath: IncrExecutorBuilder.OOPExecutor,
322 CustomizeFork: IncrExecutorBuilder.CustomizeFork);
323 if (!ResultOrErr)
324 return ResultOrErr.takeError();
325 childPid = ResultOrErr->second;
326 auto EPCOrErr = std::move(ResultOrErr->first);
327 EPC = std::move(EPCOrErr);
328 } else if (IncrExecutorBuilder.OOPExecutorConnect != "") {
329#if LLVM_ON_UNIX && LLVM_ENABLE_THREADS
330 auto EPCOrErr = connectTCPSocket(NetworkAddress: IncrExecutorBuilder.OOPExecutorConnect);
331 if (!EPCOrErr)
332 return EPCOrErr.takeError();
333 EPC = std::move(*EPCOrErr);
334#else
335 return llvm::make_error<llvm::StringError>(
336 "Out-of-process JIT over TCP is not supported on this platform",
337 std::error_code());
338#endif
339 }
340
341 std::unique_ptr<llvm::orc::LLJITBuilder> JB;
342 if (EPC) {
343 auto JBOrErr =
344 createLLJITBuilder(EPC: std::move(EPC), OrcRuntimePath: IncrExecutorBuilder.OrcRuntimePath);
345 if (!JBOrErr)
346 return JBOrErr.takeError();
347 JB = std::move(*JBOrErr);
348
349 if (IncrExecutorBuilder.UseSharedMemory)
350 JB->setMemoryManagerCreator(
351 [SlabAllocateSize = IncrExecutorBuilder.SlabAllocateSize](
352 llvm::orc::ExecutionSession &ES) {
353 return createSharedMemoryManager(EPC&: ES.getExecutorProcessControl(),
354 SlabAllocateSize);
355 });
356 }
357
358 return std::make_pair(x: std::move(JB), y&: childPid);
359}
360
361llvm::Expected<std::unique_ptr<IncrementalExecutor>>
362IncrementalExecutorBuilder::create(llvm::orc::ThreadSafeContext &TSC,
363 const clang::TargetInfo &TI) {
364 if (IE)
365 return std::move(IE);
366 llvm::Triple TT = TI.getTriple();
367 if (!TT.isOSWindows() && IsOutOfProcess) {
368 if (!JITBuilder) {
369 auto ResOrErr = outOfProcessJITBuilder(IncrExecutorBuilder: *this);
370 if (!ResOrErr)
371 return ResOrErr.takeError();
372 JITBuilder = std::move(ResOrErr->first);
373 ExecutorPID = ResOrErr->second;
374 }
375 if (!JITBuilder)
376 return llvm::make_error<llvm::StringError>(
377 Args: "Operation failed. No LLJITBuilder for out-of-process JIT",
378 Args: std::error_code());
379 }
380
381 if (!JITBuilder) {
382 auto JTMB = createJITTargetMachineBuilder(TT);
383 if (!JTMB)
384 return JTMB.takeError();
385 if (CM)
386 JTMB->setCodeModel(CM);
387 auto JB = createDefaultJITBuilder(JTMB: std::move(*JTMB));
388 if (!JB)
389 return JB.takeError();
390 JITBuilder = std::move(*JB);
391 }
392
393 llvm::Error Err = llvm::Error::success();
394 std::unique_ptr<IncrementalExecutor> Executor;
395#ifdef __EMSCRIPTEN__
396 Executor = std::make_unique<WasmIncrementalExecutor>(Err);
397#else
398 Executor = std::make_unique<OrcIncrementalExecutor>(args&: TSC, args&: *JITBuilder, args&: Err);
399#endif
400
401 if (Err)
402 return std::move(Err);
403
404 return std::move(Executor);
405}
406
407llvm::Error IncrementalExecutorBuilder::UpdateOrcRuntimePath(
408 const clang::driver::Compilation &C) {
409 if (!IsOutOfProcess)
410 return llvm::Error::success();
411
412 const clang::driver::Driver &D = C.getDriver();
413 const clang::driver::ToolChain &TC = C.getDefaultToolChain();
414
415 llvm::SmallVector<std::string, 2> OrcRTLibNames;
416
417 // Get canonical compiler-rt path
418 std::string CompilerRTPath = TC.getCompilerRT(Args: C.getArgs(), Component: "orc_rt");
419 llvm::StringRef CanonicalFilename = llvm::sys::path::filename(path: CompilerRTPath);
420
421 if (CanonicalFilename.empty()) {
422 return llvm::make_error<llvm::StringError>(
423 Args: "Could not determine OrcRuntime filename from ToolChain",
424 Args: llvm::inconvertibleErrorCode());
425 }
426
427 OrcRTLibNames.push_back(Elt: CanonicalFilename.str());
428
429 // Derive legacy spelling (libclang_rt.orc_rt -> orc_rt)
430 llvm::StringRef LegacySuffix = CanonicalFilename;
431 if (LegacySuffix.consume_front(Prefix: "libclang_rt.")) {
432 OrcRTLibNames.push_back(Elt: ("lib" + LegacySuffix).str());
433 }
434
435 // Extract directory
436 llvm::SmallString<256> OrcRTDir(CompilerRTPath);
437 llvm::sys::path::remove_filename(path&: OrcRTDir);
438
439 llvm::SmallVector<std::string, 8> triedPaths;
440
441 auto findInDir = [&](llvm::StringRef Dir) -> std::optional<std::string> {
442 for (const auto &LibName : OrcRTLibNames) {
443 llvm::SmallString<256> FullPath = Dir;
444 llvm::sys::path::append(path&: FullPath, a: LibName);
445 if (llvm::sys::fs::exists(Path: FullPath))
446 return std::string(FullPath.str());
447 triedPaths.push_back(Elt: std::string(FullPath.str()));
448 }
449 return std::nullopt;
450 };
451
452 // Try the primary directory first
453 if (auto Found = findInDir(OrcRTDir)) {
454 OrcRuntimePath = *Found;
455 return llvm::Error::success();
456 }
457
458 // We want to find the relative path from the Driver to the OrcRTDir
459 // to replicate that structure elsewhere if needed.
460 llvm::StringRef Rel = OrcRTDir.str();
461 if (!Rel.consume_front(Prefix: llvm::sys::path::parent_path(path: D.Dir))) {
462 return llvm::make_error<llvm::StringError>(
463 Args: llvm::formatv(Fmt: "OrcRuntime library path ({0}) is not located within the "
464 "Clang resource directory ({1}). Check your installation "
465 "or provide an explicit path via -resource-dir.",
466 Vals&: OrcRTDir, Vals: D.Dir)
467 .str(),
468 Args: llvm::inconvertibleErrorCode());
469 }
470
471 // Generic Backward Search (Climbing the tree)
472 // This is useful for unit tests or relocated toolchains
473 llvm::SmallString<256> Cursor(D.Dir); // Start from the driver directory
474 while (llvm::sys::path::has_parent_path(path: Cursor)) {
475 Cursor = llvm::sys::path::parent_path(path: Cursor).str();
476 llvm::SmallString<256> Candidate = Cursor;
477 llvm::sys::path::append(path&: Candidate, a: Rel);
478
479 if (auto Found = findInDir(Candidate)) {
480 OrcRuntimePath = *Found;
481 return llvm::Error::success();
482 }
483
484 // Safety check
485 if (triedPaths.size() > 32)
486 break;
487 }
488
489 // Build a helpful error string
490 std::string Joined;
491 for (size_t i = 0; i < triedPaths.size(); ++i) {
492 if (i > 0)
493 Joined += "\n ";
494 Joined += triedPaths[i];
495 }
496
497 return llvm::make_error<llvm::StringError>(
498 Args: llvm::formatv(Fmt: "OrcRuntime library not found. Checked: {0}",
499 Vals: Joined.empty() ? "<none>" : Joined)
500 .str(),
501 Args: std::make_error_code(e: std::errc::no_such_file_or_directory));
502}
503
504} // end namespace clang
505