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