xref: /llvm-project/llvm/test/Transforms/MemCpyOpt/capturing-func.ll (revision f3a928e2334a4e8e3f09406f2ce7e1cbac520f95)
1; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature
2; RUN: opt < %s -passes='require<globals-aa>,memcpyopt' -S -verify-memoryssa | FileCheck %s
3
4target datalayout = "e"
5
6declare void @foo(ptr)
7declare void @llvm.memcpy.p0.p0.i32(ptr nocapture, ptr nocapture, i32, i1) nounwind
8declare void @llvm.lifetime.start.p0(i64, ptr nocapture)
9declare void @llvm.lifetime.end.p0(i64, ptr nocapture)
10
11; Check that the transformation isn't applied if the called function can
12; capture the pointer argument (i.e. the nocapture attribute isn't present)
13define void @test() {
14; CHECK-LABEL: define {{[^@]+}}@test() {
15; CHECK-NEXT:    [[PTR1:%.*]] = alloca i8, align 1
16; CHECK-NEXT:    [[PTR2:%.*]] = alloca i8, align 1
17; CHECK-NEXT:    call void @foo(ptr [[PTR2]])
18; CHECK-NEXT:    call void @llvm.memcpy.p0.p0.i32(ptr [[PTR1]], ptr [[PTR2]], i32 1, i1 false)
19; CHECK-NEXT:    call void @foo(ptr [[PTR1]])
20; CHECK-NEXT:    ret void
21;
22  %ptr1 = alloca i8
23  %ptr2 = alloca i8
24  call void @foo(ptr %ptr2)
25  call void @llvm.memcpy.p0.p0.i32(ptr %ptr1, ptr %ptr2, i32 1, i1 false)
26  call void @foo(ptr %ptr1)
27  ret void
28}
29
30; Same as previous test, but with a bitcasted argument.
31define void @test_bitcast() {
32; CHECK-LABEL: define {{[^@]+}}@test_bitcast() {
33; CHECK-NEXT:    [[PTR1:%.*]] = alloca [2 x i8], align 1
34; CHECK-NEXT:    [[PTR2:%.*]] = alloca [2 x i8], align 1
35; CHECK-NEXT:    call void @foo(ptr [[PTR2]])
36; CHECK-NEXT:    call void @llvm.memcpy.p0.p0.i32(ptr [[PTR1]], ptr [[PTR2]], i32 2, i1 false)
37; CHECK-NEXT:    call void @foo(ptr [[PTR1]])
38; CHECK-NEXT:    ret void
39;
40  %ptr1 = alloca [2 x i8]
41  %ptr2 = alloca [2 x i8]
42  call void @foo(ptr %ptr2)
43  call void @llvm.memcpy.p0.p0.i32(ptr %ptr1, ptr %ptr2, i32 2, i1 false)
44  call void @foo(ptr %ptr1)
45  ret void
46}
47
48; Lifetime of %ptr2 ends before the potential use of the capture in the second
49; call.
50define void @test_lifetime_end() {
51; CHECK-LABEL: define {{[^@]+}}@test_lifetime_end() {
52; CHECK-NEXT:    [[PTR1:%.*]] = alloca i8, align 1
53; CHECK-NEXT:    [[PTR2:%.*]] = alloca i8, align 1
54; CHECK-NEXT:    call void @llvm.lifetime.start.p0(i64 1, ptr [[PTR2]])
55; CHECK-NEXT:    call void @foo(ptr [[PTR1]])
56; CHECK-NEXT:    call void @llvm.lifetime.end.p0(i64 1, ptr [[PTR2]])
57; CHECK-NEXT:    call void @foo(ptr [[PTR1]])
58; CHECK-NEXT:    ret void
59;
60  %ptr1 = alloca i8
61  %ptr2 = alloca i8
62  call void @llvm.lifetime.start.p0(i64 1, ptr %ptr2)
63  call void @foo(ptr %ptr2)
64  call void @llvm.memcpy.p0.p0.i32(ptr %ptr1, ptr %ptr2, i32 1, i1 false)
65  call void @llvm.lifetime.end.p0(i64 1, ptr %ptr2)
66  call void @foo(ptr %ptr1)
67  ret void
68}
69
70; Lifetime of %ptr2 does not end, because of size mismatch.
71define void @test_lifetime_not_end() {
72; CHECK-LABEL: define {{[^@]+}}@test_lifetime_not_end() {
73; CHECK-NEXT:    [[PTR1:%.*]] = alloca i8, align 1
74; CHECK-NEXT:    [[PTR2:%.*]] = alloca i8, align 1
75; CHECK-NEXT:    call void @llvm.lifetime.start.p0(i64 1, ptr [[PTR2]])
76; CHECK-NEXT:    call void @foo(ptr [[PTR2]])
77; CHECK-NEXT:    call void @llvm.memcpy.p0.p0.i32(ptr [[PTR1]], ptr [[PTR2]], i32 1, i1 false)
78; CHECK-NEXT:    call void @llvm.lifetime.end.p0(i64 0, ptr [[PTR2]])
79; CHECK-NEXT:    call void @foo(ptr [[PTR1]])
80; CHECK-NEXT:    ret void
81;
82  %ptr1 = alloca i8
83  %ptr2 = alloca i8
84  call void @llvm.lifetime.start.p0(i64 1, ptr %ptr2)
85  call void @foo(ptr %ptr2)
86  call void @llvm.memcpy.p0.p0.i32(ptr %ptr1, ptr %ptr2, i32 1, i1 false)
87  call void @llvm.lifetime.end.p0(i64 0, ptr %ptr2)
88  call void @foo(ptr %ptr1)
89  ret void
90}
91
92; Lifetime of %ptr2 ends before any potential use of the capture because we
93; return from the function.
94define void @test_function_end() {
95; CHECK-LABEL: define {{[^@]+}}@test_function_end() {
96; CHECK-NEXT:    [[PTR1:%.*]] = alloca i8, align 1
97; CHECK-NEXT:    [[PTR2:%.*]] = alloca i8, align 1
98; CHECK-NEXT:    call void @foo(ptr [[PTR1]])
99; CHECK-NEXT:    ret void
100;
101  %ptr1 = alloca i8
102  %ptr2 = alloca i8
103  call void @foo(ptr %ptr2)
104  call void @llvm.memcpy.p0.p0.i32(ptr %ptr1, ptr %ptr2, i32 1, i1 false)
105  ret void
106}
107
108; A potential use of the capture occurs in a later block, can't be optimized.
109define void @test_terminator() {
110; CHECK-LABEL: define {{[^@]+}}@test_terminator() {
111; CHECK-NEXT:    [[PTR1:%.*]] = alloca i8, align 1
112; CHECK-NEXT:    [[PTR2:%.*]] = alloca i8, align 1
113; CHECK-NEXT:    call void @foo(ptr [[PTR2]])
114; CHECK-NEXT:    call void @llvm.memcpy.p0.p0.i32(ptr [[PTR1]], ptr [[PTR2]], i32 1, i1 false)
115; CHECK-NEXT:    br label [[NEXT:%.*]]
116; CHECK:       next:
117; CHECK-NEXT:    call void @foo(ptr [[PTR1]])
118; CHECK-NEXT:    ret void
119;
120  %ptr1 = alloca i8
121  %ptr2 = alloca i8
122  call void @foo(ptr %ptr2)
123  call void @llvm.memcpy.p0.p0.i32(ptr %ptr1, ptr %ptr2, i32 1, i1 false)
124  br label %next
125
126next:
127  call void @foo(ptr %ptr1)
128  ret void
129}
130
131; This case can be optimized, but would require a scan across multiple blocks
132; and is currently not performed.
133define void @test_terminator2() {
134; CHECK-LABEL: define {{[^@]+}}@test_terminator2() {
135; CHECK-NEXT:    [[PTR1:%.*]] = alloca i8, align 1
136; CHECK-NEXT:    [[PTR2:%.*]] = alloca i8, align 1
137; CHECK-NEXT:    call void @foo(ptr [[PTR2]])
138; CHECK-NEXT:    call void @llvm.memcpy.p0.p0.i32(ptr [[PTR1]], ptr [[PTR2]], i32 1, i1 false)
139; CHECK-NEXT:    br label [[NEXT:%.*]]
140; CHECK:       next:
141; CHECK-NEXT:    ret void
142;
143  %ptr1 = alloca i8
144  %ptr2 = alloca i8
145  call void @foo(ptr %ptr2)
146  call void @llvm.memcpy.p0.p0.i32(ptr %ptr1, ptr %ptr2, i32 1, i1 false)
147  br label %next
148
149next:
150  ret void
151}
152
153declare void @capture(ptr)
154
155; This case should not be optimized, because dest is captured before the call.
156define void @test_dest_captured_before_alloca() {
157; CHECK-LABEL: define {{[^@]+}}@test_dest_captured_before_alloca() {
158; CHECK-NEXT:    [[PTR1:%.*]] = alloca i8, align 1
159; CHECK-NEXT:    [[PTR2:%.*]] = alloca i8, align 1
160; CHECK-NEXT:    call void @capture(ptr [[PTR1]])
161; CHECK-NEXT:    call void @foo(ptr [[PTR2]]) #[[ATTR2:[0-9]+]]
162; CHECK-NEXT:    call void @llvm.memcpy.p0.p0.i32(ptr [[PTR1]], ptr [[PTR2]], i32 1, i1 false)
163; CHECK-NEXT:    ret void
164;
165  %ptr1 = alloca i8
166  %ptr2 = alloca i8
167  call void @capture(ptr %ptr1)
168  call void @foo(ptr %ptr2) argmemonly
169  call void @llvm.memcpy.p0.p0.i32(ptr %ptr1, ptr %ptr2, i32 1, i1 false)
170  ret void
171}
172
173
174@g = internal global i8 0
175
176; This case should not be optimized, because @g is captured before the call
177; (being a global) and @icmp_g might depend on its identity.
178define void @test_dest_captured_before_global() {
179; CHECK-LABEL: define {{[^@]+}}@test_dest_captured_before_global() {
180; CHECK-NEXT:    [[PTR:%.*]] = alloca i8, align 1
181; CHECK-NEXT:    call void @icmp_g(ptr [[PTR]])
182; CHECK-NEXT:    call void @llvm.memcpy.p0.p0.i32(ptr @g, ptr [[PTR]], i32 1, i1 false)
183; CHECK-NEXT:    ret void
184;
185  %ptr = alloca i8
186  call void @icmp_g(ptr %ptr)
187  call void @llvm.memcpy.p0.p0.i32(ptr @g, ptr %ptr, i32 1, i1 false)
188  ret void
189}
190
191define void @icmp_g(ptr %p) {
192; CHECK-LABEL: define {{[^@]+}}@icmp_g
193; CHECK-SAME: (ptr [[P:%.*]]) {
194; CHECK-NEXT:    [[C:%.*]] = icmp eq ptr [[P]], @g
195; CHECK-NEXT:    br i1 [[C]], label [[IF:%.*]], label [[ELSE:%.*]]
196; CHECK:       if:
197; CHECK-NEXT:    store i8 1, ptr [[P]], align 1
198; CHECK-NEXT:    ret void
199; CHECK:       else:
200; CHECK-NEXT:    store i8 2, ptr [[P]], align 1
201; CHECK-NEXT:    ret void
202;
203  %c = icmp eq ptr %p, @g
204  br i1 %c, label %if, label %else
205
206if:
207  store i8 1, ptr %p
208  ret void
209
210else:
211  store i8 2, ptr %p
212  ret void
213}
214