Skip to content

Commit b1f4168

Browse files
committed
[IPSCCP] Decouple queries for function analysis results.
The SCCPSolver is using a structure (AnalysisResultsForFn) where it keeps pointers to various analyses needed by the IPSCCP pass. These analyses are requested all at the same time, which can become problematic in some cases. For example one could be retrieved via getCachedAnalysis() prior to the actual execution of the analysis. In more detail: The IPSCCP pass uses a DomTreeUpdater to preserve the PostDominatorTree in case the PostDominatorTreeAnalysis had run before IPSCCP. Starting with commit 1b12320 the IPSCCP pass may use BlockFrequencyAnalysis for some functions in the module. As a result, the PostDominatorTreeAnalysis may not run until the BlockFrequencyAnalysis has run, since the latter analysis depends on the former. Currently, we setup the DomTreeUpdater using getCachedAnalysis to retrieve a PostDominatorTree. This happens before BlockFrequencyAnalysis has run, therefore the cached analysis can become invalid by the time we use it. Differential Revision: https://reviews.llvm.org/D151666
1 parent b7052fa commit b1f4168

File tree

5 files changed

+124
-45
lines changed

5 files changed

+124
-45
lines changed

llvm/include/llvm/Transforms/IPO/FunctionSpecialization.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,6 @@ class FunctionSpecializer {
145145

146146
~FunctionSpecializer();
147147

148-
bool isClonedFunction(Function *F) { return Specializations.count(F); }
149-
150148
bool run();
151149

152150
private:

llvm/include/llvm/Transforms/Utils/SCCPSolver.h

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,6 @@ class TargetLibraryInfo;
3939
class Value;
4040
class ValueLatticeElement;
4141

42-
/// Helper struct for bundling up the analysis results per function for IPSCCP.
43-
struct AnalysisResultsForFn {
44-
std::unique_ptr<PredicateInfo> PredInfo;
45-
DominatorTree *DT;
46-
PostDominatorTree *PDT;
47-
LoopInfo *LI;
48-
};
49-
5042
/// Helper struct shared between Function Specialization and SCCP Solver.
5143
struct ArgInfo {
5244
Argument *Formal; // The Formal argument being analysed.
@@ -82,7 +74,9 @@ class SCCPSolver {
8274

8375
~SCCPSolver();
8476

85-
void addAnalysis(Function &F, AnalysisResultsForFn A);
77+
void addLoopInfo(Function &F, LoopInfo &LI);
78+
79+
void addPredicateInfo(Function &F, DominatorTree &DT, AssumptionCache &AC);
8680

8781
/// markBlockExecutable - This method can be used by clients to mark all of
8882
/// the blocks that are known to be intrinsically live in the processed unit.
@@ -93,8 +87,6 @@ class SCCPSolver {
9387

9488
const LoopInfo &getLoopInfo(Function &F);
9589

96-
DomTreeUpdater getDTU(Function &F);
97-
9890
/// trackValueOfGlobalVariable - Clients can use this method to
9991
/// inform the SCCPSolver that it should track loads and stores to the
10092
/// specified global variable if it can. This is only legal to call if

llvm/lib/Transforms/IPO/SCCP.cpp

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,8 @@ static bool runIPSCCP(
110110
std::function<const TargetLibraryInfo &(Function &)> GetTLI,
111111
std::function<TargetTransformInfo &(Function &)> GetTTI,
112112
std::function<AssumptionCache &(Function &)> GetAC,
113-
function_ref<AnalysisResultsForFn(Function &)> getAnalysis,
113+
std::function<DominatorTree &(Function &)> GetDT,
114+
std::function<LoopInfo &(Function &)> GetLI,
114115
bool IsFuncSpecEnabled) {
115116
SCCPSolver Solver(DL, GetTLI, M.getContext());
116117
FunctionSpecializer Specializer(Solver, M, FAM, GetTLI, GetTTI, GetAC);
@@ -121,7 +122,12 @@ static bool runIPSCCP(
121122
if (F.isDeclaration())
122123
continue;
123124

124-
Solver.addAnalysis(F, getAnalysis(F));
125+
DominatorTree &DT = GetDT(F);
126+
AssumptionCache &AC = GetAC(F);
127+
Solver.addPredicateInfo(F, DT, AC);
128+
129+
if (IsFuncSpecEnabled)
130+
Solver.addLoopInfo(F, GetLI(F));
125131

126132
// Determine if we can track the function's return values. If so, add the
127133
// function to the solver's set of return-tracked functions.
@@ -222,10 +228,9 @@ static bool runIPSCCP(
222228
BB, InsertedValues, NumInstRemoved, NumInstReplaced);
223229
}
224230

225-
DomTreeUpdater DTU = IsFuncSpecEnabled && Specializer.isClonedFunction(&F)
226-
? DomTreeUpdater(DomTreeUpdater::UpdateStrategy::Lazy)
227-
: Solver.getDTU(F);
228-
231+
DominatorTree *DT = FAM->getCachedResult<DominatorTreeAnalysis>(F);
232+
PostDominatorTree *PDT = FAM->getCachedResult<PostDominatorTreeAnalysis>(F);
233+
DomTreeUpdater DTU(DT, PDT, DomTreeUpdater::UpdateStrategy::Lazy);
229234
// Change dead blocks to unreachable. We do it after replacing constants
230235
// in all executable blocks, because changeToUnreachable may remove PHI
231236
// nodes in executable blocks we found values for. The function's entry
@@ -387,15 +392,14 @@ PreservedAnalyses IPSCCPPass::run(Module &M, ModuleAnalysisManager &AM) {
387392
auto GetAC = [&FAM](Function &F) -> AssumptionCache & {
388393
return FAM.getResult<AssumptionAnalysis>(F);
389394
};
390-
auto getAnalysis = [&FAM, this](Function &F) -> AnalysisResultsForFn {
391-
DominatorTree &DT = FAM.getResult<DominatorTreeAnalysis>(F);
392-
return {
393-
std::make_unique<PredicateInfo>(F, DT, FAM.getResult<AssumptionAnalysis>(F)),
394-
&DT, FAM.getCachedResult<PostDominatorTreeAnalysis>(F),
395-
isFuncSpecEnabled() ? &FAM.getResult<LoopAnalysis>(F) : nullptr };
395+
auto GetDT = [&FAM](Function &F) -> DominatorTree & {
396+
return FAM.getResult<DominatorTreeAnalysis>(F);
397+
};
398+
auto GetLI = [&FAM](Function &F) -> LoopInfo & {
399+
return FAM.getResult<LoopAnalysis>(F);
396400
};
397401

398-
if (!runIPSCCP(M, DL, &FAM, GetTLI, GetTTI, GetAC, getAnalysis,
402+
if (!runIPSCCP(M, DL, &FAM, GetTLI, GetTTI, GetAC, GetDT, GetLI,
399403
isFuncSpecEnabled()))
400404
return PreservedAnalyses::all();
401405

llvm/lib/Transforms/Utils/SCCPSolver.cpp

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,9 @@ class SCCPInstVisitor : public InstVisitor<SCCPInstVisitor> {
386386
using Edge = std::pair<BasicBlock *, BasicBlock *>;
387387
DenseSet<Edge> KnownFeasibleEdges;
388388

389-
DenseMap<Function *, AnalysisResultsForFn> AnalysisResults;
389+
DenseMap<Function *, LoopInfo *> FnLoopInfo;
390+
DenseMap<Function *, std::unique_ptr<PredicateInfo>> FnPredicateInfo;
391+
390392
DenseMap<Value *, SmallPtrSet<User *, 2>> AdditionalUsers;
391393

392394
LLVMContext &Ctx;
@@ -649,32 +651,30 @@ class SCCPInstVisitor : public InstVisitor<SCCPInstVisitor> {
649651
void visitInstruction(Instruction &I);
650652

651653
public:
652-
void addAnalysis(Function &F, AnalysisResultsForFn A) {
653-
AnalysisResults.insert({&F, std::move(A)});
654+
void addLoopInfo(Function &F, LoopInfo &LI) {
655+
FnLoopInfo.insert({&F, &LI});
656+
}
657+
658+
void addPredicateInfo(Function &F, DominatorTree &DT, AssumptionCache &AC) {
659+
FnPredicateInfo.insert({&F, std::make_unique<PredicateInfo>(F, DT, AC)});
654660
}
655661

656662
void visitCallInst(CallInst &I) { visitCallBase(I); }
657663

658664
bool markBlockExecutable(BasicBlock *BB);
659665

660666
const PredicateBase *getPredicateInfoFor(Instruction *I) {
661-
auto A = AnalysisResults.find(I->getParent()->getParent());
662-
if (A == AnalysisResults.end())
667+
auto It = FnPredicateInfo.find(I->getParent()->getParent());
668+
if (It == FnPredicateInfo.end())
663669
return nullptr;
664-
return A->second.PredInfo->getPredicateInfoFor(I);
670+
return It->second->getPredicateInfoFor(I);
665671
}
666672

667673
const LoopInfo &getLoopInfo(Function &F) {
668-
auto A = AnalysisResults.find(&F);
669-
assert(A != AnalysisResults.end() && A->second.LI &&
674+
auto It = FnLoopInfo.find(&F);
675+
assert(It != FnLoopInfo.end() && It->second &&
670676
"Need LoopInfo analysis results for function.");
671-
return *A->second.LI;
672-
}
673-
674-
DomTreeUpdater getDTU(Function &F) {
675-
auto A = AnalysisResults.find(&F);
676-
assert(A != AnalysisResults.end() && "Need analysis results for function.");
677-
return {A->second.DT, A->second.PDT, DomTreeUpdater::UpdateStrategy::Lazy};
677+
return *It->second;
678678
}
679679

680680
SCCPInstVisitor(const DataLayout &DL,
@@ -1950,8 +1950,13 @@ SCCPSolver::SCCPSolver(
19501950

19511951
SCCPSolver::~SCCPSolver() = default;
19521952

1953-
void SCCPSolver::addAnalysis(Function &F, AnalysisResultsForFn A) {
1954-
return Visitor->addAnalysis(F, std::move(A));
1953+
void SCCPSolver::addLoopInfo(Function &F, LoopInfo &LI) {
1954+
Visitor->addLoopInfo(F, LI);
1955+
}
1956+
1957+
void SCCPSolver::addPredicateInfo(Function &F, DominatorTree &DT,
1958+
AssumptionCache &AC) {
1959+
Visitor->addPredicateInfo(F, DT, AC);
19551960
}
19561961

19571962
bool SCCPSolver::markBlockExecutable(BasicBlock *BB) {
@@ -1966,8 +1971,6 @@ const LoopInfo &SCCPSolver::getLoopInfo(Function &F) {
19661971
return Visitor->getLoopInfo(F);
19671972
}
19681973

1969-
DomTreeUpdater SCCPSolver::getDTU(Function &F) { return Visitor->getDTU(F); }
1970-
19711974
void SCCPSolver::trackValueOfGlobalVariable(GlobalVariable *GV) {
19721975
Visitor->trackValueOfGlobalVariable(GV);
19731976
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
; RUN: opt -passes="ipsccp<func-spec>,print<postdomtree>" -force-specialization -funcspec-max-iters=2 -funcspec-max-clones=1 -funcspec-for-literal-constant=true -S < %s 2>&1 | FileCheck %s
2+
3+
; REQUIRES: asserts
4+
5+
; This test case is trying to validate that the postdomtree is preserved
6+
; correctly by the ipsccp pass. A tricky bug was introduced in commit
7+
; 1b1232047e83b69561 when PDT would be feched using getCachedAnalysis in order
8+
; to setup a DomTreeUpdater (to update the PDT during transformation in order
9+
; to preserve the analysis). But given that commit the PDT could end up being
10+
; required and calculated via BlockFrequency analysis. So the problem was that
11+
; when setting up the DomTreeUpdater we used a nullptr in case PDT wasn't
12+
; cached at the begininng of IPSCCP, to indicate that no updates where needed
13+
; for PDT. But then the PDT was calculated, given the input IR, and preserved
14+
; using the non-updated state (as the DTU wasn't configured for updating the
15+
; PDT).
16+
17+
; CHECK-NOT: <badref>
18+
; CHECK: Inorder PostDominator Tree: DFSNumbers invalid: 0 slow queries.
19+
; CHECK-NEXT: [1] <<exit node>> {4294967295,4294967295} [0]
20+
; CHECK-NEXT: [2] %for.body {4294967295,4294967295} [1]
21+
; CHECK-NEXT: [2] %if.end4 {4294967295,4294967295} [1]
22+
; CHECK-NEXT: [3] %entry {4294967295,4294967295} [2]
23+
; CHECK-NEXT: [2] %for.cond34 {4294967295,4294967295} [1]
24+
; CHECK-NEXT: [3] %for.cond16 {4294967295,4294967295} [2]
25+
; CHECK-NEXT: Roots: %for.body %for.cond34
26+
; CHECK-NEXT: PostDominatorTree for function: bar
27+
; CHECK-NOT: <badref>
28+
29+
declare hidden i1 @compare(ptr) align 2
30+
declare hidden { i8, ptr } @getType(ptr) align 2
31+
32+
define internal void @foo(ptr %TLI, ptr %DL, ptr %Ty, ptr %ValueVTs, ptr %Offsets, i64 %StartingOffset) {
33+
entry:
34+
%VT = alloca i64, align 8
35+
br i1 false, label %if.then, label %if.end4
36+
37+
if.then: ; preds = %entry
38+
ret void
39+
40+
if.end4: ; preds = %entry
41+
%cmp = call zeroext i1 @compare(ptr undef)
42+
br i1 %cmp, label %for.body, label %for.cond16
43+
44+
for.body: ; preds = %if.end4
45+
%add13 = add i64 %StartingOffset, undef
46+
call void @foo(ptr %TLI, ptr %DL, ptr undef, ptr %ValueVTs, ptr %Offsets, i64 %add13)
47+
unreachable
48+
49+
for.cond16: ; preds = %for.cond34, %if.end4
50+
%call27 = call { i8, ptr } @getType(ptr %VT)
51+
br label %for.cond34
52+
53+
for.cond34: ; preds = %for.body37, %for.cond16
54+
br i1 undef, label %for.body37, label %for.cond16
55+
56+
for.body37: ; preds = %for.cond34
57+
%tobool39 = icmp ne ptr %Offsets, null
58+
br label %for.cond34
59+
}
60+
61+
define hidden { ptr, i32 } @bar(ptr %this) {
62+
entry:
63+
%Offsets = alloca i64, align 8
64+
%cmp26 = call zeroext i1 @compare(ptr undef)
65+
br i1 %cmp26, label %for.body28, label %for.cond.cleanup27
66+
67+
for.cond.cleanup27: ; preds = %entry
68+
ret { ptr, i32 } undef
69+
70+
for.body28: ; preds = %entry
71+
%call33 = call zeroext i1 @compare(ptr undef)
72+
br i1 %call33, label %if.then34, label %if.end106
73+
74+
if.then34: ; preds = %for.body28
75+
call void @foo(ptr %this, ptr undef, ptr undef, ptr undef, ptr null, i64 0)
76+
unreachable
77+
78+
if.end106: ; preds = %for.body28
79+
call void @foo(ptr %this, ptr undef, ptr undef, ptr undef, ptr %Offsets, i64 0)
80+
unreachable
81+
}
82+

0 commit comments

Comments
 (0)