Skip to content

Commit 1991aa6

Browse files
authored
Reapply "[nfc][mlgo] Incrementally update DominatorTreeAnalysis in FunctionPropertiesAnalysis (#104867) (#106309)
Reverts c992690. The problem is that if there is a sequence "{delete A->B} {delete A->B} {insert A->B}" the net result is "{delete A->B}", which is not what we want. Duplicate successors may happen in cases like switch statements (as shown in the unit test). The second problem was that in `invoke` cases, some edges we speculate may get deleted don't, but are also not reachable from the inlined call site's basic block. We just need to check which edges are actually not present anymore. The fix is to sanitize the list of deletes, just like we do for inserts.
1 parent cdaebf6 commit 1991aa6

File tree

4 files changed

+175
-3
lines changed

4 files changed

+175
-3
lines changed

llvm/include/llvm/Analysis/FunctionPropertiesAnalysis.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#define LLVM_ANALYSIS_FUNCTIONPROPERTIESANALYSIS_H
1616

1717
#include "llvm/ADT/DenseSet.h"
18+
#include "llvm/IR/Dominators.h"
1819
#include "llvm/IR/PassManager.h"
1920

2021
namespace llvm {
@@ -186,7 +187,12 @@ class FunctionPropertiesUpdater {
186187
static bool isUpdateValid(Function &F, const FunctionPropertiesInfo &FPI,
187188
FunctionAnalysisManager &FAM);
188189

190+
DominatorTree &getUpdatedDominatorTree(FunctionAnalysisManager &FAM) const;
191+
189192
DenseSet<const BasicBlock *> Successors;
193+
194+
// Edges we might potentially need to remove from the dominator tree.
195+
SmallVector<DominatorTree::UpdateType, 2> DomTreeUpdates;
190196
};
191197
} // namespace llvm
192198
#endif // LLVM_ANALYSIS_FUNCTIONPROPERTIESANALYSIS_H

llvm/lib/Analysis/FunctionPropertiesAnalysis.cpp

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,20 @@ FunctionPropertiesUpdater::FunctionPropertiesUpdater(
326326
// with the CB BB ('Entry') between which the inlined callee will be pasted.
327327
Successors.insert(succ_begin(&CallSiteBB), succ_end(&CallSiteBB));
328328

329+
// the outcome of the inlining may be that some edges get lost (DCEd BBs
330+
// because inlining brought some constant, for example). We don't know which
331+
// edges will be removed, so we list all of them as potentially removable.
332+
// Some BBs have (at this point) duplicate edges. Remove duplicates, otherwise
333+
// the DT updater will not apply changes correctly.
334+
DenseSet<const BasicBlock *> Inserted;
335+
for (auto *Succ : successors(&CallSiteBB))
336+
if (Inserted.insert(Succ).second)
337+
DomTreeUpdates.emplace_back(DominatorTree::UpdateKind::Delete,
338+
const_cast<BasicBlock *>(&CallSiteBB),
339+
const_cast<BasicBlock *>(Succ));
340+
// Reuse Inserted (which has some allocated capacity at this point) below, if
341+
// we have an invoke.
342+
Inserted.clear();
329343
// Inlining only handles invoke and calls. If this is an invoke, and inlining
330344
// it pulls another invoke, the original landing pad may get split, so as to
331345
// share its content with other potential users. So the edge up to which we
@@ -336,6 +350,12 @@ FunctionPropertiesUpdater::FunctionPropertiesUpdater(
336350
if (const auto *II = dyn_cast<InvokeInst>(&CB)) {
337351
const auto *UnwindDest = II->getUnwindDest();
338352
Successors.insert(succ_begin(UnwindDest), succ_end(UnwindDest));
353+
// Same idea as above, we pretend we lose all these edges.
354+
for (auto *Succ : successors(UnwindDest))
355+
if (Inserted.insert(Succ).second)
356+
DomTreeUpdates.emplace_back(DominatorTree::UpdateKind::Delete,
357+
const_cast<BasicBlock *>(UnwindDest),
358+
const_cast<BasicBlock *>(Succ));
339359
}
340360

341361
// Exclude the CallSiteBB, if it happens to be its own successor (1-BB loop).
@@ -356,6 +376,33 @@ FunctionPropertiesUpdater::FunctionPropertiesUpdater(
356376
FPI.updateForBB(*BB, -1);
357377
}
358378

379+
DominatorTree &FunctionPropertiesUpdater::getUpdatedDominatorTree(
380+
FunctionAnalysisManager &FAM) const {
381+
auto &DT =
382+
FAM.getResult<DominatorTreeAnalysis>(const_cast<Function &>(Caller));
383+
384+
SmallVector<DominatorTree::UpdateType, 2> FinalDomTreeUpdates;
385+
386+
DenseSet<const BasicBlock *> Inserted;
387+
for (auto *Succ : successors(&CallSiteBB))
388+
if (Inserted.insert(Succ).second)
389+
FinalDomTreeUpdates.push_back({DominatorTree::UpdateKind::Insert,
390+
const_cast<BasicBlock *>(&CallSiteBB),
391+
const_cast<BasicBlock *>(Succ)});
392+
393+
// Perform the deletes last, so that any new nodes connected to nodes
394+
// participating in the edge deletion are known to the DT.
395+
for (auto &Upd : DomTreeUpdates)
396+
if (!llvm::is_contained(successors(Upd.getFrom()), Upd.getTo()))
397+
FinalDomTreeUpdates.push_back(Upd);
398+
399+
DT.applyUpdates(FinalDomTreeUpdates);
400+
#ifdef EXPENSIVE_CHECKS
401+
assert(DT.verify(DominatorTree::VerificationLevel::Full));
402+
#endif
403+
return DT;
404+
}
405+
359406
void FunctionPropertiesUpdater::finish(FunctionAnalysisManager &FAM) const {
360407
// Update feature values from the BBs that were copied from the callee, or
361408
// might have been modified because of inlining. The latter have been
@@ -383,8 +430,7 @@ void FunctionPropertiesUpdater::finish(FunctionAnalysisManager &FAM) const {
383430
// remove E.
384431
SetVector<const BasicBlock *> Reinclude;
385432
SetVector<const BasicBlock *> Unreachable;
386-
const auto &DT =
387-
FAM.getResult<DominatorTreeAnalysis>(const_cast<Function &>(Caller));
433+
auto &DT = getUpdatedDominatorTree(FAM);
388434

389435
if (&CallSiteBB != &*Caller.begin())
390436
Reinclude.insert(&*Caller.begin());
@@ -427,11 +473,17 @@ void FunctionPropertiesUpdater::finish(FunctionAnalysisManager &FAM) const {
427473

428474
const auto &LI = FAM.getResult<LoopAnalysis>(const_cast<Function &>(Caller));
429475
FPI.updateAggregateStats(Caller, LI);
476+
#ifdef EXPENSIVE_CHECKS
477+
assert(isUpdateValid(Caller, FPI, FAM));
478+
#endif
430479
}
431480

432481
bool FunctionPropertiesUpdater::isUpdateValid(Function &F,
433482
const FunctionPropertiesInfo &FPI,
434483
FunctionAnalysisManager &FAM) {
484+
if (!FAM.getResult<DominatorTreeAnalysis>(F).verify(
485+
DominatorTree::VerificationLevel::Full))
486+
return false;
435487
DominatorTree DT(F);
436488
LoopInfo LI(DT);
437489
auto Fresh = FunctionPropertiesInfo::getFunctionPropertiesInfo(F, DT, LI);

llvm/lib/Analysis/MLInlineAdvisor.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,6 @@ void MLInlineAdvisor::onSuccessfulInlining(const MLInlineAdvice &Advice,
288288
{
289289
PreservedAnalyses PA = PreservedAnalyses::all();
290290
PA.abandon<FunctionPropertiesAnalysis>();
291-
PA.abandon<DominatorTreeAnalysis>();
292291
PA.abandon<LoopAnalysis>();
293292
FAM.invalidate(*Caller, PA);
294293
}

llvm/unittests/Analysis/FunctionPropertiesAnalysisTest.cpp

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -999,4 +999,119 @@ declare <4 x ptr> @f4()
999999
EXPECT_EQ(DetailedF1Properties.CallReturnsVectorPointerCount, 1);
10001000
EnableDetailedFunctionProperties.setValue(false);
10011001
}
1002+
1003+
TEST_F(FunctionPropertiesAnalysisTest, ReAddEdges) {
1004+
LLVMContext C;
1005+
std::unique_ptr<Module> M = makeLLVMModule(C, R"IR(
1006+
define hidden void @f1(ptr noundef %destatep, i32 noundef %offset, i8 noundef zeroext %byte1) {
1007+
entry:
1008+
%cmp = icmp eq i8 %byte1, 0
1009+
br i1 %cmp, label %if.then, label %if.else
1010+
1011+
if.then: ; preds = %entry
1012+
call fastcc void @f2(ptr noundef %destatep, i32 noundef 37, i32 noundef 600)
1013+
%and = and i32 %offset, 3
1014+
switch i32 %and, label %default.unreachable [
1015+
i32 0, label %sw.bb
1016+
i32 1, label %sw.bb1
1017+
i32 2, label %sw.bb1
1018+
i32 3, label %if.end
1019+
]
1020+
1021+
sw.bb: ; preds = %if.then
1022+
call fastcc void @f2(ptr noundef %destatep, i32 noundef 57, i32 noundef 600)
1023+
br label %if.end
1024+
1025+
sw.bb1: ; preds = %if.then, %if.then
1026+
call fastcc void @f2(ptr noundef %destatep, i32 noundef 56, i32 noundef 600) #34
1027+
br label %if.end
1028+
1029+
default.unreachable: ; preds = %if.then
1030+
unreachable
1031+
1032+
if.else: ; preds = %entry
1033+
call fastcc void @f2(ptr noundef %destatep, i32 noundef 56, i32 noundef 600)
1034+
br label %if.end
1035+
1036+
if.end: ; preds = %sw.bb, %sw.bb1, %if.then, %if.else
1037+
ret void
1038+
}
1039+
1040+
define internal fastcc void @f2(ptr nocapture noundef %destatep, i32 noundef %r_enc, i32 noundef %whack) {
1041+
entry:
1042+
%enc_prob = getelementptr inbounds nuw i8, ptr %destatep, i32 512
1043+
%arrayidx = getelementptr inbounds [67 x i32], ptr %enc_prob, i32 0, i32 %r_enc
1044+
%0 = load i32, ptr %arrayidx, align 4
1045+
%sub = sub nsw i32 %0, %whack
1046+
store i32 %sub, ptr %arrayidx, align 4
1047+
ret void
1048+
}
1049+
)IR");
1050+
auto *F1 = M->getFunction("f1");
1051+
auto *F2 = M->getFunction("f2");
1052+
auto *CB = [&]() -> CallBase * {
1053+
for (auto &BB : *F1)
1054+
for (auto &I : BB)
1055+
if (auto *CB = dyn_cast<CallBase>(&I);
1056+
CB && CB->getCalledFunction() && CB->getCalledFunction() == F2)
1057+
return CB;
1058+
return nullptr;
1059+
}();
1060+
ASSERT_NE(CB, nullptr);
1061+
auto FPI = buildFPI(*F1);
1062+
FunctionPropertiesUpdater FPU(FPI, *CB);
1063+
InlineFunctionInfo IFI;
1064+
auto IR = llvm::InlineFunction(*CB, IFI);
1065+
EXPECT_TRUE(IR.isSuccess());
1066+
invalidate(*F1);
1067+
EXPECT_TRUE(FPU.finishAndTest(FAM));
1068+
}
1069+
1070+
TEST_F(FunctionPropertiesAnalysisTest, InvokeLandingCanStillBeReached) {
1071+
LLVMContext C;
1072+
// %lpad is reachable from a block not involved in the inlining decision. We
1073+
// make sure that's not the entry - otherwise the DT will be recomputed from
1074+
// scratch. The idea here is that the edge known to the inliner to potentially
1075+
// disappear - %lpad->%ehcleanup -should survive because it is still reachable
1076+
// from %middle.
1077+
std::unique_ptr<Module> M = makeLLVMModule(C,
1078+
R"IR(
1079+
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
1080+
target triple = "x86_64-pc-linux-gnu"
1081+
1082+
define i64 @f1(i32 noundef %value) {
1083+
entry:
1084+
br label %middle
1085+
middle:
1086+
%c = icmp eq i32 %value, 0
1087+
br i1 %c, label %invoke, label %lpad
1088+
invoke:
1089+
invoke fastcc void @f2() to label %cont unwind label %lpad
1090+
cont:
1091+
br label %exit
1092+
lpad:
1093+
%lp = landingpad i32 cleanup
1094+
br label %ehcleanup
1095+
ehcleanup:
1096+
resume i32 0
1097+
exit:
1098+
ret i64 1
1099+
}
1100+
define void @f2() {
1101+
ret void
1102+
}
1103+
)IR");
1104+
1105+
Function *F1 = M->getFunction("f1");
1106+
CallBase *CB = findCall(*F1);
1107+
EXPECT_NE(CB, nullptr);
1108+
1109+
auto FPI = buildFPI(*F1);
1110+
FunctionPropertiesUpdater FPU(FPI, *CB);
1111+
InlineFunctionInfo IFI;
1112+
auto IR = llvm::InlineFunction(*CB, IFI);
1113+
EXPECT_TRUE(IR.isSuccess());
1114+
invalidate(*F1);
1115+
EXPECT_TRUE(FPU.finishAndTest(FAM));
1116+
}
10021117
} // end anonymous namespace

0 commit comments

Comments
 (0)