LLVM 24.0.0git
DTLTO.cpp
Go to the documentation of this file.
1//===- DTLTO.cpp - Integrated Distributed ThinLTO implementation ----------===//
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// \file
9// This file implements support functions for Integrated Distributed ThinLTO,
10// focusing on preparing complilation jobs for distribution.
11//
12//===----------------------------------------------------------------------===//
13
14#include "llvm/DTLTO/DTLTO.h"
15
16#include "llvm/ADT/STLExtras.h"
17#include "llvm/ADT/ScopeExit.h"
19#include "llvm/ADT/StringRef.h"
20#include "llvm/LTO/LTO.h"
23#include "llvm/Support/Path.h"
27
28#include <string>
29#include <system_error>
30#include <utility>
31#include <vector>
32
33using namespace llvm;
34
35// Experimentation showed that serial deletion is most efficient, hence
36// a single thread.
37lto::DTLTO::BackgroundDeletion::BackgroundDeletion()
39
40lto::DTLTO::BackgroundDeletion::~BackgroundDeletion() { waitForTasks(); }
41
42void lto::DTLTO::BackgroundDeletion::waitForTasks() {
43 wait();
44 for (const std::string &Warning : Warnings)
45 errs() << "warning: could not remove the file " << Warning << "\n";
46 Warnings.clear();
47}
48
49void lto::DTLTO::BackgroundDeletion::removeFiles(
50 std::vector<std::string> &&Files, const Config &Conf) {
51 if (Files.empty())
52 return;
53
54 async([this, Files = std::move(Files), TTE = Conf.TimeTraceEnabled,
55 TTG = Conf.TimeTraceGranularity] {
56 if (LLVM_ENABLE_THREADS && TTE)
57 timeTraceProfilerInitialize(TTG, "Remove DTLTO temporary files");
58 {
59 TimeTraceScope TimeScope("Remove DTLTO temporary files");
60 for (const std::string &Path : Files) {
61 std::error_code EC = sys::fs::remove(Path, true);
62 if (!EC ||
63 EC == std::make_error_code(std::errc::no_such_file_or_directory))
64 continue;
65
66 Warnings.emplace_back("'" + Path + "': " + EC.message());
67 }
68 }
69 if (LLVM_ENABLE_THREADS && TTE)
71 });
72}
73
74void lto::DTLTO::waitForCleanup() { BackgroundDeleter.waitForTasks(); }
75
76// Remove temporary files created to enable distribution.
78 if (SaveTemps)
79 return;
80
81 BackgroundDeleter.removeFiles(std::move(CleanupList), Conf);
82}
83
84// Runs the DTLTO thin link phase, producing per-module summary indices,
85// import lists, and cache keys for distribution.
86Error lto::DTLTO::performThinLink() {
87 size_t NumTasks = getMaxTasks();
88 SummaryIndexFiles.resize(NumTasks);
89 ImportsFilesList.resize(NumTasks);
90 CacheKeysList.resize(NumTasks);
91
92 lto::Config &Cfg = getConfig();
94 [&](size_t task) -> std::unique_ptr<raw_svector_ostream> {
95 return std::make_unique<raw_svector_ostream>(SummaryIndexFiles[task]);
96 };
97 Cfg.GetCacheKeyOutputString = [&](size_t task) -> std::string & {
98 return CacheKeysList[task];
99 };
101 [&](size_t task) -> std::vector<std::string> & {
102 return ImportsFilesList[task];
103 };
104 return Base::run(AddStreamFunc, {});
105}
106
107// Runs the DTLTO pipeline.
109 scope_exit CleanUp([this]() { cleanup(); });
110
111 AddStreamFunc = AddStream;
112 Cache = std::move(CacheParam);
113 Conf.Dtlto = 1;
115
116 if (Error Err = performThinLink())
117 return Err;
118
119 ThinLTOTaskOffset = RegularLTO.ParallelCodeGenParallelismLevel;
120 DistributorParams.TargetTriple = RegularLTO.CombinedModule->getTargetTriple();
121
122 if (Error Err = prepareDtltoJobs())
123 return Err;
124 if (Error Err = extractLTOInputs())
125 return Err;
126 if (Error Err = performCodegen())
127 return Err;
128 if (Error Err = addObjectFilesToLink())
129 return Err;
130 return Error::success();
131}
132
133// Probes the LTO cache for a compiled native object for the given job.
134Error lto::DTLTO::checkCacheHit(Job &J) {
135 if (!Cache.isValid())
136 return Error::success();
137
138 TimeTraceScope TimeScope("Check cache for DTLTO", J.SummaryIndexPath);
139
140 auto CacheAddStreamExp = Cache(J.Task, J.CacheKey, J.ModuleID);
141 if (Error Err = CacheAddStreamExp.takeError())
142 return Err;
143 AddStreamFn &CacheAddStream = *CacheAddStreamExp;
144 // If CacheAddStream is null, we have a cache hit and at this point
145 // object file is already passed back to the linker.
146 if (!CacheAddStream) {
147 J.Cached = true; // Cache hit, mark the job as cached.
148 CachedJobs.fetch_add(1);
149 } else {
150 // If CacheAddStream is not null, we have a cache miss and we need to
151 // run the backend for codegen. Save cache 'add stream'
152 // function for a later use.
153 J.CacheAddStream = std::move(CacheAddStream);
154 }
155 return Error::success();
156}
157
158// Prepares a single DTLTO backend compilation job for a ThinLTO module.
159Error lto::DTLTO::prepareDtltoJob(StringRef ModulePath, unsigned Task) {
160 assert(Task >= ThinLTOTaskOffset && Task - ThinLTOTaskOffset < Jobs.size() &&
161 "Task index out of range for Jobs");
162 assert(Task < SummaryIndexFiles.size() && "Task index out of range");
163
164 SString ObjFilePath =
165 sys::path::parent_path(DistributorParams.LinkerOutputFile);
166 sys::path::append(ObjFilePath, sys::path::stem(ModulePath) + "." +
167 itostr(Task) + "." + UID + ".native.o");
168
169 SString SummaryIndexPathStr = ObjFilePath;
170 SummaryIndexPathStr += ".thinlto.bc";
171 SString ImportsPathStr = ModulePath;
172 ImportsPathStr += ".imports";
173
174 Job &J = Jobs[Task - ThinLTOTaskOffset];
175 J = {Task,
176 ModulePath,
177 Saver.save(ObjFilePath.str()),
178 Saver.save(SummaryIndexPathStr.str()),
179 Saver.save(ImportsPathStr.str()),
180 ImportsFilesList[Task],
181 CacheKeysList[Task],
182 nullptr,
183 false};
184
185 if (Error Err = checkCacheHit(J))
186 return Err;
187 if (!J.Cached) {
188 InputModuleIDsToExtract.insert(J.ModuleID);
189 for (StringRef ImportPath : J.ImportsFilesList)
190 InputModuleIDsToExtract.insert(ImportPath);
191
192 TimeTraceScope JobScope("Emit individual index for DTLTO",
193 J.SummaryIndexPath);
194 if (Error Err = save(SummaryIndexFiles[Task], J.SummaryIndexPath))
195 return Err;
196 }
197 if (OnIndexWriteCb)
198 OnIndexWriteCb(J.SummaryIndexPath.str());
199
200 if (ShouldEmitImportFiles)
201 if (Error Err = save(join(J.ImportsFilesList, "\n"), J.ImportsPath))
202 return Err;
203
204 if (!SaveTemps) {
205 if (!J.Cached)
206 addToCleanup(J.NativeObjectPath.str());
207 if (!ShouldEmitIndexFiles)
208 addToCleanup(J.SummaryIndexPath.str());
209 if (!ShouldEmitImportFiles)
210 addToCleanup(J.ImportsPath.str());
211 }
212 return Error::success();
213}
214
215// Derive a set of Clang options that will be shared/common for all DTLTO
216// backend compilations.
217void lto::DTLTO::buildCommonRemoteCompilerOptions() {
218 const lto::Config &C = getConfig();
219 auto &Ops = DistributorParams.CodegenOptions;
220
221 Ops.push_back(Saver.save("-O" + Twine(C.OptLevel)));
222
223 if (C.Options.EmitAddrsig)
224 Ops.push_back("-faddrsig");
225 if (C.Options.FunctionSections)
226 Ops.push_back("-ffunction-sections");
227 if (C.Options.DataSections)
228 Ops.push_back("-fdata-sections");
229 if (C.PTO.LoopInterchange)
230 Ops.push_back("-floop-interchange");
231
232 if (C.RelocModel == Reloc::PIC_)
233 // Clang doesn't have -fpic for all triples.
234 if (!DistributorParams.TargetTriple.isOSBinFormatCOFF())
235 Ops.push_back("-fpic");
236
237 // Turn on/off warnings about profile cfg mismatch (default on)
238 // --lto-pgo-warn-mismatch.
239 if (!C.PGOWarnMismatch) {
240 Ops.push_back("-mllvm");
241 Ops.push_back("-no-pgo-warn-mismatch");
242 }
243
244 // Enable sample-based profile guided optimizations.
245 // Sample profile file path --lto-sample-profile=<value>.
246 if (!C.SampleProfile.empty()) {
247 Ops.push_back(Saver.save("-fprofile-sample-use=" + Twine(C.SampleProfile)));
248 DistributorParams.CommonInputs.insert(C.SampleProfile);
249 }
250
251 // We don't know which of options will be used by Clang.
252 Ops.push_back("-Wno-unused-command-line-argument");
253
254 // Forward any supplied options.
255 if (!DistributorParams.RemoteCompilerArgs.empty())
256 for (auto &a : DistributorParams.RemoteCompilerArgs)
257 Ops.push_back(a);
258}
259
260// Initializes DTLTO state and prepares a job for each ThinLTO module.
261Error lto::DTLTO::prepareDtltoJobs() {
262 auto &ModuleMap =
263 ThinLTO.ModulesToCompile ? *ThinLTO.ModulesToCompile : ThinLTO.ModuleMap;
264
265 InputModuleIDsToExtract.clear();
266
267 if (ModuleMap.empty())
268 return Error::success();
269
270 Jobs.resize(ModuleMap.size());
271
272 for (auto [I, Mod] : enumerate(ModuleMap))
273 if (Error E = prepareDtltoJob(Mod.first, ThinLTOTaskOffset + I))
274 return E;
275
276 return Error::success();
277}
278
279// Runs the DTLTO code generation phase. Must be invoked after thinLink().
280Error lto::DTLTO::performCodegen() {
281 if (Jobs.empty())
282 return Error::success();
283 // Build common remote compiler options.
284 buildCommonRemoteCompilerOptions();
285
286 DistributionDriver Distributor(DistributorParams, Jobs, SaveTemps,
287 [&](StringRef S) { addToCleanup(S); });
288
289 if (CachedJobs.load() < Jobs.size()) {
290 if (Error E = Distributor())
291 return E;
292 }
293 return Error::success();
294}
295
296// Adds compiled object files to the link for each non-cached job.
297Error lto::DTLTO::addObjectFilesToLink() {
298 TimeTraceScope FilesScope("Add DTLTO files to the link");
299 for (auto &Job : Jobs) {
300 if (!Job.CacheKey.empty() && Job.Cached) {
301 assert(Cache.isValid());
302 continue;
303 }
304 // Load the native object from a file into a memory buffer
305 // and store its contents in the output buffer.
306 auto ObjFileMbOrErr =
308 /*RequiresNullTerminator=*/false);
309 if (std::error_code EC = ObjFileMbOrErr.getError())
311 BCError + "cannot open native object file: " + Job.NativeObjectPath +
312 ": " + EC.message(),
314
315 MemoryBufferRef ObjFileMbRef = ObjFileMbOrErr->get()->getMemBufferRef();
316 if (Cache.isValid()) {
317 // Cache hits are taken care of earlier. At this point, we could only
318 // have cache misses.
320 // Obtain a file stream for a storing a cache entry.
321 auto CachedFileStreamOrErr = Job.CacheAddStream(Job.Task, Job.ModuleID);
322 if (!CachedFileStreamOrErr)
323 return joinErrors(
324 CachedFileStreamOrErr.takeError(),
326 "Cannot get a cache file stream: %s",
328 // Store a file buffer into the cache stream.
329 auto &CacheStream = *(CachedFileStreamOrErr->get());
330 *(CacheStream.OS) << ObjFileMbRef.getBuffer();
331 if (Error Err = CacheStream.commit())
332 return Err;
333 } else {
334 if (AddBuffer) {
335 AddBuffer(Job.Task, Job.ModuleID, std::move(ObjFileMbOrErr.get()));
336 } else {
337 auto StreamOrErr = AddStreamFunc(Job.Task, Job.ModuleID);
338 if (Error Err = StreamOrErr.takeError())
339 return Err;
340 auto &Stream = *StreamOrErr->get();
341 *Stream.OS << ObjFileMbRef.getBuffer();
342 if (Error Err = Stream.commit())
343 return Err;
344 }
345 }
346 }
347 return Error::success();
348}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
static GCRegistry::Add< ShadowStackGC > C("shadow-stack", "Very portable GC for uncooperative code generators")
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
#define LLVM_ABI
Definition Compiler.h:215
const AbstractManglingParser< Derived, Alloc >::OperatorInfo AbstractManglingParser< Derived, Alloc >::Ops[]
#define I(x, y, z)
Definition MD5.cpp:57
if(PassOpts->AAPipeline)
Provides a library for accessing information about this process and other processes on the operating ...
This file contains some templates that are useful if you are working with the STL at all.
This file defines the scope_exit class, which executes user-defined cleanup logic at scope exit.
This file contains some functions that are useful when dealing with strings.
Lightweight error class with error context and mandatory checking.
Definition Error.h:159
static ErrorSuccess success()
Create a success value.
Definition Error.h:336
StringRef getBuffer() const
static ErrorOr< std::unique_ptr< MemoryBuffer > > getFile(const Twine &Filename, bool IsText=false, bool RequiresNullTerminator=true, bool IsVolatile=false, std::optional< Align > Alignment=std::nullopt)
Open the specified file as a MemoryBuffer, returning a new MemoryBuffer if successful,...
constexpr const char * data() const
Get a pointer to the start of the string (which may not be null terminated).
Definition StringRef.h:138
The TimeTraceScope is a helper class to call the begin and end functions of the time trace profiler.
virtual Error run(AddStreamFn AddStream, FileCache Cache={}) override
Runs the DTLTO pipeline.
Definition DTLTO.cpp:108
void waitForCleanup() override
Wait for LTO cleanup.
Definition DTLTO.cpp:74
Config & getConfig()
Definition DTLTO.h:123
struct llvm::lto::LTO::RegularLTOState RegularLTO
virtual void cleanup()
Definition LTO.cpp:709
Config Conf
Definition LTO.h:466
struct llvm::lto::LTO::ThinLTOState ThinLTO
unsigned getMaxTasks() const
Returns an upper bound on the number of tasks that the client may expect.
Definition LTO.cpp:1270
virtual Error run(AddStreamFn AddStream, FileCache Cache={})
Runs the LTO pipeline.
Definition LTO.cpp:1321
static LLVM_ABI Pid getProcessId()
Get the process's identifier.
LLVM_ABI StringRef stem(StringRef path LLVM_LIFETIME_BOUND, Style style=Style::native)
Get stem.
Definition Path.cpp:596
LLVM_ABI StringRef parent_path(StringRef path LLVM_LIFETIME_BOUND, Style style=Style::native)
Get parent path.
Definition Path.cpp:478
LLVM_ABI void append(SmallVectorImpl< char > &path, const Twine &a, const Twine &b="", const Twine &c="", const Twine &d="")
Append to path.
Definition Path.cpp:467
This is an optimization pass for GlobalISel generic memory operations.
ThreadPoolStrategy hardware_concurrency(unsigned ThreadCount=0)
Returns a default thread strategy where all available hardware resources are to be used,...
Definition Threading.h:190
auto enumerate(FirstRange &&First, RestRanges &&...Rest)
Given two or more input ranges, returns a new range whose values are tuples (A, B,...
Definition STLExtras.h:2554
LLVM_ABI std::error_code inconvertibleErrorCode()
The value returned by this function can be returned from convertToErrorCode for Error values where no...
Definition Error.cpp:94
Error createStringError(std::error_code EC, char const *Fmt, const Ts &... Vals)
Create formatted StringError object.
Definition Error.h:1321
LLVM_ABI void timeTraceProfilerFinishThread()
Finish a time trace profiler running on a worker thread.
Error joinErrors(Error E1, Error E2)
Concatenate errors.
Definition Error.h:442
Error make_error(ArgTs &&... Args)
Make a Error instance representing failure using the given error info type.
Definition Error.h:340
LLVM_ABI raw_fd_ostream & errs()
This returns a reference to a raw_ostream for standard error.
@ Mod
The access may modify the value stored in memory.
Definition ModRef.h:34
std::string join(IteratorT Begin, IteratorT End, StringRef Separator)
Joins the strings in the range [Begin, End), adding Separator between the elements.
SingleThreadExecutor DefaultThreadPool
Definition ThreadPool.h:262
std::function< Expected< std::unique_ptr< CachedFileStream > >( unsigned Task, const Twine &ModuleName)> AddStreamFn
This type defines the callback to add a file that is generated on the fly.
Definition Caching.h:58
std::string itostr(int64_t X)
This type represents a file cache system that manages caching of files.
Definition Caching.h:84
LTO configuration.
Definition Config.h:43
std::function< std::string &(size_t Task)> GetCacheKeyOutputString
Called by WriteIndexesThinBackend when it needs to store a bitcode module's cache key.
Definition Config.h:312
std::function< std::vector< std::string > &(size_t Task)> GetImportsListOutputArray
Called by WriteIndexesThinBackend when it needs to store a bitcode module's imports list.
Definition Config.h:307
std::function< std::unique_ptr< raw_pwrite_stream >(size_t Task)> GetSummaryIndexOutputStream
Called by WriteIndexesThinBackend when it needs to write a bitcode module's summary index.
Definition Config.h:301
AddStreamFn CacheAddStream
Definition DTLTO.h:164
StringRef NativeObjectPath
Definition DTLTO.h:154
StringRef ModuleID
Definition DTLTO.h:152
std::string CacheKey
Definition DTLTO.h:162