Follow up to d0858bffa11, add missing REQUIRES x86
[llvm-project.git] / llvm / test / Other / cgscc-devirt-iteration.ll
blob54229461cb3949e894c96be573ca1531cb009553
1 ; The CGSCC pass manager includes an SCC iteration utility that tracks indirect
2 ; calls that are turned into direct calls (devirtualization) and re-visits the
3 ; SCC to expose those calls to the SCC-based IPO passes. We trigger
4 ; devirtualization here with GVN which forwards a store through a load and to
5 ; an indirect call.
7 ; RUN: opt -aa-pipeline=basic-aa -passes='module(inferattrs),cgscc(function-attrs,function(gvn,instcombine))' -S < %s | FileCheck %s --check-prefix=CHECK --check-prefix=BEFORE
8 ; RUN: opt -aa-pipeline=basic-aa -passes='module(inferattrs),cgscc(devirt<1>(function-attrs,function(gvn,instcombine)))' -S < %s | FileCheck %s --check-prefix=CHECK --check-prefix=AFTER --check-prefix=AFTER1
9 ; RUN: opt -aa-pipeline=basic-aa -passes='module(inferattrs),cgscc(devirt<2>(function-attrs,function(gvn,instcombine)))' -S < %s | FileCheck %s --check-prefix=CHECK --check-prefix=AFTER --check-prefix=AFTER2
11 ; RUN: not --crash opt -abort-on-max-devirt-iterations-reached -aa-pipeline=basic-aa -passes='module(inferattrs),cgscc(devirt<1>(function-attrs,function(gvn,instcombine)))' -S < %s
12 ; RUN: opt -abort-on-max-devirt-iterations-reached -aa-pipeline=basic-aa -passes='module(inferattrs),cgscc(devirt<2>(function-attrs,function(gvn,instcombine)))' -S < %s
14 ; We also verify that the real O2 pipeline catches these cases.
15 ; RUN: opt -aa-pipeline=basic-aa -passes='default<O2>' -S < %s | FileCheck %s --check-prefix=CHECK --check-prefix=AFTER --check-prefix=AFTER2
17 declare void @readnone() readnone
18 ; CHECK: Function Attrs: nofree nosync memory(none)
19 ; CHECK-NEXT: declare void @readnone()
21 declare void @unknown()
22 ; CHECK-NOT: Function Attrs
23 ; CHECK-LABEL: declare void @unknown(){{ *$}}
25 ; The @test1 function checks that when we refine an indirect call to a direct
26 ; call we revisit the SCC passes to reflect the more precise information. This
27 ; is the basic functionality.
29 define void @test1() {
30 ; BEFORE-NOT: Function Attrs
31 ; AFTER: Function Attrs: nofree nosync memory(none)
32 ; CHECK-LABEL: define void @test1()
33 entry:
34   %fptr = alloca ptr
35   store ptr @readnone, ptr %fptr
36   %f = load ptr, ptr %fptr
37   call void %f()
38   ret void
41 ; The @test2_* functions check that when we need multiple (in this case 2)
42 ; repetitions to compute some state that is incrementally exposed with each
43 ; one, the limit on repetitions is enforced. So we make progress with
44 ; one repetition but not as much as with three.
46 ; This is somewhat awkward to test because we have to contrive to have a state
47 ; repetition triggered and observed with very few passes. The technique here
48 ; is to have one indirect call that can only be resolved when the entire SCC is
49 ; deduced as readonly, and mark that indirect call at the call site as readonly
50 ; to make that possible. This forces us to first deduce readonly, then
51 ; devirtualize again, and then deduce readnone.
53 declare void @readnone_with_arg(ptr) readnone
54 ; CHECK: Function Attrs: nofree nosync memory(none)
55 ; CHECK-LABEL: declare void @readnone_with_arg(ptr)
57 define void @test2_a(ptr %ignore) {
58 ; BEFORE-NOT: Function Attrs
59 ; AFTER1: Function Attrs: nofree memory(read)
60 ; AFTER2: Function Attrs: nofree nosync memory(none)
61 ; BEFORE: define void @test2_a(ptr %ignore)
62 ; AFTER: define void @test2_a(ptr readnone %ignore)
63 entry:
64   %f1ptr = alloca ptr
65   store ptr @readnone_with_arg, ptr %f1ptr
66   %f1 = load ptr, ptr %f1ptr
67   ; This indirect call is the first to be resolved, allowing us to deduce
68   ; readonly but not (yet) readnone.
69   call void %f1(ptr %ignore)
70 ; CHECK: call void @readnone_with_arg(ptr %ignore)
72   ; Bogus call to test2_b to make this a cycle.
73   call void @test2_b()
75   ret void
78 define void @test2_b() {
79 ; BEFORE-NOT: Function Attrs
80 ; AFTER1: Function Attrs: nofree memory(read)
81 ; AFTER2: Function Attrs: nofree nosync memory(none)
82 ; CHECK-LABEL: define void @test2_b()
83 entry:
84   %f2ptr = alloca ptr
85   store ptr @readnone, ptr %f2ptr
86   ; Call the other function here to prevent forwarding until the SCC has had
87   ; function attrs deduced.
88   call void @test2_a(ptr %f2ptr)
90   %f2 = load ptr, ptr %f2ptr
91   ; This is the second indirect call to be resolved, and can only be resolved
92   ; after we deduce 'readonly' for the rest of the SCC. Once it is
93   ; devirtualized, we can deduce readnone for the SCC.
94   call void %f2() readonly
95 ; BEFORE: call void %f2()
96 ; AFTER: call void @readnone()
98   ret void
101 declare ptr @memcpy(ptr, ptr, i64)
102 ; CHECK-LABEL: ptr @memcpy(
104 ; The @test3 function checks that when we refine an indirect call to an
105 ; intrinsic we still revisit the SCC pass. This also covers cases where the
106 ; value handle itself doesn't persist due to the nature of how instcombine
107 ; creates the memcpy intrinsic call, and we rely on the count of indirect calls
108 ; decreasing and the count of direct calls increasing.
109 ; Adding 'noinline' attribute to force attributes for improved matching.
110 define void @test3(ptr %src, ptr %dest, i64 %size) noinline {
111 ; CHECK: Function Attrs
112 ; CHECK-NOT: read
113 ; CHECK-SAME: noinline
114 ; BEFORE-LABEL: define void @test3(ptr %src, ptr %dest, i64 %size)
115 ; AFTER-LABEL: define void @test3(ptr nocapture readonly %src, ptr nocapture writeonly %dest, i64 %size)
116   %fptr = alloca ptr
117   store ptr @memcpy, ptr %fptr
118   %f = load ptr, ptr %fptr
119   call ptr %f(ptr %dest, ptr %src, i64 %size)
120 ; CHECK: call void @llvm.memcpy
121   ret void
124 ; A boring function that just keeps our declarations around.
125 define void @keep(ptr %sink) {
126 ; CHECK-NOT: Function Attrs
127 ; CHECK-LABEL: define void @keep(
128 entry:
129   store volatile ptr @readnone, ptr %sink
130   store volatile ptr @unknown, ptr %sink
131   store volatile ptr @memcpy, ptr %sink
132   call void @unknown()
133   ret void