1 | //===- llvm-lto: a simple command-line program to link modules with LTO ---===// |
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 | // This program takes in a list of bitcode files, links them, performs link-time |
10 | // optimization, and outputs an object file. |
11 | // |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #include "llvm-c/lto.h" |
15 | #include "llvm/ADT/ArrayRef.h" |
16 | #include "llvm/ADT/STLExtras.h" |
17 | #include "llvm/ADT/SmallString.h" |
18 | #include "llvm/ADT/StringExtras.h" |
19 | #include "llvm/ADT/StringRef.h" |
20 | #include "llvm/ADT/StringSet.h" |
21 | #include "llvm/ADT/Twine.h" |
22 | #include "llvm/Bitcode/BitcodeReader.h" |
23 | #include "llvm/Bitcode/BitcodeWriter.h" |
24 | #include "llvm/CodeGen/CommandFlags.h" |
25 | #include "llvm/IR/DiagnosticInfo.h" |
26 | #include "llvm/IR/DiagnosticPrinter.h" |
27 | #include "llvm/IR/LLVMContext.h" |
28 | #include "llvm/IR/Module.h" |
29 | #include "llvm/IR/ModuleSummaryIndex.h" |
30 | #include "llvm/IR/Verifier.h" |
31 | #include "llvm/IRReader/IRReader.h" |
32 | #include "llvm/LTO/legacy/LTOCodeGenerator.h" |
33 | #include "llvm/LTO/legacy/LTOModule.h" |
34 | #include "llvm/LTO/legacy/ThinLTOCodeGenerator.h" |
35 | #include "llvm/Support/Allocator.h" |
36 | #include "llvm/Support/Casting.h" |
37 | #include "llvm/Support/CommandLine.h" |
38 | #include "llvm/Support/Error.h" |
39 | #include "llvm/Support/ErrorHandling.h" |
40 | #include "llvm/Support/ErrorOr.h" |
41 | #include "llvm/Support/FileSystem.h" |
42 | #include "llvm/Support/InitLLVM.h" |
43 | #include "llvm/Support/MemoryBuffer.h" |
44 | #include "llvm/Support/Path.h" |
45 | #include "llvm/Support/SourceMgr.h" |
46 | #include "llvm/Support/TargetSelect.h" |
47 | #include "llvm/Support/ToolOutputFile.h" |
48 | #include "llvm/Support/raw_ostream.h" |
49 | #include "llvm/Support/WithColor.h" |
50 | #include "llvm/Target/TargetOptions.h" |
51 | #include <algorithm> |
52 | #include <cassert> |
53 | #include <cstdint> |
54 | #include <cstdlib> |
55 | #include <map> |
56 | #include <memory> |
57 | #include <string> |
58 | #include <system_error> |
59 | #include <tuple> |
60 | #include <utility> |
61 | #include <vector> |
62 | |
63 | using namespace llvm; |
64 | |
65 | static codegen::RegisterCodeGenFlags CGF; |
66 | |
67 | static cl::OptionCategory LTOCategory("LTO Options" ); |
68 | |
69 | static cl::opt<char> |
70 | OptLevel("O" , |
71 | cl::desc("Optimization level. [-O0, -O1, -O2, or -O3] " |
72 | "(default = '-O2')" ), |
73 | cl::Prefix, cl::init(Val: '2'), cl::cat(LTOCategory)); |
74 | |
75 | static cl::opt<bool> |
76 | IndexStats("thinlto-index-stats" , |
77 | cl::desc("Print statistic for the index in every input files" ), |
78 | cl::init(Val: false), cl::cat(LTOCategory)); |
79 | |
80 | static cl::opt<bool> DisableVerify( |
81 | "disable-verify" , cl::init(Val: false), |
82 | cl::desc("Do not run the verifier during the optimization pipeline" ), |
83 | cl::cat(LTOCategory)); |
84 | |
85 | static cl::opt<bool> EnableFreestanding( |
86 | "lto-freestanding" , cl::init(Val: false), |
87 | cl::desc("Enable Freestanding (disable builtins / TLI) during LTO" ), |
88 | cl::cat(LTOCategory)); |
89 | |
90 | static cl::opt<bool> UseDiagnosticHandler( |
91 | "use-diagnostic-handler" , cl::init(Val: false), |
92 | cl::desc("Use a diagnostic handler to test the handler interface" ), |
93 | cl::cat(LTOCategory)); |
94 | |
95 | static cl::opt<bool> |
96 | ThinLTO("thinlto" , cl::init(Val: false), |
97 | cl::desc("Only write combined global index for ThinLTO backends" ), |
98 | cl::cat(LTOCategory)); |
99 | |
100 | enum ThinLTOModes { |
101 | THINLINK, |
102 | THINDISTRIBUTE, |
103 | THINEMITIMPORTS, |
104 | THINPROMOTE, |
105 | THINIMPORT, |
106 | THININTERNALIZE, |
107 | THINOPT, |
108 | THINCODEGEN, |
109 | THINALL |
110 | }; |
111 | |
112 | cl::opt<ThinLTOModes> ThinLTOMode( |
113 | "thinlto-action" , cl::desc("Perform a single ThinLTO stage:" ), |
114 | cl::values( |
115 | clEnumValN( |
116 | THINLINK, "thinlink" , |
117 | "ThinLink: produces the index by linking only the summaries." ), |
118 | clEnumValN(THINDISTRIBUTE, "distributedindexes" , |
119 | "Produces individual indexes for distributed backends." ), |
120 | clEnumValN(THINEMITIMPORTS, "emitimports" , |
121 | "Emit imports files for distributed backends." ), |
122 | clEnumValN(THINPROMOTE, "promote" , |
123 | "Perform pre-import promotion (requires -thinlto-index)." ), |
124 | clEnumValN(THINIMPORT, "import" , |
125 | "Perform both promotion and " |
126 | "cross-module importing (requires " |
127 | "-thinlto-index)." ), |
128 | clEnumValN(THININTERNALIZE, "internalize" , |
129 | "Perform internalization driven by -exported-symbol " |
130 | "(requires -thinlto-index)." ), |
131 | clEnumValN(THINOPT, "optimize" , "Perform ThinLTO optimizations." ), |
132 | clEnumValN(THINCODEGEN, "codegen" , "CodeGen (expected to match llc)" ), |
133 | clEnumValN(THINALL, "run" , "Perform ThinLTO end-to-end" )), |
134 | cl::cat(LTOCategory)); |
135 | |
136 | static cl::opt<std::string> |
137 | ThinLTOIndex("thinlto-index" , |
138 | cl::desc("Provide the index produced by a ThinLink, required " |
139 | "to perform the promotion and/or importing." ), |
140 | cl::cat(LTOCategory)); |
141 | |
142 | static cl::opt<std::string> ThinLTOPrefixReplace( |
143 | "thinlto-prefix-replace" , |
144 | cl::desc("Control where files for distributed backends are " |
145 | "created. Expects 'oldprefix;newprefix' and if path " |
146 | "prefix of output file is oldprefix it will be " |
147 | "replaced with newprefix." ), |
148 | cl::cat(LTOCategory)); |
149 | |
150 | static cl::opt<std::string> ThinLTOModuleId( |
151 | "thinlto-module-id" , |
152 | cl::desc("For the module ID for the file to process, useful to " |
153 | "match what is in the index." ), |
154 | cl::cat(LTOCategory)); |
155 | |
156 | static cl::opt<std::string> ThinLTOCacheDir("thinlto-cache-dir" , |
157 | cl::desc("Enable ThinLTO caching." ), |
158 | cl::cat(LTOCategory)); |
159 | |
160 | static cl::opt<int> ThinLTOCachePruningInterval( |
161 | "thinlto-cache-pruning-interval" , cl::init(Val: 1200), |
162 | cl::desc("Set ThinLTO cache pruning interval." ), cl::cat(LTOCategory)); |
163 | |
164 | static cl::opt<uint64_t> ThinLTOCacheMaxSizeBytes( |
165 | "thinlto-cache-max-size-bytes" , |
166 | cl::desc("Set ThinLTO cache pruning directory maximum size in bytes." ), |
167 | cl::cat(LTOCategory)); |
168 | |
169 | static cl::opt<int> ThinLTOCacheMaxSizeFiles( |
170 | "thinlto-cache-max-size-files" , cl::init(Val: 1000000), |
171 | cl::desc("Set ThinLTO cache pruning directory maximum number of files." ), |
172 | cl::cat(LTOCategory)); |
173 | |
174 | static cl::opt<unsigned> ThinLTOCacheEntryExpiration( |
175 | "thinlto-cache-entry-expiration" , cl::init(Val: 604800) /* 1w */, |
176 | cl::desc("Set ThinLTO cache entry expiration time." ), cl::cat(LTOCategory)); |
177 | |
178 | static cl::opt<std::string> ThinLTOSaveTempsPrefix( |
179 | "thinlto-save-temps" , |
180 | cl::desc("Save ThinLTO temp files using filenames created by adding " |
181 | "suffixes to the given file path prefix." ), |
182 | cl::cat(LTOCategory)); |
183 | |
184 | static cl::opt<std::string> ThinLTOGeneratedObjectsDir( |
185 | "thinlto-save-objects" , |
186 | cl::desc("Save ThinLTO generated object files using filenames created in " |
187 | "the given directory." ), |
188 | cl::cat(LTOCategory)); |
189 | |
190 | static cl::opt<bool> SaveLinkedModuleFile( |
191 | "save-linked-module" , cl::init(Val: false), |
192 | cl::desc("Write linked LTO module to file before optimize" ), |
193 | cl::cat(LTOCategory)); |
194 | |
195 | static cl::opt<bool> |
196 | SaveModuleFile("save-merged-module" , cl::init(Val: false), |
197 | cl::desc("Write merged LTO module to file before CodeGen" ), |
198 | cl::cat(LTOCategory)); |
199 | |
200 | static cl::list<std::string> InputFilenames(cl::Positional, cl::OneOrMore, |
201 | cl::desc("<input bitcode files>" ), |
202 | cl::cat(LTOCategory)); |
203 | |
204 | static cl::opt<std::string> OutputFilename("o" , cl::init(Val: "" ), |
205 | cl::desc("Override output filename" ), |
206 | cl::value_desc("filename" ), |
207 | cl::cat(LTOCategory)); |
208 | |
209 | static cl::list<std::string> ExportedSymbols( |
210 | "exported-symbol" , |
211 | cl::desc("List of symbols to export from the resulting object file" ), |
212 | cl::cat(LTOCategory)); |
213 | |
214 | static cl::list<std::string> |
215 | DSOSymbols("dso-symbol" , |
216 | cl::desc("Symbol to put in the symtab in the resulting dso" ), |
217 | cl::cat(LTOCategory)); |
218 | |
219 | static cl::opt<bool> ListSymbolsOnly( |
220 | "list-symbols-only" , cl::init(Val: false), |
221 | cl::desc("Instead of running LTO, list the symbols in each IR file" ), |
222 | cl::cat(LTOCategory)); |
223 | |
224 | static cl::opt<bool> ListDependentLibrariesOnly( |
225 | "list-dependent-libraries-only" , cl::init(Val: false), |
226 | cl::desc( |
227 | "Instead of running LTO, list the dependent libraries in each IR file" ), |
228 | cl::cat(LTOCategory)); |
229 | |
230 | static cl::opt<bool> QueryHasCtorDtor( |
231 | "query-hasCtorDtor" , cl::init(Val: false), |
232 | cl::desc("Queries LTOModule::hasCtorDtor() on each IR file" )); |
233 | |
234 | static cl::opt<bool> |
235 | SetMergedModule("set-merged-module" , cl::init(Val: false), |
236 | cl::desc("Use the first input module as the merged module" ), |
237 | cl::cat(LTOCategory)); |
238 | |
239 | static cl::opt<unsigned> Parallelism("j" , cl::Prefix, cl::init(Val: 1), |
240 | cl::desc("Number of backend threads" ), |
241 | cl::cat(LTOCategory)); |
242 | |
243 | static cl::opt<bool> RestoreGlobalsLinkage( |
244 | "restore-linkage" , cl::init(Val: false), |
245 | cl::desc("Restore original linkage of globals prior to CodeGen" ), |
246 | cl::cat(LTOCategory)); |
247 | |
248 | static cl::opt<bool> CheckHasObjC( |
249 | "check-for-objc" , cl::init(Val: false), |
250 | cl::desc("Only check if the module has objective-C defined in it" ), |
251 | cl::cat(LTOCategory)); |
252 | |
253 | static cl::opt<bool> PrintMachOCPUOnly( |
254 | "print-macho-cpu-only" , cl::init(Val: false), |
255 | cl::desc("Instead of running LTO, print the mach-o cpu in each IR file" ), |
256 | cl::cat(LTOCategory)); |
257 | |
258 | static cl::opt<bool> |
259 | DebugPassManager("debug-pass-manager" , cl::init(Val: false), cl::Hidden, |
260 | cl::desc("Print pass management debugging information" ), |
261 | cl::cat(LTOCategory)); |
262 | |
263 | static cl::opt<bool> |
264 | LTOSaveBeforeOpt("lto-save-before-opt" , cl::init(Val: false), |
265 | cl::desc("Save the IR before running optimizations" )); |
266 | |
267 | static cl::opt<bool> TryUseNewDbgInfoFormat( |
268 | "try-experimental-debuginfo-iterators" , |
269 | cl::desc("Enable debuginfo iterator positions, if they're built in" ), |
270 | cl::init(Val: false), cl::Hidden); |
271 | |
272 | extern cl::opt<bool> UseNewDbgInfoFormat; |
273 | extern cl::opt<cl::boolOrDefault> LoadBitcodeIntoNewDbgInfoFormat; |
274 | extern cl::opt<cl::boolOrDefault> PreserveInputDbgFormat; |
275 | |
276 | namespace { |
277 | |
278 | struct ModuleInfo { |
279 | BitVector CanBeHidden; |
280 | }; |
281 | |
282 | } // end anonymous namespace |
283 | |
284 | static void handleDiagnostics(lto_codegen_diagnostic_severity_t Severity, |
285 | const char *Msg, void *) { |
286 | errs() << "llvm-lto: " ; |
287 | switch (Severity) { |
288 | case LTO_DS_NOTE: |
289 | errs() << "note: " ; |
290 | break; |
291 | case LTO_DS_REMARK: |
292 | errs() << "remark: " ; |
293 | break; |
294 | case LTO_DS_ERROR: |
295 | errs() << "error: " ; |
296 | break; |
297 | case LTO_DS_WARNING: |
298 | errs() << "warning: " ; |
299 | break; |
300 | } |
301 | errs() << Msg << "\n" ; |
302 | } |
303 | |
304 | static std::string CurrentActivity; |
305 | |
306 | namespace { |
307 | struct LLVMLTODiagnosticHandler : public DiagnosticHandler { |
308 | bool handleDiagnostics(const DiagnosticInfo &DI) override { |
309 | raw_ostream &OS = errs(); |
310 | OS << "llvm-lto: " ; |
311 | switch (DI.getSeverity()) { |
312 | case DS_Error: |
313 | OS << "error" ; |
314 | break; |
315 | case DS_Warning: |
316 | OS << "warning" ; |
317 | break; |
318 | case DS_Remark: |
319 | OS << "remark" ; |
320 | break; |
321 | case DS_Note: |
322 | OS << "note" ; |
323 | break; |
324 | } |
325 | if (!CurrentActivity.empty()) |
326 | OS << ' ' << CurrentActivity; |
327 | OS << ": " ; |
328 | |
329 | DiagnosticPrinterRawOStream DP(OS); |
330 | DI.print(DP); |
331 | OS << '\n'; |
332 | |
333 | if (DI.getSeverity() == DS_Error) |
334 | exit(status: 1); |
335 | return true; |
336 | } |
337 | }; |
338 | } |
339 | |
340 | static void error(const Twine &Msg) { |
341 | errs() << "llvm-lto: " << Msg << '\n'; |
342 | exit(status: 1); |
343 | } |
344 | |
345 | static void error(std::error_code EC, const Twine &Prefix) { |
346 | if (EC) |
347 | error(Msg: Prefix + ": " + EC.message()); |
348 | } |
349 | |
350 | template <typename T> |
351 | static void error(const ErrorOr<T> &V, const Twine &Prefix) { |
352 | error(V.getError(), Prefix); |
353 | } |
354 | |
355 | static void maybeVerifyModule(const Module &Mod) { |
356 | if (!DisableVerify && verifyModule(M: Mod, OS: &errs())) |
357 | error(Msg: "Broken Module" ); |
358 | } |
359 | |
360 | static std::unique_ptr<LTOModule> |
361 | getLocalLTOModule(StringRef Path, std::unique_ptr<MemoryBuffer> &Buffer, |
362 | const TargetOptions &Options) { |
363 | ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr = |
364 | MemoryBuffer::getFile(Filename: Path); |
365 | error(V: BufferOrErr, Prefix: "error loading file '" + Path + "'" ); |
366 | Buffer = std::move(BufferOrErr.get()); |
367 | CurrentActivity = ("loading file '" + Path + "'" ).str(); |
368 | std::unique_ptr<LLVMContext> Context = std::make_unique<LLVMContext>(); |
369 | Context->setDiagnosticHandler(DH: std::make_unique<LLVMLTODiagnosticHandler>(), |
370 | RespectFilters: true); |
371 | ErrorOr<std::unique_ptr<LTOModule>> Ret = LTOModule::createInLocalContext( |
372 | Context: std::move(Context), mem: Buffer->getBufferStart(), length: Buffer->getBufferSize(), |
373 | options: Options, path: Path); |
374 | CurrentActivity = "" ; |
375 | maybeVerifyModule(Mod: (*Ret)->getModule()); |
376 | return std::move(*Ret); |
377 | } |
378 | |
379 | /// Print some statistics on the index for each input files. |
380 | static void printIndexStats() { |
381 | for (auto &Filename : InputFilenames) { |
382 | ExitOnError ExitOnErr("llvm-lto: error loading file '" + Filename + "': " ); |
383 | std::unique_ptr<ModuleSummaryIndex> Index = |
384 | ExitOnErr(getModuleSummaryIndexForFile(Path: Filename)); |
385 | // Skip files without a module summary. |
386 | if (!Index) |
387 | report_fatal_error(reason: Twine(Filename) + " does not contain an index" ); |
388 | |
389 | unsigned Calls = 0, Refs = 0, Functions = 0, Alias = 0, Globals = 0; |
390 | for (auto &Summaries : *Index) { |
391 | for (auto &Summary : Summaries.second.SummaryList) { |
392 | Refs += Summary->refs().size(); |
393 | if (auto *FuncSummary = dyn_cast<FunctionSummary>(Val: Summary.get())) { |
394 | Functions++; |
395 | Calls += FuncSummary->calls().size(); |
396 | } else if (isa<AliasSummary>(Val: Summary.get())) |
397 | Alias++; |
398 | else |
399 | Globals++; |
400 | } |
401 | } |
402 | outs() << "Index " << Filename << " contains " |
403 | << (Alias + Globals + Functions) << " nodes (" << Functions |
404 | << " functions, " << Alias << " alias, " << Globals |
405 | << " globals) and " << (Calls + Refs) << " edges (" << Refs |
406 | << " refs and " << Calls << " calls)\n" ; |
407 | } |
408 | } |
409 | |
410 | /// Load each IR file and dump certain information based on active flags. |
411 | /// |
412 | /// The main point here is to provide lit-testable coverage for the LTOModule |
413 | /// functionality that's exposed by the C API. Moreover, this provides testing |
414 | /// coverage for modules that have been created in their own contexts. |
415 | static void testLTOModule(const TargetOptions &Options) { |
416 | for (auto &Filename : InputFilenames) { |
417 | std::unique_ptr<MemoryBuffer> Buffer; |
418 | std::unique_ptr<LTOModule> Module = |
419 | getLocalLTOModule(Path: Filename, Buffer, Options); |
420 | |
421 | if (ListSymbolsOnly) { |
422 | // List the symbols. |
423 | outs() << Filename << ":\n" ; |
424 | for (int I = 0, E = Module->getSymbolCount(); I != E; ++I) |
425 | outs() << Module->getSymbolName(index: I) << "\n" ; |
426 | } |
427 | if (QueryHasCtorDtor) |
428 | outs() << Filename |
429 | << ": hasCtorDtor = " << (Module->hasCtorDtor() ? "true" : "false" ) |
430 | << "\n" ; |
431 | } |
432 | } |
433 | |
434 | static std::unique_ptr<MemoryBuffer> loadFile(StringRef Filename) { |
435 | ExitOnError ExitOnErr("llvm-lto: error loading file '" + Filename.str() + |
436 | "': " ); |
437 | return ExitOnErr(errorOrToExpected(EO: MemoryBuffer::getFileOrSTDIN(Filename))); |
438 | } |
439 | |
440 | static void listDependentLibraries() { |
441 | for (auto &Filename : InputFilenames) { |
442 | auto Buffer = loadFile(Filename); |
443 | std::string E; |
444 | std::unique_ptr<lto::InputFile> Input(LTOModule::createInputFile( |
445 | buffer: Buffer->getBufferStart(), buffer_size: Buffer->getBufferSize(), path: Filename.c_str(), |
446 | out_error&: E)); |
447 | if (!Input) |
448 | error(Msg: E); |
449 | |
450 | // List the dependent libraries. |
451 | outs() << Filename << ":\n" ; |
452 | for (size_t I = 0, C = LTOModule::getDependentLibraryCount(input: Input.get()); |
453 | I != C; ++I) { |
454 | size_t L = 0; |
455 | const char *S = LTOModule::getDependentLibrary(input: Input.get(), index: I, size: &L); |
456 | assert(S); |
457 | outs() << StringRef(S, L) << "\n" ; |
458 | } |
459 | } |
460 | } |
461 | |
462 | static void printMachOCPUOnly() { |
463 | LLVMContext Context; |
464 | Context.setDiagnosticHandler(DH: std::make_unique<LLVMLTODiagnosticHandler>(), |
465 | RespectFilters: true); |
466 | TargetOptions Options = codegen::InitTargetOptionsFromCodeGenFlags(TheTriple: Triple()); |
467 | for (auto &Filename : InputFilenames) { |
468 | ErrorOr<std::unique_ptr<LTOModule>> ModuleOrErr = |
469 | LTOModule::createFromFile(Context, path: Filename, options: Options); |
470 | if (!ModuleOrErr) |
471 | error(V: ModuleOrErr, Prefix: "llvm-lto: " ); |
472 | |
473 | Expected<uint32_t> CPUType = (*ModuleOrErr)->getMachOCPUType(); |
474 | Expected<uint32_t> CPUSubType = (*ModuleOrErr)->getMachOCPUSubType(); |
475 | if (!CPUType) |
476 | error(Msg: "Error while printing mach-o cputype: " + |
477 | toString(E: CPUType.takeError())); |
478 | if (!CPUSubType) |
479 | error(Msg: "Error while printing mach-o cpusubtype: " + |
480 | toString(E: CPUSubType.takeError())); |
481 | outs() << llvm::format(Fmt: "%s:\ncputype: %u\ncpusubtype: %u\n" , |
482 | Vals: Filename.c_str(), Vals: *CPUType, Vals: *CPUSubType); |
483 | } |
484 | } |
485 | |
486 | /// Create a combined index file from the input IR files and write it. |
487 | /// |
488 | /// This is meant to enable testing of ThinLTO combined index generation, |
489 | /// currently available via the gold plugin via -thinlto. |
490 | static void createCombinedModuleSummaryIndex() { |
491 | ModuleSummaryIndex CombinedIndex(/*HaveGVs=*/false); |
492 | for (auto &Filename : InputFilenames) { |
493 | ExitOnError ExitOnErr("llvm-lto: error loading file '" + Filename + "': " ); |
494 | std::unique_ptr<MemoryBuffer> MB = |
495 | ExitOnErr(errorOrToExpected(EO: MemoryBuffer::getFileOrSTDIN(Filename))); |
496 | ExitOnErr(readModuleSummaryIndex(Buffer: *MB, CombinedIndex)); |
497 | } |
498 | // In order to use this index for testing, specifically import testing, we |
499 | // need to update any indirect call edges created from SamplePGO, so that they |
500 | // point to the correct GUIDs. |
501 | updateIndirectCalls(Index&: CombinedIndex); |
502 | std::error_code EC; |
503 | assert(!OutputFilename.empty()); |
504 | raw_fd_ostream OS(OutputFilename + ".thinlto.bc" , EC, |
505 | sys::fs::OpenFlags::OF_None); |
506 | error(EC, Prefix: "error opening the file '" + OutputFilename + ".thinlto.bc'" ); |
507 | writeIndexToFile(Index: CombinedIndex, Out&: OS); |
508 | OS.close(); |
509 | } |
510 | |
511 | /// Parse the thinlto_prefix_replace option into the \p OldPrefix and |
512 | /// \p NewPrefix strings, if it was specified. |
513 | static void getThinLTOOldAndNewPrefix(std::string &OldPrefix, |
514 | std::string &NewPrefix) { |
515 | assert(ThinLTOPrefixReplace.empty() || |
516 | ThinLTOPrefixReplace.find(';') != StringRef::npos); |
517 | StringRef PrefixReplace = ThinLTOPrefixReplace; |
518 | std::pair<StringRef, StringRef> Split = PrefixReplace.split(Separator: ";" ); |
519 | OldPrefix = Split.first.str(); |
520 | NewPrefix = Split.second.str(); |
521 | } |
522 | |
523 | /// Given the original \p Path to an output file, replace any path |
524 | /// prefix matching \p OldPrefix with \p NewPrefix. Also, create the |
525 | /// resulting directory if it does not yet exist. |
526 | static std::string getThinLTOOutputFile(StringRef Path, StringRef OldPrefix, |
527 | StringRef NewPrefix) { |
528 | if (OldPrefix.empty() && NewPrefix.empty()) |
529 | return std::string(Path); |
530 | SmallString<128> NewPath(Path); |
531 | llvm::sys::path::replace_path_prefix(Path&: NewPath, OldPrefix, NewPrefix); |
532 | StringRef ParentPath = llvm::sys::path::parent_path(path: NewPath.str()); |
533 | if (!ParentPath.empty()) { |
534 | // Make sure the new directory exists, creating it if necessary. |
535 | if (std::error_code EC = llvm::sys::fs::create_directories(path: ParentPath)) |
536 | error(EC, Prefix: "error creating the directory '" + ParentPath + "'" ); |
537 | } |
538 | return std::string(NewPath); |
539 | } |
540 | |
541 | namespace thinlto { |
542 | |
543 | std::vector<std::unique_ptr<MemoryBuffer>> |
544 | loadAllFilesForIndex(const ModuleSummaryIndex &Index) { |
545 | std::vector<std::unique_ptr<MemoryBuffer>> InputBuffers; |
546 | |
547 | for (auto &ModPath : Index.modulePaths()) { |
548 | const auto &Filename = ModPath.first(); |
549 | std::string CurrentActivity = ("loading file '" + Filename + "'" ).str(); |
550 | auto InputOrErr = MemoryBuffer::getFile(Filename); |
551 | error(V: InputOrErr, Prefix: "error " + CurrentActivity); |
552 | InputBuffers.push_back(x: std::move(*InputOrErr)); |
553 | } |
554 | return InputBuffers; |
555 | } |
556 | |
557 | std::unique_ptr<ModuleSummaryIndex> loadCombinedIndex() { |
558 | if (ThinLTOIndex.empty()) |
559 | report_fatal_error(reason: "Missing -thinlto-index for ThinLTO promotion stage" ); |
560 | ExitOnError ExitOnErr("llvm-lto: error loading file '" + ThinLTOIndex + |
561 | "': " ); |
562 | return ExitOnErr(getModuleSummaryIndexForFile(Path: ThinLTOIndex)); |
563 | } |
564 | |
565 | static std::unique_ptr<lto::InputFile> loadInputFile(MemoryBufferRef Buffer) { |
566 | ExitOnError ExitOnErr("llvm-lto: error loading input '" + |
567 | Buffer.getBufferIdentifier().str() + "': " ); |
568 | return ExitOnErr(lto::InputFile::create(Object: Buffer)); |
569 | } |
570 | |
571 | static std::unique_ptr<Module> loadModuleFromInput(lto::InputFile &File, |
572 | LLVMContext &CTX) { |
573 | auto &Mod = File.getSingleBitcodeModule(); |
574 | auto ModuleOrErr = Mod.parseModule(Context&: CTX); |
575 | if (!ModuleOrErr) { |
576 | handleAllErrors(E: ModuleOrErr.takeError(), Handlers: [&](ErrorInfoBase &EIB) { |
577 | SMDiagnostic Err = SMDiagnostic(Mod.getModuleIdentifier(), |
578 | SourceMgr::DK_Error, EIB.message()); |
579 | Err.print(ProgName: "llvm-lto" , S&: errs()); |
580 | }); |
581 | report_fatal_error(reason: "Can't load module, abort." ); |
582 | } |
583 | maybeVerifyModule(Mod: **ModuleOrErr); |
584 | if (ThinLTOModuleId.getNumOccurrences()) { |
585 | if (InputFilenames.size() != 1) |
586 | report_fatal_error(reason: "Can't override the module id for multiple files" ); |
587 | (*ModuleOrErr)->setModuleIdentifier(ThinLTOModuleId); |
588 | } |
589 | return std::move(*ModuleOrErr); |
590 | } |
591 | |
592 | static void writeModuleToFile(Module &TheModule, StringRef Filename) { |
593 | std::error_code EC; |
594 | raw_fd_ostream OS(Filename, EC, sys::fs::OpenFlags::OF_None); |
595 | error(EC, Prefix: "error opening the file '" + Filename + "'" ); |
596 | maybeVerifyModule(Mod: TheModule); |
597 | WriteBitcodeToFile(M: TheModule, Out&: OS, /* ShouldPreserveUseListOrder */ true); |
598 | } |
599 | |
600 | class ThinLTOProcessing { |
601 | public: |
602 | ThinLTOCodeGenerator ThinGenerator; |
603 | |
604 | ThinLTOProcessing(const TargetOptions &Options) { |
605 | ThinGenerator.setCodePICModel(codegen::getExplicitRelocModel()); |
606 | ThinGenerator.setTargetOptions(Options); |
607 | ThinGenerator.setCacheDir(ThinLTOCacheDir); |
608 | ThinGenerator.setCachePruningInterval(ThinLTOCachePruningInterval); |
609 | ThinGenerator.setCacheEntryExpiration(ThinLTOCacheEntryExpiration); |
610 | ThinGenerator.setCacheMaxSizeFiles(ThinLTOCacheMaxSizeFiles); |
611 | ThinGenerator.setCacheMaxSizeBytes(ThinLTOCacheMaxSizeBytes); |
612 | ThinGenerator.setFreestanding(EnableFreestanding); |
613 | ThinGenerator.setDebugPassManager(DebugPassManager); |
614 | |
615 | // Add all the exported symbols to the table of symbols to preserve. |
616 | for (unsigned i = 0; i < ExportedSymbols.size(); ++i) |
617 | ThinGenerator.preserveSymbol(Name: ExportedSymbols[i]); |
618 | } |
619 | |
620 | void run() { |
621 | switch (ThinLTOMode) { |
622 | case THINLINK: |
623 | return thinLink(); |
624 | case THINDISTRIBUTE: |
625 | return distributedIndexes(); |
626 | case THINEMITIMPORTS: |
627 | return emitImports(); |
628 | case THINPROMOTE: |
629 | return promote(); |
630 | case THINIMPORT: |
631 | return import(); |
632 | case THININTERNALIZE: |
633 | return internalize(); |
634 | case THINOPT: |
635 | return optimize(); |
636 | case THINCODEGEN: |
637 | return codegen(); |
638 | case THINALL: |
639 | return runAll(); |
640 | } |
641 | } |
642 | |
643 | private: |
644 | /// Load the input files, create the combined index, and write it out. |
645 | void thinLink() { |
646 | // Perform "ThinLink": just produce the index |
647 | if (OutputFilename.empty()) |
648 | report_fatal_error( |
649 | reason: "OutputFilename is necessary to store the combined index.\n" ); |
650 | |
651 | LLVMContext Ctx; |
652 | std::vector<std::unique_ptr<MemoryBuffer>> InputBuffers; |
653 | for (unsigned i = 0; i < InputFilenames.size(); ++i) { |
654 | auto &Filename = InputFilenames[i]; |
655 | std::string CurrentActivity = "loading file '" + Filename + "'" ; |
656 | auto InputOrErr = MemoryBuffer::getFile(Filename); |
657 | error(V: InputOrErr, Prefix: "error " + CurrentActivity); |
658 | InputBuffers.push_back(x: std::move(*InputOrErr)); |
659 | ThinGenerator.addModule(Identifier: Filename, Data: InputBuffers.back()->getBuffer()); |
660 | } |
661 | |
662 | auto CombinedIndex = ThinGenerator.linkCombinedIndex(); |
663 | if (!CombinedIndex) |
664 | report_fatal_error(reason: "ThinLink didn't create an index" ); |
665 | std::error_code EC; |
666 | raw_fd_ostream OS(OutputFilename, EC, sys::fs::OpenFlags::OF_None); |
667 | error(EC, Prefix: "error opening the file '" + OutputFilename + "'" ); |
668 | writeIndexToFile(Index: *CombinedIndex, Out&: OS); |
669 | } |
670 | |
671 | /// Load the combined index from disk, then compute and generate |
672 | /// individual index files suitable for ThinLTO distributed backend builds |
673 | /// on the files mentioned on the command line (these must match the index |
674 | /// content). |
675 | void distributedIndexes() { |
676 | if (InputFilenames.size() != 1 && !OutputFilename.empty()) |
677 | report_fatal_error(reason: "Can't handle a single output filename and multiple " |
678 | "input files, do not provide an output filename and " |
679 | "the output files will be suffixed from the input " |
680 | "ones." ); |
681 | |
682 | std::string OldPrefix, NewPrefix; |
683 | getThinLTOOldAndNewPrefix(OldPrefix, NewPrefix); |
684 | |
685 | auto Index = loadCombinedIndex(); |
686 | for (auto &Filename : InputFilenames) { |
687 | LLVMContext Ctx; |
688 | auto Buffer = loadFile(Filename); |
689 | auto Input = loadInputFile(Buffer: Buffer->getMemBufferRef()); |
690 | auto TheModule = loadModuleFromInput(File&: *Input, CTX&: Ctx); |
691 | |
692 | // Build a map of module to the GUIDs and summary objects that should |
693 | // be written to its index. |
694 | std::map<std::string, GVSummaryMapTy> ModuleToSummariesForIndex; |
695 | GVSummaryPtrSet DecSummaries; |
696 | ThinGenerator.gatherImportedSummariesForModule( |
697 | Module&: *TheModule, Index&: *Index, ModuleToSummariesForIndex, DecSummaries, File: *Input); |
698 | |
699 | std::string OutputName = OutputFilename; |
700 | if (OutputName.empty()) { |
701 | OutputName = Filename + ".thinlto.bc" ; |
702 | } |
703 | OutputName = getThinLTOOutputFile(Path: OutputName, OldPrefix, NewPrefix); |
704 | std::error_code EC; |
705 | raw_fd_ostream OS(OutputName, EC, sys::fs::OpenFlags::OF_None); |
706 | error(EC, Prefix: "error opening the file '" + OutputName + "'" ); |
707 | writeIndexToFile(Index: *Index, Out&: OS, ModuleToSummariesForIndex: &ModuleToSummariesForIndex, DecSummaries: &DecSummaries); |
708 | } |
709 | } |
710 | |
711 | /// Load the combined index from disk, compute the imports, and emit |
712 | /// the import file lists for each module to disk. |
713 | void emitImports() { |
714 | if (InputFilenames.size() != 1 && !OutputFilename.empty()) |
715 | report_fatal_error(reason: "Can't handle a single output filename and multiple " |
716 | "input files, do not provide an output filename and " |
717 | "the output files will be suffixed from the input " |
718 | "ones." ); |
719 | |
720 | std::string OldPrefix, NewPrefix; |
721 | getThinLTOOldAndNewPrefix(OldPrefix, NewPrefix); |
722 | |
723 | auto Index = loadCombinedIndex(); |
724 | for (auto &Filename : InputFilenames) { |
725 | LLVMContext Ctx; |
726 | auto Buffer = loadFile(Filename); |
727 | auto Input = loadInputFile(Buffer: Buffer->getMemBufferRef()); |
728 | auto TheModule = loadModuleFromInput(File&: *Input, CTX&: Ctx); |
729 | std::string OutputName = OutputFilename; |
730 | if (OutputName.empty()) { |
731 | OutputName = Filename + ".imports" ; |
732 | } |
733 | OutputName = |
734 | getThinLTOOutputFile(Path: OutputName, OldPrefix, NewPrefix); |
735 | ThinGenerator.emitImports(Module&: *TheModule, OutputName, Index&: *Index, File: *Input); |
736 | } |
737 | } |
738 | |
739 | /// Load the combined index from disk, then load every file referenced by |
740 | /// the index and add them to the generator, finally perform the promotion |
741 | /// on the files mentioned on the command line (these must match the index |
742 | /// content). |
743 | void promote() { |
744 | if (InputFilenames.size() != 1 && !OutputFilename.empty()) |
745 | report_fatal_error(reason: "Can't handle a single output filename and multiple " |
746 | "input files, do not provide an output filename and " |
747 | "the output files will be suffixed from the input " |
748 | "ones." ); |
749 | |
750 | auto Index = loadCombinedIndex(); |
751 | for (auto &Filename : InputFilenames) { |
752 | LLVMContext Ctx; |
753 | auto Buffer = loadFile(Filename); |
754 | auto Input = loadInputFile(Buffer: Buffer->getMemBufferRef()); |
755 | auto TheModule = loadModuleFromInput(File&: *Input, CTX&: Ctx); |
756 | |
757 | ThinGenerator.promote(Module&: *TheModule, Index&: *Index, File: *Input); |
758 | |
759 | std::string OutputName = OutputFilename; |
760 | if (OutputName.empty()) { |
761 | OutputName = Filename + ".thinlto.promoted.bc" ; |
762 | } |
763 | writeModuleToFile(TheModule&: *TheModule, Filename: OutputName); |
764 | } |
765 | } |
766 | |
767 | /// Load the combined index from disk, then load every file referenced by |
768 | /// the index and add them to the generator, then performs the promotion and |
769 | /// cross module importing on the files mentioned on the command line |
770 | /// (these must match the index content). |
771 | void import() { |
772 | if (InputFilenames.size() != 1 && !OutputFilename.empty()) |
773 | report_fatal_error(reason: "Can't handle a single output filename and multiple " |
774 | "input files, do not provide an output filename and " |
775 | "the output files will be suffixed from the input " |
776 | "ones." ); |
777 | |
778 | auto Index = loadCombinedIndex(); |
779 | auto InputBuffers = loadAllFilesForIndex(Index: *Index); |
780 | for (auto &MemBuffer : InputBuffers) |
781 | ThinGenerator.addModule(Identifier: MemBuffer->getBufferIdentifier(), |
782 | Data: MemBuffer->getBuffer()); |
783 | |
784 | for (auto &Filename : InputFilenames) { |
785 | LLVMContext Ctx; |
786 | auto Buffer = loadFile(Filename); |
787 | auto Input = loadInputFile(Buffer: Buffer->getMemBufferRef()); |
788 | auto TheModule = loadModuleFromInput(File&: *Input, CTX&: Ctx); |
789 | |
790 | ThinGenerator.crossModuleImport(Module&: *TheModule, Index&: *Index, File: *Input); |
791 | |
792 | std::string OutputName = OutputFilename; |
793 | if (OutputName.empty()) { |
794 | OutputName = Filename + ".thinlto.imported.bc" ; |
795 | } |
796 | writeModuleToFile(TheModule&: *TheModule, Filename: OutputName); |
797 | } |
798 | } |
799 | |
800 | void internalize() { |
801 | if (InputFilenames.size() != 1 && !OutputFilename.empty()) |
802 | report_fatal_error(reason: "Can't handle a single output filename and multiple " |
803 | "input files, do not provide an output filename and " |
804 | "the output files will be suffixed from the input " |
805 | "ones." ); |
806 | |
807 | if (ExportedSymbols.empty()) |
808 | errs() << "Warning: -internalize will not perform without " |
809 | "-exported-symbol\n" ; |
810 | |
811 | auto Index = loadCombinedIndex(); |
812 | auto InputBuffers = loadAllFilesForIndex(Index: *Index); |
813 | for (auto &MemBuffer : InputBuffers) |
814 | ThinGenerator.addModule(Identifier: MemBuffer->getBufferIdentifier(), |
815 | Data: MemBuffer->getBuffer()); |
816 | |
817 | for (auto &Filename : InputFilenames) { |
818 | LLVMContext Ctx; |
819 | auto Buffer = loadFile(Filename); |
820 | auto Input = loadInputFile(Buffer: Buffer->getMemBufferRef()); |
821 | auto TheModule = loadModuleFromInput(File&: *Input, CTX&: Ctx); |
822 | |
823 | ThinGenerator.internalize(Module&: *TheModule, Index&: *Index, File: *Input); |
824 | |
825 | std::string OutputName = OutputFilename; |
826 | if (OutputName.empty()) { |
827 | OutputName = Filename + ".thinlto.internalized.bc" ; |
828 | } |
829 | writeModuleToFile(TheModule&: *TheModule, Filename: OutputName); |
830 | } |
831 | } |
832 | |
833 | void optimize() { |
834 | if (InputFilenames.size() != 1 && !OutputFilename.empty()) |
835 | report_fatal_error(reason: "Can't handle a single output filename and multiple " |
836 | "input files, do not provide an output filename and " |
837 | "the output files will be suffixed from the input " |
838 | "ones." ); |
839 | if (!ThinLTOIndex.empty()) |
840 | errs() << "Warning: -thinlto-index ignored for optimize stage" ; |
841 | |
842 | for (auto &Filename : InputFilenames) { |
843 | LLVMContext Ctx; |
844 | auto Buffer = loadFile(Filename); |
845 | auto Input = loadInputFile(Buffer: Buffer->getMemBufferRef()); |
846 | auto TheModule = loadModuleFromInput(File&: *Input, CTX&: Ctx); |
847 | |
848 | ThinGenerator.optimize(Module&: *TheModule); |
849 | |
850 | std::string OutputName = OutputFilename; |
851 | if (OutputName.empty()) { |
852 | OutputName = Filename + ".thinlto.imported.bc" ; |
853 | } |
854 | writeModuleToFile(TheModule&: *TheModule, Filename: OutputName); |
855 | } |
856 | } |
857 | |
858 | void codegen() { |
859 | if (InputFilenames.size() != 1 && !OutputFilename.empty()) |
860 | report_fatal_error(reason: "Can't handle a single output filename and multiple " |
861 | "input files, do not provide an output filename and " |
862 | "the output files will be suffixed from the input " |
863 | "ones." ); |
864 | if (!ThinLTOIndex.empty()) |
865 | errs() << "Warning: -thinlto-index ignored for codegen stage" ; |
866 | |
867 | std::vector<std::unique_ptr<MemoryBuffer>> InputBuffers; |
868 | for (auto &Filename : InputFilenames) { |
869 | LLVMContext Ctx; |
870 | auto InputOrErr = MemoryBuffer::getFile(Filename); |
871 | error(V: InputOrErr, Prefix: "error " + CurrentActivity); |
872 | InputBuffers.push_back(x: std::move(*InputOrErr)); |
873 | ThinGenerator.addModule(Identifier: Filename, Data: InputBuffers.back()->getBuffer()); |
874 | } |
875 | ThinGenerator.setCodeGenOnly(true); |
876 | ThinGenerator.run(); |
877 | for (auto BinName : |
878 | zip(t&: ThinGenerator.getProducedBinaries(), u&: InputFilenames)) { |
879 | std::string OutputName = OutputFilename; |
880 | if (OutputName.empty()) |
881 | OutputName = std::get<1>(t&: BinName) + ".thinlto.o" ; |
882 | else if (OutputName == "-" ) { |
883 | outs() << std::get<0>(t&: BinName)->getBuffer(); |
884 | return; |
885 | } |
886 | |
887 | std::error_code EC; |
888 | raw_fd_ostream OS(OutputName, EC, sys::fs::OpenFlags::OF_None); |
889 | error(EC, Prefix: "error opening the file '" + OutputName + "'" ); |
890 | OS << std::get<0>(t&: BinName)->getBuffer(); |
891 | } |
892 | } |
893 | |
894 | /// Full ThinLTO process |
895 | void runAll() { |
896 | if (!OutputFilename.empty()) |
897 | report_fatal_error(reason: "Do not provide an output filename for ThinLTO " |
898 | " processing, the output files will be suffixed from " |
899 | "the input ones." ); |
900 | |
901 | if (!ThinLTOIndex.empty()) |
902 | errs() << "Warning: -thinlto-index ignored for full ThinLTO process" ; |
903 | |
904 | LLVMContext Ctx; |
905 | std::vector<std::unique_ptr<MemoryBuffer>> InputBuffers; |
906 | for (unsigned i = 0; i < InputFilenames.size(); ++i) { |
907 | auto &Filename = InputFilenames[i]; |
908 | std::string CurrentActivity = "loading file '" + Filename + "'" ; |
909 | auto InputOrErr = MemoryBuffer::getFile(Filename); |
910 | error(V: InputOrErr, Prefix: "error " + CurrentActivity); |
911 | InputBuffers.push_back(x: std::move(*InputOrErr)); |
912 | ThinGenerator.addModule(Identifier: Filename, Data: InputBuffers.back()->getBuffer()); |
913 | } |
914 | |
915 | if (!ThinLTOSaveTempsPrefix.empty()) |
916 | ThinGenerator.setSaveTempsDir(ThinLTOSaveTempsPrefix); |
917 | |
918 | if (!ThinLTOGeneratedObjectsDir.empty()) { |
919 | ThinGenerator.setGeneratedObjectsDirectory(ThinLTOGeneratedObjectsDir); |
920 | ThinGenerator.run(); |
921 | return; |
922 | } |
923 | |
924 | ThinGenerator.run(); |
925 | |
926 | auto &Binaries = ThinGenerator.getProducedBinaries(); |
927 | if (Binaries.size() != InputFilenames.size()) |
928 | report_fatal_error(reason: "Number of output objects does not match the number " |
929 | "of inputs" ); |
930 | |
931 | for (unsigned BufID = 0; BufID < Binaries.size(); ++BufID) { |
932 | auto OutputName = InputFilenames[BufID] + ".thinlto.o" ; |
933 | std::error_code EC; |
934 | raw_fd_ostream OS(OutputName, EC, sys::fs::OpenFlags::OF_None); |
935 | error(EC, Prefix: "error opening the file '" + OutputName + "'" ); |
936 | OS << Binaries[BufID]->getBuffer(); |
937 | } |
938 | } |
939 | |
940 | /// Load the combined index from disk, then load every file referenced by |
941 | }; |
942 | |
943 | } // end namespace thinlto |
944 | |
945 | int main(int argc, char **argv) { |
946 | InitLLVM X(argc, argv); |
947 | cl::HideUnrelatedOptions(Categories: {<OCategory, &getColorCategory()}); |
948 | cl::ParseCommandLineOptions(argc, argv, Overview: "llvm LTO linker\n" ); |
949 | // Load bitcode into the new debug info format by default. |
950 | if (LoadBitcodeIntoNewDbgInfoFormat == cl::boolOrDefault::BOU_UNSET) |
951 | LoadBitcodeIntoNewDbgInfoFormat = cl::boolOrDefault::BOU_TRUE; |
952 | |
953 | // RemoveDIs debug-info transition: tests may request that we /try/ to use the |
954 | // new debug-info format. |
955 | if (TryUseNewDbgInfoFormat) { |
956 | // Turn the new debug-info format on. |
957 | UseNewDbgInfoFormat = true; |
958 | } |
959 | // Since llvm-lto collects multiple IR modules together, for simplicity's sake |
960 | // we disable the "PreserveInputDbgFormat" flag to enforce a single debug info |
961 | // format. |
962 | PreserveInputDbgFormat = cl::boolOrDefault::BOU_FALSE; |
963 | |
964 | if (OptLevel < '0' || OptLevel > '3') |
965 | error(Msg: "optimization level must be between 0 and 3" ); |
966 | |
967 | // Initialize the configured targets. |
968 | InitializeAllTargets(); |
969 | InitializeAllTargetMCs(); |
970 | InitializeAllAsmPrinters(); |
971 | InitializeAllAsmParsers(); |
972 | |
973 | // set up the TargetOptions for the machine |
974 | TargetOptions Options = codegen::InitTargetOptionsFromCodeGenFlags(TheTriple: Triple()); |
975 | |
976 | if (ListSymbolsOnly || QueryHasCtorDtor) { |
977 | testLTOModule(Options); |
978 | return 0; |
979 | } |
980 | |
981 | if (ListDependentLibrariesOnly) { |
982 | listDependentLibraries(); |
983 | return 0; |
984 | } |
985 | |
986 | if (IndexStats) { |
987 | printIndexStats(); |
988 | return 0; |
989 | } |
990 | |
991 | if (CheckHasObjC) { |
992 | for (auto &Filename : InputFilenames) { |
993 | ExitOnError ExitOnErr(std::string(*argv) + ": error loading file '" + |
994 | Filename + "': " ); |
995 | std::unique_ptr<MemoryBuffer> BufferOrErr = |
996 | ExitOnErr(errorOrToExpected(EO: MemoryBuffer::getFile(Filename))); |
997 | auto Buffer = std::move(BufferOrErr.get()); |
998 | if (ExitOnErr(isBitcodeContainingObjCCategory(Buffer: *Buffer))) |
999 | outs() << "Bitcode " << Filename << " contains ObjC\n" ; |
1000 | else |
1001 | outs() << "Bitcode " << Filename << " does not contain ObjC\n" ; |
1002 | } |
1003 | return 0; |
1004 | } |
1005 | |
1006 | if (PrintMachOCPUOnly) { |
1007 | printMachOCPUOnly(); |
1008 | return 0; |
1009 | } |
1010 | |
1011 | if (ThinLTOMode.getNumOccurrences()) { |
1012 | if (ThinLTOMode.getNumOccurrences() > 1) |
1013 | report_fatal_error(reason: "You can't specify more than one -thinlto-action" ); |
1014 | thinlto::ThinLTOProcessing ThinLTOProcessor(Options); |
1015 | ThinLTOProcessor.run(); |
1016 | return 0; |
1017 | } |
1018 | |
1019 | if (ThinLTO) { |
1020 | createCombinedModuleSummaryIndex(); |
1021 | return 0; |
1022 | } |
1023 | |
1024 | unsigned BaseArg = 0; |
1025 | |
1026 | LLVMContext Context; |
1027 | Context.setDiagnosticHandler(DH: std::make_unique<LLVMLTODiagnosticHandler>(), |
1028 | RespectFilters: true); |
1029 | |
1030 | LTOCodeGenerator CodeGen(Context); |
1031 | CodeGen.setDisableVerify(DisableVerify); |
1032 | |
1033 | if (UseDiagnosticHandler) |
1034 | CodeGen.setDiagnosticHandler(handleDiagnostics, nullptr); |
1035 | |
1036 | CodeGen.setCodePICModel(codegen::getExplicitRelocModel()); |
1037 | CodeGen.setFreestanding(EnableFreestanding); |
1038 | CodeGen.setDebugPassManager(DebugPassManager); |
1039 | |
1040 | CodeGen.setDebugInfo(LTO_DEBUG_MODEL_DWARF); |
1041 | CodeGen.setTargetOptions(Options); |
1042 | CodeGen.setShouldRestoreGlobalsLinkage(RestoreGlobalsLinkage); |
1043 | |
1044 | StringSet<MallocAllocator> DSOSymbolsSet; |
1045 | for (unsigned i = 0; i < DSOSymbols.size(); ++i) |
1046 | DSOSymbolsSet.insert(key: DSOSymbols[i]); |
1047 | |
1048 | std::vector<std::string> KeptDSOSyms; |
1049 | |
1050 | for (unsigned i = BaseArg; i < InputFilenames.size(); ++i) { |
1051 | CurrentActivity = "loading file '" + InputFilenames[i] + "'" ; |
1052 | ErrorOr<std::unique_ptr<LTOModule>> ModuleOrErr = |
1053 | LTOModule::createFromFile(Context, path: InputFilenames[i], options: Options); |
1054 | std::unique_ptr<LTOModule> &Module = *ModuleOrErr; |
1055 | CurrentActivity = "" ; |
1056 | |
1057 | unsigned NumSyms = Module->getSymbolCount(); |
1058 | for (unsigned I = 0; I < NumSyms; ++I) { |
1059 | StringRef Name = Module->getSymbolName(index: I); |
1060 | if (!DSOSymbolsSet.count(Key: Name)) |
1061 | continue; |
1062 | lto_symbol_attributes Attrs = Module->getSymbolAttributes(index: I); |
1063 | unsigned Scope = Attrs & LTO_SYMBOL_SCOPE_MASK; |
1064 | if (Scope != LTO_SYMBOL_SCOPE_DEFAULT_CAN_BE_HIDDEN) |
1065 | KeptDSOSyms.push_back(x: std::string(Name)); |
1066 | } |
1067 | |
1068 | // We use the first input module as the destination module when |
1069 | // SetMergedModule is true. |
1070 | if (SetMergedModule && i == BaseArg) { |
1071 | // Transfer ownership to the code generator. |
1072 | CodeGen.setModule(std::move(Module)); |
1073 | } else if (!CodeGen.addModule(Module.get())) { |
1074 | // Print a message here so that we know addModule() did not abort. |
1075 | error(Msg: "error adding file '" + InputFilenames[i] + "'" ); |
1076 | } |
1077 | } |
1078 | |
1079 | // Add all the exported symbols to the table of symbols to preserve. |
1080 | for (unsigned i = 0; i < ExportedSymbols.size(); ++i) |
1081 | CodeGen.addMustPreserveSymbol(Sym: ExportedSymbols[i]); |
1082 | |
1083 | // Add all the dso symbols to the table of symbols to expose. |
1084 | for (unsigned i = 0; i < KeptDSOSyms.size(); ++i) |
1085 | CodeGen.addMustPreserveSymbol(Sym: KeptDSOSyms[i]); |
1086 | |
1087 | // Set cpu and attrs strings for the default target/subtarget. |
1088 | CodeGen.setCpu(codegen::getMCPU()); |
1089 | |
1090 | CodeGen.setOptLevel(OptLevel - '0'); |
1091 | CodeGen.setAttrs(codegen::getMAttrs()); |
1092 | |
1093 | if (auto FT = codegen::getExplicitFileType()) |
1094 | CodeGen.setFileType(*FT); |
1095 | |
1096 | if (!OutputFilename.empty()) { |
1097 | if (LTOSaveBeforeOpt) |
1098 | CodeGen.setSaveIRBeforeOptPath(OutputFilename + ".0.preopt.bc" ); |
1099 | |
1100 | if (SaveLinkedModuleFile) { |
1101 | std::string ModuleFilename = OutputFilename; |
1102 | ModuleFilename += ".linked.bc" ; |
1103 | std::string ErrMsg; |
1104 | |
1105 | if (!CodeGen.writeMergedModules(Path: ModuleFilename)) |
1106 | error(Msg: "writing linked module failed." ); |
1107 | } |
1108 | |
1109 | if (!CodeGen.optimize()) { |
1110 | // Diagnostic messages should have been printed by the handler. |
1111 | error(Msg: "error optimizing the code" ); |
1112 | } |
1113 | |
1114 | if (SaveModuleFile) { |
1115 | std::string ModuleFilename = OutputFilename; |
1116 | ModuleFilename += ".merged.bc" ; |
1117 | std::string ErrMsg; |
1118 | |
1119 | if (!CodeGen.writeMergedModules(Path: ModuleFilename)) |
1120 | error(Msg: "writing merged module failed." ); |
1121 | } |
1122 | |
1123 | auto AddStream = |
1124 | [&](size_t Task, |
1125 | const Twine &ModuleName) -> std::unique_ptr<CachedFileStream> { |
1126 | std::string PartFilename = OutputFilename; |
1127 | if (Parallelism != 1) |
1128 | PartFilename += "." + utostr(X: Task); |
1129 | |
1130 | std::error_code EC; |
1131 | auto S = |
1132 | std::make_unique<raw_fd_ostream>(args&: PartFilename, args&: EC, args: sys::fs::OF_None); |
1133 | if (EC) |
1134 | error(Msg: "error opening the file '" + PartFilename + "': " + EC.message()); |
1135 | return std::make_unique<CachedFileStream>(args: std::move(S)); |
1136 | }; |
1137 | |
1138 | if (!CodeGen.compileOptimized(AddStream, ParallelismLevel: Parallelism)) |
1139 | // Diagnostic messages should have been printed by the handler. |
1140 | error(Msg: "error compiling the code" ); |
1141 | |
1142 | } else { |
1143 | if (Parallelism != 1) |
1144 | error(Msg: "-j must be specified together with -o" ); |
1145 | |
1146 | if (SaveModuleFile) |
1147 | error(Msg: ": -save-merged-module must be specified with -o" ); |
1148 | |
1149 | const char *OutputName = nullptr; |
1150 | if (!CodeGen.compile_to_file(Name: &OutputName)) |
1151 | error(Msg: "error compiling the code" ); |
1152 | // Diagnostic messages should have been printed by the handler. |
1153 | |
1154 | outs() << "Wrote native object file '" << OutputName << "'\n" ; |
1155 | } |
1156 | |
1157 | return 0; |
1158 | } |
1159 | |