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