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 TimeTraceScope TimeScope("Check cache for DTLTO", J.SummaryIndexPath);
108
109 auto CacheAddStreamExp = Cache(J.Task, J.CacheKey, J.ModuleID);
110 if (Error Err = CacheAddStreamExp.takeError())
111 return Err;
112 AddStreamFn &CacheAddStream = *CacheAddStreamExp;
113 // If CacheAddStream is null, we have a cache hit and at this point
114 // object file is already passed back to the linker.
115 if (!CacheAddStream) {
116 J.Cached = true; // Cache hit, mark the job as cached.
117 CachedJobs.fetch_add(1);
118 } else {
119 // If CacheAddStream is not null, we have a cache miss and we need to
120 // run the backend for codegen. Save cache 'add stream'
121 // function for a later use.
122 J.CacheAddStream = std::move(CacheAddStream);
123 }
124 return Error::success();
125}
126
127// Prepares a single DTLTO backend compilation job for a ThinLTO module.
128Error lto::DTLTO::prepareDtltoJob(StringRef ModulePath, unsigned Task) {
129 assert(Task >= ThinLTOTaskOffset && Task - ThinLTOTaskOffset < Jobs.size() &&
130 "Task index out of range for Jobs");
131 assert(Task < SummaryIndexFiles.size() && "Task index out of range");
132
133 SString ObjFilePath =
134 sys::path::parent_path(DistributorParams.LinkerOutputFile);
135 sys::path::append(ObjFilePath, sys::path::stem(ModulePath) + "." +
136 itostr(Task) + "." + UID + ".native.o");
137
138 SString SummaryIndexPathStr = ObjFilePath;
139 SummaryIndexPathStr += ".thinlto.bc";
140 SString ImportsPathStr = ModulePath;
141 ImportsPathStr += ".imports";
142
143 Job &J = Jobs[Task - ThinLTOTaskOffset];
144 J = {Task,
145 ModulePath,
146 Saver.save(ObjFilePath.str()),
147 Saver.save(SummaryIndexPathStr.str()),
148 Saver.save(ImportsPathStr.str()),
149 ImportsFilesList[Task],
150 CacheKeysList[Task],
151 nullptr,
152 false};
153
154 if (Error Err = checkCacheHit(J))
155 return Err;
156 if (!J.Cached) {
157 TimeTraceScope JobScope("Emit individual index for DTLTO",
158 J.SummaryIndexPath);
159 if (Error Err = save(SummaryIndexFiles[Task], J.SummaryIndexPath))
160 return Err;
161 }
162 if (OnIndexWriteCb)
163 OnIndexWriteCb(J.SummaryIndexPath.str());
164
165 if (ShouldEmitImportFiles)
166 if (Error Err = save(join(J.ImportsFilesList, "\n"), J.ImportsPath))
167 return Err;
168
169 if (!SaveTemps) {
170 if (!J.Cached)
171 addToCleanup(J.NativeObjectPath.str());
172 if (!ShouldEmitIndexFiles)
173 addToCleanup(J.SummaryIndexPath.str());
174 if (!ShouldEmitImportFiles)
175 addToCleanup(J.ImportsPath.str());
176 }
177 return Error::success();
178}
179
180// Derive a set of Clang options that will be shared/common for all DTLTO
181// backend compilations.
182void lto::DTLTO::buildCommonRemoteCompilerOptions() {
183 const lto::Config &C = getConfig();
184 auto &Ops = DistributorParams.CodegenOptions;
185
186 Ops.push_back(Saver.save("-O" + Twine(C.OptLevel)));
187
188 if (C.Options.EmitAddrsig)
189 Ops.push_back("-faddrsig");
190 if (C.Options.FunctionSections)
191 Ops.push_back("-ffunction-sections");
192 if (C.Options.DataSections)
193 Ops.push_back("-fdata-sections");
194
195 if (C.RelocModel == Reloc::PIC_)
196 // Clang doesn't have -fpic for all triples.
197 if (!DistributorParams.TargetTriple.isOSBinFormatCOFF())
198 Ops.push_back("-fpic");
199
200 // Turn on/off warnings about profile cfg mismatch (default on)
201 // --lto-pgo-warn-mismatch.
202 if (!C.PGOWarnMismatch) {
203 Ops.push_back("-mllvm");
204 Ops.push_back("-no-pgo-warn-mismatch");
205 }
206
207 // Enable sample-based profile guided optimizations.
208 // Sample profile file path --lto-sample-profile=<value>.
209 if (!C.SampleProfile.empty()) {
210 Ops.push_back(Saver.save("-fprofile-sample-use=" + Twine(C.SampleProfile)));
211 DistributorParams.CommonInputs.insert(C.SampleProfile);
212 }
213
214 // We don't know which of options will be used by Clang.
215 Ops.push_back("-Wno-unused-command-line-argument");
216
217 // Forward any supplied options.
218 if (!DistributorParams.RemoteCompilerArgs.empty())
219 for (auto &a : DistributorParams.RemoteCompilerArgs)
220 Ops.push_back(a);
221}
222
223// Initializes DTLTO state and prepares a job for each ThinLTO module.
224Error lto::DTLTO::prepareDtltoJobs() {
225 auto &ModuleMap =
226 ThinLTO.ModulesToCompile ? *ThinLTO.ModulesToCompile : ThinLTO.ModuleMap;
227
228 if (ModuleMap.empty())
229 return Error::success();
230
231 Jobs.resize(ModuleMap.size());
232
233 for (auto [I, Mod] : enumerate(ModuleMap))
234 if (Error E = prepareDtltoJob(Mod.first, ThinLTOTaskOffset + I))
235 return E;
236
237 return Error::success();
238}
239
240// Runs the DTLTO code generation phase. Must be invoked after thinLink().
241Error lto::DTLTO::performCodegen() {
242 if (Jobs.empty())
243 return Error::success();
244 // Build common remote compiler options.
245 buildCommonRemoteCompilerOptions();
246
247 DistributionDriver Distributor(DistributorParams, Jobs, SaveTemps,
248 [&](StringRef S) { addToCleanup(S); });
249
250 if (CachedJobs.load() < Jobs.size()) {
251 if (Error E = Distributor())
252 return E;
253 }
254 return Error::success();
255}
256
257// Adds compiled object files to the link for each non-cached job.
258Error lto::DTLTO::addObjectFilesToLink() {
259 TimeTraceScope FilesScope("Add DTLTO files to the link");
260 for (auto &Job : Jobs) {
261 if (!Job.CacheKey.empty() && Job.Cached) {
262 assert(Cache.isValid());
263 continue;
264 }
265 // Load the native object from a file into a memory buffer
266 // and store its contents in the output buffer.
267 auto ObjFileMbOrErr =
268 MemoryBuffer::getFile(Job.NativeObjectPath, /*IsText=*/false,
269 /*RequiresNullTerminator=*/false);
270 if (std::error_code EC = ObjFileMbOrErr.getError())
272 BCError + "cannot open native object file: " + Job.NativeObjectPath +
273 ": " + EC.message(),
275
276 MemoryBufferRef ObjFileMbRef = ObjFileMbOrErr->get()->getMemBufferRef();
277 if (Cache.isValid()) {
278 // Cache hits are taken care of earlier. At this point, we could only
279 // have cache misses.
280 assert(Job.CacheAddStream);
281 // Obtain a file stream for a storing a cache entry.
282 auto CachedFileStreamOrErr = Job.CacheAddStream(Job.Task, Job.ModuleID);
283 if (!CachedFileStreamOrErr)
284 return joinErrors(
285 CachedFileStreamOrErr.takeError(),
287 "Cannot get a cache file stream: %s",
288 Job.NativeObjectPath.data()));
289 // Store a file buffer into the cache stream.
290 auto &CacheStream = *(CachedFileStreamOrErr->get());
291 *(CacheStream.OS) << ObjFileMbRef.getBuffer();
292 if (Error Err = CacheStream.commit())
293 return Err;
294 } else {
295 if (AddBuffer) {
296 AddBuffer(Job.Task, Job.ModuleID, std::move(ObjFileMbOrErr.get()));
297 } else {
298 auto StreamOrErr = AddStreamFunc(Job.Task, Job.ModuleID);
299 if (Error Err = StreamOrErr.takeError())
300 return Err;
301 auto &Stream = *StreamOrErr->get();
302 *Stream.OS << ObjFileMbRef.getBuffer();
303 if (Error Err = Stream.commit())
304 return Err;
305 }
306 }
307 }
308 return Error::success();
309}
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,...
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:77
struct llvm::lto::LTO::RegularLTOState RegularLTO
Config Conf
Definition LTO.h:457
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