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 | static 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 | namespace { |
268 | |
269 | struct ModuleInfo { |
270 | BitVector CanBeHidden; |
271 | }; |
272 | |
273 | } // end anonymous namespace |
274 | |
275 | static void handleDiagnostics(lto_codegen_diagnostic_severity_t Severity, |
276 | const char *Msg, void *) { |
277 | errs() << "llvm-lto: " ; |
278 | switch (Severity) { |
279 | case LTO_DS_NOTE: |
280 | errs() << "note: " ; |
281 | break; |
282 | case LTO_DS_REMARK: |
283 | errs() << "remark: " ; |
284 | break; |
285 | case LTO_DS_ERROR: |
286 | errs() << "error: " ; |
287 | break; |
288 | case LTO_DS_WARNING: |
289 | errs() << "warning: " ; |
290 | break; |
291 | } |
292 | errs() << Msg << "\n" ; |
293 | } |
294 | |
295 | static std::string CurrentActivity; |
296 | |
297 | namespace { |
298 | struct LLVMLTODiagnosticHandler : public DiagnosticHandler { |
299 | bool handleDiagnostics(const DiagnosticInfo &DI) override { |
300 | raw_ostream &OS = errs(); |
301 | OS << "llvm-lto: " ; |
302 | switch (DI.getSeverity()) { |
303 | case DS_Error: |
304 | OS << "error" ; |
305 | break; |
306 | case DS_Warning: |
307 | OS << "warning" ; |
308 | break; |
309 | case DS_Remark: |
310 | OS << "remark" ; |
311 | break; |
312 | case DS_Note: |
313 | OS << "note" ; |
314 | break; |
315 | } |
316 | if (!CurrentActivity.empty()) |
317 | OS << ' ' << CurrentActivity; |
318 | OS << ": " ; |
319 | |
320 | DiagnosticPrinterRawOStream DP(OS); |
321 | DI.print(DP); |
322 | OS << '\n'; |
323 | |
324 | if (DI.getSeverity() == DS_Error) |
325 | exit(status: 1); |
326 | return true; |
327 | } |
328 | }; |
329 | } |
330 | |
331 | static void error(const Twine &Msg) { |
332 | errs() << "llvm-lto: " << Msg << '\n'; |
333 | exit(status: 1); |
334 | } |
335 | |
336 | static void error(std::error_code EC, const Twine &Prefix) { |
337 | if (EC) |
338 | error(Msg: Prefix + ": " + EC.message()); |
339 | } |
340 | |
341 | template <typename T> |
342 | static void error(const ErrorOr<T> &V, const Twine &Prefix) { |
343 | error(V.getError(), Prefix); |
344 | } |
345 | |
346 | static void maybeVerifyModule(const Module &Mod) { |
347 | if (!DisableVerify && verifyModule(M: Mod, OS: &errs())) |
348 | error(Msg: "Broken Module" ); |
349 | } |
350 | |
351 | static std::unique_ptr<LTOModule> |
352 | getLocalLTOModule(StringRef Path, std::unique_ptr<MemoryBuffer> &Buffer, |
353 | const TargetOptions &Options) { |
354 | ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr = |
355 | MemoryBuffer::getFile(Filename: Path); |
356 | error(V: BufferOrErr, Prefix: "error loading file '" + Path + "'" ); |
357 | Buffer = std::move(BufferOrErr.get()); |
358 | CurrentActivity = ("loading file '" + Path + "'" ).str(); |
359 | std::unique_ptr<LLVMContext> Context = std::make_unique<LLVMContext>(); |
360 | Context->setDiagnosticHandler(DH: std::make_unique<LLVMLTODiagnosticHandler>(), |
361 | RespectFilters: true); |
362 | ErrorOr<std::unique_ptr<LTOModule>> Ret = LTOModule::createInLocalContext( |
363 | Context: std::move(Context), mem: Buffer->getBufferStart(), length: Buffer->getBufferSize(), |
364 | options: Options, path: Path); |
365 | CurrentActivity = "" ; |
366 | maybeVerifyModule(Mod: (*Ret)->getModule()); |
367 | return std::move(*Ret); |
368 | } |
369 | |
370 | /// Print some statistics on the index for each input files. |
371 | static void printIndexStats() { |
372 | for (auto &Filename : InputFilenames) { |
373 | ExitOnError ExitOnErr("llvm-lto: error loading file '" + Filename + "': " ); |
374 | std::unique_ptr<ModuleSummaryIndex> Index = |
375 | ExitOnErr(getModuleSummaryIndexForFile(Path: Filename)); |
376 | // Skip files without a module summary. |
377 | if (!Index) |
378 | report_fatal_error(reason: Twine(Filename) + " does not contain an index" ); |
379 | |
380 | unsigned Calls = 0, Refs = 0, Functions = 0, Alias = 0, Globals = 0; |
381 | for (auto &Summaries : *Index) { |
382 | for (auto &Summary : Summaries.second.SummaryList) { |
383 | Refs += Summary->refs().size(); |
384 | if (auto *FuncSummary = dyn_cast<FunctionSummary>(Val: Summary.get())) { |
385 | Functions++; |
386 | Calls += FuncSummary->calls().size(); |
387 | } else if (isa<AliasSummary>(Val: Summary.get())) |
388 | Alias++; |
389 | else |
390 | Globals++; |
391 | } |
392 | } |
393 | outs() << "Index " << Filename << " contains " |
394 | << (Alias + Globals + Functions) << " nodes (" << Functions |
395 | << " functions, " << Alias << " alias, " << Globals |
396 | << " globals) and " << (Calls + Refs) << " edges (" << Refs |
397 | << " refs and " << Calls << " calls)\n" ; |
398 | } |
399 | } |
400 | |
401 | /// Print the lto symbol attributes. |
402 | static void printLTOSymbolAttributes(lto_symbol_attributes Attrs) { |
403 | outs() << "{ " ; |
404 | unsigned Permission = Attrs & LTO_SYMBOL_PERMISSIONS_MASK; |
405 | switch (Permission) { |
406 | case LTO_SYMBOL_PERMISSIONS_CODE: |
407 | outs() << "function " ; |
408 | break; |
409 | case LTO_SYMBOL_PERMISSIONS_DATA: |
410 | outs() << "data " ; |
411 | break; |
412 | case LTO_SYMBOL_PERMISSIONS_RODATA: |
413 | outs() << "constant " ; |
414 | break; |
415 | } |
416 | unsigned Definition = Attrs & LTO_SYMBOL_DEFINITION_MASK; |
417 | switch (Definition) { |
418 | case LTO_SYMBOL_DEFINITION_REGULAR: |
419 | outs() << "defined " ; |
420 | break; |
421 | case LTO_SYMBOL_DEFINITION_TENTATIVE: |
422 | outs() << "common " ; |
423 | break; |
424 | case LTO_SYMBOL_DEFINITION_WEAK: |
425 | outs() << "weak " ; |
426 | break; |
427 | case LTO_SYMBOL_DEFINITION_UNDEFINED: |
428 | outs() << "extern " ; |
429 | break; |
430 | case LTO_SYMBOL_DEFINITION_WEAKUNDEF: |
431 | outs() << "extern-weak " ; |
432 | break; |
433 | } |
434 | unsigned Scope = Attrs & LTO_SYMBOL_SCOPE_MASK; |
435 | switch (Scope) { |
436 | case LTO_SYMBOL_SCOPE_INTERNAL: |
437 | outs() << "internal " ; |
438 | break; |
439 | case LTO_SYMBOL_SCOPE_HIDDEN: |
440 | outs() << "hidden " ; |
441 | break; |
442 | case LTO_SYMBOL_SCOPE_PROTECTED: |
443 | outs() << "protected " ; |
444 | break; |
445 | case LTO_SYMBOL_SCOPE_DEFAULT: |
446 | outs() << "default " ; |
447 | break; |
448 | case LTO_SYMBOL_SCOPE_DEFAULT_CAN_BE_HIDDEN: |
449 | outs() << "omitted " ; |
450 | break; |
451 | } |
452 | if (Attrs & LTO_SYMBOL_COMDAT) |
453 | outs() << "comdat " ; |
454 | if (Attrs & LTO_SYMBOL_ALIAS) |
455 | outs() << "alias " ; |
456 | outs() << "}" ; |
457 | } |
458 | |
459 | /// Load each IR file and dump certain information based on active flags. |
460 | /// |
461 | /// The main point here is to provide lit-testable coverage for the LTOModule |
462 | /// functionality that's exposed by the C API. Moreover, this provides testing |
463 | /// coverage for modules that have been created in their own contexts. |
464 | static void testLTOModule(const TargetOptions &Options) { |
465 | for (auto &Filename : InputFilenames) { |
466 | std::unique_ptr<MemoryBuffer> Buffer; |
467 | std::unique_ptr<LTOModule> Module = |
468 | getLocalLTOModule(Path: Filename, Buffer, Options); |
469 | |
470 | if (ListSymbolsOnly) { |
471 | // List the symbols. |
472 | outs() << Filename << ":\n" ; |
473 | for (int I = 0, E = Module->getSymbolCount(); I != E; ++I) { |
474 | outs() << Module->getSymbolName(index: I) << " " ; |
475 | printLTOSymbolAttributes(Attrs: Module->getSymbolAttributes(index: I)); |
476 | outs() << "\n" ; |
477 | } |
478 | for (int I = 0, E = Module->getAsmUndefSymbolCount(); I != E; ++I) |
479 | outs() << Module->getAsmUndefSymbolName(index: I) << " { asm extern }\n" ; |
480 | } |
481 | if (QueryHasCtorDtor) |
482 | outs() << Filename |
483 | << ": hasCtorDtor = " << (Module->hasCtorDtor() ? "true" : "false" ) |
484 | << "\n" ; |
485 | } |
486 | } |
487 | |
488 | static std::unique_ptr<MemoryBuffer> loadFile(StringRef Filename) { |
489 | ExitOnError ExitOnErr("llvm-lto: error loading file '" + Filename.str() + |
490 | "': " ); |
491 | return ExitOnErr(errorOrToExpected(EO: MemoryBuffer::getFileOrSTDIN(Filename))); |
492 | } |
493 | |
494 | static void listDependentLibraries() { |
495 | for (auto &Filename : InputFilenames) { |
496 | auto Buffer = loadFile(Filename); |
497 | std::string E; |
498 | std::unique_ptr<lto::InputFile> Input(LTOModule::createInputFile( |
499 | buffer: Buffer->getBufferStart(), buffer_size: Buffer->getBufferSize(), path: Filename.c_str(), |
500 | out_error&: E)); |
501 | if (!Input) |
502 | error(Msg: E); |
503 | |
504 | // List the dependent libraries. |
505 | outs() << Filename << ":\n" ; |
506 | for (size_t I = 0, C = LTOModule::getDependentLibraryCount(input: Input.get()); |
507 | I != C; ++I) { |
508 | size_t L = 0; |
509 | const char *S = LTOModule::getDependentLibrary(input: Input.get(), index: I, size: &L); |
510 | assert(S); |
511 | outs() << StringRef(S, L) << "\n" ; |
512 | } |
513 | } |
514 | } |
515 | |
516 | static void printMachOCPUOnly() { |
517 | LLVMContext Context; |
518 | Context.setDiagnosticHandler(DH: std::make_unique<LLVMLTODiagnosticHandler>(), |
519 | RespectFilters: true); |
520 | TargetOptions Options = codegen::InitTargetOptionsFromCodeGenFlags(TheTriple: Triple()); |
521 | for (auto &Filename : InputFilenames) { |
522 | ErrorOr<std::unique_ptr<LTOModule>> ModuleOrErr = |
523 | LTOModule::createFromFile(Context, path: Filename, options: Options); |
524 | if (!ModuleOrErr) |
525 | error(V: ModuleOrErr, Prefix: "llvm-lto: " ); |
526 | |
527 | Expected<uint32_t> CPUType = (*ModuleOrErr)->getMachOCPUType(); |
528 | Expected<uint32_t> CPUSubType = (*ModuleOrErr)->getMachOCPUSubType(); |
529 | if (!CPUType) |
530 | error(Msg: "Error while printing mach-o cputype: " + |
531 | toString(E: CPUType.takeError())); |
532 | if (!CPUSubType) |
533 | error(Msg: "Error while printing mach-o cpusubtype: " + |
534 | toString(E: CPUSubType.takeError())); |
535 | outs() << llvm::format(Fmt: "%s:\ncputype: %u\ncpusubtype: %u\n" , |
536 | Vals: Filename.c_str(), Vals: *CPUType, Vals: *CPUSubType); |
537 | } |
538 | } |
539 | |
540 | /// Create a combined index file from the input IR files and write it. |
541 | /// |
542 | /// This is meant to enable testing of ThinLTO combined index generation, |
543 | /// currently available via the gold plugin via -thinlto. |
544 | static void createCombinedModuleSummaryIndex() { |
545 | ModuleSummaryIndex CombinedIndex(/*HaveGVs=*/false); |
546 | for (auto &Filename : InputFilenames) { |
547 | ExitOnError ExitOnErr("llvm-lto: error loading file '" + Filename + "': " ); |
548 | std::unique_ptr<MemoryBuffer> MB = |
549 | ExitOnErr(errorOrToExpected(EO: MemoryBuffer::getFileOrSTDIN(Filename))); |
550 | ExitOnErr(readModuleSummaryIndex(Buffer: *MB, CombinedIndex)); |
551 | } |
552 | // In order to use this index for testing, specifically import testing, we |
553 | // need to update any indirect call edges created from SamplePGO, so that they |
554 | // point to the correct GUIDs. |
555 | updateIndirectCalls(Index&: CombinedIndex); |
556 | std::error_code EC; |
557 | assert(!OutputFilename.empty()); |
558 | raw_fd_ostream OS(OutputFilename + ".thinlto.bc" , EC, |
559 | sys::fs::OpenFlags::OF_None); |
560 | error(EC, Prefix: "error opening the file '" + OutputFilename + ".thinlto.bc'" ); |
561 | writeIndexToFile(Index: CombinedIndex, Out&: OS); |
562 | OS.close(); |
563 | } |
564 | |
565 | /// Parse the thinlto_prefix_replace option into the \p OldPrefix and |
566 | /// \p NewPrefix strings, if it was specified. |
567 | static void getThinLTOOldAndNewPrefix(std::string &OldPrefix, |
568 | std::string &NewPrefix) { |
569 | assert(ThinLTOPrefixReplace.empty() || |
570 | ThinLTOPrefixReplace.find(';') != StringRef::npos); |
571 | StringRef PrefixReplace = ThinLTOPrefixReplace; |
572 | std::pair<StringRef, StringRef> Split = PrefixReplace.split(Separator: ";" ); |
573 | OldPrefix = Split.first.str(); |
574 | NewPrefix = Split.second.str(); |
575 | } |
576 | |
577 | /// Given the original \p Path to an output file, replace any path |
578 | /// prefix matching \p OldPrefix with \p NewPrefix. Also, create the |
579 | /// resulting directory if it does not yet exist. |
580 | static std::string getThinLTOOutputFile(StringRef Path, StringRef OldPrefix, |
581 | StringRef NewPrefix) { |
582 | if (OldPrefix.empty() && NewPrefix.empty()) |
583 | return std::string(Path); |
584 | SmallString<128> NewPath(Path); |
585 | llvm::sys::path::replace_path_prefix(Path&: NewPath, OldPrefix, NewPrefix); |
586 | StringRef ParentPath = llvm::sys::path::parent_path(path: NewPath.str()); |
587 | if (!ParentPath.empty()) { |
588 | // Make sure the new directory exists, creating it if necessary. |
589 | if (std::error_code EC = llvm::sys::fs::create_directories(path: ParentPath)) |
590 | error(EC, Prefix: "error creating the directory '" + ParentPath + "'" ); |
591 | } |
592 | return std::string(NewPath); |
593 | } |
594 | |
595 | namespace thinlto { |
596 | |
597 | std::vector<std::unique_ptr<MemoryBuffer>> |
598 | loadAllFilesForIndex(const ModuleSummaryIndex &Index) { |
599 | std::vector<std::unique_ptr<MemoryBuffer>> InputBuffers; |
600 | |
601 | for (auto &ModPath : Index.modulePaths()) { |
602 | const auto &Filename = ModPath.first(); |
603 | std::string CurrentActivity = ("loading file '" + Filename + "'" ).str(); |
604 | auto InputOrErr = MemoryBuffer::getFile(Filename); |
605 | error(V: InputOrErr, Prefix: "error " + CurrentActivity); |
606 | InputBuffers.push_back(x: std::move(*InputOrErr)); |
607 | } |
608 | return InputBuffers; |
609 | } |
610 | |
611 | std::unique_ptr<ModuleSummaryIndex> loadCombinedIndex() { |
612 | if (ThinLTOIndex.empty()) |
613 | report_fatal_error(reason: "Missing -thinlto-index for ThinLTO promotion stage" ); |
614 | ExitOnError ExitOnErr("llvm-lto: error loading file '" + ThinLTOIndex + |
615 | "': " ); |
616 | return ExitOnErr(getModuleSummaryIndexForFile(Path: ThinLTOIndex)); |
617 | } |
618 | |
619 | static std::unique_ptr<lto::InputFile> loadInputFile(MemoryBufferRef Buffer) { |
620 | ExitOnError ExitOnErr("llvm-lto: error loading input '" + |
621 | Buffer.getBufferIdentifier().str() + "': " ); |
622 | return ExitOnErr(lto::InputFile::create(Object: Buffer)); |
623 | } |
624 | |
625 | static std::unique_ptr<Module> loadModuleFromInput(lto::InputFile &File, |
626 | LLVMContext &CTX) { |
627 | auto &Mod = File.getSingleBitcodeModule(); |
628 | auto ModuleOrErr = Mod.parseModule(Context&: CTX); |
629 | if (!ModuleOrErr) { |
630 | handleAllErrors(E: ModuleOrErr.takeError(), Handlers: [&](ErrorInfoBase &EIB) { |
631 | SMDiagnostic Err = SMDiagnostic(Mod.getModuleIdentifier(), |
632 | SourceMgr::DK_Error, EIB.message()); |
633 | Err.print(ProgName: "llvm-lto" , S&: errs()); |
634 | }); |
635 | report_fatal_error(reason: "Can't load module, abort." ); |
636 | } |
637 | maybeVerifyModule(Mod: **ModuleOrErr); |
638 | if (ThinLTOModuleId.getNumOccurrences()) { |
639 | if (InputFilenames.size() != 1) |
640 | report_fatal_error(reason: "Can't override the module id for multiple files" ); |
641 | (*ModuleOrErr)->setModuleIdentifier(ThinLTOModuleId); |
642 | } |
643 | return std::move(*ModuleOrErr); |
644 | } |
645 | |
646 | static void writeModuleToFile(Module &TheModule, StringRef Filename) { |
647 | std::error_code EC; |
648 | raw_fd_ostream OS(Filename, EC, sys::fs::OpenFlags::OF_None); |
649 | error(EC, Prefix: "error opening the file '" + Filename + "'" ); |
650 | maybeVerifyModule(Mod: TheModule); |
651 | WriteBitcodeToFile(M: TheModule, Out&: OS, /* ShouldPreserveUseListOrder */ true); |
652 | } |
653 | |
654 | class ThinLTOProcessing { |
655 | public: |
656 | ThinLTOCodeGenerator ThinGenerator; |
657 | |
658 | ThinLTOProcessing(const TargetOptions &Options) { |
659 | ThinGenerator.setCodePICModel(codegen::getExplicitRelocModel()); |
660 | ThinGenerator.setTargetOptions(Options); |
661 | ThinGenerator.setCacheDir(ThinLTOCacheDir); |
662 | ThinGenerator.setCachePruningInterval(ThinLTOCachePruningInterval); |
663 | ThinGenerator.setCacheEntryExpiration(ThinLTOCacheEntryExpiration); |
664 | ThinGenerator.setCacheMaxSizeFiles(ThinLTOCacheMaxSizeFiles); |
665 | ThinGenerator.setCacheMaxSizeBytes(ThinLTOCacheMaxSizeBytes); |
666 | ThinGenerator.setFreestanding(EnableFreestanding); |
667 | ThinGenerator.setDebugPassManager(DebugPassManager); |
668 | |
669 | // Add all the exported symbols to the table of symbols to preserve. |
670 | for (unsigned i = 0; i < ExportedSymbols.size(); ++i) |
671 | ThinGenerator.preserveSymbol(Name: ExportedSymbols[i]); |
672 | } |
673 | |
674 | void run() { |
675 | switch (ThinLTOMode) { |
676 | case THINLINK: |
677 | return thinLink(); |
678 | case THINDISTRIBUTE: |
679 | return distributedIndexes(); |
680 | case THINEMITIMPORTS: |
681 | return emitImports(); |
682 | case THINPROMOTE: |
683 | return promote(); |
684 | case THINIMPORT: |
685 | return import(); |
686 | case THININTERNALIZE: |
687 | return internalize(); |
688 | case THINOPT: |
689 | return optimize(); |
690 | case THINCODEGEN: |
691 | return codegen(); |
692 | case THINALL: |
693 | return runAll(); |
694 | } |
695 | } |
696 | |
697 | private: |
698 | /// Load the input files, create the combined index, and write it out. |
699 | void thinLink() { |
700 | // Perform "ThinLink": just produce the index |
701 | if (OutputFilename.empty()) |
702 | report_fatal_error( |
703 | reason: "OutputFilename is necessary to store the combined index.\n" ); |
704 | |
705 | LLVMContext Ctx; |
706 | std::vector<std::unique_ptr<MemoryBuffer>> InputBuffers; |
707 | for (unsigned i = 0; i < InputFilenames.size(); ++i) { |
708 | auto &Filename = InputFilenames[i]; |
709 | std::string CurrentActivity = "loading file '" + Filename + "'" ; |
710 | auto InputOrErr = MemoryBuffer::getFile(Filename); |
711 | error(V: InputOrErr, Prefix: "error " + CurrentActivity); |
712 | InputBuffers.push_back(x: std::move(*InputOrErr)); |
713 | ThinGenerator.addModule(Identifier: Filename, Data: InputBuffers.back()->getBuffer()); |
714 | } |
715 | |
716 | auto CombinedIndex = ThinGenerator.linkCombinedIndex(); |
717 | if (!CombinedIndex) |
718 | report_fatal_error(reason: "ThinLink didn't create an index" ); |
719 | std::error_code EC; |
720 | raw_fd_ostream OS(OutputFilename, EC, sys::fs::OpenFlags::OF_None); |
721 | error(EC, Prefix: "error opening the file '" + OutputFilename + "'" ); |
722 | writeIndexToFile(Index: *CombinedIndex, Out&: OS); |
723 | } |
724 | |
725 | /// Load the combined index from disk, then compute and generate |
726 | /// individual index files suitable for ThinLTO distributed backend builds |
727 | /// on the files mentioned on the command line (these must match the index |
728 | /// content). |
729 | void distributedIndexes() { |
730 | if (InputFilenames.size() != 1 && !OutputFilename.empty()) |
731 | report_fatal_error(reason: "Can't handle a single output filename and multiple " |
732 | "input files, do not provide an output filename and " |
733 | "the output files will be suffixed from the input " |
734 | "ones." ); |
735 | |
736 | std::string OldPrefix, NewPrefix; |
737 | getThinLTOOldAndNewPrefix(OldPrefix, NewPrefix); |
738 | |
739 | auto Index = loadCombinedIndex(); |
740 | for (auto &Filename : InputFilenames) { |
741 | LLVMContext Ctx; |
742 | auto Buffer = loadFile(Filename); |
743 | auto Input = loadInputFile(Buffer: Buffer->getMemBufferRef()); |
744 | auto TheModule = loadModuleFromInput(File&: *Input, CTX&: Ctx); |
745 | |
746 | // Build a map of module to the GUIDs and summary objects that should |
747 | // be written to its index. |
748 | ModuleToSummariesForIndexTy ModuleToSummariesForIndex; |
749 | GVSummaryPtrSet DecSummaries; |
750 | ThinGenerator.gatherImportedSummariesForModule( |
751 | Module&: *TheModule, Index&: *Index, ModuleToSummariesForIndex, DecSummaries, File: *Input); |
752 | |
753 | std::string OutputName = OutputFilename; |
754 | if (OutputName.empty()) { |
755 | OutputName = Filename + ".thinlto.bc" ; |
756 | } |
757 | OutputName = getThinLTOOutputFile(Path: OutputName, OldPrefix, NewPrefix); |
758 | std::error_code EC; |
759 | raw_fd_ostream OS(OutputName, EC, sys::fs::OpenFlags::OF_None); |
760 | error(EC, Prefix: "error opening the file '" + OutputName + "'" ); |
761 | writeIndexToFile(Index: *Index, Out&: OS, ModuleToSummariesForIndex: &ModuleToSummariesForIndex, DecSummaries: &DecSummaries); |
762 | } |
763 | } |
764 | |
765 | /// Load the combined index from disk, compute the imports, and emit |
766 | /// the import file lists for each module to disk. |
767 | void emitImports() { |
768 | if (InputFilenames.size() != 1 && !OutputFilename.empty()) |
769 | report_fatal_error(reason: "Can't handle a single output filename and multiple " |
770 | "input files, do not provide an output filename and " |
771 | "the output files will be suffixed from the input " |
772 | "ones." ); |
773 | |
774 | std::string OldPrefix, NewPrefix; |
775 | getThinLTOOldAndNewPrefix(OldPrefix, NewPrefix); |
776 | |
777 | auto Index = loadCombinedIndex(); |
778 | for (auto &Filename : InputFilenames) { |
779 | LLVMContext Ctx; |
780 | auto Buffer = loadFile(Filename); |
781 | auto Input = loadInputFile(Buffer: Buffer->getMemBufferRef()); |
782 | auto TheModule = loadModuleFromInput(File&: *Input, CTX&: Ctx); |
783 | std::string OutputName = OutputFilename; |
784 | if (OutputName.empty()) { |
785 | OutputName = Filename + ".imports" ; |
786 | } |
787 | OutputName = |
788 | getThinLTOOutputFile(Path: OutputName, OldPrefix, NewPrefix); |
789 | ThinGenerator.emitImports(Module&: *TheModule, OutputName, Index&: *Index, File: *Input); |
790 | } |
791 | } |
792 | |
793 | /// Load the combined index from disk, then load every file referenced by |
794 | /// the index and add them to the generator, finally perform the promotion |
795 | /// on the files mentioned on the command line (these must match the index |
796 | /// content). |
797 | void promote() { |
798 | if (InputFilenames.size() != 1 && !OutputFilename.empty()) |
799 | report_fatal_error(reason: "Can't handle a single output filename and multiple " |
800 | "input files, do not provide an output filename and " |
801 | "the output files will be suffixed from the input " |
802 | "ones." ); |
803 | |
804 | auto Index = loadCombinedIndex(); |
805 | for (auto &Filename : InputFilenames) { |
806 | LLVMContext Ctx; |
807 | auto Buffer = loadFile(Filename); |
808 | auto Input = loadInputFile(Buffer: Buffer->getMemBufferRef()); |
809 | auto TheModule = loadModuleFromInput(File&: *Input, CTX&: Ctx); |
810 | |
811 | ThinGenerator.promote(Module&: *TheModule, Index&: *Index, File: *Input); |
812 | |
813 | std::string OutputName = OutputFilename; |
814 | if (OutputName.empty()) { |
815 | OutputName = Filename + ".thinlto.promoted.bc" ; |
816 | } |
817 | writeModuleToFile(TheModule&: *TheModule, Filename: OutputName); |
818 | } |
819 | } |
820 | |
821 | /// Load the combined index from disk, then load every file referenced by |
822 | /// the index and add them to the generator, then performs the promotion and |
823 | /// cross module importing on the files mentioned on the command line |
824 | /// (these must match the index content). |
825 | void import() { |
826 | if (InputFilenames.size() != 1 && !OutputFilename.empty()) |
827 | report_fatal_error(reason: "Can't handle a single output filename and multiple " |
828 | "input files, do not provide an output filename and " |
829 | "the output files will be suffixed from the input " |
830 | "ones." ); |
831 | |
832 | auto Index = loadCombinedIndex(); |
833 | auto InputBuffers = loadAllFilesForIndex(Index: *Index); |
834 | for (auto &MemBuffer : InputBuffers) |
835 | ThinGenerator.addModule(Identifier: MemBuffer->getBufferIdentifier(), |
836 | Data: MemBuffer->getBuffer()); |
837 | |
838 | for (auto &Filename : InputFilenames) { |
839 | LLVMContext Ctx; |
840 | auto Buffer = loadFile(Filename); |
841 | auto Input = loadInputFile(Buffer: Buffer->getMemBufferRef()); |
842 | auto TheModule = loadModuleFromInput(File&: *Input, CTX&: Ctx); |
843 | |
844 | ThinGenerator.crossModuleImport(Module&: *TheModule, Index&: *Index, File: *Input); |
845 | |
846 | std::string OutputName = OutputFilename; |
847 | if (OutputName.empty()) { |
848 | OutputName = Filename + ".thinlto.imported.bc" ; |
849 | } |
850 | writeModuleToFile(TheModule&: *TheModule, Filename: OutputName); |
851 | } |
852 | } |
853 | |
854 | void internalize() { |
855 | if (InputFilenames.size() != 1 && !OutputFilename.empty()) |
856 | report_fatal_error(reason: "Can't handle a single output filename and multiple " |
857 | "input files, do not provide an output filename and " |
858 | "the output files will be suffixed from the input " |
859 | "ones." ); |
860 | |
861 | if (ExportedSymbols.empty()) |
862 | errs() << "Warning: -internalize will not perform without " |
863 | "-exported-symbol\n" ; |
864 | |
865 | auto Index = loadCombinedIndex(); |
866 | auto InputBuffers = loadAllFilesForIndex(Index: *Index); |
867 | for (auto &MemBuffer : InputBuffers) |
868 | ThinGenerator.addModule(Identifier: MemBuffer->getBufferIdentifier(), |
869 | Data: MemBuffer->getBuffer()); |
870 | |
871 | for (auto &Filename : InputFilenames) { |
872 | LLVMContext Ctx; |
873 | auto Buffer = loadFile(Filename); |
874 | auto Input = loadInputFile(Buffer: Buffer->getMemBufferRef()); |
875 | auto TheModule = loadModuleFromInput(File&: *Input, CTX&: Ctx); |
876 | |
877 | ThinGenerator.internalize(Module&: *TheModule, Index&: *Index, File: *Input); |
878 | |
879 | std::string OutputName = OutputFilename; |
880 | if (OutputName.empty()) { |
881 | OutputName = Filename + ".thinlto.internalized.bc" ; |
882 | } |
883 | writeModuleToFile(TheModule&: *TheModule, Filename: OutputName); |
884 | } |
885 | } |
886 | |
887 | void optimize() { |
888 | if (InputFilenames.size() != 1 && !OutputFilename.empty()) |
889 | report_fatal_error(reason: "Can't handle a single output filename and multiple " |
890 | "input files, do not provide an output filename and " |
891 | "the output files will be suffixed from the input " |
892 | "ones." ); |
893 | if (!ThinLTOIndex.empty()) |
894 | errs() << "Warning: -thinlto-index ignored for optimize stage" ; |
895 | |
896 | for (auto &Filename : InputFilenames) { |
897 | LLVMContext Ctx; |
898 | auto Buffer = loadFile(Filename); |
899 | auto Input = loadInputFile(Buffer: Buffer->getMemBufferRef()); |
900 | auto TheModule = loadModuleFromInput(File&: *Input, CTX&: Ctx); |
901 | |
902 | ThinGenerator.optimize(Module&: *TheModule); |
903 | |
904 | std::string OutputName = OutputFilename; |
905 | if (OutputName.empty()) { |
906 | OutputName = Filename + ".thinlto.imported.bc" ; |
907 | } |
908 | writeModuleToFile(TheModule&: *TheModule, Filename: OutputName); |
909 | } |
910 | } |
911 | |
912 | void codegen() { |
913 | if (InputFilenames.size() != 1 && !OutputFilename.empty()) |
914 | report_fatal_error(reason: "Can't handle a single output filename and multiple " |
915 | "input files, do not provide an output filename and " |
916 | "the output files will be suffixed from the input " |
917 | "ones." ); |
918 | if (!ThinLTOIndex.empty()) |
919 | errs() << "Warning: -thinlto-index ignored for codegen stage" ; |
920 | |
921 | std::vector<std::unique_ptr<MemoryBuffer>> InputBuffers; |
922 | for (auto &Filename : InputFilenames) { |
923 | LLVMContext Ctx; |
924 | auto InputOrErr = MemoryBuffer::getFile(Filename); |
925 | error(V: InputOrErr, Prefix: "error " + CurrentActivity); |
926 | InputBuffers.push_back(x: std::move(*InputOrErr)); |
927 | ThinGenerator.addModule(Identifier: Filename, Data: InputBuffers.back()->getBuffer()); |
928 | } |
929 | ThinGenerator.setCodeGenOnly(true); |
930 | ThinGenerator.run(); |
931 | for (auto BinName : |
932 | zip(t&: ThinGenerator.getProducedBinaries(), u&: InputFilenames)) { |
933 | std::string OutputName = OutputFilename; |
934 | if (OutputName.empty()) |
935 | OutputName = std::get<1>(t&: BinName) + ".thinlto.o" ; |
936 | else if (OutputName == "-" ) { |
937 | outs() << std::get<0>(t&: BinName)->getBuffer(); |
938 | return; |
939 | } |
940 | |
941 | std::error_code EC; |
942 | raw_fd_ostream OS(OutputName, EC, sys::fs::OpenFlags::OF_None); |
943 | error(EC, Prefix: "error opening the file '" + OutputName + "'" ); |
944 | OS << std::get<0>(t&: BinName)->getBuffer(); |
945 | } |
946 | } |
947 | |
948 | /// Full ThinLTO process |
949 | void runAll() { |
950 | if (!OutputFilename.empty()) |
951 | report_fatal_error(reason: "Do not provide an output filename for ThinLTO " |
952 | " processing, the output files will be suffixed from " |
953 | "the input ones." ); |
954 | |
955 | if (!ThinLTOIndex.empty()) |
956 | errs() << "Warning: -thinlto-index ignored for full ThinLTO process" ; |
957 | |
958 | LLVMContext Ctx; |
959 | std::vector<std::unique_ptr<MemoryBuffer>> InputBuffers; |
960 | for (unsigned i = 0; i < InputFilenames.size(); ++i) { |
961 | auto &Filename = InputFilenames[i]; |
962 | std::string CurrentActivity = "loading file '" + Filename + "'" ; |
963 | auto InputOrErr = MemoryBuffer::getFile(Filename); |
964 | error(V: InputOrErr, Prefix: "error " + CurrentActivity); |
965 | InputBuffers.push_back(x: std::move(*InputOrErr)); |
966 | ThinGenerator.addModule(Identifier: Filename, Data: InputBuffers.back()->getBuffer()); |
967 | } |
968 | |
969 | if (!ThinLTOSaveTempsPrefix.empty()) |
970 | ThinGenerator.setSaveTempsDir(ThinLTOSaveTempsPrefix); |
971 | |
972 | if (!ThinLTOGeneratedObjectsDir.empty()) { |
973 | ThinGenerator.setGeneratedObjectsDirectory(ThinLTOGeneratedObjectsDir); |
974 | ThinGenerator.run(); |
975 | return; |
976 | } |
977 | |
978 | ThinGenerator.run(); |
979 | |
980 | auto &Binaries = ThinGenerator.getProducedBinaries(); |
981 | if (Binaries.size() != InputFilenames.size()) |
982 | report_fatal_error(reason: "Number of output objects does not match the number " |
983 | "of inputs" ); |
984 | |
985 | for (unsigned BufID = 0; BufID < Binaries.size(); ++BufID) { |
986 | auto OutputName = InputFilenames[BufID] + ".thinlto.o" ; |
987 | std::error_code EC; |
988 | raw_fd_ostream OS(OutputName, EC, sys::fs::OpenFlags::OF_None); |
989 | error(EC, Prefix: "error opening the file '" + OutputName + "'" ); |
990 | OS << Binaries[BufID]->getBuffer(); |
991 | } |
992 | } |
993 | |
994 | /// Load the combined index from disk, then load every file referenced by |
995 | }; |
996 | |
997 | } // end namespace thinlto |
998 | |
999 | int main(int argc, char **argv) { |
1000 | InitLLVM X(argc, argv); |
1001 | cl::HideUnrelatedOptions(Categories: {<OCategory, &getColorCategory()}); |
1002 | cl::ParseCommandLineOptions(argc, argv, Overview: "llvm LTO linker\n" ); |
1003 | |
1004 | if (OptLevel < '0' || OptLevel > '3') |
1005 | error(Msg: "optimization level must be between 0 and 3" ); |
1006 | |
1007 | // Initialize the configured targets. |
1008 | InitializeAllTargets(); |
1009 | InitializeAllTargetMCs(); |
1010 | InitializeAllAsmPrinters(); |
1011 | InitializeAllAsmParsers(); |
1012 | |
1013 | // set up the TargetOptions for the machine |
1014 | TargetOptions Options = codegen::InitTargetOptionsFromCodeGenFlags(TheTriple: Triple()); |
1015 | |
1016 | if (ListSymbolsOnly || QueryHasCtorDtor) { |
1017 | testLTOModule(Options); |
1018 | return 0; |
1019 | } |
1020 | |
1021 | if (ListDependentLibrariesOnly) { |
1022 | listDependentLibraries(); |
1023 | return 0; |
1024 | } |
1025 | |
1026 | if (IndexStats) { |
1027 | printIndexStats(); |
1028 | return 0; |
1029 | } |
1030 | |
1031 | if (CheckHasObjC) { |
1032 | for (auto &Filename : InputFilenames) { |
1033 | ExitOnError ExitOnErr(std::string(*argv) + ": error loading file '" + |
1034 | Filename + "': " ); |
1035 | std::unique_ptr<MemoryBuffer> BufferOrErr = |
1036 | ExitOnErr(errorOrToExpected(EO: MemoryBuffer::getFile(Filename))); |
1037 | auto Buffer = std::move(BufferOrErr.get()); |
1038 | if (ExitOnErr(isBitcodeContainingObjCCategory(Buffer: *Buffer))) |
1039 | outs() << "Bitcode " << Filename << " contains ObjC\n" ; |
1040 | else |
1041 | outs() << "Bitcode " << Filename << " does not contain ObjC\n" ; |
1042 | } |
1043 | return 0; |
1044 | } |
1045 | |
1046 | if (PrintMachOCPUOnly) { |
1047 | printMachOCPUOnly(); |
1048 | return 0; |
1049 | } |
1050 | |
1051 | if (ThinLTOMode.getNumOccurrences()) { |
1052 | if (ThinLTOMode.getNumOccurrences() > 1) |
1053 | report_fatal_error(reason: "You can't specify more than one -thinlto-action" ); |
1054 | thinlto::ThinLTOProcessing ThinLTOProcessor(Options); |
1055 | ThinLTOProcessor.run(); |
1056 | return 0; |
1057 | } |
1058 | |
1059 | if (ThinLTO) { |
1060 | createCombinedModuleSummaryIndex(); |
1061 | return 0; |
1062 | } |
1063 | |
1064 | unsigned BaseArg = 0; |
1065 | |
1066 | LLVMContext Context; |
1067 | Context.setDiagnosticHandler(DH: std::make_unique<LLVMLTODiagnosticHandler>(), |
1068 | RespectFilters: true); |
1069 | |
1070 | LTOCodeGenerator CodeGen(Context); |
1071 | CodeGen.setDisableVerify(DisableVerify); |
1072 | |
1073 | if (UseDiagnosticHandler) |
1074 | CodeGen.setDiagnosticHandler(handleDiagnostics, nullptr); |
1075 | |
1076 | CodeGen.setCodePICModel(codegen::getExplicitRelocModel()); |
1077 | CodeGen.setFreestanding(EnableFreestanding); |
1078 | CodeGen.setDebugPassManager(DebugPassManager); |
1079 | |
1080 | CodeGen.setDebugInfo(LTO_DEBUG_MODEL_DWARF); |
1081 | CodeGen.setTargetOptions(Options); |
1082 | CodeGen.setShouldRestoreGlobalsLinkage(RestoreGlobalsLinkage); |
1083 | |
1084 | StringSet<MallocAllocator> DSOSymbolsSet(llvm::from_range, DSOSymbols); |
1085 | |
1086 | std::vector<std::string> KeptDSOSyms; |
1087 | |
1088 | for (unsigned i = BaseArg; i < InputFilenames.size(); ++i) { |
1089 | CurrentActivity = "loading file '" + InputFilenames[i] + "'" ; |
1090 | ErrorOr<std::unique_ptr<LTOModule>> ModuleOrErr = |
1091 | LTOModule::createFromFile(Context, path: InputFilenames[i], options: Options); |
1092 | std::unique_ptr<LTOModule> &Module = *ModuleOrErr; |
1093 | CurrentActivity = "" ; |
1094 | |
1095 | unsigned NumSyms = Module->getSymbolCount(); |
1096 | for (unsigned I = 0; I < NumSyms; ++I) { |
1097 | StringRef Name = Module->getSymbolName(index: I); |
1098 | if (!DSOSymbolsSet.count(Key: Name)) |
1099 | continue; |
1100 | lto_symbol_attributes Attrs = Module->getSymbolAttributes(index: I); |
1101 | unsigned Scope = Attrs & LTO_SYMBOL_SCOPE_MASK; |
1102 | if (Scope != LTO_SYMBOL_SCOPE_DEFAULT_CAN_BE_HIDDEN) |
1103 | KeptDSOSyms.push_back(x: std::string(Name)); |
1104 | } |
1105 | |
1106 | // We use the first input module as the destination module when |
1107 | // SetMergedModule is true. |
1108 | if (SetMergedModule && i == BaseArg) { |
1109 | // Transfer ownership to the code generator. |
1110 | CodeGen.setModule(std::move(Module)); |
1111 | } else if (!CodeGen.addModule(Module.get())) { |
1112 | // Print a message here so that we know addModule() did not abort. |
1113 | error(Msg: "error adding file '" + InputFilenames[i] + "'" ); |
1114 | } |
1115 | } |
1116 | |
1117 | // Add all the exported symbols to the table of symbols to preserve. |
1118 | for (unsigned i = 0; i < ExportedSymbols.size(); ++i) |
1119 | CodeGen.addMustPreserveSymbol(Sym: ExportedSymbols[i]); |
1120 | |
1121 | // Add all the dso symbols to the table of symbols to expose. |
1122 | for (unsigned i = 0; i < KeptDSOSyms.size(); ++i) |
1123 | CodeGen.addMustPreserveSymbol(Sym: KeptDSOSyms[i]); |
1124 | |
1125 | // Set cpu and attrs strings for the default target/subtarget. |
1126 | CodeGen.setCpu(codegen::getMCPU()); |
1127 | |
1128 | CodeGen.setOptLevel(OptLevel - '0'); |
1129 | CodeGen.setAttrs(codegen::getMAttrs()); |
1130 | |
1131 | if (auto FT = codegen::getExplicitFileType()) |
1132 | CodeGen.setFileType(*FT); |
1133 | |
1134 | if (!OutputFilename.empty()) { |
1135 | if (LTOSaveBeforeOpt) |
1136 | CodeGen.setSaveIRBeforeOptPath(OutputFilename + ".0.preopt.bc" ); |
1137 | |
1138 | if (SaveLinkedModuleFile) { |
1139 | std::string ModuleFilename = OutputFilename; |
1140 | ModuleFilename += ".linked.bc" ; |
1141 | |
1142 | if (!CodeGen.writeMergedModules(Path: ModuleFilename)) |
1143 | error(Msg: "writing linked module failed." ); |
1144 | } |
1145 | |
1146 | if (!CodeGen.optimize()) { |
1147 | // Diagnostic messages should have been printed by the handler. |
1148 | error(Msg: "error optimizing the code" ); |
1149 | } |
1150 | |
1151 | if (SaveModuleFile) { |
1152 | std::string ModuleFilename = OutputFilename; |
1153 | ModuleFilename += ".merged.bc" ; |
1154 | |
1155 | if (!CodeGen.writeMergedModules(Path: ModuleFilename)) |
1156 | error(Msg: "writing merged module failed." ); |
1157 | } |
1158 | |
1159 | auto AddStream = |
1160 | [&](size_t Task, |
1161 | const Twine &ModuleName) -> std::unique_ptr<CachedFileStream> { |
1162 | std::string PartFilename = OutputFilename; |
1163 | if (Parallelism != 1) |
1164 | PartFilename += "." + utostr(X: Task); |
1165 | |
1166 | std::error_code EC; |
1167 | auto S = |
1168 | std::make_unique<raw_fd_ostream>(args&: PartFilename, args&: EC, args: sys::fs::OF_None); |
1169 | if (EC) |
1170 | error(Msg: "error opening the file '" + PartFilename + "': " + EC.message()); |
1171 | return std::make_unique<CachedFileStream>(args: std::move(S)); |
1172 | }; |
1173 | |
1174 | if (!CodeGen.compileOptimized(AddStream, ParallelismLevel: Parallelism)) |
1175 | // Diagnostic messages should have been printed by the handler. |
1176 | error(Msg: "error compiling the code" ); |
1177 | |
1178 | } else { |
1179 | if (Parallelism != 1) |
1180 | error(Msg: "-j must be specified together with -o" ); |
1181 | |
1182 | if (SaveModuleFile) |
1183 | error(Msg: ": -save-merged-module must be specified with -o" ); |
1184 | |
1185 | const char *OutputName = nullptr; |
1186 | if (!CodeGen.compile_to_file(Name: &OutputName)) |
1187 | error(Msg: "error compiling the code" ); |
1188 | // Diagnostic messages should have been printed by the handler. |
1189 | |
1190 | outs() << "Wrote native object file '" << OutputName << "'\n" ; |
1191 | } |
1192 | |
1193 | return 0; |
1194 | } |
1195 | |