LLVM 23.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"
20#include "llvm/ADT/StringRef.h"
21#include "llvm/LTO/LTO.h"
24#include "llvm/Support/Path.h"
28
29#include <string>
30
31using namespace llvm;
32
33// Remove temporary files created to enable distribution.
34void lto::DTLTO::cleanup() {
35 if (!SaveTemps) {
36 // Remove one file, report error if any.
37 auto removeFile = [](StringRef FileName) -> void {
38 std::error_code EC = sys::fs::remove(FileName, true);
39 if (EC &&
40 EC != std::make_error_code(std::errc::no_such_file_or_directory))
41 errs() << "warning: could not remove the file '" << FileName
42 << "': " << EC.message() << "\n";
43 };
44
45 TimeTraceScope JobScope("Remove DTLTO temporary files");
46 for (const auto &Name : CleanupList)
47 removeFile(Name);
48 // Clean the CleanupList for safety.
49 CleanupList.clear();
50 }
51}
52
53// Runs the DTLTO thin link phase, producing per-module summary indices,
54// import lists, and cache keys for distribution.
55Error lto::DTLTO::performThinLink() {
56 size_t NumTasks = getMaxTasks();
57 SummaryIndexFiles.resize(NumTasks);
58 ImportsFilesList.resize(NumTasks);
59 CacheKeysList.resize(NumTasks);
60
61 lto::Config &Cfg = getConfig();
63 [&](size_t task) -> std::unique_ptr<raw_svector_ostream> {
64 return std::make_unique<raw_svector_ostream>(SummaryIndexFiles[task]);
65 };
66 Cfg.GetCacheKeyOutputString = [&](size_t task) -> std::string & {
67 return CacheKeysList[task];
68 };
70 [&](size_t task) -> std::vector<std::string> & {
71 return ImportsFilesList[task];
72 };
73 return Base::run(AddStreamFunc, {});
74}
75
76// Runs the DTLTO pipeline.
78 scope_exit CleanUp([this]() { cleanup(); });
79
80 AddStreamFunc = AddStream;
81 Cache = std::move(CacheParam);
82 Conf.Dtlto = 1;
84
85 if (Error Err = performThinLink())
86 return Err;
87
88 ThinLTOTaskOffset = RegularLTO.ParallelCodeGenParallelismLevel;
89 DistributorParams.TargetTriple = RegularLTO.CombinedModule->getTargetTriple();
90
91 if (Error Err = prepareDtltoJobs())
92 return Err;
93 if (Error Err = serializeLTOInputs())
94 return Err;
95 if (Error Err = performCodegen())
96 return Err;
97 if (Error Err = addObjectFilesToLink())
98 return Err;
99 return Error::success();
100}
101
102// Probes the LTO cache for a compiled native object for the given job.
103Error lto::DTLTO::checkCacheHit(Job &J) {
104 if (!Cache.isValid())
105 return Error::success();
106
107 auto CacheAddStreamExp = Cache(J.Task, J.CacheKey, J.ModuleID);
108 if (Error Err = CacheAddStreamExp.takeError())
109 return Err;
110 AddStreamFn &CacheAddStream = *CacheAddStreamExp;
111 // If CacheAddStream is null, we have a cache hit and at this point
112 // object file is already passed back to the linker.
113 if (!CacheAddStream) {
114 J.Cached = true; // Cache hit, mark the job as cached.
115 CachedJobs.fetch_add(1);
116 } else {
117 // If CacheAddStream is not null, we have a cache miss and we need to
118 // run the backend for codegen. Save cache 'add stream'
119 // function for a later use.
120 J.CacheAddStream = std::move(CacheAddStream);
121 }
122 return Error::success();
123}
124
125// Prepares a single DTLTO backend compilation job for a ThinLTO module.
126Error lto::DTLTO::prepareDtltoJob(StringRef ModulePath, unsigned Task) {
127 assert(Task >= ThinLTOTaskOffset && Task - ThinLTOTaskOffset < Jobs.size() &&
128 "Task index out of range for Jobs");
129 assert(Task < SummaryIndexFiles.size() && "Task index out of range");
130
131 SString ObjFilePath =
132 sys::path::parent_path(DistributorParams.LinkerOutputFile);
133 sys::path::append(ObjFilePath, sys::path::stem(ModulePath) + "." +
134 itostr(Task) + "." + UID + ".native.o");
135
136 SString SummaryIndexPathStr = ObjFilePath;
137 SummaryIndexPathStr += ".thinlto.bc";
138 SString ImportsPathStr = ModulePath;
139 ImportsPathStr += ".imports";
140
141 Job &J = Jobs[Task - ThinLTOTaskOffset];
142 J = {Task,
143 ModulePath,
144 Saver.save(ObjFilePath.str()),
145 Saver.save(SummaryIndexPathStr.str()),
146 Saver.save(ImportsPathStr.str()),
147 ImportsFilesList[Task],
148 CacheKeysList[Task],
149 nullptr,
150 false};
151
152 if (Error Err = checkCacheHit(J))
153 return Err;
154 if (!J.Cached) {
155 TimeTraceScope JobScope("Emit individual index for DTLTO",
156 J.SummaryIndexPath);
157 if (Error Err = save(SummaryIndexFiles[Task], J.SummaryIndexPath))
158 return Err;
159 }
160 if (OnIndexWriteCb)
161 OnIndexWriteCb(J.SummaryIndexPath.str());
162
163 if (ShouldEmitImportFiles)
164 if (Error Err = save(join(J.ImportsFilesList, "\n"), J.ImportsPath))
165 return Err;
166
167 if (!SaveTemps) {
168 if (!J.Cached)
169 addToCleanup(J.NativeObjectPath.str());
170 if (!ShouldEmitIndexFiles)
171 addToCleanup(J.SummaryIndexPath.str());
172 if (!ShouldEmitImportFiles)
173 addToCleanup(J.ImportsPath.str());
174 }
175 return Error::success();
176}
177
178// Derive a set of Clang options that will be shared/common for all DTLTO
179// backend compilations.
180void lto::DTLTO::buildCommonRemoteCompilerOptions() {
181 const lto::Config &C = getConfig();
182 auto &Ops = DistributorParams.CodegenOptions;
183
184 Ops.push_back(Saver.save("-O" + Twine(C.OptLevel)));
185
186 if (C.Options.EmitAddrsig)
187 Ops.push_back("-faddrsig");
188 if (C.Options.FunctionSections)
189 Ops.push_back("-ffunction-sections");
190 if (C.Options.DataSections)
191 Ops.push_back("-fdata-sections");
192
193 if (C.RelocModel == Reloc::PIC_)
194 // Clang doesn't have -fpic for all triples.
195 if (!DistributorParams.TargetTriple.isOSBinFormatCOFF())
196 Ops.push_back("-fpic");
197
198 // Turn on/off warnings about profile cfg mismatch (default on)
199 // --lto-pgo-warn-mismatch.
200 if (!C.PGOWarnMismatch) {
201 Ops.push_back("-mllvm");
202 Ops.push_back("-no-pgo-warn-mismatch");
203 }
204
205 // Enable sample-based profile guided optimizations.
206 // Sample profile file path --lto-sample-profile=<value>.
207 if (!C.SampleProfile.empty()) {
208 Ops.push_back(Saver.save("-fprofile-sample-use=" + Twine(C.SampleProfile)));
209 DistributorParams.CommonInputs.insert(C.SampleProfile);
210 }
211
212 // We don't know which of options will be used by Clang.
213 Ops.push_back("-Wno-unused-command-line-argument");
214
215 // Forward any supplied options.
216 if (!DistributorParams.RemoteCompilerArgs.empty())
217 for (auto &a : DistributorParams.RemoteCompilerArgs)
218 Ops.push_back(a);
219}
220
221// Initializes DTLTO state and prepares a job for each ThinLTO module.
222Error lto::DTLTO::prepareDtltoJobs() {
223 auto &ModuleMap =
224 ThinLTO.ModulesToCompile ? *ThinLTO.ModulesToCompile : ThinLTO.ModuleMap;
225
226 if (ModuleMap.empty())
227 return Error::success();
228
229 Jobs.resize(ModuleMap.size());
230
231 for (auto [I, Mod] : enumerate(ModuleMap))
232 if (Error E = prepareDtltoJob(Mod.first, ThinLTOTaskOffset + I))
233 return E;
234
235 return Error::success();
236}
237
238// Runs the DTLTO code generation phase. Must be invoked after thinLink().
239Error lto::DTLTO::performCodegen() {
240 if (Jobs.empty())
241 return Error::success();
242 // Build common remote compiler options.
243 buildCommonRemoteCompilerOptions();
244
245 DistributionDriver Distributor(DistributorParams, Jobs, SaveTemps,
246 [&](StringRef S) { addToCleanup(S); });
247
248 if (CachedJobs.load() < Jobs.size()) {
249 if (Error E = Distributor())
250 return E;
251 }
252 return Error::success();
253}
254
255// Adds compiled object files to the link for each non-cached job.
256Error lto::DTLTO::addObjectFilesToLink() {
257 TimeTraceScope FilesScope("Add DTLTO files to the link");
258 for (auto &Job : Jobs) {
259 if (!Job.CacheKey.empty() && Job.Cached) {
260 assert(Cache.isValid());
261 continue;
262 }
263 // Load the native object from a file into a memory buffer
264 // and store its contents in the output buffer.
265 auto ObjFileMbOrErr =
266 MemoryBuffer::getFile(Job.NativeObjectPath, /*IsText=*/false,
267 /*RequiresNullTerminator=*/false);
268 if (std::error_code EC = ObjFileMbOrErr.getError())
270 BCError + "cannot open native object file: " + Job.NativeObjectPath +
271 ": " + EC.message(),
273
274 MemoryBufferRef ObjFileMbRef = ObjFileMbOrErr->get()->getMemBufferRef();
275 if (Cache.isValid()) {
276 // Cache hits are taken care of earlier. At this point, we could only
277 // have cache misses.
278 assert(Job.CacheAddStream);
279 // Obtain a file stream for a storing a cache entry.
280 auto CachedFileStreamOrErr = Job.CacheAddStream(Job.Task, Job.ModuleID);
281 if (!CachedFileStreamOrErr)
282 return joinErrors(
283 CachedFileStreamOrErr.takeError(),
285 "Cannot get a cache file stream: %s",
286 Job.NativeObjectPath.data()));
287 // Store a file buffer into the cache stream.
288 auto &CacheStream = *(CachedFileStreamOrErr->get());
289 *(CacheStream.OS) << ObjFileMbRef.getBuffer();
290 if (Error Err = CacheStream.commit())
291 return Err;
292 } else {
293 if (AddBuffer) {
294 AddBuffer(Job.Task, Job.ModuleID, std::move(ObjFileMbOrErr.get()));
295 } else {
296 auto StreamOrErr = AddStreamFunc(Job.Task, Job.ModuleID);
297 if (Error Err = StreamOrErr.takeError())
298 return Err;
299 auto &Stream = *StreamOrErr->get();
300 *Stream.OS << ObjFileMbRef.getBuffer();
301 if (Error Err = Stream.commit())
302 return Err;
303 }
304 }
305 }
306 return Error::success();
307}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
static void cleanup(BlockFrequencyInfoImplBase &BFI)
Clear all memory not needed downstream.
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
#define LLVM_ABI
Definition Compiler.h:213
const AbstractManglingParser< Derived, Alloc >::OperatorInfo AbstractManglingParser< Derived, Alloc >::Ops[]
#define I(x, y, z)
Definition MD5.cpp:57
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 make_scope_exit function, which executes user-defined cleanup logic at scope ex...
This file defines the SmallString class.
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,...
virtual LLVM_ABI Error run(AddStreamFn AddStream, FileCache Cache={}) override
Runs the DTLTO pipeline.
Definition DTLTO.cpp:77
struct llvm::lto::LTO::RegularLTOState RegularLTO
Config Conf
Definition LTO.h:459
static LLVM_ABI Pid getProcessId()
Get the process's identifier.
@ C
The default llvm calling convention, compatible with C.
Definition CallingConv.h:34
LLVM_ABI std::error_code remove(const Twine &path, bool IgnoreNonExisting=true)
Remove path.
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.
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:2553
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
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.
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
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