| ; Test for a subtle bug when computing analyses during inlining and mutating |
| ; the SCC structure. Without care, this can fail to invalidate analyses. |
| ; |
| ; RUN: opt < %s -aa-pipeline= -passes='cgscc(inline,function(verify<domtree>))' -debug-pass-manager -inline-deferral -S 2>&1 | FileCheck %s |
| |
| ; First we check that the passes run in the way we expect. Otherwise this test |
| ; may stop testing anything. |
| ; |
| ; CHECK: Running pass: InlinerPass on (test1_f, test1_g, test1_h) |
| ; CHECK: Running analysis: DominatorTreeAnalysis on test1_f |
| ; CHECK: Invalidating analysis: DominatorTreeAnalysis on test1_f |
| ; CHECK: Invalidating analysis: LoopAnalysis on test1_f |
| ; CHECK: Invalidating analysis: BranchProbabilityAnalysis on test1_f |
| ; CHECK: Invalidating analysis: BlockFrequencyAnalysis on test1_f |
| ; CHECK: Running analysis: DominatorTreeAnalysis on test1_g |
| ; CHECK: Invalidating analysis: DominatorTreeAnalysis on test1_g |
| ; CHECK: Invalidating analysis: LoopAnalysis on test1_g |
| ; CHECK: Invalidating analysis: BranchProbabilityAnalysis on test1_g |
| ; CHECK: Invalidating analysis: BlockFrequencyAnalysis on test1_g |
| ; CHECK: Invalidating analysis: DominatorTreeAnalysis on test1_h |
| ; CHECK: Invalidating analysis: LoopAnalysis on test1_h |
| ; CHECK: Invalidating analysis: BranchProbabilityAnalysis on test1_h |
| ; CHECK: Invalidating analysis: BlockFrequencyAnalysis on test1_h |
| ; CHECK-NOT: Invalidating analysis: |
| ; CHECK: Running pass: DominatorTreeVerifierPass on test1_g |
| ; CHECK-NEXT: Running analysis: DominatorTreeAnalysis on test1_g |
| ; CHECK-NOT: Invalidating analysis: |
| ; CHECK: Running pass: DominatorTreeVerifierPass on test1_h |
| ; CHECK-NEXT: Running analysis: DominatorTreeAnalysis on test1_h |
| ; CHECK-NOT: Invalidating analysis: |
| ; CHECK: Running pass: DominatorTreeVerifierPass on test1_f |
| |
| ; An external function used to control branches. |
| declare i1 @flag() |
| ; CHECK-LABEL: declare i1 @flag() |
| |
| ; The utility function with interesting control flow that gets inlined below to |
| ; perturb the dominator tree. |
| define internal void @callee() { |
| entry: |
| %ptr = alloca i8 |
| %flag = call i1 @flag() |
| br i1 %flag, label %then, label %else |
| |
| then: |
| store volatile i8 42, ptr %ptr |
| br label %return |
| |
| else: |
| store volatile i8 -42, ptr %ptr |
| br label %return |
| |
| return: |
| ret void |
| } |
| |
| ; The 'test1_' prefixed functions work to carefully test that incrementally |
| ; reducing an SCC in the inliner cannot accidentially leave stale function |
| ; analysis results due to failing to invalidate them for all the functions. |
| |
| ; We visit this function first in the inliner, and while we inline callee |
| ; perturbing the CFG, we don't inline anything else and the SCC structure |
| ; remains in tact. |
| define void @test1_f() { |
| ; CHECK-LABEL: define void @test1_f() |
| entry: |
| ; We force this edge to survive inlining. |
| call void @test1_g() noinline |
| ; CHECK: call void @test1_g() |
| |
| ; Pull interesting CFG into this function. |
| call void @callee() |
| ; CHECK-NOT: call void @callee() |
| |
| ret void |
| ; CHECK: ret void |
| } |
| |
| ; We visit this function second and here we inline the edge to 'test1_f' |
| ; separating it into its own SCC. The current SCC is now just 'test1_g' and |
| ; 'test1_h'. |
| define void @test1_g() { |
| ; CHECK-LABEL: define void @test1_g() |
| entry: |
| ; This edge gets inlined away. |
| call void @test1_f() |
| ; CHECK-NOT: call void @test1_f() |
| ; CHECK: call void @test1_g() |
| |
| ; We force this edge to survive inlining. |
| call void @test1_h() noinline |
| ; CHECK: call void @test1_h() |
| |
| ; Pull interesting CFG into this function. |
| call void @callee() |
| ; CHECK-NOT: call void @callee() |
| |
| ret void |
| ; CHECK: ret void |
| } |
| |
| ; The inliner visits this last function. It can't actually break any cycles |
| ; here, but because we visit this function we compute fresh analyses for it. |
| ; These analyses are then invalidated when we inline callee disrupting the |
| ; CFG, and it is important that they be freed. |
| define void @test1_h() { |
| ; CHECK-LABEL: define void @test1_h() |
| entry: |
| call void @test1_g() |
| ; CHECK: call void @test1_g() |
| |
| ; Pull interesting CFG into this function. |
| call void @callee() |
| ; CHECK-NOT: call void @callee() |
| |
| ret void |
| ; CHECK: ret void |
| } |
| |
| ; The 'test2_' prefixed code works to carefully trigger forming an SCC with |
| ; a dominator tree for one of the functions but not the other and without even |
| ; a function analysis manager proxy for the SCC that things get merged into. |
| ; Without proper handling when updating the call graph this will find a stale |
| ; dominator tree. |
| |
| @test2_global = external global i32, align 4 |
| |
| define void @test2_hoge(ptr %arg) { |
| ; CHECK-LABEL: define void @test2_hoge( |
| bb: |
| %tmp2 = call zeroext i1 %arg(ptr @test2_global) |
| ; CHECK: call zeroext i1 %arg( |
| br label %bb3 |
| |
| bb3: |
| %tmp5 = call zeroext i1 %arg(ptr @test2_global) |
| ; CHECK: call zeroext i1 %arg( |
| br i1 %tmp5, label %bb3, label %bb6 |
| |
| bb6: |
| ret void |
| } |
| |
| define zeroext i1 @test2_widget(ptr %arg) { |
| ; CHECK-LABEL: define zeroext i1 @test2_widget( |
| bb: |
| %tmp1 = alloca i8, align 1 |
| %tmp2 = alloca i32, align 4 |
| call void @test2_quux() |
| ; CHECK-NOT: call |
| ; |
| ; CHECK: call zeroext i1 @test2_widget(ptr @test2_global) |
| ; CHECK-NEXT: br label %[[NEW_BB:.*]] |
| ; |
| ; CHECK: [[NEW_BB]]: |
| ; CHECK-NEXT: call zeroext i1 @test2_widget(ptr @test2_global) |
| ; |
| ; CHECK: {{.*}}: |
| |
| call void @test2_hoge.1(ptr %arg) |
| ; CHECK-NEXT: call void @test2_hoge.1( |
| |
| %tmp4 = call zeroext i1 @test2_barney(ptr %tmp2) |
| %tmp5 = zext i1 %tmp4 to i32 |
| store i32 %tmp5, ptr %tmp2, align 4 |
| %tmp6 = call zeroext i1 @test2_barney(ptr null) |
| call void @test2_ham(ptr %tmp1) |
| ; CHECK: call void @test2_ham( |
| |
| call void @test2_quux() |
| ; CHECK-NOT: call |
| ; |
| ; CHECK: call zeroext i1 @test2_widget(ptr @test2_global) |
| ; CHECK-NEXT: br label %[[NEW_BB:.*]] |
| ; |
| ; CHECK: [[NEW_BB]]: |
| ; CHECK-NEXT: call zeroext i1 @test2_widget(ptr @test2_global) |
| ; |
| ; CHECK: {{.*}}: |
| ret i1 true |
| ; CHECK-NEXT: ret i1 true |
| } |
| |
| define internal void @test2_quux() { |
| ; CHECK-NOT: @test2_quux |
| bb: |
| call void @test2_hoge(ptr @test2_widget) |
| ret void |
| } |
| |
| declare void @test2_hoge.1(ptr) |
| |
| declare zeroext i1 @test2_barney(ptr) |
| |
| declare void @test2_ham(ptr) |