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/AbsoluteSymbols.h"
31#include "llvm/ExecutionEngine/Orc/Debugging/DebuggerSupport.h"
32#include "llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h"
33#include "llvm/ExecutionEngine/Orc/ExecutionUtils.h"
34#include "llvm/ExecutionEngine/Orc/ExecutorProcessControl.h"
35#include "llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h"
36#include "llvm/ExecutionEngine/Orc/LLJIT.h"
37#include "llvm/ExecutionEngine/Orc/MapperJITLinkMemoryManager.h"
38#include "llvm/ExecutionEngine/Orc/Shared/SimpleRemoteEPCUtils.h"
39#include "llvm/ExecutionEngine/Orc/SharedMemoryMapSPS.h"
40#include "llvm/ExecutionEngine/Orc/SimpleRemoteEPC.h"
41
42#include "llvm/Support/Error.h"
43#include "llvm/Support/FileSystem.h"
44#include "llvm/Support/FormatVariadic.h"
45#include "llvm/Support/Path.h"
46#include "llvm/Support/raw_ostream.h"
47
48#include "llvm/TargetParser/Host.h"
49
50#include <array>
51#include <functional>
52#include <memory>
53#include <optional>
54#include <string>
55#include <utility>
56
57#ifdef LLVM_ON_UNIX
58#include <netdb.h>
59#include <netinet/in.h>
60#include <sys/socket.h>
61#include <unistd.h>
62#endif
63
64// Address of the host's emulated-TLS runtime entry point, or null if the host
65// cannot provide one. clang-repl's JIT always lowers thread_local to emulated
66// TLS (JITTargetMachineBuilder forces EmulatedTLS on), so JIT'd code references
67// __emutls_get_address on every target. That symbol lives in the compiler
68// runtime -- libgcc_s.so on a glibc toolchain, or the compiler-rt builtins
69// static archive on Darwin and on compiler-rt-rtlib toolchains. When it is only
70// in a static archive and nothing else references it, it is never linked in and
71// ORC's process-symbol lookup cannot resolve it. Referencing it here
72// force-links the archive member so it is present regardless of how the host
73// provides it. Excluded where an emulated-TLS runtime is not guaranteed on the
74// link line, so the reference would fail to link: non-Unix (MSVC has no such
75// runtime), Emscripten (the wasm executor below does not use this JIT path),
76// and AIX / z/OS (whose runtimes may not provide the symbol). On those hosts
77// thread_locals instead rely on process-symbol lookup, unchanged from before.
78#if defined(LLVM_ON_UNIX) && !defined(__EMSCRIPTEN__) && !defined(_AIX) && \
79 !defined(__MVS__) && !defined(__FreeBSD__)
80extern "C" void *__emutls_get_address(void *);
81static void *getEmuTLSGetAddressPtr() {
82 return reinterpret_cast<void *>(&__emutls_get_address);
83}
84#else
85static void *getEmuTLSGetAddressPtr() { return nullptr; }
86#endif
87
88namespace clang {
89IncrementalExecutorBuilder::~IncrementalExecutorBuilder() = default;
90
91static llvm::Expected<llvm::orc::JITTargetMachineBuilder>
92createJITTargetMachineBuilder(const llvm::Triple &TT) {
93 if (TT.getTriple() == llvm::sys::getProcessTriple())
94 // This fails immediately if the target backend is not registered
95 return llvm::orc::JITTargetMachineBuilder::detectHost();
96
97 // If the target backend is not registered, LLJITBuilder::create() will fail
98 return llvm::orc::JITTargetMachineBuilder(TT);
99}
100
101static llvm::Expected<std::unique_ptr<llvm::orc::LLJITBuilder>>
102createDefaultJITBuilder(llvm::orc::JITTargetMachineBuilder JTMB) {
103 auto JITBuilder = std::make_unique<llvm::orc::LLJITBuilder>();
104 JITBuilder->setJITTargetMachineBuilder(std::move(JTMB));
105 JITBuilder->setPrePlatformSetup([](llvm::orc::LLJIT &J) {
106 // Try to enable debugging of JIT'd code (only works with JITLink for
107 // ELF and MachO).
108 consumeError(Err: llvm::orc::enableDebuggerSupport(J));
109 return llvm::Error::success();
110 });
111 return std::move(JITBuilder);
112}
113
114Expected<std::unique_ptr<llvm::jitlink::JITLinkMemoryManager>>
115createSharedMemoryManager(llvm::orc::ExecutorProcessControl &EPC,
116 unsigned SlabAllocateSize) {
117 auto &ES = EPC.getExecutionSession();
118 auto B = llvm::orc::sps::createSharedMemoryMapBindings(ES);
119 if (!B)
120 return B.takeError();
121
122 size_t SlabSize;
123 if (llvm::Triple(llvm::sys::getProcessTriple()).isOSWindows())
124 SlabSize = 1024 * 1024;
125 else
126 SlabSize = 1024 * 1024 * 1024;
127
128 if (SlabAllocateSize > 0)
129 SlabSize = SlabAllocateSize;
130
131 return llvm::orc::MapperJITLinkMemoryManager::CreateWithMapper<
132 llvm::orc::SharedMemoryMapper>(ReservationGranularity: SlabSize, A&: ES, A: std::move(*B));
133}
134
135static llvm::Expected<
136 std::pair<std::unique_ptr<llvm::orc::SimpleRemoteEPC>, uint32_t>>
137launchExecutor(llvm::StringRef ExecutablePath,
138 std::function<void()> CustomizeFork) {
139#ifndef LLVM_ON_UNIX
140 // FIXME: Add support for Windows.
141 return llvm::make_error<llvm::StringError>(
142 "-" + ExecutablePath + " not supported on non-unix platforms",
143 llvm::inconvertibleErrorCode());
144#elif !LLVM_ENABLE_THREADS
145 // Out of process mode using SimpleRemoteEPC depends on threads.
146 return llvm::make_error<llvm::StringError>(
147 "-" + ExecutablePath +
148 " requires threads, but LLVM was built with "
149 "LLVM_ENABLE_THREADS=Off",
150 llvm::inconvertibleErrorCode());
151#else
152
153 if (!llvm::sys::fs::can_execute(Path: ExecutablePath))
154 return llvm::make_error<llvm::StringError>(
155 Args: llvm::formatv(Fmt: "Specified executor invalid: {0}", Vals&: ExecutablePath),
156 Args: llvm::inconvertibleErrorCode());
157
158 constexpr int ReadEnd = 0;
159 constexpr int WriteEnd = 1;
160
161 // Pipe FDs.
162 int ToExecutor[2];
163 int FromExecutor[2];
164
165 uint32_t ChildPID;
166
167 // Create pipes to/from the executor..
168 if (pipe(pipedes: ToExecutor) != 0 || pipe(pipedes: FromExecutor) != 0)
169 return llvm::make_error<llvm::StringError>(
170 Args: "Unable to create pipe for executor", Args: llvm::inconvertibleErrorCode());
171
172 ChildPID = fork();
173
174 if (ChildPID == 0) {
175 // In the child...
176
177 // Close the parent ends of the pipes
178 close(fd: ToExecutor[WriteEnd]);
179 close(fd: FromExecutor[ReadEnd]);
180
181 if (CustomizeFork)
182 CustomizeFork();
183
184 // Execute the child process.
185 std::unique_ptr<char[]> ExecutorPath, FDSpecifier;
186 {
187 ExecutorPath = std::make_unique<char[]>(num: ExecutablePath.size() + 1);
188 strcpy(dest: ExecutorPath.get(), src: ExecutablePath.data());
189
190 std::string FDSpecifierStr("filedescs=");
191 FDSpecifierStr += llvm::utostr(X: ToExecutor[ReadEnd]);
192 FDSpecifierStr += ',';
193 FDSpecifierStr += llvm::utostr(X: FromExecutor[WriteEnd]);
194 FDSpecifier = std::make_unique<char[]>(num: FDSpecifierStr.size() + 1);
195 strcpy(dest: FDSpecifier.get(), src: FDSpecifierStr.c_str());
196 }
197
198 char *const Args[] = {ExecutorPath.get(), FDSpecifier.get(), nullptr};
199 int RC = execvp(file: ExecutorPath.get(), argv: Args);
200 if (RC != 0) {
201 llvm::errs() << "unable to launch out-of-process executor \""
202 << ExecutorPath.get() << "\"\n";
203 exit(status: 1);
204 }
205 }
206 // else we're the parent...
207
208 // Close the child ends of the pipes
209 close(fd: ToExecutor[ReadEnd]);
210 close(fd: FromExecutor[WriteEnd]);
211
212 auto EPCOrErr =
213 llvm::orc::SimpleRemoteEPC::Create<llvm::orc::FDSimpleRemoteEPCTransport>(
214 D: std::make_unique<llvm::orc::DynamicThreadPoolTaskDispatcher>(
215 args: std::nullopt),
216 TransportTCtorArgs&: FromExecutor[ReadEnd], TransportTCtorArgs&: ToExecutor[WriteEnd]);
217 if (!EPCOrErr)
218 return EPCOrErr.takeError();
219 return std::make_pair(x: std::move(*EPCOrErr), y&: ChildPID);
220#endif
221}
222
223#if LLVM_ON_UNIX && LLVM_ENABLE_THREADS
224
225static Expected<int> connectTCPSocketImpl(std::string Host,
226 std::string PortStr) {
227 addrinfo *AI;
228 addrinfo Hints{};
229 Hints.ai_family = AF_INET;
230 Hints.ai_socktype = SOCK_STREAM;
231 Hints.ai_flags = AI_NUMERICSERV;
232
233 if (int EC = getaddrinfo(name: Host.c_str(), service: PortStr.c_str(), req: &Hints, pai: &AI))
234 return llvm::make_error<llvm::StringError>(
235 Args: llvm::formatv(Fmt: "address resolution failed ({0})", Vals: strerror(errnum: EC)),
236 Args: llvm::inconvertibleErrorCode());
237 // Cycle through the returned addrinfo structures and connect to the first
238 // reachable endpoint.
239 int SockFD;
240 addrinfo *Server;
241 for (Server = AI; Server != nullptr; Server = Server->ai_next) {
242 // socket might fail, e.g. if the address family is not supported. Skip to
243 // the next addrinfo structure in such a case.
244 if ((SockFD = socket(domain: AI->ai_family, type: AI->ai_socktype, protocol: AI->ai_protocol)) < 0)
245 continue;
246
247 // If connect returns null, we exit the loop with a working socket.
248 if (connect(fd: SockFD, addr: Server->ai_addr, len: Server->ai_addrlen) == 0)
249 break;
250
251 close(fd: SockFD);
252 }
253 freeaddrinfo(ai: AI);
254
255 // If we reached the end of the loop without connecting to a valid endpoint,
256 // dump the last error that was logged in socket() or connect().
257 if (Server == nullptr)
258 return llvm::make_error<llvm::StringError>(Args: "invalid hostname",
259 Args: llvm::inconvertibleErrorCode());
260
261 return SockFD;
262}
263
264static llvm::Expected<std::unique_ptr<llvm::orc::SimpleRemoteEPC>>
265connectTCPSocket(llvm::StringRef NetworkAddress) {
266#ifndef LLVM_ON_UNIX
267 // FIXME: Add TCP support for Windows.
268 return llvm::make_error<llvm::StringError>(
269 "-" + NetworkAddress + " not supported on non-unix platforms",
270 llvm::inconvertibleErrorCode());
271#elif !LLVM_ENABLE_THREADS
272 // Out of process mode using SimpleRemoteEPC depends on threads.
273 return llvm::make_error<llvm::StringError>(
274 "-" + NetworkAddress +
275 " requires threads, but LLVM was built with "
276 "LLVM_ENABLE_THREADS=Off",
277 llvm::inconvertibleErrorCode());
278#else
279
280 auto CreateErr = [NetworkAddress](Twine Details) {
281 return llvm::make_error<llvm::StringError>(
282 Args: formatv(Fmt: "Failed to connect TCP socket '{0}': {1}", Vals: NetworkAddress,
283 Vals&: Details),
284 Args: llvm::inconvertibleErrorCode());
285 };
286
287 StringRef Host, PortStr;
288 std::tie(args&: Host, args&: PortStr) = NetworkAddress.split(Separator: ':');
289 if (Host.empty())
290 return CreateErr("Host name for -" + NetworkAddress + " can not be empty");
291 if (PortStr.empty())
292 return CreateErr("Port number in -" + NetworkAddress + " can not be empty");
293 int Port = 0;
294 if (PortStr.getAsInteger(Radix: 10, Result&: Port))
295 return CreateErr("Port number '" + PortStr + "' is not a valid integer");
296
297 Expected<int> SockFD = connectTCPSocketImpl(Host: Host.str(), PortStr: PortStr.str());
298 if (!SockFD)
299 return SockFD.takeError();
300
301 return llvm::orc::SimpleRemoteEPC::Create<
302 llvm::orc::FDSimpleRemoteEPCTransport>(
303 D: std::make_unique<llvm::orc::DynamicThreadPoolTaskDispatcher>(
304 args: std::nullopt),
305 TransportTCtorArgs&: *SockFD, TransportTCtorArgs&: *SockFD);
306#endif
307}
308#endif // _WIN32
309
310static llvm::Expected<std::unique_ptr<llvm::orc::LLJITBuilder>>
311createLLJITBuilder(std::unique_ptr<llvm::orc::ExecutorProcessControl> EPC,
312 llvm::StringRef OrcRuntimePath) {
313 auto JTMB = createJITTargetMachineBuilder(TT: EPC->getTargetTriple());
314 if (!JTMB)
315 return JTMB.takeError();
316 auto JB = createDefaultJITBuilder(JTMB: std::move(*JTMB));
317 if (!JB)
318 return JB.takeError();
319
320 (*JB)->setExecutorProcessControl(std::move(EPC));
321 (*JB)->setPlatformSetUp(
322 llvm::orc::ExecutorNativePlatform(OrcRuntimePath.str()));
323
324 return std::move(*JB);
325}
326
327static llvm::Expected<
328 std::pair<std::unique_ptr<llvm::orc::LLJITBuilder>, uint32_t>>
329outOfProcessJITBuilder(const IncrementalExecutorBuilder &IncrExecutorBuilder) {
330 std::unique_ptr<llvm::orc::ExecutorProcessControl> EPC;
331 uint32_t childPid = -1;
332 if (!IncrExecutorBuilder.OOPExecutor.empty()) {
333 // Launch an out-of-process executor locally in a child process.
334 auto ResultOrErr = launchExecutor(ExecutablePath: IncrExecutorBuilder.OOPExecutor,
335 CustomizeFork: IncrExecutorBuilder.CustomizeFork);
336 if (!ResultOrErr)
337 return ResultOrErr.takeError();
338 childPid = ResultOrErr->second;
339 auto EPCOrErr = std::move(ResultOrErr->first);
340 EPC = std::move(EPCOrErr);
341 } else if (IncrExecutorBuilder.OOPExecutorConnect != "") {
342#if LLVM_ON_UNIX && LLVM_ENABLE_THREADS
343 auto EPCOrErr = connectTCPSocket(NetworkAddress: IncrExecutorBuilder.OOPExecutorConnect);
344 if (!EPCOrErr)
345 return EPCOrErr.takeError();
346 EPC = std::move(*EPCOrErr);
347#else
348 return llvm::make_error<llvm::StringError>(
349 "Out-of-process JIT over TCP is not supported on this platform",
350 std::error_code());
351#endif
352 }
353
354 std::unique_ptr<llvm::orc::LLJITBuilder> JB;
355 if (EPC) {
356 auto JBOrErr =
357 createLLJITBuilder(EPC: std::move(EPC), OrcRuntimePath: IncrExecutorBuilder.OrcRuntimePath);
358 if (!JBOrErr)
359 return JBOrErr.takeError();
360 JB = std::move(*JBOrErr);
361
362 if (IncrExecutorBuilder.UseSharedMemory)
363 JB->setMemoryManagerCreator(
364 [SlabAllocateSize = IncrExecutorBuilder.SlabAllocateSize](
365 llvm::orc::ExecutionSession &ES) {
366 return createSharedMemoryManager(EPC&: ES.getExecutorProcessControl(),
367 SlabAllocateSize);
368 });
369 }
370
371 return std::make_pair(x: std::move(JB), y&: childPid);
372}
373
374llvm::Expected<std::unique_ptr<IncrementalExecutor>>
375IncrementalExecutorBuilder::create(llvm::orc::ThreadSafeContext &TSC,
376 const clang::TargetInfo &TI) {
377 if (IE)
378 return std::move(IE);
379 llvm::Triple TT = TI.getTriple();
380 if (!TT.isOSWindows() && IsOutOfProcess) {
381 if (!JITBuilder) {
382 auto ResOrErr = outOfProcessJITBuilder(IncrExecutorBuilder: *this);
383 if (!ResOrErr)
384 return ResOrErr.takeError();
385 JITBuilder = std::move(ResOrErr->first);
386 ExecutorPID = ResOrErr->second;
387 }
388 if (!JITBuilder)
389 return llvm::make_error<llvm::StringError>(
390 Args: "Operation failed. No LLJITBuilder for out-of-process JIT",
391 Args: std::error_code());
392 }
393
394 if (!JITBuilder) {
395 auto JTMB = createJITTargetMachineBuilder(TT);
396 if (!JTMB)
397 return JTMB.takeError();
398 if (CM)
399 JTMB->setCodeModel(CM);
400 auto JB = createDefaultJITBuilder(JTMB: std::move(*JTMB));
401 if (!JB)
402 return JB.takeError();
403 JITBuilder = std::move(*JB);
404 // TODO: Switch to native TLS once clang-repl can adopt the ORC runtime
405 // (which provides __emutls_get_address and supports the full TLS
406 // lifecycle). That will also remove the in-process-only constraint below.
407 //
408 // clang-repl lowers thread_local to emulated TLS on every target (see
409 // JITTargetMachineBuilder), so JIT'd code calls __emutls_get_address. When
410 // the host cannot resolve that symbol through process-symbol lookup
411 // (Darwin, and ELF toolchains that link compiler-rt builtins rather than
412 // libgcc_s), define the force-linked host symbol (see
413 // getEmuTLSGetAddressPtr) as an absolute symbol so it is visible to JIT'd
414 // code. This is harmless where process-symbol lookup would already resolve
415 // it: an already-defined symbol shadows the process-symbols generator.
416 // In-process execution only -- the host address is meaningless in an
417 // out-of-process executor.
418 if (void *EmuTLSGetAddress = getEmuTLSGetAddressPtr())
419 JITBuilder->setNotifyCreatedCallback(
420 [EmuTLSGetAddress](llvm::orc::LLJIT &J) {
421 auto &JD = J.getProcessSymbolsJITDylib()
422 ? *J.getProcessSymbolsJITDylib()
423 : J.getMainJITDylib();
424 return JD.define(MU: llvm::orc::absoluteSymbols(
425 Symbols: {{J.mangleAndIntern(UnmangledName: "__emutls_get_address"),
426 {llvm::orc::ExecutorAddr::fromPtr(Ptr: EmuTLSGetAddress),
427 llvm::JITSymbolFlags::Exported}}}));
428 });
429 }
430
431 llvm::Error Err = llvm::Error::success();
432 std::unique_ptr<IncrementalExecutor> Executor;
433#ifdef __EMSCRIPTEN__
434 Executor = std::make_unique<WasmIncrementalExecutor>(Err);
435#else
436 Executor = std::make_unique<OrcIncrementalExecutor>(args&: TSC, args&: *JITBuilder, args&: Err);
437#endif
438
439 if (Err)
440 return std::move(Err);
441
442 return std::move(Executor);
443}
444
445llvm::Error IncrementalExecutorBuilder::UpdateOrcRuntimePath(
446 const clang::driver::Compilation &C) {
447 if (!IsOutOfProcess)
448 return llvm::Error::success();
449
450 const clang::driver::Driver &D = C.getDriver();
451 const clang::driver::ToolChain &TC = C.getDefaultToolChain();
452
453 llvm::SmallVector<std::string, 2> OrcRTLibNames;
454
455 // Get canonical compiler-rt path
456 std::string CompilerRTPath = TC.getCompilerRT(Args: C.getArgs(), Component: "orc_rt");
457 llvm::StringRef CanonicalFilename = llvm::sys::path::filename(path: CompilerRTPath);
458
459 if (CanonicalFilename.empty()) {
460 return llvm::make_error<llvm::StringError>(
461 Args: "Could not determine OrcRuntime filename from ToolChain",
462 Args: llvm::inconvertibleErrorCode());
463 }
464
465 OrcRTLibNames.push_back(Elt: CanonicalFilename.str());
466
467 // Derive legacy spelling (libclang_rt.orc_rt -> orc_rt)
468 llvm::StringRef LegacySuffix = CanonicalFilename;
469 if (LegacySuffix.consume_front(Prefix: "libclang_rt.")) {
470 OrcRTLibNames.push_back(Elt: ("lib" + LegacySuffix).str());
471 }
472
473 // Extract directory
474 llvm::SmallString<256> OrcRTDir(CompilerRTPath);
475 llvm::sys::path::remove_filename(path&: OrcRTDir);
476
477 llvm::SmallVector<std::string, 8> triedPaths;
478
479 auto findInDir = [&](llvm::StringRef Dir) -> std::optional<std::string> {
480 for (const auto &LibName : OrcRTLibNames) {
481 llvm::SmallString<256> FullPath = Dir;
482 llvm::sys::path::append(path&: FullPath, a: LibName);
483 if (llvm::sys::fs::exists(Path: FullPath))
484 return std::string(FullPath.str());
485 triedPaths.push_back(Elt: std::string(FullPath.str()));
486 }
487 return std::nullopt;
488 };
489
490 // Try the primary directory first
491 if (auto Found = findInDir(OrcRTDir)) {
492 OrcRuntimePath = *Found;
493 return llvm::Error::success();
494 }
495
496 // We want to find the relative path from the Driver to the OrcRTDir
497 // to replicate that structure elsewhere if needed.
498 llvm::StringRef Rel = OrcRTDir.str();
499 if (!Rel.consume_front(Prefix: llvm::sys::path::parent_path(path: D.Dir))) {
500 return llvm::make_error<llvm::StringError>(
501 Args: llvm::formatv(Fmt: "OrcRuntime library path ({0}) is not located within the "
502 "Clang resource directory ({1}). Check your installation "
503 "or provide an explicit path via -resource-dir.",
504 Vals&: OrcRTDir, Vals: D.Dir)
505 .str(),
506 Args: llvm::inconvertibleErrorCode());
507 }
508
509 // Generic Backward Search (Climbing the tree)
510 // This is useful for unit tests or relocated toolchains
511 llvm::SmallString<256> Cursor(D.Dir); // Start from the driver directory
512 while (llvm::sys::path::has_parent_path(path: Cursor)) {
513 Cursor = llvm::sys::path::parent_path(path: Cursor).str();
514 llvm::SmallString<256> Candidate = Cursor;
515 llvm::sys::path::append(path&: Candidate, a: Rel);
516
517 if (auto Found = findInDir(Candidate)) {
518 OrcRuntimePath = *Found;
519 return llvm::Error::success();
520 }
521
522 // Safety check
523 if (triedPaths.size() > 32)
524 break;
525 }
526
527 // Build a helpful error string
528 std::string Joined;
529 for (size_t i = 0; i < triedPaths.size(); ++i) {
530 if (i > 0)
531 Joined += "\n ";
532 Joined += triedPaths[i];
533 }
534
535 return llvm::make_error<llvm::StringError>(
536 Args: llvm::formatv(Fmt: "OrcRuntime library not found. Checked: {0}",
537 Vals: Joined.empty() ? "<none>" : Joined)
538 .str(),
539 Args: std::make_error_code(e: std::errc::no_such_file_or_directory));
540}
541
542} // end namespace clang
543