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 "InputFiles.h" |
12 | #include "Symbols.h" |
13 | #include "lld/Common/Args.h" |
14 | #include "lld/Common/ErrorHandler.h" |
15 | #include "lld/Common/Strings.h" |
16 | #include "lld/Common/TargetOptionsCommandFlags.h" |
17 | #include "llvm/ADT/STLExtras.h" |
18 | #include "llvm/ADT/SmallString.h" |
19 | #include "llvm/ADT/StringRef.h" |
20 | #include "llvm/ADT/Twine.h" |
21 | #include "llvm/IR/DiagnosticPrinter.h" |
22 | #include "llvm/LTO/Config.h" |
23 | #include "llvm/LTO/LTO.h" |
24 | #include "llvm/Object/SymbolicFile.h" |
25 | #include "llvm/Support/Caching.h" |
26 | #include "llvm/Support/CodeGen.h" |
27 | #include "llvm/Support/Error.h" |
28 | #include "llvm/Support/FileSystem.h" |
29 | #include "llvm/Support/MemoryBuffer.h" |
30 | #include "llvm/Support/raw_ostream.h" |
31 | #include <algorithm> |
32 | #include <cstddef> |
33 | #include <memory> |
34 | #include <string> |
35 | #include <system_error> |
36 | #include <vector> |
37 | |
38 | using namespace llvm; |
39 | |
40 | namespace lld::wasm { |
41 | static std::unique_ptr<lto::LTO> createLTO() { |
42 | lto::Config c; |
43 | c.Options = initTargetOptionsFromCodeGenFlags(); |
44 | |
45 | // Always emit a section per function/data with LTO. |
46 | c.Options.FunctionSections = true; |
47 | c.Options.DataSections = true; |
48 | |
49 | c.DisableVerify = config->disableVerify; |
50 | c.DiagHandler = diagnosticHandler; |
51 | c.OptLevel = config->ltoo; |
52 | c.MAttrs = getMAttrs(); |
53 | c.CGOptLevel = config->ltoCgo; |
54 | c.DebugPassManager = config->ltoDebugPassManager; |
55 | |
56 | if (config->relocatable) |
57 | c.RelocModel = std::nullopt; |
58 | else if (ctx.isPic) |
59 | c.RelocModel = Reloc::PIC_; |
60 | else |
61 | c.RelocModel = Reloc::Static; |
62 | |
63 | if (config->saveTemps) |
64 | checkError(e: c.addSaveTemps(OutputFileName: config->outputFile.str() + "." , |
65 | /*UseInputModulePath*/ true)); |
66 | lto::ThinBackend backend = lto::createInProcessThinBackend( |
67 | Parallelism: llvm::heavyweight_hardware_concurrency(Num: config->thinLTOJobs)); |
68 | return std::make_unique<lto::LTO>(args: std::move(c), args&: backend, |
69 | args&: config->ltoPartitions); |
70 | } |
71 | |
72 | BitcodeCompiler::BitcodeCompiler() : ltoObj(createLTO()) {} |
73 | |
74 | BitcodeCompiler::~BitcodeCompiler() = default; |
75 | |
76 | static void undefine(Symbol *s) { |
77 | if (auto f = dyn_cast<DefinedFunction>(Val: s)) |
78 | replaceSymbol<UndefinedFunction>(s: f, arg: f->getName(), arg: std::nullopt, |
79 | arg: std::nullopt, arg: 0, arg: f->getFile(), |
80 | arg&: f->signature); |
81 | else if (isa<DefinedData>(Val: s)) |
82 | replaceSymbol<UndefinedData>(s, arg: s->getName(), arg: 0, arg: s->getFile()); |
83 | else |
84 | llvm_unreachable("unexpected symbol kind" ); |
85 | } |
86 | |
87 | void BitcodeCompiler::add(BitcodeFile &f) { |
88 | lto::InputFile &obj = *f.obj; |
89 | unsigned symNum = 0; |
90 | ArrayRef<Symbol *> syms = f.getSymbols(); |
91 | std::vector<lto::SymbolResolution> resols(syms.size()); |
92 | |
93 | // Provide a resolution to the LTO API for each symbol. |
94 | for (const lto::InputFile::Symbol &objSym : obj.symbols()) { |
95 | Symbol *sym = syms[symNum]; |
96 | lto::SymbolResolution &r = resols[symNum]; |
97 | ++symNum; |
98 | |
99 | // Ideally we shouldn't check for SF_Undefined but currently IRObjectFile |
100 | // reports two symbols for module ASM defined. Without this check, lld |
101 | // flags an undefined in IR with a definition in ASM as prevailing. |
102 | // Once IRObjectFile is fixed to report only one symbol this hack can |
103 | // be removed. |
104 | r.Prevailing = !objSym.isUndefined() && sym->getFile() == &f; |
105 | r.VisibleToRegularObj = config->relocatable || sym->isUsedInRegularObj || |
106 | sym->isNoStrip() || |
107 | (r.Prevailing && sym->isExported()); |
108 | if (r.Prevailing) |
109 | undefine(s: sym); |
110 | |
111 | // We tell LTO to not apply interprocedural optimization for wrapped |
112 | // (with --wrap) symbols because otherwise LTO would inline them while |
113 | // their values are still not final. |
114 | r.LinkerRedefined = !sym->canInline; |
115 | } |
116 | checkError(e: ltoObj->add(Obj: std::move(f.obj), Res: resols)); |
117 | } |
118 | |
119 | // Merge all the bitcode files we have seen, codegen the result |
120 | // and return the resulting objects. |
121 | std::vector<StringRef> BitcodeCompiler::compile() { |
122 | unsigned maxTasks = ltoObj->getMaxTasks(); |
123 | buf.resize(new_size: maxTasks); |
124 | files.resize(new_size: maxTasks); |
125 | |
126 | // The --thinlto-cache-dir option specifies the path to a directory in which |
127 | // to cache native object files for ThinLTO incremental builds. If a path was |
128 | // specified, configure LTO to use it as the cache directory. |
129 | FileCache cache; |
130 | if (!config->thinLTOCacheDir.empty()) |
131 | cache = check(e: localCache(CacheNameRef: "ThinLTO" , TempFilePrefixRef: "Thin" , CacheDirectoryPathRef: config->thinLTOCacheDir, |
132 | AddBuffer: [&](size_t task, const Twine &moduleName, |
133 | std::unique_ptr<MemoryBuffer> mb) { |
134 | files[task] = std::move(mb); |
135 | })); |
136 | |
137 | checkError(e: ltoObj->run( |
138 | AddStream: [&](size_t task, const Twine &moduleName) { |
139 | return std::make_unique<CachedFileStream>( |
140 | args: std::make_unique<raw_svector_ostream>(args&: buf[task])); |
141 | }, |
142 | Cache: cache)); |
143 | |
144 | if (!config->thinLTOCacheDir.empty()) |
145 | pruneCache(Path: config->thinLTOCacheDir, Policy: config->thinLTOCachePolicy, Files: files); |
146 | |
147 | std::vector<StringRef> ret; |
148 | for (unsigned i = 0; i != maxTasks; ++i) { |
149 | if (buf[i].empty()) |
150 | continue; |
151 | if (config->saveTemps) { |
152 | if (i == 0) |
153 | saveBuffer(buffer: buf[i], path: config->outputFile + ".lto.o" ); |
154 | else |
155 | saveBuffer(buffer: buf[i], path: config->outputFile + Twine(i) + ".lto.o" ); |
156 | } |
157 | ret.emplace_back(args: buf[i].data(), args: buf[i].size()); |
158 | } |
159 | |
160 | for (std::unique_ptr<MemoryBuffer> &file : files) |
161 | if (file) |
162 | ret.push_back(x: file->getBuffer()); |
163 | |
164 | return ret; |
165 | } |
166 | |
167 | } // namespace lld::wasm |
168 | |