Skip to content

Commit

Permalink
[llvm] A Unified LTO Bitcode Frontend
Browse files Browse the repository at this point in the history
Here's a high level summary of the changes in this patch. For more
information on rational, see the RFC.
(https://discourse.llvm.org/t/rfc-a-unified-lto-bitcode-frontend/61774).

  - Add config parameter to LTO backend, specifying which LTO mode is
    desired when using unified LTO.
  - Add unified LTO flag to the summary index for efficiency. Unified
    LTO modules can be detected without parsing the module.
  - Make sure that the ModuleID is generated by incorporating more types
    of symbols.

Differential Revision: https://reviews.llvm.org/D123803
  • Loading branch information
ormris committed Jul 5, 2023
1 parent 156913c commit a1ca3af
Show file tree
Hide file tree
Showing 24 changed files with 452 additions and 55 deletions.
1 change: 1 addition & 0 deletions llvm/include/llvm/Bitcode/BitcodeReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ struct ParserCallbacks {
bool IsThinLTO;
bool HasSummary;
bool EnableSplitLTOUnit;
bool UnifiedLTO;
};

/// Represents a module in a bitcode file.
Expand Down
13 changes: 10 additions & 3 deletions llvm/include/llvm/IR/ModuleSummaryIndex.h
Original file line number Diff line number Diff line change
Expand Up @@ -1327,6 +1327,9 @@ class ModuleSummaryIndex {
// True if the index was created for a module compiled with -fsplit-lto-unit.
bool EnableSplitLTOUnit;

// True if the index was created for a module compiled with -funified-lto
bool UnifiedLTO;

// True if some of the modules were compiled with -fsplit-lto-unit and
// some were not. Set when the combined index is created during the thin link.
bool PartiallySplitLTOUnits = false;
Expand Down Expand Up @@ -1372,9 +1375,10 @@ class ModuleSummaryIndex {

public:
// See HaveGVs variable comment.
ModuleSummaryIndex(bool HaveGVs, bool EnableSplitLTOUnit = false)
: HaveGVs(HaveGVs), EnableSplitLTOUnit(EnableSplitLTOUnit), Saver(Alloc),
BlockCount(0) {}
ModuleSummaryIndex(bool HaveGVs, bool EnableSplitLTOUnit = false,
bool UnifiedLTO = false)
: HaveGVs(HaveGVs), EnableSplitLTOUnit(EnableSplitLTOUnit),
UnifiedLTO(UnifiedLTO), Saver(Alloc), BlockCount(0) {}

// Current version for the module summary in bitcode files.
// The BitcodeSummaryVersion should be bumped whenever we introduce changes
Expand Down Expand Up @@ -1532,6 +1536,9 @@ class ModuleSummaryIndex {
bool enableSplitLTOUnit() const { return EnableSplitLTOUnit; }
void setEnableSplitLTOUnit() { EnableSplitLTOUnit = true; }

bool hasUnifiedLTO() const { return UnifiedLTO; }
void setUnifiedLTO() { UnifiedLTO = true; }

bool partiallySplitLTOUnits() const { return PartiallySplitLTOUnits; }
void setPartiallySplitLTOUnits() { PartiallySplitLTOUnits = true; }

Expand Down
18 changes: 17 additions & 1 deletion llvm/include/llvm/LTO/LTO.h
Original file line number Diff line number Diff line change
Expand Up @@ -255,13 +255,26 @@ class LTO {
friend InputFile;

public:
/// Unified LTO modes
enum LTOKind {
/// Any LTO mode without Unified LTO. The default mode.
LTOK_Default,

/// Regular LTO, with Unified LTO enabled.
LTOK_UnifiedRegular,

/// ThinLTO, with Unified LTO enabled.
LTOK_UnifiedThin,
};

/// Create an LTO object. A default constructed LTO object has a reasonable
/// production configuration, but you can customize it by passing arguments to
/// this constructor.
/// FIXME: We do currently require the DiagHandler field to be set in Conf.
/// Until that is fixed, a Config argument is required.
LTO(Config Conf, ThinBackend Backend = nullptr,
unsigned ParallelCodeGenParallelismLevel = 1);
unsigned ParallelCodeGenParallelismLevel = 1,
LTOKind LTOMode = LTOK_Default);
~LTO();

/// Add an input file to the LTO link, using the provided symbol resolutions.
Expand Down Expand Up @@ -421,6 +434,9 @@ class LTO {

mutable bool CalledGetMaxTasks = false;

// LTO mode when using Unified LTO.
LTOKind LTOMode;

// Use Optional to distinguish false from not yet initialized.
std::optional<bool> EnableSplitLTOUnit;

Expand Down
3 changes: 3 additions & 0 deletions llvm/include/llvm/Passes/PassBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ class PipelineTuningOptions {
/// that of the flag: `-enable-npm-call-graph-profile`.
bool CallGraphProfile;

// Add LTO pipeline tuning option to enable the unified LTO pipeline.
bool UnifiedLTO;

/// Tuning option to enable/disable function merging. Its default value is
/// false.
bool MergeFunctions;
Expand Down
6 changes: 5 additions & 1 deletion llvm/lib/Analysis/ModuleSummaryAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -786,10 +786,14 @@ ModuleSummaryIndex llvm::buildModuleSummaryIndex(
std::function<const StackSafetyInfo *(const Function &F)> GetSSICallback) {
assert(PSI);
bool EnableSplitLTOUnit = false;
bool UnifiedLTO = false;
if (auto *MD = mdconst::extract_or_null<ConstantInt>(
M.getModuleFlag("EnableSplitLTOUnit")))
EnableSplitLTOUnit = MD->getZExtValue();
ModuleSummaryIndex Index(/*HaveGVs=*/true, EnableSplitLTOUnit);
if (auto *MD =
mdconst::extract_or_null<ConstantInt>(M.getModuleFlag("UnifiedLTO")))
UnifiedLTO = MD->getZExtValue();
ModuleSummaryIndex Index(/*HaveGVs=*/true, EnableSplitLTOUnit, UnifiedLTO);

// Identify the local values in the llvm.used and llvm.compiler.used sets,
// which should not be exported as they would then require renaming and
Expand Down
55 changes: 34 additions & 21 deletions llvm/lib/Bitcode/Reader/BitcodeReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8033,25 +8033,28 @@ Expected<std::unique_ptr<ModuleSummaryIndex>> BitcodeModule::getSummary() {
return std::move(Index);
}

static Expected<bool> getEnableSplitLTOUnitFlag(BitstreamCursor &Stream,
unsigned ID) {
static Expected<std::pair<bool, bool>>
getEnableSplitLTOUnitAndUnifiedFlag(BitstreamCursor &Stream,
unsigned ID,
BitcodeLTOInfo &LTOInfo) {
if (Error Err = Stream.EnterSubBlock(ID))
return std::move(Err);
SmallVector<uint64_t, 64> Record;

while (true) {
BitstreamEntry Entry;
std::pair<bool, bool> Result = {false,false};
if (Error E = Stream.advanceSkippingSubblocks().moveInto(Entry))
return std::move(E);

switch (Entry.Kind) {
case BitstreamEntry::SubBlock: // Handled for us already.
case BitstreamEntry::Error:
return error("Malformed block");
case BitstreamEntry::EndBlock:
// If no flags record found, conservatively return true to mimic
// behavior before this flag was added.
return true;
case BitstreamEntry::EndBlock: {
// If no flags record found, set both flags to false.
return Result;
}
case BitstreamEntry::Record:
// The interesting case.
break;
Expand All @@ -8068,9 +8071,13 @@ static Expected<bool> getEnableSplitLTOUnitFlag(BitstreamCursor &Stream,
case bitc::FS_FLAGS: { // [flags]
uint64_t Flags = Record[0];
// Scan flags.
assert(Flags <= 0x1ff && "Unexpected bits in flag");
assert(Flags <= 0x2ff && "Unexpected bits in flag");

bool EnableSplitLTOUnit = Flags & 0x8;
bool UnifiedLTO = Flags & 0x200;
Result = {EnableSplitLTOUnit, UnifiedLTO};

return Flags & 0x8;
return Result;
}
}
}
Expand All @@ -8096,25 +8103,31 @@ Expected<BitcodeLTOInfo> BitcodeModule::getLTOInfo() {
return error("Malformed block");
case BitstreamEntry::EndBlock:
return BitcodeLTOInfo{/*IsThinLTO=*/false, /*HasSummary=*/false,
/*EnableSplitLTOUnit=*/false};
/*EnableSplitLTOUnit=*/false, /*UnifiedLTO=*/false};

case BitstreamEntry::SubBlock:
if (Entry.ID == bitc::GLOBALVAL_SUMMARY_BLOCK_ID) {
Expected<bool> EnableSplitLTOUnit =
getEnableSplitLTOUnitFlag(Stream, Entry.ID);
if (!EnableSplitLTOUnit)
return EnableSplitLTOUnit.takeError();
return BitcodeLTOInfo{/*IsThinLTO=*/true, /*HasSummary=*/true,
*EnableSplitLTOUnit};
BitcodeLTOInfo LTOInfo;
Expected<std::pair<bool, bool>> Flags =
getEnableSplitLTOUnitAndUnifiedFlag(Stream, Entry.ID, LTOInfo);
if (!Flags)
return Flags.takeError();
std::tie(LTOInfo.EnableSplitLTOUnit, LTOInfo.UnifiedLTO) = Flags.get();
LTOInfo.IsThinLTO = true;
LTOInfo.HasSummary = true;
return LTOInfo;
}

if (Entry.ID == bitc::FULL_LTO_GLOBALVAL_SUMMARY_BLOCK_ID) {
Expected<bool> EnableSplitLTOUnit =
getEnableSplitLTOUnitFlag(Stream, Entry.ID);
if (!EnableSplitLTOUnit)
return EnableSplitLTOUnit.takeError();
return BitcodeLTOInfo{/*IsThinLTO=*/false, /*HasSummary=*/true,
*EnableSplitLTOUnit};
BitcodeLTOInfo LTOInfo;
Expected<std::pair<bool, bool>> Flags =
getEnableSplitLTOUnitAndUnifiedFlag(Stream, Entry.ID, LTOInfo);
if (!Flags)
return Flags.takeError();
std::tie(LTOInfo.EnableSplitLTOUnit, LTOInfo.UnifiedLTO) = Flags.get();
LTOInfo.IsThinLTO = false;
LTOInfo.HasSummary = true;
return LTOInfo;
}

// Ignore other sub-blocks.
Expand Down
5 changes: 4 additions & 1 deletion llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4086,6 +4086,9 @@ void ModuleBitcodeWriterBase::writePerModuleGlobalValueSummary() {
// Bits 1-3 are set only in the combined index, skip them.
if (Index->enableSplitLTOUnit())
Flags |= 0x8;
if (Index->hasUnifiedLTO())
Flags |= 0x200;

Stream.EmitRecord(bitc::FS_FLAGS, ArrayRef<uint64_t>{Flags});

if (Index->begin() == Index->end()) {
Expand All @@ -4112,7 +4115,7 @@ void ModuleBitcodeWriterBase::writePerModuleGlobalValueSummary() {
auto Abbv = std::make_shared<BitCodeAbbrev>();
Abbv->Add(BitCodeAbbrevOp(bitc::FS_PERMODULE_PROFILE));
Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // valueid
Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // flags
Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // flags
Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // instcount
Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 4)); // fflags
Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 4)); // numrefs
Expand Down
8 changes: 7 additions & 1 deletion llvm/lib/IR/ModuleSummaryIndex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,13 @@ uint64_t ModuleSummaryIndex::getFlags() const {
Flags |= 0x80;
if (withSupportsHotColdNew())
Flags |= 0x100;
if (hasUnifiedLTO())
Flags |= 0x200;
return Flags;
}

void ModuleSummaryIndex::setFlags(uint64_t Flags) {
assert(Flags <= 0x1ff && "Unexpected bits in flag");
assert(Flags <= 0x2ff && "Unexpected bits in flag");
// 1 bit: WithGlobalValueDeadStripping flag.
// Set on combined index only.
if (Flags & 0x1)
Expand Down Expand Up @@ -151,6 +153,10 @@ void ModuleSummaryIndex::setFlags(uint64_t Flags) {
// Set on combined index only.
if (Flags & 0x100)
setWithSupportsHotColdNew();
// 1 bit: WithUnifiedLTO flag.
// Set on combined index only.
if (Flags & 0x200)
setUnifiedLTO();
}

// Collect for the given module the list of function it defines
Expand Down
51 changes: 45 additions & 6 deletions llvm/lib/LTO/LTO.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -603,10 +603,10 @@ LTO::ThinLTOState::ThinLTOState(ThinBackend Backend)
}

LTO::LTO(Config Conf, ThinBackend Backend,
unsigned ParallelCodeGenParallelismLevel)
unsigned ParallelCodeGenParallelismLevel, LTOKind LTOMode)
: Conf(std::move(Conf)),
RegularLTO(ParallelCodeGenParallelismLevel, this->Conf),
ThinLTO(std::move(Backend)) {}
ThinLTO(std::move(Backend)), LTOMode(LTOMode) {}

// Requires a destructor for MapVector<BitcodeModule>.
LTO::~LTO() = default;
Expand Down Expand Up @@ -747,12 +747,25 @@ Error LTO::addModule(InputFile &Input, unsigned ModI,
EnableSplitLTOUnit = LTOInfo->EnableSplitLTOUnit;

BitcodeModule BM = Input.Mods[ModI];

if ((LTOMode == LTOK_UnifiedRegular || LTOMode == LTOK_UnifiedThin) &&
!LTOInfo->UnifiedLTO)
return make_error<StringError>(
"unified LTO compilation must use "
"compatible bitcode modules (use -funified-lto)",
inconvertibleErrorCode());

if (LTOInfo->UnifiedLTO && LTOMode == LTOK_Default)
LTOMode = LTOK_UnifiedThin;

bool IsThinLTO = LTOInfo->IsThinLTO && (LTOMode != LTOK_UnifiedRegular);

auto ModSyms = Input.module_symbols(ModI);
addModuleToGlobalRes(ModSyms, {ResI, ResE},
LTOInfo->IsThinLTO ? ThinLTO.ModuleMap.size() + 1 : 0,
IsThinLTO ? ThinLTO.ModuleMap.size() + 1 : 0,
LTOInfo->HasSummary);

if (LTOInfo->IsThinLTO)
if (IsThinLTO)
return addThinLTO(BM, ModSyms, ResI, ResE);

RegularLTO.EmptyCombinedModule = false;
Expand Down Expand Up @@ -820,6 +833,15 @@ LTO::addRegularLTO(BitcodeModule BM, ArrayRef<InputFile::Symbol> Syms,

if (Error Err = M.materializeMetadata())
return std::move(Err);

// If cfi.functions is present and we are in regular LTO mode, LowerTypeTests
// will rename local functions in the merged module as "<function name>.1".
// This causes linking errors, since other parts of the module expect the
// original function name.
if (LTOMode == LTOK_UnifiedRegular)
if (NamedMDNode *CfiFunctionsMD = M.getNamedMetadata("cfi.functions"))
M.eraseNamedMetadata(CfiFunctionsMD);

UpgradeDebugInfo(M);

ModuleSymbolTable SymTab;
Expand Down Expand Up @@ -1214,6 +1236,7 @@ Error LTO::runRegularLTO(AddStreamFn AddStream) {
RegularLTO.CombinedModule->getContext(), Conf.RemarksFilename,
Conf.RemarksPasses, Conf.RemarksFormat, Conf.RemarksWithHotness,
Conf.RemarksHotnessThreshold);
LLVM_DEBUG(dbgs() << "Running regular LTO\n");
if (!DiagFileOrErr)
return DiagFileOrErr.takeError();
DiagnosticOutputFile = std::move(*DiagFileOrErr);
Expand Down Expand Up @@ -1277,18 +1300,33 @@ Error LTO::runRegularLTO(AddStreamFn AddStream) {

if (!Conf.CodeGenOnly) {
for (const auto &R : GlobalResolutions) {
GlobalValue *GV =
RegularLTO.CombinedModule->getNamedValue(R.second.IRName);
if (!R.second.isPrevailingIRSymbol())
continue;
if (R.second.Partition != 0 &&
R.second.Partition != GlobalResolution::External)
continue;

GlobalValue *GV =
RegularLTO.CombinedModule->getNamedValue(R.second.IRName);
// Ignore symbols defined in other partitions.
// Also skip declarations, which are not allowed to have internal linkage.
if (!GV || GV->hasLocalLinkage() || GV->isDeclaration())
continue;

// Symbols that are marked DLLImport or DLLExport should not be
// internalized, as they are either externally visible or referencing
// external symbols. Symbols that have AvailableExternally or Appending
// linkage might be used by future passes and should be kept as is.
// These linkages are seen in Unified regular LTO, because the process
// of creating split LTO units introduces symbols with that linkage into
// one of the created modules. Normally, only the ThinLTO backend would
// compile this module, but Unified Regular LTO processes both
// modules created by the splitting process as regular LTO modules.
if ((LTOMode == LTOKind::LTOK_UnifiedRegular) &&
((GV->getDLLStorageClass() != GlobalValue::DefaultStorageClass) ||
GV->hasAvailableExternallyLinkage() || GV->hasAppendingLinkage()))
continue;

GV->setUnnamedAddr(R.second.UnnamedAddr ? GlobalValue::UnnamedAddr::Global
: GlobalValue::UnnamedAddr::None);
if (EnableLTOInternalization && R.second.Partition == 0)
Expand Down Expand Up @@ -1606,6 +1644,7 @@ ThinBackend lto::createWriteIndexesThinBackend(

Error LTO::runThinLTO(AddStreamFn AddStream, FileCache Cache,
const DenseSet<GlobalValue::GUID> &GUIDPreservedSymbols) {
LLVM_DEBUG(dbgs() << "Running ThinLTO\n");
ThinLTO.CombinedIndex.releaseTemporaryMemory();
timeTraceProfilerBegin("ThinLink", StringRef(""));
auto TimeTraceScopeExit = llvm::make_scope_exit([]() {
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/LTO/LTOBackend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,7 @@ Error lto::backend(const Config &C, AddStreamFn AddStream,

std::unique_ptr<TargetMachine> TM = createTargetMachine(C, *TOrErr, Mod);

LLVM_DEBUG(dbgs() << "Running regular LTO\n");
if (!C.CodeGenOnly) {
if (!opt(C, TM.get(), 0, Mod, /*IsThinLTO=*/false,
/*ExportSummary=*/&CombinedIndex, /*ImportSummary=*/nullptr,
Expand Down Expand Up @@ -566,6 +567,7 @@ Error lto::thinBackend(const Config &Conf, unsigned Task, AddStreamFn AddStream,
// the module, if applicable.
Mod.setPartialSampleProfileRatio(CombinedIndex);

LLVM_DEBUG(dbgs() << "Running ThinLTO\n");
if (Conf.CodeGenOnly) {
codegen(Conf, TM.get(), AddStream, Task, Mod, CombinedIndex);
return finalizeOptimizationRemarks(std::move(DiagnosticOutputFile));
Expand Down
8 changes: 7 additions & 1 deletion llvm/lib/Passes/PassBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1379,7 +1379,13 @@ Error PassBuilder::parseModulePass(ModulePassManager &MPM,
} else if (Matches[1] == "thinlto") {
MPM.addPass(buildThinLTODefaultPipeline(L, nullptr));
} else if (Matches[1] == "lto-pre-link") {
MPM.addPass(buildLTOPreLinkDefaultPipeline(L));
if (PTO.UnifiedLTO)
// When UnifiedLTO is enabled, use the ThinLTO pre-link pipeline. This
// avoids compile-time performance regressions and keeps the pre-link
// LTO pipeline "unified" for both LTO modes.
MPM.addPass(buildThinLTOPreLinkDefaultPipeline(L));
else
MPM.addPass(buildLTOPreLinkDefaultPipeline(L));
} else {
assert(Matches[1] == "lto" && "Not one of the matched options!");
MPM.addPass(buildLTODefaultPipeline(L, nullptr));
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/Passes/PassBuilderPipelines.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,7 @@ PipelineTuningOptions::PipelineTuningOptions() {
LicmMssaOptCap = SetLicmMssaOptCap;
LicmMssaNoAccForPromotionCap = SetLicmMssaNoAccForPromotionCap;
CallGraphProfile = true;
UnifiedLTO = false;
MergeFunctions = EnableMergeFunctions;
InlinerThreshold = -1;
EagerlyInvalidateAnalyses = EnableEagerlyInvalidateAnalyses;
Expand Down
Loading

0 comments on commit a1ca3af

Please sign in to comment.