summaryrefslogtreecommitdiff
path: root/test/Other
diff options
context:
space:
mode:
authorChandler Carruth <chandlerc@gmail.com>2017-09-14 08:33:57 +0000
committerChandler Carruth <chandlerc@gmail.com>2017-09-14 08:33:57 +0000
commitdbaacccc317df48f9f6ab720f1eee08577824ec7 (patch)
tree8940f74e64abc307d9e763a5111227e2d19b8fc6 /test/Other
parentb3a1f36a1bab27647f61021b1c3632b3b9d43e6a (diff)
[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
Diffstat (limited to 'test/Other')
-rw-r--r--test/Other/cgscc-disconnected-invalidation.ll54
1 files changed, 54 insertions, 0 deletions
diff --git a/test/Other/cgscc-disconnected-invalidation.ll b/test/Other/cgscc-disconnected-invalidation.ll
new file mode 100644
index 00000000000..0567605fcf9
--- /dev/null
+++ b/test/Other/cgscc-disconnected-invalidation.ll
@@ -0,0 +1,54 @@
+; Test that patterns of transformations which disconnect a region of the call
+; graph mid-traversal and then invalidate it function correctly.
+;
+; RUN: opt -S -passes='cgscc(inline,function(simplify-cfg))' < %s | FileCheck %s
+
+define internal void @test_scc_internal(i1 %flag) {
+; CHECK-NOT: @test_scc_internal
+entry:
+ br i1 %flag, label %then, label %else
+
+then:
+ call void @test_scc_internal(i1 false)
+ call void @test_scc_external()
+ br label %else
+
+else:
+ ret void
+}
+
+define void @test_scc_external() {
+; CHECK-LABEL: define void @test_scc_external()
+entry:
+ call void @test_scc_internal(i1 false)
+ ret void
+}
+
+define internal void @test_refscc_internal(i1 %flag, i8* %ptr) {
+; CHECK-NOT: @test_refscc_internal
+entry:
+ br i1 %flag, label %then, label %else
+
+then:
+ call void @test_refscc_internal(i1 false, i8* bitcast (i8* ()* @test_refscc_external to i8*))
+ br label %else
+
+else:
+ ret void
+}
+
+define i8* @test_refscc_external() {
+; CHECK-LABEL: define i8* @test_refscc_external()
+entry:
+ br i1 true, label %then, label %else
+; CHECK-NEXT: entry:
+; CHECK-NEXT: ret i8* null
+; CHECK-NEXT: }
+; CHECK-NOT: @test_refscc_internal
+
+then:
+ ret i8* null
+
+else:
+ ret i8* bitcast (void (i1, i8*)* @test_refscc_internal to i8*)
+}