Skip to content

Commit 73ef96c

Browse files
committed
[tests] highlight cornercase w/deref hoisting from D95815
The main point of committing this early is to have a negative test in tree. Nothing fails in the current tests if we implement this (currently unsound) optimization.
1 parent 68b0595 commit 73ef96c

File tree

1 file changed

+323
-0
lines changed

1 file changed

+323
-0
lines changed
Lines changed: 323 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,323 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
2+
; RUN: opt -S -basic-aa -licm < %s | FileCheck %s
3+
4+
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
5+
target triple = "x86_64-unknown-linux-gnu"
6+
7+
declare void @unknown()
8+
declare void @init(i8* nocapture)
9+
declare void @use(i8)
10+
11+
define i8 @test_sink_alloca() {
12+
; CHECK-LABEL: @test_sink_alloca(
13+
; CHECK-NEXT: entry:
14+
; CHECK-NEXT: [[A:%.*]] = alloca [32 x i8], align 1
15+
; CHECK-NEXT: [[A_RAW:%.*]] = bitcast [32 x i8]* [[A]] to i8*
16+
; CHECK-NEXT: call void @init(i8* [[A_RAW]])
17+
; CHECK-NEXT: br label [[FOR_BODY:%.*]]
18+
; CHECK: for.body:
19+
; CHECK-NEXT: [[IV:%.*]] = phi i64 [ [[IV_NEXT:%.*]], [[FOR_BODY]] ], [ 0, [[ENTRY:%.*]] ]
20+
; CHECK-NEXT: call void @unknown()
21+
; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i64 [[IV]], 1
22+
; CHECK-NEXT: [[EXITCOND:%.*]] = icmp eq i64 [[IV_NEXT]], 200
23+
; CHECK-NEXT: br i1 [[EXITCOND]], label [[FOR_END:%.*]], label [[FOR_BODY]]
24+
; CHECK: for.end:
25+
; CHECK-NEXT: [[ADDR_LE:%.*]] = getelementptr i8, i8* [[A_RAW]], i32 31
26+
; CHECK-NEXT: [[RES_LE:%.*]] = load i8, i8* [[ADDR_LE]], align 1
27+
; CHECK-NEXT: ret i8 [[RES_LE]]
28+
;
29+
entry:
30+
%a = alloca [32 x i8]
31+
%a.raw = bitcast [32 x i8]* %a to i8*
32+
call void @init(i8* %a.raw)
33+
br label %for.body
34+
35+
for.body:
36+
%iv = phi i64 [ %iv.next, %for.body ], [ 0, %entry ]
37+
call void @unknown() ;; may throw
38+
%addr = getelementptr i8, i8* %a.raw, i32 31
39+
%res = load i8, i8* %addr
40+
%iv.next = add nuw nsw i64 %iv, 1
41+
%exitcond = icmp eq i64 %iv.next, 200
42+
br i1 %exitcond, label %for.end, label %for.body
43+
44+
for.end:
45+
ret i8 %res
46+
}
47+
48+
define i8 @test_hoist_alloca() {
49+
; CHECK-LABEL: @test_hoist_alloca(
50+
; CHECK-NEXT: entry:
51+
; CHECK-NEXT: [[A:%.*]] = alloca [32 x i8], align 1
52+
; CHECK-NEXT: [[A_RAW:%.*]] = bitcast [32 x i8]* [[A]] to i8*
53+
; CHECK-NEXT: call void @init(i8* [[A_RAW]])
54+
; CHECK-NEXT: [[ADDR:%.*]] = getelementptr i8, i8* [[A_RAW]], i32 31
55+
; CHECK-NEXT: [[RES:%.*]] = load i8, i8* [[ADDR]], align 1
56+
; CHECK-NEXT: br label [[FOR_BODY:%.*]]
57+
; CHECK: for.body:
58+
; CHECK-NEXT: [[IV:%.*]] = phi i64 [ [[IV_NEXT:%.*]], [[FOR_BODY]] ], [ 0, [[ENTRY:%.*]] ]
59+
; CHECK-NEXT: call void @unknown()
60+
; CHECK-NEXT: call void @use(i8 [[RES]])
61+
; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i64 [[IV]], 1
62+
; CHECK-NEXT: [[EXITCOND:%.*]] = icmp eq i64 [[IV_NEXT]], 200
63+
; CHECK-NEXT: br i1 [[EXITCOND]], label [[FOR_END:%.*]], label [[FOR_BODY]]
64+
; CHECK: for.end:
65+
; CHECK-NEXT: [[RES_LCSSA:%.*]] = phi i8 [ [[RES]], [[FOR_BODY]] ]
66+
; CHECK-NEXT: ret i8 [[RES_LCSSA]]
67+
;
68+
entry:
69+
%a = alloca [32 x i8]
70+
%a.raw = bitcast [32 x i8]* %a to i8*
71+
call void @init(i8* %a.raw)
72+
br label %for.body
73+
74+
for.body:
75+
%iv = phi i64 [ %iv.next, %for.body ], [ 0, %entry ]
76+
call void @unknown() ;; may throw
77+
%addr = getelementptr i8, i8* %a.raw, i32 31
78+
%res = load i8, i8* %addr
79+
call void @use(i8 %res)
80+
%iv.next = add nuw nsw i64 %iv, 1
81+
%exitcond = icmp eq i64 %iv.next, 200
82+
br i1 %exitcond, label %for.end, label %for.body
83+
84+
for.end:
85+
ret i8 %res
86+
}
87+
88+
; The attributes listed here are a) inferred by -O3 from the names
89+
; and b) required for a standalone test. We're very inconsistent about
90+
; which decisions we drive from TLI vs assume attributes have been infered.
91+
declare void @free(i8* nocapture)
92+
declare noalias i8* @malloc(i64)
93+
94+
define i8 @test_sink_malloc() {
95+
; CHECK-LABEL: @test_sink_malloc(
96+
; CHECK-NEXT: entry:
97+
; CHECK-NEXT: [[A_RAW:%.*]] = call nonnull i8* @malloc(i64 32)
98+
; CHECK-NEXT: call void @init(i8* [[A_RAW]])
99+
; CHECK-NEXT: br label [[FOR_BODY:%.*]]
100+
; CHECK: for.body:
101+
; CHECK-NEXT: [[IV:%.*]] = phi i64 [ [[IV_NEXT:%.*]], [[FOR_BODY]] ], [ 0, [[ENTRY:%.*]] ]
102+
; CHECK-NEXT: call void @unknown()
103+
; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i64 [[IV]], 1
104+
; CHECK-NEXT: [[EXITCOND:%.*]] = icmp eq i64 [[IV_NEXT]], 200
105+
; CHECK-NEXT: br i1 [[EXITCOND]], label [[FOR_END:%.*]], label [[FOR_BODY]]
106+
; CHECK: for.end:
107+
; CHECK-NEXT: [[ADDR_LE:%.*]] = getelementptr i8, i8* [[A_RAW]], i32 31
108+
; CHECK-NEXT: [[RES_LE:%.*]] = load i8, i8* [[ADDR_LE]], align 1
109+
; CHECK-NEXT: call void @free(i8* [[A_RAW]])
110+
; CHECK-NEXT: ret i8 [[RES_LE]]
111+
;
112+
entry:
113+
; Mark as nonnull to simplify test
114+
%a.raw = call nonnull i8* @malloc(i64 32)
115+
call void @init(i8* %a.raw)
116+
br label %for.body
117+
118+
for.body:
119+
%iv = phi i64 [ %iv.next, %for.body ], [ 0, %entry ]
120+
call void @unknown() ;; may throw
121+
%addr = getelementptr i8, i8* %a.raw, i32 31
122+
%res = load i8, i8* %addr
123+
%iv.next = add nuw nsw i64 %iv, 1
124+
%exitcond = icmp eq i64 %iv.next, 200
125+
br i1 %exitcond, label %for.end, label %for.body
126+
127+
for.end:
128+
call void @free(i8* %a.raw)
129+
ret i8 %res
130+
}
131+
132+
; TODO: We can hoist the load in this case.
133+
define i8 @test_hoist_malloc() {
134+
; CHECK-LABEL: @test_hoist_malloc(
135+
; CHECK-NEXT: entry:
136+
; CHECK-NEXT: [[A_RAW:%.*]] = call nonnull i8* @malloc(i64 32)
137+
; CHECK-NEXT: call void @init(i8* [[A_RAW]])
138+
; CHECK-NEXT: [[ADDR:%.*]] = getelementptr i8, i8* [[A_RAW]], i32 31
139+
; CHECK-NEXT: br label [[FOR_BODY:%.*]]
140+
; CHECK: for.body:
141+
; CHECK-NEXT: [[IV:%.*]] = phi i64 [ [[IV_NEXT:%.*]], [[FOR_BODY]] ], [ 0, [[ENTRY:%.*]] ]
142+
; CHECK-NEXT: call void @unknown()
143+
; CHECK-NEXT: [[RES:%.*]] = load i8, i8* [[ADDR]], align 1
144+
; CHECK-NEXT: call void @use(i8 [[RES]])
145+
; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i64 [[IV]], 1
146+
; CHECK-NEXT: [[EXITCOND:%.*]] = icmp eq i64 [[IV_NEXT]], 200
147+
; CHECK-NEXT: br i1 [[EXITCOND]], label [[FOR_END:%.*]], label [[FOR_BODY]]
148+
; CHECK: for.end:
149+
; CHECK-NEXT: [[RES_LCSSA:%.*]] = phi i8 [ [[RES]], [[FOR_BODY]] ]
150+
; CHECK-NEXT: call void @free(i8* [[A_RAW]])
151+
; CHECK-NEXT: ret i8 [[RES_LCSSA]]
152+
;
153+
entry:
154+
%a.raw = call nonnull i8* @malloc(i64 32)
155+
call void @init(i8* %a.raw)
156+
br label %for.body
157+
158+
for.body:
159+
%iv = phi i64 [ %iv.next, %for.body ], [ 0, %entry ]
160+
call void @unknown() ;; may throw
161+
%addr = getelementptr i8, i8* %a.raw, i32 31
162+
%res = load i8, i8* %addr
163+
call void @use(i8 %res)
164+
%iv.next = add nuw nsw i64 %iv, 1
165+
%exitcond = icmp eq i64 %iv.next, 200
166+
br i1 %exitcond, label %for.end, label %for.body
167+
168+
for.end:
169+
call void @free(i8* %a.raw)
170+
ret i8 %res
171+
}
172+
173+
; TODO: We can hoist the load in this case.
174+
define i8 @test_hoist_malloc_leak() {
175+
; CHECK-LABEL: @test_hoist_malloc_leak(
176+
; CHECK-NEXT: entry:
177+
; CHECK-NEXT: [[A_RAW:%.*]] = call nonnull i8* @malloc(i64 32)
178+
; CHECK-NEXT: call void @init(i8* [[A_RAW]])
179+
; CHECK-NEXT: [[ADDR:%.*]] = getelementptr i8, i8* [[A_RAW]], i32 31
180+
; CHECK-NEXT: br label [[FOR_BODY:%.*]]
181+
; CHECK: for.body:
182+
; CHECK-NEXT: [[IV:%.*]] = phi i64 [ [[IV_NEXT:%.*]], [[FOR_BODY]] ], [ 0, [[ENTRY:%.*]] ]
183+
; CHECK-NEXT: call void @unknown()
184+
; CHECK-NEXT: [[RES:%.*]] = load i8, i8* [[ADDR]], align 1
185+
; CHECK-NEXT: call void @use(i8 [[RES]])
186+
; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i64 [[IV]], 1
187+
; CHECK-NEXT: [[EXITCOND:%.*]] = icmp eq i64 [[IV_NEXT]], 200
188+
; CHECK-NEXT: br i1 [[EXITCOND]], label [[FOR_END:%.*]], label [[FOR_BODY]]
189+
; CHECK: for.end:
190+
; CHECK-NEXT: [[RES_LCSSA:%.*]] = phi i8 [ [[RES]], [[FOR_BODY]] ]
191+
; CHECK-NEXT: ret i8 [[RES_LCSSA]]
192+
;
193+
entry:
194+
%a.raw = call nonnull i8* @malloc(i64 32)
195+
call void @init(i8* %a.raw)
196+
br label %for.body
197+
198+
for.body:
199+
%iv = phi i64 [ %iv.next, %for.body ], [ 0, %entry ]
200+
call void @unknown() ;; may throw
201+
%addr = getelementptr i8, i8* %a.raw, i32 31
202+
%res = load i8, i8* %addr
203+
call void @use(i8 %res)
204+
%iv.next = add nuw nsw i64 %iv, 1
205+
%exitcond = icmp eq i64 %iv.next, 200
206+
br i1 %exitcond, label %for.end, label %for.body
207+
208+
for.end:
209+
ret i8 %res
210+
}
211+
212+
; In this case, we can't hoist the load out of the loop as the memory it
213+
; accesses may have been conditionally freed in a manner correlated with
214+
; whether the load is reached in the loop.
215+
define void @test_hoist_malloc_cond_free(i1 %c) {
216+
; CHECK-LABEL: @test_hoist_malloc_cond_free(
217+
; CHECK-NEXT: entry:
218+
; CHECK-NEXT: [[A_RAW:%.*]] = call nonnull i8* @malloc(i64 32)
219+
; CHECK-NEXT: call void @init(i8* [[A_RAW]])
220+
; CHECK-NEXT: br i1 [[C:%.*]], label [[COND_FREE:%.*]], label [[PREHEADER:%.*]]
221+
; CHECK: cond.free:
222+
; CHECK-NEXT: call void @free(i8* [[A_RAW]])
223+
; CHECK-NEXT: br label [[PREHEADER]]
224+
; CHECK: preheader:
225+
; CHECK-NEXT: [[ADDR:%.*]] = getelementptr i8, i8* [[A_RAW]], i32 31
226+
; CHECK-NEXT: br label [[FOR_BODY:%.*]]
227+
; CHECK: for.body:
228+
; CHECK-NEXT: [[IV:%.*]] = phi i64 [ [[IV_NEXT:%.*]], [[LOOP_LATCH:%.*]] ], [ 0, [[PREHEADER]] ]
229+
; CHECK-NEXT: br i1 [[C]], label [[FOR_END:%.*]], label [[LOOP_LATCH]]
230+
; CHECK: loop.latch:
231+
; CHECK-NEXT: call void @unknown()
232+
; CHECK-NEXT: [[RES:%.*]] = load i8, i8* [[ADDR]], align 1
233+
; CHECK-NEXT: call void @use(i8 [[RES]])
234+
; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i64 [[IV]], 1
235+
; CHECK-NEXT: [[EXITCOND:%.*]] = icmp eq i64 [[IV_NEXT]], 200
236+
; CHECK-NEXT: br i1 [[EXITCOND]], label [[FOR_END]], label [[FOR_BODY]]
237+
; CHECK: for.end:
238+
; CHECK-NEXT: ret void
239+
;
240+
entry:
241+
%a.raw = call nonnull i8* @malloc(i64 32)
242+
call void @init(i8* %a.raw)
243+
br i1 %c, label %cond.free, label %preheader
244+
cond.free:
245+
call void @free(i8* %a.raw)
246+
br label %preheader
247+
preheader:
248+
br label %for.body
249+
250+
for.body:
251+
%iv = phi i64 [ %iv.next, %loop.latch ], [ 0, %preheader ]
252+
br i1 %c, label %for.end, label %loop.latch
253+
254+
loop.latch:
255+
call void @unknown() ;; may throw
256+
%addr = getelementptr i8, i8* %a.raw, i32 31
257+
%res = load i8, i8* %addr
258+
call void @use(i8 %res)
259+
%iv.next = add nuw nsw i64 %iv, 1
260+
%exitcond = icmp eq i64 %iv.next, 200
261+
br i1 %exitcond, label %for.end, label %for.body
262+
263+
for.end:
264+
ret void
265+
}
266+
267+
define i8 @test_sink_malloc_cond_free(i1 %c) {
268+
; CHECK-LABEL: @test_sink_malloc_cond_free(
269+
; CHECK-NEXT: entry:
270+
; CHECK-NEXT: [[A_RAW:%.*]] = call nonnull i8* @malloc(i64 32)
271+
; CHECK-NEXT: call void @init(i8* [[A_RAW]])
272+
; CHECK-NEXT: br i1 [[C:%.*]], label [[COND_FREE:%.*]], label [[PREHEADER:%.*]]
273+
; CHECK: cond.free:
274+
; CHECK-NEXT: call void @free(i8* [[A_RAW]])
275+
; CHECK-NEXT: br label [[PREHEADER]]
276+
; CHECK: preheader:
277+
; CHECK-NEXT: br label [[FOR_BODY:%.*]]
278+
; CHECK: for.body:
279+
; CHECK-NEXT: [[IV:%.*]] = phi i64 [ [[IV_NEXT:%.*]], [[LOOP_LATCH:%.*]] ], [ 0, [[PREHEADER]] ]
280+
; CHECK-NEXT: br i1 [[C]], label [[FOR_END_SPLIT_LOOP_EXIT1:%.*]], label [[LOOP_LATCH]]
281+
; CHECK: loop.latch:
282+
; CHECK-NEXT: call void @unknown()
283+
; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i64 [[IV]], 1
284+
; CHECK-NEXT: [[EXITCOND:%.*]] = icmp eq i64 [[IV_NEXT]], 200
285+
; CHECK-NEXT: br i1 [[EXITCOND]], label [[FOR_END_SPLIT_LOOP_EXIT:%.*]], label [[FOR_BODY]]
286+
; CHECK: for.end.split.loop.exit:
287+
; CHECK-NEXT: [[ADDR_LE:%.*]] = getelementptr i8, i8* [[A_RAW]], i32 31
288+
; CHECK-NEXT: [[RES_LE:%.*]] = load i8, i8* [[ADDR_LE]], align 1
289+
; CHECK-NEXT: br label [[FOR_END:%.*]]
290+
; CHECK: for.end.split.loop.exit1:
291+
; CHECK-NEXT: [[PHI_PH2:%.*]] = phi i8 [ 0, [[FOR_BODY]] ]
292+
; CHECK-NEXT: br label [[FOR_END]]
293+
; CHECK: for.end:
294+
; CHECK-NEXT: [[PHI:%.*]] = phi i8 [ [[RES_LE]], [[FOR_END_SPLIT_LOOP_EXIT]] ], [ [[PHI_PH2]], [[FOR_END_SPLIT_LOOP_EXIT1]] ]
295+
; CHECK-NEXT: ret i8 [[PHI]]
296+
;
297+
entry:
298+
%a.raw = call nonnull i8* @malloc(i64 32)
299+
call void @init(i8* %a.raw)
300+
br i1 %c, label %cond.free, label %preheader
301+
cond.free:
302+
call void @free(i8* %a.raw)
303+
br label %preheader
304+
preheader:
305+
br label %for.body
306+
307+
for.body:
308+
%iv = phi i64 [ %iv.next, %loop.latch ], [ 0, %preheader ]
309+
br i1 %c, label %for.end, label %loop.latch
310+
311+
loop.latch:
312+
call void @unknown() ;; may throw
313+
%addr = getelementptr i8, i8* %a.raw, i32 31
314+
%res = load i8, i8* %addr
315+
%iv.next = add nuw nsw i64 %iv, 1
316+
%exitcond = icmp eq i64 %iv.next, 200
317+
br i1 %exitcond, label %for.end, label %for.body
318+
319+
for.end:
320+
%phi = phi i8 [%res, %loop.latch], [0, %for.body]
321+
ret i8 %phi
322+
}
323+

0 commit comments

Comments
 (0)