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 InputModuleIDsToSerialize.insert(J.ModuleID);
158 for (StringRef ImportPath : J.ImportsFilesList)
159 InputModuleIDsToSerialize.insert(ImportPath);
160
161 TimeTraceScope JobScope("Emit individual index for DTLTO",
162 J.SummaryIndexPath);
163 if (Error Err = save(SummaryIndexFiles[Task], J.SummaryIndexPath))
164 return Err;
165 }
166 if (OnIndexWriteCb)
167 OnIndexWriteCb(J.SummaryIndexPath.str());
168
169 if (ShouldEmitImportFiles)
170 if (Error Err = save(join(J.ImportsFilesList, "\n"), J.ImportsPath))
171 return Err;
172
173 if (!SaveTemps) {
174 if (!J.Cached)
175 addToCleanup(J.NativeObjectPath.str());
176 if (!ShouldEmitIndexFiles)
177 addToCleanup(J.SummaryIndexPath.str());
178 if (!ShouldEmitImportFiles)
179 addToCleanup(J.ImportsPath.str());
180 }
181 return Error::success();
182}
183
184// Derive a set of Clang options that will be shared/common for all DTLTO
185// backend compilations.
186void lto::DTLTO::buildCommonRemoteCompilerOptions() {
187 const lto::Config &C = getConfig();
188 auto &Ops = DistributorParams.CodegenOptions;
189
190 Ops.push_back(Saver.save("-O" + Twine(C.OptLevel)));
191
192 if (C.Options.EmitAddrsig)
193 Ops.push_back("-faddrsig");
194 if (C.Options.FunctionSections)
195 Ops.push_back("-ffunction-sections");
196 if (C.Options.DataSections)
197 Ops.push_back("-fdata-sections");
198
199 if (C.RelocModel == Reloc::PIC_)
200 // Clang doesn't have -fpic for all triples.
201 if (!DistributorParams.TargetTriple.isOSBinFormatCOFF())
202 Ops.push_back("-fpic");
203
204 // Turn on/off warnings about profile cfg mismatch (default on)
205 // --lto-pgo-warn-mismatch.
206 if (!C.PGOWarnMismatch) {
207 Ops.push_back("-mllvm");
208 Ops.push_back("-no-pgo-warn-mismatch");
209 }
210
211 // Enable sample-based profile guided optimizations.
212 // Sample profile file path --lto-sample-profile=<value>.
213 if (!C.SampleProfile.empty()) {
214 Ops.push_back(Saver.save("-fprofile-sample-use=" + Twine(C.SampleProfile)));
215 DistributorParams.CommonInputs.insert(C.SampleProfile);
216 }
217
218 // We don't know which of options will be used by Clang.
219 Ops.push_back("-Wno-unused-command-line-argument");
220
221 // Forward any supplied options.
222 if (!DistributorParams.RemoteCompilerArgs.empty())
223 for (auto &a : DistributorParams.RemoteCompilerArgs)
224 Ops.push_back(a);
225}
226
227// Initializes DTLTO state and prepares a job for each ThinLTO module.
228Error lto::DTLTO::prepareDtltoJobs() {
229 auto &ModuleMap =
230 ThinLTO.ModulesToCompile ? *ThinLTO.ModulesToCompile : ThinLTO.ModuleMap;
231
232 InputModuleIDsToSerialize.clear();
233
234 if (ModuleMap.empty())
235 return Error::success();
236
237 Jobs.resize(ModuleMap.size());
238
239 for (auto [I, Mod] : enumerate(ModuleMap))
240 if (Error E = prepareDtltoJob(Mod.first, ThinLTOTaskOffset + I))
241 return E;
242
243 return Error::success();
244}
245
246// Runs the DTLTO code generation phase. Must be invoked after thinLink().
247Error lto::DTLTO::performCodegen() {
248 if (Jobs.empty())
249 return Error::success();
250 // Build common remote compiler options.
251 buildCommonRemoteCompilerOptions();
252
253 DistributionDriver Distributor(DistributorParams, Jobs, SaveTemps,
254 [&](StringRef S) { addToCleanup(S); });
255
256 if (CachedJobs.load() < Jobs.size()) {
257 if (Error E = Distributor())
258 return E;
259 }
260 return Error::success();
261}
262
263// Adds compiled object files to the link for each non-cached job.
264Error lto::DTLTO::addObjectFilesToLink() {
265 TimeTraceScope FilesScope("Add DTLTO files to the link");
266 for (auto &Job : Jobs) {
267 if (!Job.CacheKey.empty() && Job.Cached) {
268 assert(Cache.isValid());
269 continue;
270 }
271 // Load the native object from a file into a memory buffer
272 // and store its contents in the output buffer.
273 auto ObjFileMbOrErr =
274 MemoryBuffer::getFile(Job.NativeObjectPath, /*IsText=*/false,
275 /*RequiresNullTerminator=*/false);
276 if (std::error_code EC = ObjFileMbOrErr.getError())
278 BCError + "cannot open native object file: " + Job.NativeObjectPath +
279 ": " + EC.message(),
281
282 MemoryBufferRef ObjFileMbRef = ObjFileMbOrErr->get()->getMemBufferRef();
283 if (Cache.isValid()) {
284 // Cache hits are taken care of earlier. At this point, we could only
285 // have cache misses.
286 assert(Job.CacheAddStream);
287 // Obtain a file stream for a storing a cache entry.
288 auto CachedFileStreamOrErr = Job.CacheAddStream(Job.Task, Job.ModuleID);
289 if (!CachedFileStreamOrErr)
290 return joinErrors(
291 CachedFileStreamOrErr.takeError(),
293 "Cannot get a cache file stream: %s",
294 Job.NativeObjectPath.data()));
295 // Store a file buffer into the cache stream.
296 auto &CacheStream = *(CachedFileStreamOrErr->get());
297 *(CacheStream.OS) << ObjFileMbRef.getBuffer();
298 if (Error Err = CacheStream.commit())
299 return Err;
300 } else {
301 if (AddBuffer) {
302 AddBuffer(Job.Task, Job.ModuleID, std::move(ObjFileMbOrErr.get()));
303 } else {
304 auto StreamOrErr = AddStreamFunc(Job.Task, Job.ModuleID);
305 if (Error Err = StreamOrErr.takeError())
306 return Err;
307 auto &Stream = *StreamOrErr->get();
308 *Stream.OS << ObjFileMbRef.getBuffer();
309 if (Error Err = Stream.commit())
310 return Err;
311 }
312 }
313 }
314 return Error::success();
315}
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:215
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: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
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