Skip to content

Commit

Permalink
Restore "[WPD/LowerTypeTests] Delay lowering/removal of type tests un…
Browse files Browse the repository at this point in the history
…til after ICP"

This restores commit 80d0a13, and the
follow on fix in 873c0d0, with a new
fix for test failures after a 2-stage clang bootstrap, and a more robust
fix for the Chromium build failure that an earlier version partially
fixed. See also discussion on D75201.

Reviewers: evgeny777

Subscribers: mehdi_amini, Prazek, hiraditya, steven_wu, dexonsmith, arphaman, davidxl, cfe-commits, llvm-commits

Tags: #clang, #llvm

Differential Revision: https://reviews.llvm.org/D73242
  • Loading branch information
teresajohnson committed Jul 14, 2020
1 parent 02c3f70 commit 6014c46
Show file tree
Hide file tree
Showing 25 changed files with 376 additions and 53 deletions.
5 changes: 3 additions & 2 deletions llvm/include/llvm/IR/ModuleSummaryIndex.h
Expand Up @@ -894,7 +894,8 @@ struct TypeTestResolution {
Single, ///< Single element (last example in "Short Inline Bit Vectors")
AllOnes, ///< All-ones bit vector ("Eliminating Bit Vector Checks for
/// All-Ones Bit Vectors")
} TheKind = Unsat;
Unknown, ///< Unknown (analysis not performed, don't lower)
} TheKind = Unknown;

/// Range of size-1 expressed as a bit width. For example, if the size is in
/// range [1,256], this number will be 8. This helps generate the most compact
Expand Down Expand Up @@ -1092,7 +1093,7 @@ class ModuleSummaryIndex {
// in the way some record are interpreted, like flags for instance.
// Note that incrementing this may require changes in both BitcodeReader.cpp
// and BitcodeWriter.cpp.
static constexpr uint64_t BitcodeSummaryVersion = 8;
static constexpr uint64_t BitcodeSummaryVersion = 9;

// Regular LTO module name for ASM writer
static constexpr const char *getRegularLTOModuleName() {
Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/IR/ModuleSummaryIndexYAML.h
Expand Up @@ -17,6 +17,7 @@ namespace yaml {

template <> struct ScalarEnumerationTraits<TypeTestResolution::Kind> {
static void enumeration(IO &io, TypeTestResolution::Kind &value) {
io.enumCase(value, "Unknown", TypeTestResolution::Unknown);
io.enumCase(value, "Unsat", TypeTestResolution::Unsat);
io.enumCase(value, "ByteArray", TypeTestResolution::ByteArray);
io.enumCase(value, "Inline", TypeTestResolution::Inline);
Expand Down
3 changes: 3 additions & 0 deletions llvm/lib/AsmParser/LLParser.cpp
Expand Up @@ -7799,6 +7799,9 @@ bool LLParser::ParseTypeTestResolution(TypeTestResolution &TTRes) {
return true;

switch (Lex.getKind()) {
case lltok::kw_unknown:
TTRes.TheKind = TypeTestResolution::Unknown;
break;
case lltok::kw_unsat:
TTRes.TheKind = TypeTestResolution::Unsat;
break;
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/IR/AsmWriter.cpp
Expand Up @@ -2861,6 +2861,8 @@ static const char *getWholeProgDevirtResByArgKindName(

static const char *getTTResKindName(TypeTestResolution::Kind K) {
switch (K) {
case TypeTestResolution::Unknown:
return "unknown";
case TypeTestResolution::Unsat:
return "unsat";
case TypeTestResolution::ByteArray:
Expand Down
16 changes: 16 additions & 0 deletions llvm/lib/Passes/PassBuilder.cpp
Expand Up @@ -971,6 +971,12 @@ ModulePassManager PassBuilder::buildModuleSimplificationPipeline(
if (AttributorRun & AttributorRunOption::MODULE)
MPM.addPass(AttributorPass());

// Lower type metadata and the type.test intrinsic in the ThinLTO
// post link pipeline after ICP. This is to enable usage of the type
// tests in ICP sequences.
if (Phase == ThinLTOPhase::PostLink)
MPM.addPass(LowerTypeTestsPass(nullptr, nullptr, true));

// Interprocedural constant propagation now that basic cleanup has occurred
// and prior to optimizing globals.
// FIXME: This position in the pipeline hasn't been carefully considered in
Expand Down Expand Up @@ -1355,6 +1361,9 @@ PassBuilder::buildLTODefaultPipeline(OptimizationLevel Level, bool DebugLogging,
// metadata and intrinsics.
MPM.addPass(WholeProgramDevirtPass(ExportSummary, nullptr));
MPM.addPass(LowerTypeTestsPass(ExportSummary, nullptr));
// Run a second time to clean up any type tests left behind by WPD for use
// in ICP.
MPM.addPass(LowerTypeTestsPass(nullptr, nullptr, true));
return MPM;
}

Expand Down Expand Up @@ -1421,6 +1430,10 @@ PassBuilder::buildLTODefaultPipeline(OptimizationLevel Level, bool DebugLogging,
// The LowerTypeTestsPass needs to run to lower type metadata and the
// type.test intrinsics. The pass does nothing if CFI is disabled.
MPM.addPass(LowerTypeTestsPass(ExportSummary, nullptr));
// Run a second time to clean up any type tests left behind by WPD for use
// in ICP (which is performed earlier than this in the regular LTO
// pipeline).
MPM.addPass(LowerTypeTestsPass(nullptr, nullptr, true));
return MPM;
}

Expand Down Expand Up @@ -1548,6 +1561,9 @@ PassBuilder::buildLTODefaultPipeline(OptimizationLevel Level, bool DebugLogging,
// to be run at link time if CFI is enabled. This pass does nothing if
// CFI is disabled.
MPM.addPass(LowerTypeTestsPass(ExportSummary, nullptr));
// Run a second time to clean up any type tests left behind by WPD for use
// in ICP (which is performed earlier than this in the regular LTO pipeline).
MPM.addPass(LowerTypeTestsPass(nullptr, nullptr, true));

// Enable splitting late in the FullLTO post-link pipeline. This is done in
// the same stage in the old pass manager (\ref addLateLTOOptimizationPasses).
Expand Down
21 changes: 15 additions & 6 deletions llvm/lib/Transforms/IPO/LowerTypeTests.cpp
Expand Up @@ -735,6 +735,9 @@ static bool isKnownTypeIdMember(Metadata *TypeId, const DataLayout &DL,
/// replace the call with.
Value *LowerTypeTestsModule::lowerTypeTestCall(Metadata *TypeId, CallInst *CI,
const TypeIdLowering &TIL) {
// Delay lowering if the resolution is currently unknown.
if (TIL.TheKind == TypeTestResolution::Unknown)
return nullptr;
if (TIL.TheKind == TypeTestResolution::Unsat)
return ConstantInt::getFalse(M.getContext());

Expand Down Expand Up @@ -1036,14 +1039,18 @@ void LowerTypeTestsModule::importTypeTest(CallInst *CI) {
report_fatal_error("Second argument of llvm.type.test must be metadata");

auto TypeIdStr = dyn_cast<MDString>(TypeIdMDVal->getMetadata());
// If this is a local unpromoted type, which doesn't have a metadata string,
// treat as Unknown and delay lowering, so that we can still utilize it for
// later optimizations.
if (!TypeIdStr)
report_fatal_error(
"Second argument of llvm.type.test must be a metadata string");
return;

TypeIdLowering TIL = importTypeId(TypeIdStr->getString());
Value *Lowered = lowerTypeTestCall(TypeIdStr, CI, TIL);
CI->replaceAllUsesWith(Lowered);
CI->eraseFromParent();
if (Lowered) {
CI->replaceAllUsesWith(Lowered);
CI->eraseFromParent();
}
}

// ThinLTO backend: the function F has a jump table entry; update this module
Expand Down Expand Up @@ -1166,8 +1173,10 @@ void LowerTypeTestsModule::lowerTypeTestCalls(
for (CallInst *CI : TIUI.CallSites) {
++NumTypeTestCallsLowered;
Value *Lowered = lowerTypeTestCall(TypeId, CI, TIL);
CI->replaceAllUsesWith(Lowered);
CI->eraseFromParent();
if (Lowered) {
CI->replaceAllUsesWith(Lowered);
CI->eraseFromParent();
}
}
}
}
Expand Down
12 changes: 9 additions & 3 deletions llvm/lib/Transforms/IPO/PassManagerBuilder.cpp
Expand Up @@ -515,6 +515,7 @@ void PassManagerBuilder::populateModulePassManager(
MPM.add(createBarrierNoopPass());

if (PerformThinLTO) {
MPM.add(createLowerTypeTestsPass(nullptr, nullptr, true));
// Drop available_externally and unreferenced globals. This is necessary
// with ThinLTO in order to avoid leaving undefined references to dead
// globals in the object file.
Expand Down Expand Up @@ -548,9 +549,11 @@ void PassManagerBuilder::populateModulePassManager(
// inter-module indirect calls. For that we perform indirect call promotion
// earlier in the pass pipeline, here before globalopt. Otherwise imported
// available_externally functions look unreferenced and are removed.
if (PerformThinLTO)
if (PerformThinLTO) {
MPM.add(createPGOIndirectCallPromotionLegacyPass(/*InLTO = */ true,
!PGOSampleUse.empty()));
MPM.add(createLowerTypeTestsPass(nullptr, nullptr, true));
}

// For SamplePGO in ThinLTO compile phase, we do not want to unroll loops
// as it will change the CFG too much to make the 2nd profile annotation
Expand Down Expand Up @@ -1079,8 +1082,8 @@ void PassManagerBuilder::populateThinLTOPassManager(
PM.add(createVerifierPass());

if (ImportSummary) {
// These passes import type identifier resolutions for whole-program
// devirtualization and CFI. They must run early because other passes may
// This pass imports type identifier resolutions for whole-program
// devirtualization and CFI. It must run early because other passes may
// disturb the specific instruction patterns that these passes look for,
// creating dependencies on resolutions that may not appear in the summary.
//
Expand Down Expand Up @@ -1128,6 +1131,9 @@ void PassManagerBuilder::populateLTOPassManager(legacy::PassManagerBase &PM) {
// control flow integrity mechanisms (-fsanitize=cfi*) and needs to run at
// link time if CFI is enabled. The pass does nothing if CFI is disabled.
PM.add(createLowerTypeTestsPass(ExportSummary, nullptr));
// Run a second time to clean up any type tests left behind by WPD for use
// in ICP (which is performed earlier than this in the regular LTO pipeline).
PM.add(createLowerTypeTestsPass(nullptr, nullptr, true));

if (OptLevel != 0)
addLateLTOOptimizationPasses(PM);
Expand Down
105 changes: 79 additions & 26 deletions llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp
Expand Up @@ -540,7 +540,9 @@ struct DevirtModule {

bool areRemarksEnabled();

void scanTypeTestUsers(Function *TypeTestFunc);
void
scanTypeTestUsers(Function *TypeTestFunc,
DenseMap<Metadata *, std::set<TypeMemberInfo>> &TypeIdMap);
void scanTypeCheckedLoadUsers(Function *TypeCheckedLoadFunc);

void buildTypeIdentifierMap(
Expand Down Expand Up @@ -1705,7 +1707,9 @@ bool DevirtModule::areRemarksEnabled() {
return false;
}

void DevirtModule::scanTypeTestUsers(Function *TypeTestFunc) {
void DevirtModule::scanTypeTestUsers(
Function *TypeTestFunc,
DenseMap<Metadata *, std::set<TypeMemberInfo>> &TypeIdMap) {
// Find all virtual calls via a virtual table pointer %p under an assumption
// of the form llvm.assume(llvm.type.test(%p, %md)). This indicates that %p
// points to a member of the type identifier %md. Group calls by (type ID,
Expand All @@ -1724,22 +1728,59 @@ void DevirtModule::scanTypeTestUsers(Function *TypeTestFunc) {
auto &DT = LookupDomTree(*CI->getFunction());
findDevirtualizableCallsForTypeTest(DevirtCalls, Assumes, CI, DT);

Metadata *TypeId =
cast<MetadataAsValue>(CI->getArgOperand(1))->getMetadata();
// If we found any, add them to CallSlots.
if (!Assumes.empty()) {
Metadata *TypeId =
cast<MetadataAsValue>(CI->getArgOperand(1))->getMetadata();
Value *Ptr = CI->getArgOperand(0)->stripPointerCasts();
for (DevirtCallSite Call : DevirtCalls)
CallSlots[{TypeId, Call.Offset}].addCallSite(Ptr, Call.CB, nullptr);
}

// We no longer need the assumes or the type test.
for (auto Assume : Assumes)
Assume->eraseFromParent();
// We can't use RecursivelyDeleteTriviallyDeadInstructions here because we
// may use the vtable argument later.
if (CI->use_empty())
CI->eraseFromParent();
auto RemoveTypeTestAssumes = [&]() {
// We no longer need the assumes or the type test.
for (auto Assume : Assumes)
Assume->eraseFromParent();
// We can't use RecursivelyDeleteTriviallyDeadInstructions here because we
// may use the vtable argument later.
if (CI->use_empty())
CI->eraseFromParent();
};

// At this point we could remove all type test assume sequences, as they
// were originally inserted for WPD. However, we can keep these in the
// code stream for later analysis (e.g. to help drive more efficient ICP
// sequences). They will eventually be removed by a second LowerTypeTests
// invocation that cleans them up. In order to do this correctly, the first
// LowerTypeTests invocation needs to know that they have "Unknown" type
// test resolution, so that they aren't treated as Unsat and lowered to
// False, which will break any uses on assumes. Below we remove any type
// test assumes that will not be treated as Unknown by LTT.

// The type test assumes will be treated by LTT as Unsat if the type id is
// not used on a global (in which case it has no entry in the TypeIdMap).
if (!TypeIdMap.count(TypeId))
RemoveTypeTestAssumes();

// For ThinLTO importing, we need to remove the type test assumes if this is
// an MDString type id without a corresponding TypeIdSummary. Any
// non-MDString type ids are ignored and treated as Unknown by LTT, so their
// type test assumes can be kept. If the MDString type id is missing a
// TypeIdSummary (e.g. because there was no use on a vcall, preventing the
// exporting phase of WPD from analyzing it), then it would be treated as
// Unsat by LTT and we need to remove its type test assumes here. If not
// used on a vcall we don't need them for later optimization use in any
// case.
else if (ImportSummary && isa<MDString>(TypeId)) {
const TypeIdSummary *TidSummary =
ImportSummary->getTypeIdSummary(cast<MDString>(TypeId)->getString());
if (!TidSummary)
RemoveTypeTestAssumes();
else
// If one was created it should not be Unsat, because if we reached here
// the type id was used on a global.
assert(TidSummary->TTRes.TheKind != TypeTestResolution::Unsat);
}
}
}

Expand Down Expand Up @@ -1931,8 +1972,13 @@ bool DevirtModule::run() {
(!TypeCheckedLoadFunc || TypeCheckedLoadFunc->use_empty()))
return false;

// Rebuild type metadata into a map for easy lookup.
std::vector<VTableBits> Bits;
DenseMap<Metadata *, std::set<TypeMemberInfo>> TypeIdMap;
buildTypeIdentifierMap(Bits, TypeIdMap);

if (TypeTestFunc && AssumeFunc)
scanTypeTestUsers(TypeTestFunc);
scanTypeTestUsers(TypeTestFunc, TypeIdMap);

if (TypeCheckedLoadFunc)
scanTypeCheckedLoadUsers(TypeCheckedLoadFunc);
Expand All @@ -1954,10 +2000,6 @@ bool DevirtModule::run() {
return true;
}

// Rebuild type metadata into a map for easy lookup.
std::vector<VTableBits> Bits;
DenseMap<Metadata *, std::set<TypeMemberInfo>> TypeIdMap;
buildTypeIdentifierMap(Bits, TypeIdMap);
if (TypeIdMap.empty())
return true;

Expand Down Expand Up @@ -2014,14 +2056,22 @@ bool DevirtModule::run() {
// function implementation at offset S.first.ByteOffset, and add to
// TargetsForSlot.
std::vector<VirtualCallTarget> TargetsForSlot;
if (tryFindVirtualCallTargets(TargetsForSlot, TypeIdMap[S.first.TypeID],
WholeProgramDevirtResolution *Res = nullptr;
const std::set<TypeMemberInfo> &TypeMemberInfos = TypeIdMap[S.first.TypeID];
if (ExportSummary && isa<MDString>(S.first.TypeID) &&
TypeMemberInfos.size())
// For any type id used on a global's type metadata, create the type id
// summary resolution regardless of whether we can devirtualize, so that
// lower type tests knows the type id is not Unsat. If it was not used on
// a global's type metadata, the TypeIdMap entry set will be empty, and
// we don't want to create an entry (with the default Unknown type
// resolution), which can prevent detection of the Unsat.
Res = &ExportSummary
->getOrInsertTypeIdSummary(
cast<MDString>(S.first.TypeID)->getString())
.WPDRes[S.first.ByteOffset];
if (tryFindVirtualCallTargets(TargetsForSlot, TypeMemberInfos,
S.first.ByteOffset)) {
WholeProgramDevirtResolution *Res = nullptr;
if (ExportSummary && isa<MDString>(S.first.TypeID))
Res = &ExportSummary
->getOrInsertTypeIdSummary(
cast<MDString>(S.first.TypeID)->getString())
.WPDRes[S.first.ByteOffset];

if (!trySingleImplDevirt(ExportSummary, TargetsForSlot, S.second, Res)) {
DidVirtualConstProp |=
Expand Down Expand Up @@ -2135,11 +2185,14 @@ void DevirtIndex::run() {
std::vector<ValueInfo> TargetsForSlot;
auto TidSummary = ExportSummary.getTypeIdCompatibleVtableSummary(S.first.TypeID);
assert(TidSummary);
// Create the type id summary resolution regardlness of whether we can
// devirtualize, so that lower type tests knows the type id is used on
// a global and not Unsat.
WholeProgramDevirtResolution *Res =
&ExportSummary.getOrInsertTypeIdSummary(S.first.TypeID)
.WPDRes[S.first.ByteOffset];
if (tryFindVirtualCallTargets(TargetsForSlot, *TidSummary,
S.first.ByteOffset)) {
WholeProgramDevirtResolution *Res =
&ExportSummary.getOrInsertTypeIdSummary(S.first.TypeID)
.WPDRes[S.first.ByteOffset];

if (!trySingleImplDevirt(TargetsForSlot, S.first, S.second, Res,
DevirtTargets))
Expand Down
2 changes: 1 addition & 1 deletion llvm/test/Bitcode/summary_version.ll
Expand Up @@ -2,7 +2,7 @@
; RUN: opt -module-summary %s -o - | llvm-bcanalyzer -dump | FileCheck %s

; CHECK: <GLOBALVAL_SUMMARY_BLOCK
; CHECK: <VERSION op0=8/>
; CHECK: <VERSION op0=9/>



Expand Down
1 change: 1 addition & 0 deletions llvm/test/Other/new-pm-lto-defaults.ll
Expand Up @@ -99,6 +99,7 @@
; CHECK-O2-NEXT: Running analysis: DemandedBitsAnalysis
; CHECK-O2-NEXT: Running pass: CrossDSOCFIPass
; CHECK-O2-NEXT: Running pass: LowerTypeTestsPass
; CHECK-O-NEXT: Running pass: LowerTypeTestsPass
; CHECK-O2-NEXT: Running pass: ModuleToFunctionPassAdaptor<{{.*}}SimplifyCFGPass>
; CHECK-O2-NEXT: Running pass: EliminateAvailableExternallyPass
; CHECK-O2-NEXT: Running pass: GlobalDCEPass
Expand Down
1 change: 1 addition & 0 deletions llvm/test/Other/new-pm-thinlto-defaults.ll
Expand Up @@ -79,6 +79,7 @@
; CHECK-O-NEXT: Running pass: LowerExpectIntrinsicPass
; CHECK-O3-NEXT: Running pass: CallSiteSplittingPass
; CHECK-O-NEXT: Finished llvm::Function pass manager run.
; CHECK-POSTLINK-O-NEXT: Running pass: LowerTypeTestsPass
; CHECK-O-NEXT: Running pass: IPSCCPPass
; CHECK-O-NEXT: Running pass: CalledValuePropagationPass
; CHECK-O-NEXT: Running pass: GlobalOptPass
Expand Down
1 change: 1 addition & 0 deletions llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll
Expand Up @@ -48,6 +48,7 @@
; CHECK-O-NEXT: Running pass: LowerExpectIntrinsicPass
; CHECK-O3-NEXT: Running pass: CallSiteSplittingPass
; CHECK-O-NEXT: Finished {{.*}}Function pass manager run.
; CHECK-O-NEXT: Running pass: LowerTypeTestsPass
; CHECK-O-NEXT: Running pass: IPSCCPPass
; CHECK-O-NEXT: Running pass: CalledValuePropagationPass
; CHECK-O-NEXT: Running pass: GlobalOptPass
Expand Down
Expand Up @@ -59,6 +59,7 @@
; CHECK-O-NEXT: Running analysis: CallGraphAnalysis
; CHECK-O-NEXT: Running pass: RequireAnalysisPass<{{.*}}ProfileSummaryAnalysis
; CHECK-O-NEXT: Running pass: PGOIndirectCallPromotion
; CHECK-O-NEXT: Running pass: LowerTypeTestsPass
; CHECK-O-NEXT: Running pass: IPSCCPPass
; CHECK-O-NEXT: Running pass: CalledValuePropagationPass
; CHECK-O-NEXT: Running pass: GlobalOptPass
Expand Down

0 comments on commit 6014c46

Please sign in to comment.