llvm.org GIT mirror llvm / dbaaccc
[PM/CGSCC] Teach the CGSCC pass manager components to gracefully handle invalidated SCCs even when we do not have an updated SCC to redirect towards. This comes up in a fairly subtle and surprising circumstance: we need to have a connected but internal node in the call graph which later becomes a disconnected island, and then gets deleted. All of this needs to happen mid-CGSCC walk. Because it is disconnected, we have no way of computing a new "current" SCC when it gets deleted. Instead, we need to explicitly check for a deleted "current" SCC and bail out of the current CGSCC step. This will bubble all the way up to the post-order walk and then resume correctly. I've included minimal tests for this bug. The specific behavior matches something we've seen in the wild with the new PM combined with ThinLTO and sample PGO, but I've not yet confirmed whether this is the only issue there. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@313242 91177308-0d34-0410-b5e6-96231b3b80d8 Chandler Carruth 2 years ago
3 changed file(s) with 74 addition(s) and 4 deletion(s). Raw diff Collapse all Expand all
428428 UR.UpdatedC = nullptr;
429429 PreservedAnalyses PassPA = Pass.run(*C, CGAM, CG, UR);
430430
431 // Update the SCC and RefSCC if necessary.
432 C = UR.UpdatedC ? UR.UpdatedC : C;
433 RC = UR.UpdatedRC ? UR.UpdatedRC : RC;
434
435 // If the CGSCC pass wasn't able to provide a valid updated SCC,
436 // the current SCC may simply need to be skipped if invalid.
437 if (UR.InvalidatedSCCs.count(C)) {
438 DEBUG(dbgs() << "Skipping invalidated root or island SCC!\n");
439 break;
440 }
441 // Check that we didn't miss any update scenario.
442 assert(C->begin() != C->end() && "Cannot have an empty SCC!");
443
431444 // We handle invalidating the CGSCC analysis manager's information
432445 // for the (potentially updated) SCC here. Note that any other SCCs
433446 // whose structure has changed should have been invalidated by
434447 // whatever was updating the call graph. This SCC gets invalidated
435448 // late as it contains the nodes that were actively being
436449 // processed.
437 CGAM.invalidate(*(UR.UpdatedC ? UR.UpdatedC : C), PassPA);
450 CGAM.invalidate(*C, PassPA);
438451
439452 // Then intersect the preserved set so that invalidation of module
440453 // analyses will eventually occur when the module pass completes.
449462 // apart, at most converging on a DAG of single nodes.
450463 // FIXME: If we ever start having RefSCC passes, we'll want to
451464 // iterate there too.
452 RC = UR.UpdatedRC ? UR.UpdatedRC : RC;
453 C = UR.UpdatedC ? UR.UpdatedC : C;
454465 if (UR.UpdatedC)
455466 DEBUG(dbgs() << "Re-running SCC passes after a refinement of the "
456467 "current SCC: "
7171 // Update the SCC if necessary.
7272 C = UR.UpdatedC ? UR.UpdatedC : C;
7373
74 // If the CGSCC pass wasn't able to provide a valid updated SCC, the
75 // current SCC may simply need to be skipped if invalid.
76 if (UR.InvalidatedSCCs.count(C)) {
77 DEBUG(dbgs() << "Skipping invalidated root or island SCC!\n");
78 break;
79 }
7480 // Check that we didn't miss any update scenario.
75 assert(!UR.InvalidatedSCCs.count(C) && "Processing an invalid SCC!");
7681 assert(C->begin() != C->end() && "Cannot have an empty SCC!");
7782
7883 // Update the analysis manager as each pass runs and potentially
0 ; Test that patterns of transformations which disconnect a region of the call
1 ; graph mid-traversal and then invalidate it function correctly.
2 ;
3 ; RUN: opt -S -passes='cgscc(inline,function(simplify-cfg))' < %s | FileCheck %s
4
5 define internal void @test_scc_internal(i1 %flag) {
6 ; CHECK-NOT: @test_scc_internal
7 entry:
8 br i1 %flag, label %then, label %else
9
10 then:
11 call void @test_scc_internal(i1 false)
12 call void @test_scc_external()
13 br label %else
14
15 else:
16 ret void
17 }
18
19 define void @test_scc_external() {
20 ; CHECK-LABEL: define void @test_scc_external()
21 entry:
22 call void @test_scc_internal(i1 false)
23 ret void
24 }
25
26 define internal void @test_refscc_internal(i1 %flag, i8* %ptr) {
27 ; CHECK-NOT: @test_refscc_internal
28 entry:
29 br i1 %flag, label %then, label %else
30
31 then:
32 call void @test_refscc_internal(i1 false, i8* bitcast (i8* ()* @test_refscc_external to i8*))
33 br label %else
34
35 else:
36 ret void
37 }
38
39 define i8* @test_refscc_external() {
40 ; CHECK-LABEL: define i8* @test_refscc_external()
41 entry:
42 br i1 true, label %then, label %else
43 ; CHECK-NEXT: entry:
44 ; CHECK-NEXT: ret i8* null
45 ; CHECK-NEXT: }
46 ; CHECK-NOT: @test_refscc_internal
47
48 then:
49 ret i8* null
50
51 else:
52 ret i8* bitcast (void (i1, i8*)* @test_refscc_internal to i8*)
53 }