Skip to content

Commit

Permalink
[NewPM] Fix a nasty bug with analysis invalidation in the new PM.
Browse files Browse the repository at this point in the history
The issue here is that we actually allow CGSCC passes to mutate IR (and
therefore invalidate analyses) outside of the current SCC. At a minimum,
we need to support mutating parent and ancestor SCCs to support the
ArgumentPromotion pass which rewrites all calls to a function.

However, the analysis invalidation infrastructure is heavily based
around not needing to invalidate the same IR-unit at multiple levels.
With Loop passes for example, they don't invalidate other Loops. So we
need to customize how we handle CGSCC invalidation. Doing this without
gratuitously re-running analyses is even harder. I've avoided most of
these by using an out-of-band preserved set to accumulate the cross-SCC
invalidation, but it still isn't perfect in the case of re-visiting the
same SCC repeatedly *but* it coming off the worklist. Unclear how
important this use case really is, but I wanted to call it out.

Another wrinkle is that in order for this to successfully propagate to
function analyses, we have to make sure we have a proxy from the SCC to
the Function level. That requires pre-creating the necessary proxy.

The motivating test case now works cleanly and is added for
ArgumentPromotion.

Thanks for the review from Philip and Wei!

Differential Revision: https://reviews.llvm.org/D59869

llvm-svn: 357137
  • Loading branch information
chandlerc committed Mar 28, 2019
1 parent 8ff4585 commit 923ff55
Show file tree
Hide file tree
Showing 8 changed files with 298 additions and 184 deletions.
389 changes: 220 additions & 169 deletions llvm/include/llvm/Analysis/CGSCCPassManager.h

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions llvm/lib/Analysis/CGSCCPassManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,12 @@ PassManager<LazyCallGraph::SCC, CGSCCAnalysisManager, LazyCallGraph &,
// ...getContext().yield();
}

// Before we mark all of *this* SCC's analyses as preserved below, intersect
// this with the cross-SCC preserved analysis set. This is used to allow
// CGSCC passes to mutate ancestor SCCs and still trigger proper invalidation
// for them.
UR.CrossSCCPA.intersect(PA);

// Invalidation was handled after each pass in the above loop for the current
// SCC. Therefore, the remaining analysis results in the AnalysisManager are
// preserved. We mark this with a set so that we don't need to inspect each
Expand Down
4 changes: 4 additions & 0 deletions llvm/test/Other/new-pass-manager.ll
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@
; CHECK-CGSCC-PASS-NEXT: Running analysis: InnerAnalysisManagerProxy<{{.*(FunctionAnalysisManager|AnalysisManager<.*Function.*>).*}},{{.*}}Module>
; CHECK-CGSCC-PASS-NEXT: Running analysis: LazyCallGraphAnalysis
; CHECK-CGSCC-PASS-NEXT: Running analysis: TargetLibraryAnalysis
; CHECK-CGSCC-PASS-NEXT: Running analysis: FunctionAnalysisManagerCGSCCProxy
; CHECK-CGSCC-PASS-NEXT: Running analysis: PassInstrumentationAnalysis
; CHECK-CGSCC-PASS-NEXT: Running analysis: OuterAnalysisManagerProxy<{{.*}}LazyCallGraph::SCC{{.*}}>
; CHECK-CGSCC-PASS-NEXT: Starting CGSCC pass manager run
; CHECK-CGSCC-PASS-NEXT: Running pass: NoOpCGSCCPass
; CHECK-CGSCC-PASS-NEXT: Finished CGSCC pass manager run
Expand Down Expand Up @@ -410,7 +412,9 @@
; CHECK-REPEAT-CGSCC-PASS-NEXT: Running analysis: InnerAnalysisManagerProxy<{{.*(FunctionAnalysisManager|AnalysisManager<.*Function.*>).*}},{{.*}}Module>
; CHECK-REPEAT-CGSCC-PASS-NEXT: Running analysis: LazyCallGraphAnalysis
; CHECK-REPEAT-CGSCC-PASS-NEXT: Running analysis: TargetLibraryAnalysis
; CHECK-REPEAT-CGSCC-PASS-NEXT: Running analysis: FunctionAnalysisManagerCGSCCProxy
; CHECK-REPEAT-CGSCC-PASS-NEXT: Running analysis: PassInstrumentationAnalysis
; CHECK-REPEAT-CGSCC-PASS-NEXT: Running analysis: OuterAnalysisManagerProxy<{{.*}}LazyCallGraph::SCC{{.*}}>
; CHECK-REPEAT-CGSCC-PASS-NEXT: Starting CGSCC pass manager run
; CHECK-REPEAT-CGSCC-PASS-NEXT: Running pass: RepeatedPass
; CHECK-REPEAT-CGSCC-PASS-NEXT: Starting CGSCC pass manager run
Expand Down
4 changes: 2 additions & 2 deletions llvm/test/Other/new-pm-defaults.ll
Original file line number Diff line number Diff line change
Expand Up @@ -117,11 +117,11 @@
; CHECK-O-NEXT: Running pass: ModuleToPostOrderCGSCCPassAdaptor<{{.*}}LazyCallGraph{{.*}}>
; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy
; CHECK-O-NEXT: Running analysis: LazyCallGraphAnalysis
; CHECK-O-NEXT: Running analysis: FunctionAnalysisManagerCGSCCProxy
; CHECK-O-NEXT: Running analysis: PassInstrumentationAnalysis
; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy<{{.*}}LazyCallGraph::SCC{{.*}}>
; CHECK-O-NEXT: Starting CGSCC pass manager run.
; CHECK-O-NEXT: Running pass: InlinerPass
; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy<{{.*}}LazyCallGraph{{.*}}>
; CHECK-O-NEXT: Running analysis: FunctionAnalysisManagerCGSCCProxy
; CHECK-O-NEXT: Running pass: PostOrderFunctionAttrsPass
; CHECK-O3-NEXT: Running pass: ArgumentPromotionPass
; CHECK-O-NEXT: Running pass: CGSCCToFunctionPassAdaptor<{{.*}}PassManager{{.*}}>
Expand Down
4 changes: 2 additions & 2 deletions llvm/test/Other/new-pm-thinlto-defaults.ll
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,11 @@
; CHECK-O-NEXT: Running pass: ModuleToPostOrderCGSCCPassAdaptor<{{.*}}LazyCallGraph{{.*}}>
; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy
; CHECK-O-NEXT: Running analysis: LazyCallGraphAnalysis
; CHECK-O-NEXT: Running analysis: FunctionAnalysisManagerCGSCCProxy
; CHECK-O-NEXT: Running analysis: PassInstrumentationAnalysis
; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy<{{.*}}LazyCallGraph::SCC{{.*}}>
; CHECK-O-NEXT: Starting CGSCC pass manager run.
; CHECK-O-NEXT: Running pass: InlinerPass
; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy<{{.*}}LazyCallGraph{{.*}}>
; CHECK-O-NEXT: Running analysis: FunctionAnalysisManagerCGSCCProxy
; CHECK-O-NEXT: Running pass: PostOrderFunctionAttrsPass
; CHECK-O3-NEXT: Running pass: ArgumentPromotionPass
; CHECK-O-NEXT: Running pass: CGSCCToFunctionPassAdaptor<{{.*}}PassManager{{.*}}>
Expand Down
50 changes: 50 additions & 0 deletions llvm/test/Transforms/ArgumentPromotion/invalidation.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
; Check that when argument promotion changes a function in some parent node of
; the call graph, any analyses that happened to be cached for that function are
; actually invalidated. We are using `demanded-bits` here because when printed
; it will end up caching a value for every instruction, making it easy to
; detect the instruction-level changes that will fail here. With improper
; invalidation this will crash in the second printer as it tries to reuse
; now-invalid demanded bits.
;
; RUN: opt < %s -passes='function(print<demanded-bits>),cgscc(argpromotion,function(print<demanded-bits>))' -S | FileCheck %s

@G = constant i32 0

define internal i32 @a(i32* %x) {
; CHECK-LABEL: define internal i32 @a(
; CHECK-SAME: i32 %[[V:.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: ret i32 %[[V]]
; CHECK-NEXT: }
entry:
%v = load i32, i32* %x
ret i32 %v
}

define i32 @b() {
; CHECK-LABEL: define i32 @b()
; CHECK-NEXT: entry:
; CHECK-NEXT: %[[L:.*]] = load i32, i32* @G
; CHECK-NEXT: %[[V:.*]] = call i32 @a(i32 %[[L]])
; CHECK-NEXT: ret i32 %[[V]]
; CHECK-NEXT: }
entry:
%v = call i32 @a(i32* @G)
ret i32 %v
}

define i32 @c() {
; CHECK-LABEL: define i32 @c()
; CHECK-NEXT: entry:
; CHECK-NEXT: %[[L:.*]] = load i32, i32* @G
; CHECK-NEXT: %[[V1:.*]] = call i32 @a(i32 %[[L]])
; CHECK-NEXT: %[[V2:.*]] = call i32 @b()
; CHECK-NEXT: %[[RESULT:.*]] = add i32 %[[V1]], %[[V2]]
; CHECK-NEXT: ret i32 %[[RESULT]]
; CHECK-NEXT: }
entry:
%v1 = call i32 @a(i32* @G)
%v2 = call i32 @b()
%result = add i32 %v1, %v2
ret i32 %result
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
;
; CHECK-LABEL: Starting llvm::Module pass manager run.
; CHECK: Running pass: InlinerPass on (test1_f, test1_g, test1_h)
; CHECK: Running analysis: FunctionAnalysisManagerCGSCCProxy on (test1_f, test1_g, test1_h)
; CHECK: Running analysis: DominatorTreeAnalysis on test1_f
; CHECK: Running analysis: DominatorTreeAnalysis on test1_g
; CHECK: Invalidating all non-preserved analyses for: (test1_f)
Expand Down
24 changes: 14 additions & 10 deletions llvm/unittests/Analysis/CGSCCPassManagerTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1255,26 +1255,30 @@ TEST_F(CGSCCPassManagerTest, TestAnalysisInvalidationCGSCCUpdate) {
MPM.run(*M, MAM);

// We run over four SCCs the first time. But then we split an SCC into three.
// And then we merge those three back into one.
EXPECT_EQ(4 + 3 + 1, SCCAnalysisRuns);
// And then we merge those three back into one. However, this also
// invalidates all three SCCs further down in the PO walk.
EXPECT_EQ(4 + 3 + 1 + 3, SCCAnalysisRuns);
// The module analysis pass should be run three times.
EXPECT_EQ(3, ModuleAnalysisRuns);
// We run over four SCCs the first time. Then over the two new ones. Then the
// entire module is invalidated causing a full run over all seven. Then we
// fold three SCCs back to one, and then run over the whole module again.
EXPECT_EQ(4 + 2 + 7 + 1 + 4, IndirectSCCAnalysisRuns);
EXPECT_EQ(4 + 2 + 7 + 1 + 4, DoublyIndirectSCCAnalysisRuns);
// fold three SCCs back to one, re-compute for it and the two SCCs above it
// in the graph, and then run over the whole module again.
EXPECT_EQ(4 + 2 + 7 + 1 + 3 + 4, IndirectSCCAnalysisRuns);
EXPECT_EQ(4 + 2 + 7 + 1 + 3 + 4, DoublyIndirectSCCAnalysisRuns);

// First we run over all six functions. Then we re-run it over three when we
// split their SCCs. Then we re-run over the whole module. Then we re-run
// over three functions merged back into a single SCC, and then over the
// whole module again.
EXPECT_EQ(6 + 3 + 6 + 3 + 6, FunctionAnalysisRuns);
// over three functions merged back into a single SCC, then those three
// functions again, the two functions in SCCs above it in the graph, and then
// over the whole module again.
EXPECT_EQ(6 + 3 + 6 + 3 + 3 + 2 + 6, FunctionAnalysisRuns);

// Re run the function analysis over the entire module, and then re-run it
// over the `(h3, h1, h2)` SCC due to invalidation. Then we re-run it over
// the entire module, then the three functions merged back into a single SCC,
// and then over the whole module.
EXPECT_EQ(6 + 3 + 6 + 3 + 6, IndirectFunctionAnalysisRuns);
// those three functions again, then the two functions in SCCs above it in
// the graph, and then over the whole module.
EXPECT_EQ(6 + 3 + 6 + 3 + 3 + 2 + 6, IndirectFunctionAnalysisRuns);
}
}

0 comments on commit 923ff55

Please sign in to comment.