Currently there are two serialization modes for bitstream Remarks: standalone and separate. The separate mode splits remark metadata (e.g. the string table) from actual remark data. The metadata is written into the object file by the AsmPrinter, while the remark data is stored in a separate remarks file. This means we can't use bitstream remarks with tools like opt that don't generate an object file. Also, it is confusing to post-process bitstream remarks files, because only the standalone files can be read by llvm-remarkutil. We always need to use dsymutil to convert the separate files to standalone files, which only works for MachO. It is not possible for clang/opt to directly emit bitstream remark files in standalone mode, because the string table can only be serialized after all remarks were emitted. Therefore, this change completely removes the separate serialization mode. Instead, the remark string table is now always written to the end of the remarks file. This requires us to tell the serializer when to finalize remark serialization. This automatically happens when the serializer goes out of scope. However, often the remark file goes out of scope before the serializer is destroyed. To diagnose this, I have added an assert to alert users that they need to explicitly call finalizeLLVMOptimizationRemarks. This change paves the way for further improvements to the remark infrastructure, including more tooling (e.g. #159784), size optimizations for bitstream remarks, and more. Pull Request: https://github.com/llvm/llvm-project/pull/156715
759 lines
24 KiB
C++
759 lines
24 KiB
C++
//===-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"));
|
|
|
|
static cl::opt<bool>
|
|
LTORunCSIRInstr("cs-profile-generate",
|
|
cl::desc("Perform context sensitive PGO instrumentation"));
|
|
|
|
static cl::opt<std::string>
|
|
LTOCSIRProfile("cs-profile-path",
|
|
cl::desc("Context sensitive profile file path"));
|
|
} // 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.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);
|
|
|
|
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();
|
|
|
|
// Add an appropriate DataLayout instance for this module...
|
|
MergedModule->setDataLayout(TargetMach->createDataLayout());
|
|
|
|
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>())) {
|
|
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);
|
|
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);
|
|
Stream.flush();
|
|
|
|
// 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));
|
|
}
|