1//===- LTO.cpp ------------------------------------------------------------===//
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#include "LTO.h"
10#include "Config.h"
11#include "Driver.h"
12#include "InputFiles.h"
13#include "Symbols.h"
14#include "Target.h"
15
16#include "lld/Common/CommonLinkerContext.h"
17#include "lld/Common/Filesystem.h"
18#include "lld/Common/Strings.h"
19#include "lld/Common/TargetOptionsCommandFlags.h"
20#include "llvm/Bitcode/BitcodeWriter.h"
21#include "llvm/LTO/Config.h"
22#include "llvm/LTO/LTO.h"
23#include "llvm/Support/Caching.h"
24#include "llvm/Support/FileSystem.h"
25#include "llvm/Support/Path.h"
26#include "llvm/Support/raw_ostream.h"
27
28using namespace lld;
29using namespace lld::macho;
30using namespace llvm;
31using namespace llvm::MachO;
32using namespace llvm::sys;
33
34static std::string getThinLTOOutputFile(StringRef modulePath) {
35 return lto::getThinLTOOutputFile(Path: modulePath, OldPrefix: config->thinLTOPrefixReplaceOld,
36 NewPrefix: config->thinLTOPrefixReplaceNew);
37}
38
39static lto::Config createConfig() {
40 lto::Config c;
41 c.Options = initTargetOptionsFromCodeGenFlags();
42 c.Options.EmitAddrsig = config->icfLevel == ICFLevel::safe ||
43 config->icfLevel == ICFLevel::safe_thunks;
44 for (StringRef C : config->mllvmOpts)
45 c.MllvmArgs.emplace_back(args: C.str());
46 for (StringRef pluginFn : config->passPlugins)
47 c.PassPluginFilenames.push_back(x: std::string(pluginFn));
48 c.OptPipeline = std::string(config->ltoNewPmPasses);
49 c.CodeModel = getCodeModelFromCMModel();
50 c.CPU = getCPUStr();
51 c.MAttrs = getMAttrs();
52 c.DiagHandler = diagnosticHandler;
53
54 c.AlwaysEmitRegularLTOObj = !config->ltoObjPath.empty();
55
56 c.TimeTraceEnabled = config->timeTraceEnabled;
57 c.TimeTraceGranularity = config->timeTraceGranularity;
58 c.DebugPassManager = config->ltoDebugPassManager;
59 c.CSIRProfile = std::string(config->csProfilePath);
60 c.RunCSIRInstr = config->csProfileGenerate;
61 c.PGOWarnMismatch = config->pgoWarnMismatch;
62 c.DisableVerify = config->disableVerify;
63 c.OptLevel = config->ltoo;
64 c.CGOptLevel = config->ltoCgo;
65
66 c.PTO.LoopVectorization = c.OptLevel > 1;
67 c.PTO.SLPVectorization = c.OptLevel > 1;
68
69 if (config->saveTemps)
70 checkError(e: c.addSaveTemps(OutputFileName: config->outputFile.str() + ".",
71 /*UseInputModulePath=*/true));
72
73 if (config->emitLLVM) {
74 llvm::StringRef outputFile = config->outputFile;
75 c.PreCodeGenModuleHook = [outputFile](size_t task, const Module &m) {
76 if (std::unique_ptr<raw_fd_ostream> os = openLTOOutputFile(file: outputFile))
77 WriteBitcodeToFile(M: m, Out&: *os, ShouldPreserveUseListOrder: false);
78 return false;
79 };
80 }
81
82 return c;
83}
84
85// If `originalPath` exists, hardlinks `path` to `originalPath`. If that fails,
86// or `originalPath` is not set, saves `buffer` to `path`.
87static void saveOrHardlinkBuffer(StringRef buffer, const Twine &path,
88 std::optional<StringRef> originalPath) {
89 if (originalPath) {
90 // Delete the hardlink if it exists. Otherwise, it is possible for the
91 // create_hard_link to fail (as the hardlink exists already), and when
92 // saveBuffer is subsequently called the hardlink'd file may get truncated
93 // and reading from it causes a crash.
94 fs::remove(path);
95 auto err = fs::create_hard_link(to: *originalPath, from: path);
96 if (!err)
97 return;
98 }
99 saveBuffer(buffer, path);
100}
101
102BitcodeCompiler::BitcodeCompiler() {
103 // Initialize indexFile.
104 if (!config->thinLTOIndexOnlyArg.empty())
105 indexFile = openFile(file: config->thinLTOIndexOnlyArg);
106
107 // Initialize ltoObj.
108 lto::ThinBackend backend;
109 auto onIndexWrite = [&](StringRef S) { thinIndices.erase(V: S); };
110 if (config->thinLTOIndexOnly) {
111 backend = lto::createWriteIndexesThinBackend(
112 Parallelism: llvm::hardware_concurrency(Num: config->thinLTOJobs),
113 OldPrefix: std::string(config->thinLTOPrefixReplaceOld),
114 NewPrefix: std::string(config->thinLTOPrefixReplaceNew),
115 NativeObjectPrefix: std::string(config->thinLTOPrefixReplaceNativeObject),
116 ShouldEmitImportsFiles: config->thinLTOEmitImportsFiles, LinkedObjectsFile: indexFile.get(), OnWrite: onIndexWrite);
117 } else {
118 backend = lto::createInProcessThinBackend(
119 Parallelism: llvm::heavyweight_hardware_concurrency(Num: config->thinLTOJobs),
120 OnWrite: onIndexWrite, ShouldEmitIndexFiles: config->thinLTOEmitIndexFiles,
121 ShouldEmitImportsFiles: config->thinLTOEmitImportsFiles);
122 }
123
124 ltoObj = std::make_unique<lto::LTO>(args: createConfig(), args&: backend);
125}
126
127void BitcodeCompiler::add(BitcodeFile &f) {
128 lto::InputFile &obj = *f.obj;
129
130 if (config->thinLTOEmitIndexFiles)
131 thinIndices.insert(V: obj.getName());
132
133 ArrayRef<lto::InputFile::Symbol> objSyms = obj.symbols();
134 std::vector<lto::SymbolResolution> resols;
135 resols.reserve(n: objSyms.size());
136
137 // Provide a resolution to the LTO API for each symbol.
138 bool exportDynamic =
139 config->outputType != MH_EXECUTE || config->exportDynamic;
140 auto symIt = f.symbols.begin();
141 for (const lto::InputFile::Symbol &objSym : objSyms) {
142 resols.emplace_back();
143 lto::SymbolResolution &r = resols.back();
144 Symbol *sym = *symIt++;
145
146 // Ideally we shouldn't check for SF_Undefined but currently IRObjectFile
147 // reports two symbols for module ASM defined. Without this check, lld
148 // flags an undefined in IR with a definition in ASM as prevailing.
149 // Once IRObjectFile is fixed to report only one symbol this hack can
150 // be removed.
151 r.Prevailing = !objSym.isUndefined() && sym->getFile() == &f;
152
153 if (const auto *defined = dyn_cast<Defined>(Val: sym)) {
154 r.ExportDynamic =
155 defined->isExternal() && !defined->privateExtern && exportDynamic;
156 r.FinalDefinitionInLinkageUnit =
157 !defined->isExternalWeakDef() && !defined->interposable;
158 } else if (const auto *common = dyn_cast<CommonSymbol>(Val: sym)) {
159 r.ExportDynamic = !common->privateExtern && exportDynamic;
160 r.FinalDefinitionInLinkageUnit = true;
161 }
162
163 r.VisibleToRegularObj =
164 sym->isUsedInRegularObj || (r.Prevailing && r.ExportDynamic);
165
166 // Un-define the symbol so that we don't get duplicate symbol errors when we
167 // load the ObjFile emitted by LTO compilation.
168 if (r.Prevailing)
169 replaceSymbol<Undefined>(s: sym, arg: sym->getName(), arg: sym->getFile(),
170 arg: RefState::Strong, /*wasBitcodeSymbol=*/arg: true);
171
172 // TODO: set the other resolution configs properly
173 }
174 checkError(e: ltoObj->add(Obj: std::move(f.obj), Res: resols));
175 hasFiles = true;
176}
177
178// If LazyObjFile has not been added to link, emit empty index files.
179// This is needed because this is what GNU gold plugin does and we have a
180// distributed build system that depends on that behavior.
181static void thinLTOCreateEmptyIndexFiles() {
182 DenseSet<StringRef> linkedBitCodeFiles;
183 for (InputFile *file : inputFiles)
184 if (auto *f = dyn_cast<BitcodeFile>(Val: file))
185 if (!f->lazy)
186 linkedBitCodeFiles.insert(V: f->getName());
187
188 for (InputFile *file : inputFiles) {
189 if (auto *f = dyn_cast<BitcodeFile>(Val: file)) {
190 if (!f->lazy)
191 continue;
192 if (linkedBitCodeFiles.contains(V: f->getName()))
193 continue;
194 std::string path =
195 replaceThinLTOSuffix(path: getThinLTOOutputFile(modulePath: f->obj->getName()));
196 std::unique_ptr<raw_fd_ostream> os = openFile(file: path + ".thinlto.bc");
197 if (!os)
198 continue;
199
200 ModuleSummaryIndex m(/*HaveGVs=*/false);
201 m.setSkipModuleByDistributedBackend();
202 writeIndexToFile(Index: m, Out&: *os);
203 if (config->thinLTOEmitImportsFiles)
204 openFile(file: path + ".imports");
205 }
206 }
207}
208
209// Merge all the bitcode files we have seen, codegen the result
210// and return the resulting ObjectFile(s).
211std::vector<ObjFile *> BitcodeCompiler::compile() {
212 unsigned maxTasks = ltoObj->getMaxTasks();
213 buf.resize(new_size: maxTasks);
214 files.resize(new_size: maxTasks);
215
216 // The -cache_path_lto option specifies the path to a directory in which
217 // to cache native object files for ThinLTO incremental builds. If a path was
218 // specified, configure LTO to use it as the cache directory.
219 FileCache cache;
220 if (!config->thinLTOCacheDir.empty())
221 cache = check(e: localCache(CacheNameRef: "ThinLTO", TempFilePrefixRef: "Thin", CacheDirectoryPathRef: config->thinLTOCacheDir,
222 AddBuffer: [&](size_t task, const Twine &moduleName,
223 std::unique_ptr<MemoryBuffer> mb) {
224 files[task] = std::move(mb);
225 }));
226
227 if (hasFiles)
228 checkError(e: ltoObj->run(
229 AddStream: [&](size_t task, const Twine &moduleName) {
230 return std::make_unique<CachedFileStream>(
231 args: std::make_unique<raw_svector_ostream>(args&: buf[task]));
232 },
233 Cache: cache));
234
235 // Emit empty index files for non-indexed files
236 for (StringRef s : thinIndices) {
237 std::string path = getThinLTOOutputFile(modulePath: s);
238 openFile(file: path + ".thinlto.bc");
239 if (config->thinLTOEmitImportsFiles)
240 openFile(file: path + ".imports");
241 }
242
243 if (config->thinLTOEmitIndexFiles)
244 thinLTOCreateEmptyIndexFiles();
245
246 // In ThinLTO mode, Clang passes a temporary directory in -object_path_lto,
247 // while the argument is a single file in FullLTO mode.
248 bool objPathIsDir = true;
249 if (!config->ltoObjPath.empty()) {
250 if (std::error_code ec = fs::create_directories(path: config->ltoObjPath))
251 fatal(msg: "cannot create LTO object path " + config->ltoObjPath + ": " +
252 ec.message());
253
254 if (!fs::is_directory(Path: config->ltoObjPath)) {
255 objPathIsDir = false;
256 unsigned objCount =
257 count_if(Range&: buf, P: [](const SmallString<0> &b) { return !b.empty(); });
258 if (objCount > 1)
259 fatal(msg: "-object_path_lto must specify a directory when using ThinLTO");
260 }
261 }
262
263 auto outputFilePath = [objPathIsDir](int i) {
264 SmallString<261> filePath("/tmp/lto.tmp");
265 if (!config->ltoObjPath.empty()) {
266 filePath = config->ltoObjPath;
267 if (objPathIsDir)
268 path::append(path&: filePath, a: Twine(i) + "." +
269 getArchitectureName(Arch: config->arch()) +
270 ".lto.o");
271 }
272 return filePath;
273 };
274
275 // ThinLTO with index only option is required to generate only the index
276 // files. After that, we exit from linker and ThinLTO backend runs in a
277 // distributed environment.
278 if (config->thinLTOIndexOnly) {
279 if (!config->ltoObjPath.empty())
280 saveBuffer(buffer: buf[0], path: outputFilePath(0));
281 if (indexFile)
282 indexFile->close();
283 return {};
284 }
285
286 if (!config->thinLTOCacheDir.empty())
287 check(
288 e: pruneCache(Path: config->thinLTOCacheDir, Policy: config->thinLTOCachePolicy, Files: files));
289
290 std::vector<ObjFile *> ret;
291 for (unsigned i = 0; i < maxTasks; ++i) {
292 // Get the native object contents either from the cache or from memory. Do
293 // not use the cached MemoryBuffer directly to ensure dsymutil does not
294 // race with the cache pruner.
295 StringRef objBuf;
296 std::optional<StringRef> cachePath;
297 if (files[i]) {
298 objBuf = files[i]->getBuffer();
299 cachePath = files[i]->getBufferIdentifier();
300 } else {
301 objBuf = buf[i];
302 }
303 if (objBuf.empty())
304 continue;
305
306 // FIXME: should `saveTemps` and `ltoObjPath` use the same file name?
307 if (config->saveTemps)
308 saveBuffer(buffer: objBuf,
309 path: config->outputFile + ((i == 0) ? "" : Twine(i)) + ".lto.o");
310
311 auto filePath = outputFilePath(i);
312 uint32_t modTime = 0;
313 if (!config->ltoObjPath.empty()) {
314 saveOrHardlinkBuffer(buffer: objBuf, path: filePath, originalPath: cachePath);
315 modTime = getModTime(path: filePath);
316 }
317 ret.push_back(x: make<ObjFile>(
318 args: MemoryBufferRef(objBuf, saver().save(S: filePath.str())), args&: modTime,
319 /*archiveName=*/args: "", /*lazy=*/args: false,
320 /*forceHidden=*/args: false, /*compatArch=*/args: true, /*builtFromBitcode=*/args: true));
321 }
322
323 return ret;
324}
325