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