clang 22.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 file implements the class which performs incremental code execution.
10//
11//===----------------------------------------------------------------------===//
12
13#include "IncrementalExecutor.h"
14
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_UNIX
41#include <netdb.h>
42#include <netinet/in.h>
43#include <sys/socket.h>
44#include <unistd.h>
45#endif // LLVM_ON_UNIX
46
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
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 for
63 // 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,
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
87
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
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.
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
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
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 else
160 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
170IncrementalExecutor::launchExecutor(llvm::StringRef ExecutablePath,
171 bool UseSharedMemory,
172 unsigned SlabAllocateSize,
173 std::function<void()> CustomizeFork) {
174#ifndef LLVM_ON_UNIX
175 // 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_THREADS
180 // 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#else
187
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 pipes
213 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 pipes
244 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#endif
263}
264
265#if LLVM_ON_UNIX && LLVM_ENABLE_THREADS
266
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 first
280 // 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 to
285 // 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
307IncrementalExecutor::connectTCPSocket(llvm::StringRef NetworkAddress,
308 bool UseSharedMemory,
309 unsigned SlabAllocateSize) {
310#ifndef LLVM_ON_UNIX
311 // 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_THREADS
316 // 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#else
323
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#endif
358}
359#endif // _WIN32
360
361} // namespace clang
LLVM_ATTRIBUTE_USED void linkComponents()
Defines the clang::TargetOptions class.
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=nullptr)
virtual llvm::Error runCtors() const
virtual llvm::Error addModule(PartialTranslationUnit &PTU)
virtual llvm::Expected< llvm::orc::ExecutorAddr > getSymbolAddress(llvm::StringRef Name, SymbolNameKind NameKind) const
static llvm::Expected< std::unique_ptr< llvm::orc::LLJITBuilder > > createDefaultJITBuilder(llvm::orc::JITTargetMachineBuilder JTMB)
virtual llvm::Error removeModule(PartialTranslationUnit &PTU)
IncrementalExecutor(llvm::orc::ThreadSafeContext &TSC)
Defines the clang::TargetInfo interface.
The JSON file list parser is used to communicate input to InstallAPI.
Expected< std::unique_ptr< llvm::jitlink::JITLinkMemoryManager > > createSharedMemoryManager(llvm::orc::SimpleRemoteEPC &SREPC, unsigned SlabAllocateSize)
int const char * function
Definition c++config.h:31
The class keeps track of various objects created as part of processing incremental inputs.
std::unique_ptr< llvm::Module > TheModule
The llvm IR produced for the input.