1 | //===-LTOCodeGenerator.cpp - LLVM Link Time Optimizer ---------------------===// |
---|---|
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 file implements the Link Time Optimization library. This library is |
10 | // intended to be used by linker to optimize code at link time. |
11 | // |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #include "llvm/LTO/legacy/LTOCodeGenerator.h" |
15 | |
16 | #include "llvm/ADT/Statistic.h" |
17 | #include "llvm/ADT/StringExtras.h" |
18 | #include "llvm/Analysis/TargetLibraryInfo.h" |
19 | #include "llvm/Analysis/TargetTransformInfo.h" |
20 | #include "llvm/Bitcode/BitcodeWriter.h" |
21 | #include "llvm/CodeGen/CommandFlags.h" |
22 | #include "llvm/CodeGen/TargetSubtargetInfo.h" |
23 | #include "llvm/Config/config.h" |
24 | #include "llvm/IR/DataLayout.h" |
25 | #include "llvm/IR/DebugInfo.h" |
26 | #include "llvm/IR/DerivedTypes.h" |
27 | #include "llvm/IR/DiagnosticInfo.h" |
28 | #include "llvm/IR/DiagnosticPrinter.h" |
29 | #include "llvm/IR/LLVMContext.h" |
30 | #include "llvm/IR/LLVMRemarkStreamer.h" |
31 | #include "llvm/IR/LegacyPassManager.h" |
32 | #include "llvm/IR/Mangler.h" |
33 | #include "llvm/IR/Module.h" |
34 | #include "llvm/IR/PassTimingInfo.h" |
35 | #include "llvm/IR/Verifier.h" |
36 | #include "llvm/LTO/LTO.h" |
37 | #include "llvm/LTO/LTOBackend.h" |
38 | #include "llvm/LTO/legacy/LTOModule.h" |
39 | #include "llvm/LTO/legacy/UpdateCompilerUsed.h" |
40 | #include "llvm/Linker/Linker.h" |
41 | #include "llvm/MC/TargetRegistry.h" |
42 | #include "llvm/Remarks/HotnessThresholdParser.h" |
43 | #include "llvm/Support/CommandLine.h" |
44 | #include "llvm/Support/FileSystem.h" |
45 | #include "llvm/Support/MemoryBuffer.h" |
46 | #include "llvm/Support/Process.h" |
47 | #include "llvm/Support/Signals.h" |
48 | #include "llvm/Support/ToolOutputFile.h" |
49 | #include "llvm/Support/raw_ostream.h" |
50 | #include "llvm/Target/TargetOptions.h" |
51 | #include "llvm/TargetParser/Host.h" |
52 | #include "llvm/TargetParser/SubtargetFeature.h" |
53 | #include "llvm/Transforms/IPO.h" |
54 | #include "llvm/Transforms/IPO/Internalize.h" |
55 | #include "llvm/Transforms/IPO/WholeProgramDevirt.h" |
56 | #include "llvm/Transforms/Utils/ModuleUtils.h" |
57 | #include <optional> |
58 | #include <system_error> |
59 | using namespace llvm; |
60 | |
61 | const char* LTOCodeGenerator::getVersionString() { |
62 | return PACKAGE_NAME " version "PACKAGE_VERSION; |
63 | } |
64 | |
65 | namespace llvm { |
66 | cl::opt<bool> LTODiscardValueNames( |
67 | "lto-discard-value-names", |
68 | cl::desc("Strip names from Value during LTO (other than GlobalValue)."), |
69 | #ifdef NDEBUG |
70 | cl::init(true), |
71 | #else |
72 | cl::init(Val: false), |
73 | #endif |
74 | cl::Hidden); |
75 | |
76 | cl::opt<bool> RemarksWithHotness( |
77 | "lto-pass-remarks-with-hotness", |
78 | cl::desc("With PGO, include profile count in optimization remarks"), |
79 | cl::Hidden); |
80 | |
81 | cl::opt<std::optional<uint64_t>, false, remarks::HotnessThresholdParser> |
82 | RemarksHotnessThreshold( |
83 | "lto-pass-remarks-hotness-threshold", |
84 | cl::desc("Minimum profile count required for an " |
85 | "optimization remark to be output." |
86 | " Use 'auto' to apply the threshold from profile summary."), |
87 | cl::value_desc("uint or 'auto'"), cl::init(Val: 0), cl::Hidden); |
88 | |
89 | cl::opt<std::string> |
90 | RemarksFilename("lto-pass-remarks-output", |
91 | cl::desc("Output filename for pass remarks"), |
92 | cl::value_desc("filename")); |
93 | |
94 | cl::opt<std::string> |
95 | RemarksPasses("lto-pass-remarks-filter", |
96 | cl::desc("Only record optimization remarks from passes whose " |
97 | "names match the given regular expression"), |
98 | cl::value_desc("regex")); |
99 | |
100 | cl::opt<std::string> RemarksFormat( |
101 | "lto-pass-remarks-format", |
102 | cl::desc("The format used for serializing remarks (default: YAML)"), |
103 | cl::value_desc("format"), cl::init(Val: "yaml")); |
104 | |
105 | static cl::opt<std::string> |
106 | LTOStatsFile("lto-stats-file", |
107 | cl::desc("Save statistics to the specified file"), cl::Hidden); |
108 | |
109 | static cl::opt<std::string> AIXSystemAssemblerPath( |
110 | "lto-aix-system-assembler", |
111 | cl::desc("Path to a system assembler, picked up on AIX only"), |
112 | cl::value_desc("path")); |
113 | |
114 | static cl::opt<bool> |
115 | LTORunCSIRInstr("cs-profile-generate", |
116 | cl::desc("Perform context sensitive PGO instrumentation")); |
117 | |
118 | static cl::opt<std::string> |
119 | LTOCSIRProfile("cs-profile-path", |
120 | cl::desc("Context sensitive profile file path")); |
121 | } // namespace llvm |
122 | |
123 | LTOCodeGenerator::LTOCodeGenerator(LLVMContext &Context) |
124 | : Context(Context), MergedModule(new Module("ld-temp.o", Context)), |
125 | TheLinker(new Linker(*MergedModule)) { |
126 | Context.setDiscardValueNames(LTODiscardValueNames); |
127 | Context.enableDebugTypeODRUniquing(); |
128 | |
129 | Config.CodeModel = std::nullopt; |
130 | Config.StatsFile = LTOStatsFile; |
131 | Config.RunCSIRInstr = LTORunCSIRInstr; |
132 | Config.CSIRProfile = LTOCSIRProfile; |
133 | } |
134 | |
135 | LTOCodeGenerator::~LTOCodeGenerator() = default; |
136 | |
137 | void LTOCodeGenerator::setAsmUndefinedRefs(LTOModule *Mod) { |
138 | AsmUndefinedRefs.insert_range(R: Mod->getAsmUndefinedRefs()); |
139 | } |
140 | |
141 | bool LTOCodeGenerator::addModule(LTOModule *Mod) { |
142 | assert(&Mod->getModule().getContext() == &Context && |
143 | "Expected module in same context"); |
144 | |
145 | bool ret = TheLinker->linkInModule(Src: Mod->takeModule()); |
146 | setAsmUndefinedRefs(Mod); |
147 | |
148 | // We've just changed the input, so let's make sure we verify it. |
149 | HasVerifiedInput = false; |
150 | |
151 | return !ret; |
152 | } |
153 | |
154 | void LTOCodeGenerator::setModule(std::unique_ptr<LTOModule> Mod) { |
155 | assert(&Mod->getModule().getContext() == &Context && |
156 | "Expected module in same context"); |
157 | |
158 | AsmUndefinedRefs.clear(); |
159 | |
160 | MergedModule = Mod->takeModule(); |
161 | TheLinker = std::make_unique<Linker>(args&: *MergedModule); |
162 | setAsmUndefinedRefs(&*Mod); |
163 | |
164 | // We've just changed the input, so let's make sure we verify it. |
165 | HasVerifiedInput = false; |
166 | } |
167 | |
168 | void LTOCodeGenerator::setTargetOptions(const TargetOptions &Options) { |
169 | Config.Options = Options; |
170 | } |
171 | |
172 | void LTOCodeGenerator::setDebugInfo(lto_debug_model Debug) { |
173 | switch (Debug) { |
174 | case LTO_DEBUG_MODEL_NONE: |
175 | EmitDwarfDebugInfo = false; |
176 | return; |
177 | |
178 | case LTO_DEBUG_MODEL_DWARF: |
179 | EmitDwarfDebugInfo = true; |
180 | return; |
181 | } |
182 | llvm_unreachable("Unknown debug format!"); |
183 | } |
184 | |
185 | void LTOCodeGenerator::setOptLevel(unsigned Level) { |
186 | Config.OptLevel = Level; |
187 | Config.PTO.LoopVectorization = Config.OptLevel > 1; |
188 | Config.PTO.SLPVectorization = Config.OptLevel > 1; |
189 | std::optional<CodeGenOptLevel> CGOptLevelOrNone = |
190 | CodeGenOpt::getLevel(OL: Config.OptLevel); |
191 | assert(CGOptLevelOrNone && "Unknown optimization level!"); |
192 | Config.CGOptLevel = *CGOptLevelOrNone; |
193 | } |
194 | |
195 | bool LTOCodeGenerator::writeMergedModules(StringRef Path) { |
196 | if (!determineTarget()) |
197 | return false; |
198 | |
199 | // We always run the verifier once on the merged module. |
200 | verifyMergedModuleOnce(); |
201 | |
202 | // mark which symbols can not be internalized |
203 | applyScopeRestrictions(); |
204 | |
205 | // create output file |
206 | std::error_code EC; |
207 | ToolOutputFile Out(Path, EC, sys::fs::OF_None); |
208 | if (EC) { |
209 | std::string ErrMsg = "could not open bitcode file for writing: "; |
210 | ErrMsg += Path.str() + ": "+ EC.message(); |
211 | emitError(ErrMsg); |
212 | return false; |
213 | } |
214 | |
215 | // write bitcode to it |
216 | WriteBitcodeToFile(M: *MergedModule, Out&: Out.os(), ShouldPreserveUseListOrder: ShouldEmbedUselists); |
217 | Out.os().close(); |
218 | |
219 | if (Out.os().has_error()) { |
220 | std::string ErrMsg = "could not write bitcode file: "; |
221 | ErrMsg += Path.str() + ": "+ Out.os().error().message(); |
222 | emitError(ErrMsg); |
223 | Out.os().clear_error(); |
224 | return false; |
225 | } |
226 | |
227 | Out.keep(); |
228 | return true; |
229 | } |
230 | |
231 | bool LTOCodeGenerator::useAIXSystemAssembler() { |
232 | const auto &Triple = TargetMach->getTargetTriple(); |
233 | return Triple.isOSAIX() && Config.Options.DisableIntegratedAS; |
234 | } |
235 | |
236 | bool LTOCodeGenerator::runAIXSystemAssembler(SmallString<128> &AssemblyFile) { |
237 | assert(useAIXSystemAssembler() && |
238 | "Runing AIX system assembler when integrated assembler is available!"); |
239 | |
240 | // Set the system assembler path. |
241 | SmallString<256> AssemblerPath("/usr/bin/as"); |
242 | if (!llvm::AIXSystemAssemblerPath.empty()) { |
243 | if (llvm::sys::fs::real_path(path: llvm::AIXSystemAssemblerPath, output&: AssemblerPath, |
244 | /* expand_tilde */ true)) { |
245 | emitError( |
246 | ErrMsg: "Cannot find the assembler specified by lto-aix-system-assembler"); |
247 | return false; |
248 | } |
249 | } |
250 | |
251 | // Setup the LDR_CNTRL variable |
252 | std::string LDR_CNTRL_var = "LDR_CNTRL=MAXDATA32=0xA0000000@DSA"; |
253 | if (std::optional<std::string> V = sys::Process::GetEnv(name: "LDR_CNTRL")) |
254 | LDR_CNTRL_var += ("@"+ *V); |
255 | |
256 | // Prepare inputs for the assember. |
257 | const auto &Triple = TargetMach->getTargetTriple(); |
258 | const char *Arch = Triple.isArch64Bit() ? "-a64": "-a32"; |
259 | std::string ObjectFileName(AssemblyFile); |
260 | ObjectFileName[ObjectFileName.size() - 1] = 'o'; |
261 | SmallVector<StringRef, 8> Args = { |
262 | "/bin/env", LDR_CNTRL_var, |
263 | AssemblerPath, Arch, |
264 | "-many", "-o", |
265 | ObjectFileName, AssemblyFile}; |
266 | |
267 | // Invoke the assembler. |
268 | int RC = sys::ExecuteAndWait(Program: Args[0], Args); |
269 | |
270 | // Handle errors. |
271 | if (RC < -1) { |
272 | emitError(ErrMsg: "LTO assembler exited abnormally"); |
273 | return false; |
274 | } |
275 | if (RC < 0) { |
276 | emitError(ErrMsg: "Unable to invoke LTO assembler"); |
277 | return false; |
278 | } |
279 | if (RC > 0) { |
280 | emitError(ErrMsg: "LTO assembler invocation returned non-zero"); |
281 | return false; |
282 | } |
283 | |
284 | // Cleanup. |
285 | remove(filename: AssemblyFile.c_str()); |
286 | |
287 | // Fix the output file name. |
288 | AssemblyFile = ObjectFileName; |
289 | |
290 | return true; |
291 | } |
292 | |
293 | bool LTOCodeGenerator::compileOptimizedToFile(const char **Name) { |
294 | if (useAIXSystemAssembler()) |
295 | setFileType(CodeGenFileType::AssemblyFile); |
296 | |
297 | // make unique temp output file to put generated code |
298 | SmallString<128> Filename; |
299 | |
300 | auto AddStream = |
301 | [&](size_t Task, |
302 | const Twine &ModuleName) -> std::unique_ptr<CachedFileStream> { |
303 | StringRef Extension( |
304 | Config.CGFileType == CodeGenFileType::AssemblyFile ? "s": "o"); |
305 | |
306 | int FD; |
307 | std::error_code EC = |
308 | sys::fs::createTemporaryFile(Prefix: "lto-llvm", Suffix: Extension, ResultFD&: FD, ResultPath&: Filename); |
309 | if (EC) |
310 | emitError(ErrMsg: EC.message()); |
311 | |
312 | return std::make_unique<CachedFileStream>( |
313 | args: std::make_unique<llvm::raw_fd_ostream>(args&: FD, args: true)); |
314 | }; |
315 | |
316 | bool genResult = compileOptimized(AddStream, ParallelismLevel: 1); |
317 | |
318 | if (!genResult) { |
319 | sys::fs::remove(path: Twine(Filename)); |
320 | return false; |
321 | } |
322 | |
323 | // If statistics were requested, save them to the specified file or |
324 | // print them out after codegen. |
325 | if (StatsFile) |
326 | PrintStatisticsJSON(OS&: StatsFile->os()); |
327 | else if (AreStatisticsEnabled()) |
328 | PrintStatistics(); |
329 | |
330 | if (useAIXSystemAssembler()) |
331 | if (!runAIXSystemAssembler(AssemblyFile&: Filename)) |
332 | return false; |
333 | |
334 | NativeObjectPath = Filename.c_str(); |
335 | *Name = NativeObjectPath.c_str(); |
336 | return true; |
337 | } |
338 | |
339 | std::unique_ptr<MemoryBuffer> |
340 | LTOCodeGenerator::compileOptimized() { |
341 | const char *name; |
342 | if (!compileOptimizedToFile(Name: &name)) |
343 | return nullptr; |
344 | |
345 | // read .o file into memory buffer |
346 | ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr = MemoryBuffer::getFile( |
347 | Filename: name, /*IsText=*/false, /*RequiresNullTerminator=*/false); |
348 | if (std::error_code EC = BufferOrErr.getError()) { |
349 | emitError(ErrMsg: EC.message()); |
350 | sys::fs::remove(path: NativeObjectPath); |
351 | return nullptr; |
352 | } |
353 | |
354 | // remove temp files |
355 | sys::fs::remove(path: NativeObjectPath); |
356 | |
357 | return std::move(*BufferOrErr); |
358 | } |
359 | |
360 | bool LTOCodeGenerator::compile_to_file(const char **Name) { |
361 | if (!optimize()) |
362 | return false; |
363 | |
364 | return compileOptimizedToFile(Name); |
365 | } |
366 | |
367 | std::unique_ptr<MemoryBuffer> LTOCodeGenerator::compile() { |
368 | if (!optimize()) |
369 | return nullptr; |
370 | |
371 | return compileOptimized(); |
372 | } |
373 | |
374 | bool LTOCodeGenerator::determineTarget() { |
375 | if (TargetMach) |
376 | return true; |
377 | |
378 | TripleStr = MergedModule->getTargetTriple().str(); |
379 | llvm::Triple Triple(TripleStr); |
380 | if (TripleStr.empty()) { |
381 | TripleStr = sys::getDefaultTargetTriple(); |
382 | MergedModule->setTargetTriple(Triple); |
383 | } |
384 | |
385 | // create target machine from info for merged modules |
386 | std::string ErrMsg; |
387 | MArch = TargetRegistry::lookupTarget(TripleStr, Error&: ErrMsg); |
388 | if (!MArch) { |
389 | emitError(ErrMsg); |
390 | return false; |
391 | } |
392 | |
393 | // Construct LTOModule, hand over ownership of module and target. Use MAttr as |
394 | // the default set of features. |
395 | SubtargetFeatures Features(join(R&: Config.MAttrs, Separator: "")); |
396 | Features.getDefaultSubtargetFeatures(Triple); |
397 | FeatureStr = Features.getString(); |
398 | if (Config.CPU.empty()) |
399 | Config.CPU = lto::getThinLTODefaultCPU(TheTriple: Triple); |
400 | |
401 | // If data-sections is not explicitly set or unset, set data-sections by |
402 | // default to match the behaviour of lld and gold plugin. |
403 | if (!codegen::getExplicitDataSections()) |
404 | Config.Options.DataSections = true; |
405 | |
406 | TargetMach = createTargetMachine(); |
407 | assert(TargetMach && "Unable to create target machine"); |
408 | |
409 | return true; |
410 | } |
411 | |
412 | std::unique_ptr<TargetMachine> LTOCodeGenerator::createTargetMachine() { |
413 | assert(MArch && "MArch is not set!"); |
414 | return std::unique_ptr<TargetMachine>(MArch->createTargetMachine( |
415 | TT: Triple(TripleStr), CPU: Config.CPU, Features: FeatureStr, Options: Config.Options, |
416 | RM: Config.RelocModel, CM: std::nullopt, OL: Config.CGOptLevel)); |
417 | } |
418 | |
419 | // If a linkonce global is present in the MustPreserveSymbols, we need to make |
420 | // sure we honor this. To force the compiler to not drop it, we add it to the |
421 | // "llvm.compiler.used" global. |
422 | void LTOCodeGenerator::preserveDiscardableGVs( |
423 | Module &TheModule, |
424 | llvm::function_ref<bool(const GlobalValue &)> mustPreserveGV) { |
425 | std::vector<GlobalValue *> Used; |
426 | auto mayPreserveGlobal = [&](GlobalValue &GV) { |
427 | if (!GV.isDiscardableIfUnused() || GV.isDeclaration() || |
428 | !mustPreserveGV(GV)) |
429 | return; |
430 | if (GV.hasAvailableExternallyLinkage()) |
431 | return emitWarning( |
432 | ErrMsg: (Twine("Linker asked to preserve available_externally global: '") + |
433 | GV.getName() + "'").str()); |
434 | if (GV.hasInternalLinkage()) |
435 | return emitWarning(ErrMsg: (Twine("Linker asked to preserve internal global: '") + |
436 | GV.getName() + "'").str()); |
437 | Used.push_back(x: &GV); |
438 | }; |
439 | for (auto &GV : TheModule) |
440 | mayPreserveGlobal(GV); |
441 | for (auto &GV : TheModule.globals()) |
442 | mayPreserveGlobal(GV); |
443 | for (auto &GV : TheModule.aliases()) |
444 | mayPreserveGlobal(GV); |
445 | |
446 | if (Used.empty()) |
447 | return; |
448 | |
449 | appendToCompilerUsed(M&: TheModule, Values: Used); |
450 | } |
451 | |
452 | void LTOCodeGenerator::applyScopeRestrictions() { |
453 | if (ScopeRestrictionsDone) |
454 | return; |
455 | |
456 | // Declare a callback for the internalize pass that will ask for every |
457 | // candidate GlobalValue if it can be internalized or not. |
458 | Mangler Mang; |
459 | SmallString<64> MangledName; |
460 | auto mustPreserveGV = [&](const GlobalValue &GV) -> bool { |
461 | // Unnamed globals can't be mangled, but they can't be preserved either. |
462 | if (!GV.hasName()) |
463 | return false; |
464 | |
465 | // Need to mangle the GV as the "MustPreserveSymbols" StringSet is filled |
466 | // with the linker supplied name, which on Darwin includes a leading |
467 | // underscore. |
468 | MangledName.clear(); |
469 | MangledName.reserve(N: GV.getName().size() + 1); |
470 | Mang.getNameWithPrefix(OutName&: MangledName, GV: &GV, /*CannotUsePrivateLabel=*/false); |
471 | return MustPreserveSymbols.count(Key: MangledName); |
472 | }; |
473 | |
474 | // Preserve linkonce value on linker request |
475 | preserveDiscardableGVs(TheModule&: *MergedModule, mustPreserveGV); |
476 | |
477 | if (!ShouldInternalize) |
478 | return; |
479 | |
480 | if (ShouldRestoreGlobalsLinkage) { |
481 | // Record the linkage type of non-local symbols so they can be restored |
482 | // prior |
483 | // to module splitting. |
484 | auto RecordLinkage = [&](const GlobalValue &GV) { |
485 | if (!GV.hasAvailableExternallyLinkage() && !GV.hasLocalLinkage() && |
486 | GV.hasName()) |
487 | ExternalSymbols.insert(KV: std::make_pair(x: GV.getName(), y: GV.getLinkage())); |
488 | }; |
489 | for (auto &GV : *MergedModule) |
490 | RecordLinkage(GV); |
491 | for (auto &GV : MergedModule->globals()) |
492 | RecordLinkage(GV); |
493 | for (auto &GV : MergedModule->aliases()) |
494 | RecordLinkage(GV); |
495 | } |
496 | |
497 | // Update the llvm.compiler_used globals to force preserving libcalls and |
498 | // symbols referenced from asm |
499 | updateCompilerUsed(TheModule&: *MergedModule, TM: *TargetMach, AsmUndefinedRefs); |
500 | |
501 | internalizeModule(TheModule&: *MergedModule, MustPreserveGV: mustPreserveGV); |
502 | |
503 | ScopeRestrictionsDone = true; |
504 | } |
505 | |
506 | /// Restore original linkage for symbols that may have been internalized |
507 | void LTOCodeGenerator::restoreLinkageForExternals() { |
508 | if (!ShouldInternalize || !ShouldRestoreGlobalsLinkage) |
509 | return; |
510 | |
511 | assert(ScopeRestrictionsDone && |
512 | "Cannot externalize without internalization!"); |
513 | |
514 | if (ExternalSymbols.empty()) |
515 | return; |
516 | |
517 | auto externalize = [this](GlobalValue &GV) { |
518 | if (!GV.hasLocalLinkage() || !GV.hasName()) |
519 | return; |
520 | |
521 | auto I = ExternalSymbols.find(Key: GV.getName()); |
522 | if (I == ExternalSymbols.end()) |
523 | return; |
524 | |
525 | GV.setLinkage(I->second); |
526 | }; |
527 | |
528 | llvm::for_each(Range: MergedModule->functions(), F: externalize); |
529 | llvm::for_each(Range: MergedModule->globals(), F: externalize); |
530 | llvm::for_each(Range: MergedModule->aliases(), F: externalize); |
531 | } |
532 | |
533 | void LTOCodeGenerator::verifyMergedModuleOnce() { |
534 | // Only run on the first call. |
535 | if (HasVerifiedInput) |
536 | return; |
537 | HasVerifiedInput = true; |
538 | |
539 | bool BrokenDebugInfo = false; |
540 | if (verifyModule(M: *MergedModule, OS: &dbgs(), BrokenDebugInfo: &BrokenDebugInfo)) |
541 | report_fatal_error(reason: "Broken module found, compilation aborted!"); |
542 | if (BrokenDebugInfo) { |
543 | emitWarning(ErrMsg: "Invalid debug info found, debug info will be stripped"); |
544 | StripDebugInfo(M&: *MergedModule); |
545 | } |
546 | } |
547 | |
548 | void LTOCodeGenerator::finishOptimizationRemarks() { |
549 | if (DiagnosticOutputFile) { |
550 | DiagnosticOutputFile->keep(); |
551 | // FIXME: LTOCodeGenerator dtor is not invoked on Darwin |
552 | DiagnosticOutputFile->os().flush(); |
553 | } |
554 | } |
555 | |
556 | /// Optimize merged modules using various IPO passes |
557 | bool LTOCodeGenerator::optimize() { |
558 | if (!this->determineTarget()) |
559 | return false; |
560 | |
561 | // libLTO parses options late, so re-set them here. |
562 | Context.setDiscardValueNames(LTODiscardValueNames); |
563 | |
564 | auto DiagFileOrErr = lto::setupLLVMOptimizationRemarks( |
565 | Context, RemarksFilename, RemarksPasses, RemarksFormat, |
566 | RemarksWithHotness, RemarksHotnessThreshold); |
567 | if (!DiagFileOrErr) { |
568 | errs() << "Error: "<< toString(E: DiagFileOrErr.takeError()) << "\n"; |
569 | report_fatal_error(reason: "Can't get an output file for the remarks"); |
570 | } |
571 | DiagnosticOutputFile = std::move(*DiagFileOrErr); |
572 | |
573 | // Setup output file to emit statistics. |
574 | auto StatsFileOrErr = lto::setupStatsFile(LTOStatsFile); |
575 | if (!StatsFileOrErr) { |
576 | errs() << "Error: "<< toString(E: StatsFileOrErr.takeError()) << "\n"; |
577 | report_fatal_error(reason: "Can't get an output file for the statistics"); |
578 | } |
579 | StatsFile = std::move(StatsFileOrErr.get()); |
580 | |
581 | // Currently there is no support for enabling whole program visibility via a |
582 | // linker option in the old LTO API, but this call allows it to be specified |
583 | // via the internal option. Must be done before WPD invoked via the optimizer |
584 | // pipeline run below. |
585 | updatePublicTypeTestCalls(M&: *MergedModule, |
586 | /* WholeProgramVisibilityEnabledInLTO */ false); |
587 | updateVCallVisibilityInModule( |
588 | M&: *MergedModule, |
589 | /* WholeProgramVisibilityEnabledInLTO */ false, |
590 | // FIXME: These need linker information via a |
591 | // TBD new interface. |
592 | /*DynamicExportSymbols=*/{}, |
593 | /*ValidateAllVtablesHaveTypeInfos=*/false, |
594 | /*IsVisibleToRegularObj=*/[](StringRef) { return true; }); |
595 | |
596 | // We always run the verifier once on the merged module, the `DisableVerify` |
597 | // parameter only applies to subsequent verify. |
598 | verifyMergedModuleOnce(); |
599 | |
600 | // Mark which symbols can not be internalized |
601 | this->applyScopeRestrictions(); |
602 | |
603 | // Add an appropriate DataLayout instance for this module... |
604 | MergedModule->setDataLayout(TargetMach->createDataLayout()); |
605 | |
606 | if (!SaveIRBeforeOptPath.empty()) { |
607 | std::error_code EC; |
608 | raw_fd_ostream OS(SaveIRBeforeOptPath, EC, sys::fs::OF_None); |
609 | if (EC) |
610 | report_fatal_error(reason: Twine("Failed to open ") + SaveIRBeforeOptPath + |
611 | " to save optimized bitcode\n"); |
612 | WriteBitcodeToFile(M: *MergedModule, Out&: OS, |
613 | /* ShouldPreserveUseListOrder */ true); |
614 | } |
615 | |
616 | ModuleSummaryIndex CombinedIndex(false); |
617 | TargetMach = createTargetMachine(); |
618 | if (!opt(Conf: Config, TM: TargetMach.get(), Task: 0, Mod&: *MergedModule, /*IsThinLTO=*/false, |
619 | /*ExportSummary=*/&CombinedIndex, /*ImportSummary=*/nullptr, |
620 | /*CmdArgs*/ std::vector<uint8_t>())) { |
621 | emitError(ErrMsg: "LTO middle-end optimizations failed"); |
622 | return false; |
623 | } |
624 | |
625 | return true; |
626 | } |
627 | |
628 | bool LTOCodeGenerator::compileOptimized(AddStreamFn AddStream, |
629 | unsigned ParallelismLevel) { |
630 | if (!this->determineTarget()) |
631 | return false; |
632 | |
633 | // We always run the verifier once on the merged module. If it has already |
634 | // been called in optimize(), this call will return early. |
635 | verifyMergedModuleOnce(); |
636 | |
637 | // Re-externalize globals that may have been internalized to increase scope |
638 | // for splitting |
639 | restoreLinkageForExternals(); |
640 | |
641 | ModuleSummaryIndex CombinedIndex(false); |
642 | |
643 | Config.CodeGenOnly = true; |
644 | Error Err = backend(C: Config, AddStream, ParallelCodeGenParallelismLevel: ParallelismLevel, M&: *MergedModule, |
645 | CombinedIndex); |
646 | assert(!Err && "unexpected code-generation failure"); |
647 | (void)Err; |
648 | |
649 | // If statistics were requested, save them to the specified file or |
650 | // print them out after codegen. |
651 | if (StatsFile) |
652 | PrintStatisticsJSON(OS&: StatsFile->os()); |
653 | else if (AreStatisticsEnabled()) |
654 | PrintStatistics(); |
655 | |
656 | reportAndResetTimings(); |
657 | |
658 | finishOptimizationRemarks(); |
659 | |
660 | return true; |
661 | } |
662 | |
663 | void LTOCodeGenerator::setCodeGenDebugOptions(ArrayRef<StringRef> Options) { |
664 | for (StringRef Option : Options) |
665 | CodegenOptions.push_back(x: Option.str()); |
666 | } |
667 | |
668 | void LTOCodeGenerator::parseCodeGenDebugOptions() { |
669 | if (!CodegenOptions.empty()) |
670 | llvm::parseCommandLineOptions(Options&: CodegenOptions); |
671 | } |
672 | |
673 | void llvm::parseCommandLineOptions(std::vector<std::string> &Options) { |
674 | if (!Options.empty()) { |
675 | // ParseCommandLineOptions() expects argv[0] to be program name. |
676 | std::vector<const char *> CodegenArgv(1, "libLLVMLTO"); |
677 | for (std::string &Arg : Options) |
678 | CodegenArgv.push_back(x: Arg.c_str()); |
679 | cl::ParseCommandLineOptions(argc: CodegenArgv.size(), argv: CodegenArgv.data()); |
680 | } |
681 | } |
682 | |
683 | void LTOCodeGenerator::DiagnosticHandler(const DiagnosticInfo &DI) { |
684 | // Map the LLVM internal diagnostic severity to the LTO diagnostic severity. |
685 | lto_codegen_diagnostic_severity_t Severity; |
686 | switch (DI.getSeverity()) { |
687 | case DS_Error: |
688 | Severity = LTO_DS_ERROR; |
689 | break; |
690 | case DS_Warning: |
691 | Severity = LTO_DS_WARNING; |
692 | break; |
693 | case DS_Remark: |
694 | Severity = LTO_DS_REMARK; |
695 | break; |
696 | case DS_Note: |
697 | Severity = LTO_DS_NOTE; |
698 | break; |
699 | } |
700 | // Create the string that will be reported to the external diagnostic handler. |
701 | std::string MsgStorage; |
702 | raw_string_ostream Stream(MsgStorage); |
703 | DiagnosticPrinterRawOStream DP(Stream); |
704 | DI.print(DP); |
705 | Stream.flush(); |
706 | |
707 | // If this method has been called it means someone has set up an external |
708 | // diagnostic handler. Assert on that. |
709 | assert(DiagHandler && "Invalid diagnostic handler"); |
710 | (*DiagHandler)(Severity, MsgStorage.c_str(), DiagContext); |
711 | } |
712 | |
713 | namespace { |
714 | struct LTODiagnosticHandler : public DiagnosticHandler { |
715 | LTOCodeGenerator *CodeGenerator; |
716 | LTODiagnosticHandler(LTOCodeGenerator *CodeGenPtr) |
717 | : CodeGenerator(CodeGenPtr) {} |
718 | bool handleDiagnostics(const DiagnosticInfo &DI) override { |
719 | CodeGenerator->DiagnosticHandler(DI); |
720 | return true; |
721 | } |
722 | }; |
723 | } |
724 | |
725 | void |
726 | LTOCodeGenerator::setDiagnosticHandler(lto_diagnostic_handler_t DiagHandler, |
727 | void *Ctxt) { |
728 | this->DiagHandler = DiagHandler; |
729 | this->DiagContext = Ctxt; |
730 | if (!DiagHandler) |
731 | return Context.setDiagnosticHandler(DH: nullptr); |
732 | // Register the LTOCodeGenerator stub in the LLVMContext to forward the |
733 | // diagnostic to the external DiagHandler. |
734 | Context.setDiagnosticHandler(DH: std::make_unique<LTODiagnosticHandler>(args: this), |
735 | RespectFilters: true); |
736 | } |
737 | |
738 | namespace { |
739 | class LTODiagnosticInfo : public DiagnosticInfo { |
740 | const Twine &Msg; |
741 | public: |
742 | LTODiagnosticInfo(const Twine &DiagMsg LLVM_LIFETIME_BOUND, |
743 | DiagnosticSeverity Severity = DS_Error) |
744 | : DiagnosticInfo(DK_Linker, Severity), Msg(DiagMsg) {} |
745 | void print(DiagnosticPrinter &DP) const override { DP << Msg; } |
746 | }; |
747 | } |
748 | |
749 | void LTOCodeGenerator::emitError(const std::string &ErrMsg) { |
750 | if (DiagHandler) |
751 | (*DiagHandler)(LTO_DS_ERROR, ErrMsg.c_str(), DiagContext); |
752 | else |
753 | Context.diagnose(DI: LTODiagnosticInfo(ErrMsg)); |
754 | } |
755 | |
756 | void LTOCodeGenerator::emitWarning(const std::string &ErrMsg) { |
757 | if (DiagHandler) |
758 | (*DiagHandler)(LTO_DS_WARNING, ErrMsg.c_str(), DiagContext); |
759 | else |
760 | Context.diagnose(DI: LTODiagnosticInfo(ErrMsg, DS_Warning)); |
761 | } |
762 |
Definitions
- getVersionString
- LTODiscardValueNames
- RemarksWithHotness
- RemarksHotnessThreshold
- RemarksFilename
- RemarksPasses
- RemarksFormat
- LTOStatsFile
- AIXSystemAssemblerPath
- LTORunCSIRInstr
- LTOCSIRProfile
- LTOCodeGenerator
- ~LTOCodeGenerator
- setAsmUndefinedRefs
- addModule
- setModule
- setTargetOptions
- setDebugInfo
- setOptLevel
- writeMergedModules
- useAIXSystemAssembler
- runAIXSystemAssembler
- compileOptimizedToFile
- compileOptimized
- compile_to_file
- compile
- determineTarget
- createTargetMachine
- preserveDiscardableGVs
- applyScopeRestrictions
- restoreLinkageForExternals
- verifyMergedModuleOnce
- finishOptimizationRemarks
- optimize
- compileOptimized
- setCodeGenDebugOptions
- parseCodeGenDebugOptions
- parseCommandLineOptions
- DiagnosticHandler
- LTODiagnosticHandler
- LTODiagnosticHandler
- handleDiagnostics
- setDiagnosticHandler
- LTODiagnosticInfo
- LTODiagnosticInfo
- emitError
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more