1; Make sure that even without some external devirtualization iteration tool, 2; the CGSCC pass manager correctly observes and re-visits SCCs that change 3; structure due to devirtualization. We trigger devirtualization here with GVN 4; which forwards a store through a load and to an indirect call. 5; 6; RUN: opt -aa-pipeline=basic-aa -passes='cgscc(function-attrs)' -S < %s | FileCheck %s --check-prefix=BEFORE 7; RUN: opt -aa-pipeline=basic-aa -passes='cgscc(function-attrs,function(gvn))' -S < %s | FileCheck %s --check-prefix=AFTER 8; 9; Also check that adding an extra CGSCC pass after the function update but 10; without requiring the outer manager to iterate doesn't break any invariant. 11; RUN: opt -aa-pipeline=basic-aa -passes='cgscc(function-attrs,function(gvn),function-attrs)' -S < %s | FileCheck %s --check-prefix=AFTER 12 13declare void @readnone() readnone 14declare void @unknown() 15 16; The @test1_* checks that if we refine an indirect call to a direct call and 17; in the process change the very structure of the call graph we also revisit 18; that component of the graph and do so in an up-to-date fashion. 19 20; BEFORE: define void @test1_a1() { 21; AFTER: define void @test1_a1() { 22define void @test1_a1() { 23 %fptr = alloca void()* 24 store void()* @test1_b2, void()** %fptr 25 store void()* @test1_b1, void()** %fptr 26 %f = load void()*, void()** %fptr 27 call void %f() 28 ret void 29} 30 31; BEFORE: define void @test1_b1() { 32; AFTER: define void @test1_b1() { 33define void @test1_b1() { 34 call void @unknown() 35 call void @test1_a1() 36 ret void 37} 38 39; BEFORE: define void @test1_a2() { 40; AFTER: define void @test1_a2() #0 { 41define void @test1_a2() { 42 %fptr = alloca void()* 43 store void()* @test1_b1, void()** %fptr 44 store void()* @test1_b2, void()** %fptr 45 %f = load void()*, void()** %fptr 46 call void %f() 47 ret void 48} 49 50; BEFORE: define void @test1_b2() { 51; AFTER: define void @test1_b2() #0 { 52define void @test1_b2() { 53 call void @readnone() 54 call void @test1_a2() 55 ret void 56} 57 58 59; The @test2_* set of functions exercise a case where running function passes 60; introduces a new post-order relationship that was not present originally and 61; makes sure we walk across the SCCs in that order. 62 63; CHECK: define void @test2_a() { 64define void @test2_a() { 65 call void @test2_b1() 66 call void @test2_b2() 67 call void @test2_b3() 68 call void @unknown() 69 ret void 70} 71 72; CHECK: define void @test2_b1() #0 { 73define void @test2_b1() { 74 %fptr = alloca void()* 75 store void()* @test2_a, void()** %fptr 76 store void()* @readnone, void()** %fptr 77 %f = load void()*, void()** %fptr 78 call void %f() 79 ret void 80} 81 82; CHECK: define void @test2_b2() #0 { 83define void @test2_b2() { 84 %fptr = alloca void()* 85 store void()* @test2_a, void()** %fptr 86 store void()* @test2_b2, void()** %fptr 87 store void()* @test2_b3, void()** %fptr 88 store void()* @test2_b1, void()** %fptr 89 %f = load void()*, void()** %fptr 90 call void %f() 91 ret void 92} 93 94; CHECK: define void @test2_b3() #0 { 95define void @test2_b3() { 96 %fptr = alloca void()* 97 store void()* @test2_a, void()** %fptr 98 store void()* @test2_b2, void()** %fptr 99 store void()* @test2_b3, void()** %fptr 100 store void()* @test2_b1, void()** %fptr 101 %f = load void()*, void()** %fptr 102 call void %f() 103 ret void 104} 105 106; CHECK: attributes #0 = { readnone } 107