| //===-LTOCodeGenerator.cpp - LLVM Link Time Optimizer ---------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // This file implements the Link Time Optimization library. This library is | |
| // intended to be used by linker to optimize code at link time. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "llvm/LTO/legacy/LTOCodeGenerator.h" | |
| #include "llvm/ADT/Statistic.h" | |
| #include "llvm/ADT/StringExtras.h" | |
| #include "llvm/Analysis/TargetLibraryInfo.h" | |
| #include "llvm/Analysis/TargetTransformInfo.h" | |
| #include "llvm/Bitcode/BitcodeWriter.h" | |
| #include "llvm/CodeGen/CommandFlags.h" | |
| #include "llvm/CodeGen/TargetSubtargetInfo.h" | |
| #include "llvm/Config/config.h" | |
| #include "llvm/IR/DataLayout.h" | |
| #include "llvm/IR/DebugInfo.h" | |
| #include "llvm/IR/DerivedTypes.h" | |
| #include "llvm/IR/DiagnosticInfo.h" | |
| #include "llvm/IR/DiagnosticPrinter.h" | |
| #include "llvm/IR/LLVMContext.h" | |
| #include "llvm/IR/LLVMRemarkStreamer.h" | |
| #include "llvm/IR/LegacyPassManager.h" | |
| #include "llvm/IR/Mangler.h" | |
| #include "llvm/IR/Module.h" | |
| #include "llvm/IR/PassTimingInfo.h" | |
| #include "llvm/IR/Verifier.h" | |
| #include "llvm/LTO/LTO.h" | |
| #include "llvm/LTO/LTOBackend.h" | |
| #include "llvm/LTO/legacy/LTOModule.h" | |
| #include "llvm/LTO/legacy/UpdateCompilerUsed.h" | |
| #include "llvm/Linker/Linker.h" | |
| #include "llvm/MC/TargetRegistry.h" | |
| #include "llvm/Remarks/HotnessThresholdParser.h" | |
| #include "llvm/Support/CommandLine.h" | |
| #include "llvm/Support/FileSystem.h" | |
| #include "llvm/Support/MemoryBuffer.h" | |
| #include "llvm/Support/Process.h" | |
| #include "llvm/Support/Signals.h" | |
| #include "llvm/Support/ToolOutputFile.h" | |
| #include "llvm/Support/raw_ostream.h" | |
| #include "llvm/Target/TargetOptions.h" | |
| #include "llvm/TargetParser/Host.h" | |
| #include "llvm/TargetParser/SubtargetFeature.h" | |
| #include "llvm/Transforms/IPO.h" | |
| #include "llvm/Transforms/IPO/Internalize.h" | |
| #include "llvm/Transforms/IPO/WholeProgramDevirt.h" | |
| #include "llvm/Transforms/Utils/ModuleUtils.h" | |
| #include <optional> | |
| #include <system_error> | |
| using namespace llvm; | |
| const char* LTOCodeGenerator::getVersionString() { | |
| return PACKAGE_NAME " version " PACKAGE_VERSION; | |
| } | |
| namespace llvm { | |
| cl::opt<bool> LTODiscardValueNames( | |
| "lto-discard-value-names", | |
| cl::desc("Strip names from Value during LTO (other than GlobalValue)."), | |
| #ifdef NDEBUG | |
| cl::init(true), | |
| #else | |
| cl::init(false), | |
| #endif | |
| cl::Hidden); | |
| cl::opt<bool> RemarksWithHotness( | |
| "lto-pass-remarks-with-hotness", | |
| cl::desc("With PGO, include profile count in optimization remarks"), | |
| cl::Hidden); | |
| cl::opt<std::optional<uint64_t>, false, remarks::HotnessThresholdParser> | |
| RemarksHotnessThreshold( | |
| "lto-pass-remarks-hotness-threshold", | |
| cl::desc("Minimum profile count required for an " | |
| "optimization remark to be output." | |
| " Use 'auto' to apply the threshold from profile summary."), | |
| cl::value_desc("uint or 'auto'"), cl::init(0), cl::Hidden); | |
| cl::opt<std::string> | |
| RemarksFilename("lto-pass-remarks-output", | |
| cl::desc("Output filename for pass remarks"), | |
| cl::value_desc("filename")); | |
| cl::opt<std::string> | |
| RemarksPasses("lto-pass-remarks-filter", | |
| cl::desc("Only record optimization remarks from passes whose " | |
| "names match the given regular expression"), | |
| cl::value_desc("regex")); | |
| cl::opt<std::string> RemarksFormat( | |
| "lto-pass-remarks-format", | |
| cl::desc("The format used for serializing remarks (default: YAML)"), | |
| cl::value_desc("format"), cl::init("yaml")); | |
| static cl::opt<std::string> | |
| LTOStatsFile("lto-stats-file", | |
| cl::desc("Save statistics to the specified file"), cl::Hidden); | |
| static cl::opt<std::string> AIXSystemAssemblerPath( | |
| "lto-aix-system-assembler", | |
| cl::desc("Path to a system assembler, picked up on AIX only"), | |
| cl::value_desc("path")); | |
| cl::opt<bool> | |
| LTORunCSIRInstr("cs-profile-generate", | |
| cl::desc("Perform context sensitive PGO instrumentation")); | |
| cl::opt<std::string> | |
| LTOCSIRProfile("cs-profile-path", | |
| cl::desc("Context sensitive profile file path")); | |
| extern cl::opt<std::string> SampleProfileFile; | |
| } // namespace llvm | |
| LTOCodeGenerator::LTOCodeGenerator(LLVMContext &Context) | |
| : Context(Context), MergedModule(new Module("ld-temp.o", Context)), | |
| TheLinker(new Linker(*MergedModule)) { | |
| Context.setDiscardValueNames(LTODiscardValueNames); | |
| Context.enableDebugTypeODRUniquing(); | |
| Config.CodeModel = std::nullopt; | |
| Config.StatsFile = LTOStatsFile; | |
| Config.RunCSIRInstr = LTORunCSIRInstr; | |
| Config.CSIRProfile = LTOCSIRProfile; | |
| } | |
| LTOCodeGenerator::~LTOCodeGenerator() = default; | |
| void LTOCodeGenerator::setAsmUndefinedRefs(LTOModule *Mod) { | |
| AsmUndefinedRefs.insert_range(Mod->getAsmUndefinedRefs()); | |
| } | |
| bool LTOCodeGenerator::addModule(LTOModule *Mod) { | |
| assert(&Mod->getModule().getContext() == &Context && | |
| "Expected module in same context"); | |
| bool ret = TheLinker->linkInModule(Mod->takeModule()); | |
| setAsmUndefinedRefs(Mod); | |
| // We've just changed the input, so let's make sure we verify it. | |
| HasVerifiedInput = false; | |
| return !ret; | |
| } | |
| void LTOCodeGenerator::setModule(std::unique_ptr<LTOModule> Mod) { | |
| assert(&Mod->getModule().getContext() == &Context && | |
| "Expected module in same context"); | |
| AsmUndefinedRefs.clear(); | |
| MergedModule = Mod->takeModule(); | |
| TheLinker = std::make_unique<Linker>(*MergedModule); | |
| setAsmUndefinedRefs(&*Mod); | |
| // We've just changed the input, so let's make sure we verify it. | |
| HasVerifiedInput = false; | |
| } | |
| void LTOCodeGenerator::setTargetOptions(const TargetOptions &Options) { | |
| Config.Options = Options; | |
| } | |
| void LTOCodeGenerator::setDebugInfo(lto_debug_model Debug) { | |
| switch (Debug) { | |
| case LTO_DEBUG_MODEL_NONE: | |
| EmitDwarfDebugInfo = false; | |
| return; | |
| case LTO_DEBUG_MODEL_DWARF: | |
| EmitDwarfDebugInfo = true; | |
| return; | |
| } | |
| llvm_unreachable("Unknown debug format!"); | |
| } | |
| void LTOCodeGenerator::setOptLevel(unsigned Level) { | |
| Config.OptLevel = Level; | |
| Config.PTO.LoopVectorization = Config.OptLevel > 1; | |
| Config.PTO.SLPVectorization = Config.OptLevel > 1; | |
| std::optional<CodeGenOptLevel> CGOptLevelOrNone = | |
| CodeGenOpt::getLevel(Config.OptLevel); | |
| assert(CGOptLevelOrNone && "Unknown optimization level!"); | |
| Config.CGOptLevel = *CGOptLevelOrNone; | |
| } | |
| bool LTOCodeGenerator::writeMergedModules(StringRef Path) { | |
| if (!determineTarget()) | |
| return false; | |
| // We always run the verifier once on the merged module. | |
| verifyMergedModuleOnce(); | |
| // mark which symbols can not be internalized | |
| applyScopeRestrictions(); | |
| // create output file | |
| std::error_code EC; | |
| ToolOutputFile Out(Path, EC, sys::fs::OF_None); | |
| if (EC) { | |
| std::string ErrMsg = "could not open bitcode file for writing: "; | |
| ErrMsg += Path.str() + ": " + EC.message(); | |
| emitError(ErrMsg); | |
| return false; | |
| } | |
| // write bitcode to it | |
| WriteBitcodeToFile(*MergedModule, Out.os(), ShouldEmbedUselists); | |
| Out.os().close(); | |
| if (Out.os().has_error()) { | |
| std::string ErrMsg = "could not write bitcode file: "; | |
| ErrMsg += Path.str() + ": " + Out.os().error().message(); | |
| emitError(ErrMsg); | |
| Out.os().clear_error(); | |
| return false; | |
| } | |
| Out.keep(); | |
| return true; | |
| } | |
| bool LTOCodeGenerator::useAIXSystemAssembler() { | |
| const auto &Triple = TargetMach->getTargetTriple(); | |
| return Triple.isOSAIX() && Config.Options.MCOptions.DisableIntegratedAS; | |
| } | |
| bool LTOCodeGenerator::runAIXSystemAssembler(SmallString<128> &AssemblyFile) { | |
| assert(useAIXSystemAssembler() && | |
| "Runing AIX system assembler when integrated assembler is available!"); | |
| // Set the system assembler path. | |
| SmallString<256> AssemblerPath("/usr/bin/as"); | |
| if (!llvm::AIXSystemAssemblerPath.empty()) { | |
| if (llvm::sys::fs::real_path(llvm::AIXSystemAssemblerPath, AssemblerPath, | |
| /* expand_tilde */ true)) { | |
| emitError( | |
| "Cannot find the assembler specified by lto-aix-system-assembler"); | |
| return false; | |
| } | |
| } | |
| // Setup the LDR_CNTRL variable | |
| std::string LDR_CNTRL_var = "LDR_CNTRL=MAXDATA32=0xA0000000@DSA"; | |
| if (std::optional<std::string> V = sys::Process::GetEnv("LDR_CNTRL")) | |
| LDR_CNTRL_var += ("@" + *V); | |
| // Prepare inputs for the assember. | |
| const auto &Triple = TargetMach->getTargetTriple(); | |
| const char *Arch = Triple.isArch64Bit() ? "-a64" : "-a32"; | |
| std::string ObjectFileName(AssemblyFile); | |
| ObjectFileName[ObjectFileName.size() - 1] = 'o'; | |
| SmallVector<StringRef, 8> Args = { | |
| "/bin/env", LDR_CNTRL_var, | |
| AssemblerPath, Arch, | |
| "-many", "-o", | |
| ObjectFileName, AssemblyFile}; | |
| // Invoke the assembler. | |
| int RC = sys::ExecuteAndWait(Args[0], Args); | |
| // Handle errors. | |
| if (RC < -1) { | |
| emitError("LTO assembler exited abnormally"); | |
| return false; | |
| } | |
| if (RC < 0) { | |
| emitError("Unable to invoke LTO assembler"); | |
| return false; | |
| } | |
| if (RC > 0) { | |
| emitError("LTO assembler invocation returned non-zero"); | |
| return false; | |
| } | |
| // Cleanup. | |
| remove(AssemblyFile.c_str()); | |
| // Fix the output file name. | |
| AssemblyFile = ObjectFileName; | |
| return true; | |
| } | |
| bool LTOCodeGenerator::compileOptimizedToFile(const char **Name) { | |
| if (useAIXSystemAssembler()) | |
| setFileType(CodeGenFileType::AssemblyFile); | |
| // make unique temp output file to put generated code | |
| SmallString<128> Filename; | |
| auto AddStream = | |
| [&](size_t Task, | |
| const Twine &ModuleName) -> std::unique_ptr<CachedFileStream> { | |
| StringRef Extension( | |
| Config.CGFileType == CodeGenFileType::AssemblyFile ? "s" : "o"); | |
| int FD; | |
| std::error_code EC = | |
| sys::fs::createTemporaryFile("lto-llvm", Extension, FD, Filename); | |
| if (EC) | |
| emitError(EC.message()); | |
| return std::make_unique<CachedFileStream>( | |
| std::make_unique<llvm::raw_fd_ostream>(FD, true)); | |
| }; | |
| bool genResult = compileOptimized(AddStream, 1); | |
| if (!genResult) { | |
| sys::fs::remove(Twine(Filename)); | |
| return false; | |
| } | |
| // If statistics were requested, save them to the specified file or | |
| // print them out after codegen. | |
| if (StatsFile) | |
| PrintStatisticsJSON(StatsFile->os()); | |
| else if (AreStatisticsEnabled()) | |
| PrintStatistics(); | |
| if (useAIXSystemAssembler()) | |
| if (!runAIXSystemAssembler(Filename)) | |
| return false; | |
| NativeObjectPath = Filename.c_str(); | |
| *Name = NativeObjectPath.c_str(); | |
| return true; | |
| } | |
| std::unique_ptr<MemoryBuffer> | |
| LTOCodeGenerator::compileOptimized() { | |
| const char *name; | |
| if (!compileOptimizedToFile(&name)) | |
| return nullptr; | |
| // read .o file into memory buffer | |
| ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr = MemoryBuffer::getFile( | |
| name, /*IsText=*/false, /*RequiresNullTerminator=*/false); | |
| if (std::error_code EC = BufferOrErr.getError()) { | |
| emitError(EC.message()); | |
| sys::fs::remove(NativeObjectPath); | |
| return nullptr; | |
| } | |
| // remove temp files | |
| sys::fs::remove(NativeObjectPath); | |
| return std::move(*BufferOrErr); | |
| } | |
| bool LTOCodeGenerator::compile_to_file(const char **Name) { | |
| if (!optimize()) | |
| return false; | |
| return compileOptimizedToFile(Name); | |
| } | |
| std::unique_ptr<MemoryBuffer> LTOCodeGenerator::compile() { | |
| if (!optimize()) | |
| return nullptr; | |
| return compileOptimized(); | |
| } | |
| bool LTOCodeGenerator::determineTarget() { | |
| if (TargetMach) | |
| return true; | |
| if (MergedModule->getTargetTriple().empty()) | |
| MergedModule->setTargetTriple(Triple(sys::getDefaultTargetTriple())); | |
| // create target machine from info for merged modules | |
| std::string ErrMsg; | |
| MArch = TargetRegistry::lookupTarget(MergedModule->getTargetTriple(), ErrMsg); | |
| if (!MArch) { | |
| emitError(ErrMsg); | |
| return false; | |
| } | |
| // Construct LTOModule, hand over ownership of module and target. Use MAttr as | |
| // the default set of features. | |
| SubtargetFeatures Features(join(Config.MAttrs, "")); | |
| Features.getDefaultSubtargetFeatures(MergedModule->getTargetTriple()); | |
| FeatureStr = Features.getString(); | |
| if (Config.CPU.empty()) | |
| Config.CPU = lto::getThinLTODefaultCPU(MergedModule->getTargetTriple()); | |
| // If data-sections is not explicitly set or unset, set data-sections by | |
| // default to match the behaviour of lld and gold plugin. | |
| if (!codegen::getExplicitDataSections()) | |
| Config.Options.DataSections = true; | |
| TargetMach = createTargetMachine(); | |
| assert(TargetMach && "Unable to create target machine"); | |
| return true; | |
| } | |
| std::unique_ptr<TargetMachine> LTOCodeGenerator::createTargetMachine() { | |
| assert(MArch && "MArch is not set!"); | |
| return std::unique_ptr<TargetMachine>(MArch->createTargetMachine( | |
| MergedModule->getTargetTriple(), Config.CPU, FeatureStr, Config.Options, | |
| Config.RelocModel, std::nullopt, Config.CGOptLevel)); | |
| } | |
| // If a linkonce global is present in the MustPreserveSymbols, we need to make | |
| // sure we honor this. To force the compiler to not drop it, we add it to the | |
| // "llvm.compiler.used" global. | |
| void LTOCodeGenerator::preserveDiscardableGVs( | |
| Module &TheModule, | |
| llvm::function_ref<bool(const GlobalValue &)> mustPreserveGV) { | |
| std::vector<GlobalValue *> Used; | |
| auto mayPreserveGlobal = [&](GlobalValue &GV) { | |
| if (!GV.isDiscardableIfUnused() || GV.isDeclaration() || | |
| !mustPreserveGV(GV)) | |
| return; | |
| if (GV.hasAvailableExternallyLinkage()) | |
| return emitWarning( | |
| (Twine("Linker asked to preserve available_externally global: '") + | |
| GV.getName() + "'").str()); | |
| if (GV.hasInternalLinkage()) | |
| return emitWarning((Twine("Linker asked to preserve internal global: '") + | |
| GV.getName() + "'").str()); | |
| Used.push_back(&GV); | |
| }; | |
| for (auto &GV : TheModule) | |
| mayPreserveGlobal(GV); | |
| for (auto &GV : TheModule.globals()) | |
| mayPreserveGlobal(GV); | |
| for (auto &GV : TheModule.aliases()) | |
| mayPreserveGlobal(GV); | |
| if (Used.empty()) | |
| return; | |
| appendToCompilerUsed(TheModule, Used); | |
| } | |
| void LTOCodeGenerator::applyScopeRestrictions() { | |
| if (ScopeRestrictionsDone) | |
| return; | |
| // Declare a callback for the internalize pass that will ask for every | |
| // candidate GlobalValue if it can be internalized or not. | |
| Mangler Mang; | |
| SmallString<64> MangledName; | |
| auto mustPreserveGV = [&](const GlobalValue &GV) -> bool { | |
| // Unnamed globals can't be mangled, but they can't be preserved either. | |
| if (!GV.hasName()) | |
| return false; | |
| // Need to mangle the GV as the "MustPreserveSymbols" StringSet is filled | |
| // with the linker supplied name, which on Darwin includes a leading | |
| // underscore. | |
| MangledName.clear(); | |
| MangledName.reserve(GV.getName().size() + 1); | |
| Mang.getNameWithPrefix(MangledName, &GV, /*CannotUsePrivateLabel=*/false); | |
| return MustPreserveSymbols.count(MangledName); | |
| }; | |
| // Preserve linkonce value on linker request | |
| preserveDiscardableGVs(*MergedModule, mustPreserveGV); | |
| if (!ShouldInternalize) | |
| return; | |
| if (ShouldRestoreGlobalsLinkage) { | |
| // Record the linkage type of non-local symbols so they can be restored | |
| // prior | |
| // to module splitting. | |
| auto RecordLinkage = [&](const GlobalValue &GV) { | |
| if (!GV.hasAvailableExternallyLinkage() && !GV.hasLocalLinkage() && | |
| GV.hasName()) | |
| ExternalSymbols.insert(std::make_pair(GV.getName(), GV.getLinkage())); | |
| }; | |
| for (auto &GV : *MergedModule) | |
| RecordLinkage(GV); | |
| for (auto &GV : MergedModule->globals()) | |
| RecordLinkage(GV); | |
| for (auto &GV : MergedModule->aliases()) | |
| RecordLinkage(GV); | |
| } | |
| // Update the llvm.compiler_used globals to force preserving libcalls and | |
| // symbols referenced from asm | |
| updateCompilerUsed(*MergedModule, *TargetMach, AsmUndefinedRefs); | |
| internalizeModule(*MergedModule, mustPreserveGV); | |
| ScopeRestrictionsDone = true; | |
| } | |
| /// Restore original linkage for symbols that may have been internalized | |
| void LTOCodeGenerator::restoreLinkageForExternals() { | |
| if (!ShouldInternalize || !ShouldRestoreGlobalsLinkage) | |
| return; | |
| assert(ScopeRestrictionsDone && | |
| "Cannot externalize without internalization!"); | |
| if (ExternalSymbols.empty()) | |
| return; | |
| auto externalize = [this](GlobalValue &GV) { | |
| if (!GV.hasLocalLinkage() || !GV.hasName()) | |
| return; | |
| auto I = ExternalSymbols.find(GV.getName()); | |
| if (I == ExternalSymbols.end()) | |
| return; | |
| GV.setLinkage(I->second); | |
| }; | |
| llvm::for_each(MergedModule->functions(), externalize); | |
| llvm::for_each(MergedModule->globals(), externalize); | |
| llvm::for_each(MergedModule->aliases(), externalize); | |
| } | |
| void LTOCodeGenerator::verifyMergedModuleOnce() { | |
| // Only run on the first call. | |
| if (HasVerifiedInput) | |
| return; | |
| HasVerifiedInput = true; | |
| bool BrokenDebugInfo = false; | |
| if (verifyModule(*MergedModule, &dbgs(), &BrokenDebugInfo)) | |
| report_fatal_error("Broken module found, compilation aborted!"); | |
| if (BrokenDebugInfo) { | |
| emitWarning("Invalid debug info found, debug info will be stripped"); | |
| StripDebugInfo(*MergedModule); | |
| } | |
| } | |
| void LTOCodeGenerator::finishOptimizationRemarks() { | |
| if (DiagnosticOutputFile) { | |
| DiagnosticOutputFile->keep(); | |
| // FIXME: LTOCodeGenerator dtor is not invoked on Darwin | |
| DiagnosticOutputFile.finalize(); | |
| DiagnosticOutputFile->os().flush(); | |
| } | |
| } | |
| /// Optimize merged modules using various IPO passes | |
| bool LTOCodeGenerator::optimize() { | |
| if (!this->determineTarget()) | |
| return false; | |
| // libLTO parses options late, so re-set them here. | |
| Context.setDiscardValueNames(LTODiscardValueNames); | |
| Config.StatsFile = LTOStatsFile; | |
| Config.RunCSIRInstr = LTORunCSIRInstr; | |
| Config.CSIRProfile = LTOCSIRProfile; | |
| Config.SampleProfile = SampleProfileFile; | |
| auto DiagFileOrErr = lto::setupLLVMOptimizationRemarks( | |
| Context, RemarksFilename, RemarksPasses, RemarksFormat, | |
| RemarksWithHotness, RemarksHotnessThreshold); | |
| if (!DiagFileOrErr) { | |
| errs() << "Error: " << toString(DiagFileOrErr.takeError()) << "\n"; | |
| report_fatal_error("Can't get an output file for the remarks"); | |
| } | |
| DiagnosticOutputFile = std::move(*DiagFileOrErr); | |
| // Setup output file to emit statistics. | |
| auto StatsFileOrErr = lto::setupStatsFile(LTOStatsFile); | |
| if (!StatsFileOrErr) { | |
| errs() << "Error: " << toString(StatsFileOrErr.takeError()) << "\n"; | |
| report_fatal_error("Can't get an output file for the statistics"); | |
| } | |
| StatsFile = std::move(StatsFileOrErr.get()); | |
| // Currently there is no support for enabling whole program visibility via a | |
| // linker option in the old LTO API, but this call allows it to be specified | |
| // via the internal option. Must be done before WPD invoked via the optimizer | |
| // pipeline run below. | |
| updatePublicTypeTestCalls(*MergedModule, | |
| /* WholeProgramVisibilityEnabledInLTO */ false); | |
| updateVCallVisibilityInModule( | |
| *MergedModule, | |
| /* WholeProgramVisibilityEnabledInLTO */ false, | |
| // FIXME: These need linker information via a | |
| // TBD new interface. | |
| /*DynamicExportSymbols=*/{}, | |
| /*ValidateAllVtablesHaveTypeInfos=*/false, | |
| /*IsVisibleToRegularObj=*/[](StringRef) { return true; }); | |
| // We always run the verifier once on the merged module, the `DisableVerify` | |
| // parameter only applies to subsequent verify. | |
| verifyMergedModuleOnce(); | |
| // Mark which symbols can not be internalized | |
| this->applyScopeRestrictions(); | |
| // Seed a DataLayout only if the merged module does not already carry one, so | |
| // an input module's own DataLayout is preserved. Compute it from the module's | |
| // ABI so the target-abi module flag is respected. | |
| if (MergedModule->getDataLayout().isDefault()) { | |
| MergedModule->setDataLayout( | |
| MergedModule->getTargetTriple().computeDataLayout( | |
| TargetMach->getTargetABIName(*MergedModule))); | |
| } | |
| if (!SaveIRBeforeOptPath.empty()) { | |
| std::error_code EC; | |
| raw_fd_ostream OS(SaveIRBeforeOptPath, EC, sys::fs::OF_None); | |
| if (EC) | |
| report_fatal_error(Twine("Failed to open ") + SaveIRBeforeOptPath + | |
| " to save optimized bitcode\n"); | |
| WriteBitcodeToFile(*MergedModule, OS, | |
| /* ShouldPreserveUseListOrder */ true); | |
| } | |
| ModuleSummaryIndex CombinedIndex(false); | |
| TargetMach = createTargetMachine(); | |
| if (!opt(Config, TargetMach.get(), 0, *MergedModule, /*IsThinLTO=*/false, | |
| /*ExportSummary=*/&CombinedIndex, /*ImportSummary=*/nullptr, | |
| /*CmdArgs*/ std::vector<uint8_t>(), /*BitcodeLibFuncs=*/{})) { | |
| emitError("LTO middle-end optimizations failed"); | |
| return false; | |
| } | |
| return true; | |
| } | |
| bool LTOCodeGenerator::compileOptimized(AddStreamFn AddStream, | |
| unsigned ParallelismLevel) { | |
| if (!this->determineTarget()) | |
| return false; | |
| // We always run the verifier once on the merged module. If it has already | |
| // been called in optimize(), this call will return early. | |
| verifyMergedModuleOnce(); | |
| // Re-externalize globals that may have been internalized to increase scope | |
| // for splitting | |
| restoreLinkageForExternals(); | |
| ModuleSummaryIndex CombinedIndex(false); | |
| Config.CodeGenOnly = true; | |
| Error Err = backend(Config, AddStream, ParallelismLevel, *MergedModule, | |
| CombinedIndex, /*BitcodeLibFuncs=*/{}); | |
| assert(!Err && "unexpected code-generation failure"); | |
| (void)Err; | |
| // If statistics were requested, save them to the specified file or | |
| // print them out after codegen. | |
| if (StatsFile) | |
| PrintStatisticsJSON(StatsFile->os()); | |
| else if (AreStatisticsEnabled()) | |
| PrintStatistics(); | |
| reportAndResetTimings(); | |
| finishOptimizationRemarks(); | |
| return true; | |
| } | |
| void LTOCodeGenerator::setCodeGenDebugOptions(ArrayRef<StringRef> Options) { | |
| for (StringRef Option : Options) | |
| CodegenOptions.push_back(Option.str()); | |
| } | |
| void LTOCodeGenerator::parseCodeGenDebugOptions() { | |
| if (!CodegenOptions.empty()) | |
| llvm::parseCommandLineOptions(CodegenOptions); | |
| } | |
| void llvm::parseCommandLineOptions(std::vector<std::string> &Options) { | |
| if (!Options.empty()) { | |
| // ParseCommandLineOptions() expects argv[0] to be program name. | |
| std::vector<const char *> CodegenArgv(1, "libLLVMLTO"); | |
| for (std::string &Arg : Options) | |
| CodegenArgv.push_back(Arg.c_str()); | |
| cl::ParseCommandLineOptions(CodegenArgv.size(), CodegenArgv.data()); | |
| } | |
| } | |
| void LTOCodeGenerator::DiagnosticHandler(const DiagnosticInfo &DI) { | |
| // Map the LLVM internal diagnostic severity to the LTO diagnostic severity. | |
| lto_codegen_diagnostic_severity_t Severity; | |
| switch (DI.getSeverity()) { | |
| case DS_Error: | |
| Severity = LTO_DS_ERROR; | |
| break; | |
| case DS_Warning: | |
| Severity = LTO_DS_WARNING; | |
| break; | |
| case DS_Remark: | |
| Severity = LTO_DS_REMARK; | |
| break; | |
| case DS_Note: | |
| Severity = LTO_DS_NOTE; | |
| break; | |
| } | |
| // Create the string that will be reported to the external diagnostic handler. | |
| std::string MsgStorage; | |
| raw_string_ostream Stream(MsgStorage); | |
| DiagnosticPrinterRawOStream DP(Stream); | |
| DI.print(DP); | |
| // If this method has been called it means someone has set up an external | |
| // diagnostic handler. Assert on that. | |
| assert(DiagHandler && "Invalid diagnostic handler"); | |
| (*DiagHandler)(Severity, MsgStorage.c_str(), DiagContext); | |
| } | |
| namespace { | |
| struct LTODiagnosticHandler : public DiagnosticHandler { | |
| LTOCodeGenerator *CodeGenerator; | |
| LTODiagnosticHandler(LTOCodeGenerator *CodeGenPtr) | |
| : CodeGenerator(CodeGenPtr) {} | |
| bool handleDiagnostics(const DiagnosticInfo &DI) override { | |
| CodeGenerator->DiagnosticHandler(DI); | |
| return true; | |
| } | |
| }; | |
| } | |
| void | |
| LTOCodeGenerator::setDiagnosticHandler(lto_diagnostic_handler_t DiagHandler, | |
| void *Ctxt) { | |
| this->DiagHandler = DiagHandler; | |
| this->DiagContext = Ctxt; | |
| if (!DiagHandler) | |
| return Context.setDiagnosticHandler(nullptr); | |
| // Register the LTOCodeGenerator stub in the LLVMContext to forward the | |
| // diagnostic to the external DiagHandler. | |
| Context.setDiagnosticHandler(std::make_unique<LTODiagnosticHandler>(this), | |
| true); | |
| } | |
| namespace { | |
| class LTODiagnosticInfo : public DiagnosticInfo { | |
| const Twine &Msg; | |
| public: | |
| LTODiagnosticInfo(const Twine &DiagMsg LLVM_LIFETIME_BOUND, | |
| DiagnosticSeverity Severity = DS_Error) | |
| : DiagnosticInfo(DK_Linker, Severity), Msg(DiagMsg) {} | |
| void print(DiagnosticPrinter &DP) const override { DP << Msg; } | |
| }; | |
| } | |
| void LTOCodeGenerator::emitError(const std::string &ErrMsg) { | |
| if (DiagHandler) | |
| (*DiagHandler)(LTO_DS_ERROR, ErrMsg.c_str(), DiagContext); | |
| else | |
| Context.diagnose(LTODiagnosticInfo(ErrMsg)); | |
| } | |
| void LTOCodeGenerator::emitWarning(const std::string &ErrMsg) { | |
| if (DiagHandler) | |
| (*DiagHandler)(LTO_DS_WARNING, ErrMsg.c_str(), DiagContext); | |
| else | |
| Context.diagnose(LTODiagnosticInfo(ErrMsg, DS_Warning)); | |
| } |