1; RUN: opt -S -codegenprepare -mtriple=thumbv7m -disable-complex-addr-modes=false -addr-sink-new-select=true -addr-sink-new-phis=true < %s | FileCheck %s
2
3target datalayout = "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64"
4
5@gv1 = common global i32 0, align 4
6@gv2 = common global i32 0, align 4
7
8; Phi selects between ptr and gep with ptr as base and constant offset
9define void @test_phi_onegep_offset(i32* %ptr, i32 %value) {
10; CHECK-LABEL: @test_phi_onegep_offset
11; CHECK-NOT: phi i32* [ %ptr, %entry ], [ %gep, %if.then ]
12; CHECK: phi i32 [ 4, %if.then ], [ 0, %entry ]
13entry:
14  %cmp = icmp sgt i32 %value, 0
15  br i1 %cmp, label %if.then, label %if.end
16
17if.then:
18  %gep = getelementptr inbounds i32, i32* %ptr, i32 1
19  br label %if.end
20
21if.end:
22  %phi = phi i32* [ %ptr, %entry ], [ %gep, %if.then ]
23  store i32 %value, i32* %phi, align 4
24  ret void
25}
26
27; Phi selects between two geps with same base, different constant offsets
28define void @test_phi_twogep_offset(i32* %ptr, i32 %value) {
29; CHECK-LABEL: @test_phi_twogep_offset
30; CHECK-NOT: phi i32* [ %gep1, %if.then ], [ %gep2, %if.else ]
31; CHECK: phi i32 [ 8, %if.else ], [ 4, %if.then ]
32entry:
33  %cmp = icmp sgt i32 %value, 0
34  br i1 %cmp, label %if.then, label %if.else
35
36if.then:
37  %gep1 = getelementptr inbounds i32, i32* %ptr, i32 1
38  br label %if.end
39
40if.else:
41  %gep2 = getelementptr inbounds i32, i32* %ptr, i32 2
42  br label %if.end
43
44if.end:
45  %phi = phi i32* [ %gep1, %if.then ], [ %gep2, %if.else ]
46  store i32 %value, i32* %phi, align 4
47  ret void
48}
49
50; Phi selects between ptr and gep with ptr as base and nonconstant offset
51define void @test_phi_onegep_nonconst_offset(i32* %ptr, i32 %value, i32 %off) {
52; CHECK-LABEL: @test_phi_onegep_nonconst_offset
53; CHECK-NOT: phi i32* [ %ptr, %entry ], [ %gep, %if.then ]
54; CHECK: phi i32 [ %off, %if.then ], [ 0, %entry ]
55entry:
56  %cmp = icmp sgt i32 %value, 0
57  br i1 %cmp, label %if.then, label %if.end
58
59if.then:
60  %gep = getelementptr inbounds i32, i32* %ptr, i32 %off
61  br label %if.end
62
63if.end:
64  %phi = phi i32* [ %ptr, %entry ], [ %gep, %if.then ]
65  store i32 %value, i32* %phi, align 4
66  ret void
67}
68
69; Phi selects between two geps with same base, different nonconstant offsets
70define void @test_phi_twogep_nonconst_offset(i32* %ptr, i32 %value, i32 %off1, i32 %off2) {
71; CHECK-LABEL: @test_phi_twogep_nonconst_offset
72; CHECK-NOT: phi i32* [ %gep1, %if.then ], [ %gep2, %if.else ]
73; CHECK: phi i32 [ %off2, %if.else ], [ %off1, %if.then ]
74entry:
75  %cmp = icmp sgt i32 %value, 0
76  br i1 %cmp, label %if.then, label %if.else
77
78if.then:
79  %gep1 = getelementptr inbounds i32, i32* %ptr, i32 %off1
80  br label %if.end
81
82if.else:
83  %gep2 = getelementptr inbounds i32, i32* %ptr, i32 %off2
84  br label %if.end
85
86if.end:
87  %phi = phi i32* [ %gep1, %if.then ], [ %gep2, %if.else ]
88  store i32 %value, i32* %phi, align 4
89  ret void
90}
91
92; Phi selects between two geps with different base, same constant offset
93define void @test_phi_twogep_base(i32* %ptr1, i32* %ptr2, i32 %value) {
94; CHECK-LABEL: @test_phi_twogep_base
95; CHECK-NOT: phi i32* [ %gep1, %if.then ], [ %gep2, %if.else ]
96; CHECK: phi i32* [ %ptr2, %if.else ], [ %ptr1, %if.then ]
97entry:
98  %cmp = icmp sgt i32 %value, 0
99  br i1 %cmp, label %if.then, label %if.else
100
101if.then:
102  %gep1 = getelementptr inbounds i32, i32* %ptr1, i32 1
103  br label %if.end
104
105if.else:
106  %gep2 = getelementptr inbounds i32, i32* %ptr2, i32 1
107  br label %if.end
108
109if.end:
110  %phi = phi i32* [ %gep1, %if.then ], [ %gep2, %if.else ]
111  store i32 %value, i32* %phi, align 4
112  ret void
113}
114
115; Phi selects between two geps with different base global variables, same constant offset
116define void @test_phi_twogep_base_gv(i32 %value) {
117; CHECK-LABEL: @test_phi_twogep_base_gv
118; CHECK-NOT: phi i32* [ %gep1, %if.then ], [ %gep2, %if.else ]
119; CHECK: phi i32* [ @gv2, %if.else ], [ @gv1, %if.then ]
120entry:
121  %cmp = icmp sgt i32 %value, 0
122  br i1 %cmp, label %if.then, label %if.else
123
124if.then:
125  %gep1 = getelementptr inbounds i32, i32* @gv1, i32 1
126  br label %if.end
127
128if.else:
129  %gep2 = getelementptr inbounds i32, i32* @gv2, i32 1
130  br label %if.end
131
132if.end:
133  %phi = phi i32* [ %gep1, %if.then ], [ %gep2, %if.else ]
134  store i32 %value, i32* %phi, align 4
135  ret void
136}
137
138; Phi selects between ptr and gep with ptr as base and constant offset
139define void @test_select_onegep_offset(i32* %ptr, i32 %value) {
140; CHECK-LABEL: @test_select_onegep_offset
141; CHECK-NOT: select i1 %cmp, i32* %ptr, i32* %gep
142; CHECK: select i1 %cmp, i32 0, i32 4
143entry:
144  %cmp = icmp sgt i32 %value, 0
145  %gep = getelementptr inbounds i32, i32* %ptr, i32 1
146  %select = select i1 %cmp, i32* %ptr, i32* %gep
147  store i32 %value, i32* %select, align 4
148  ret void
149}
150
151; Select between two geps with same base, different constant offsets
152define void @test_select_twogep_offset(i32* %ptr, i32 %value) {
153; CHECK-LABEL: @test_select_twogep_offset
154; CHECK-NOT: select i1 %cmp, i32* %gep1, i32* %gep2
155; CHECK: select i1 %cmp, i32 4, i32 8
156entry:
157  %cmp = icmp sgt i32 %value, 0
158  %gep1 = getelementptr inbounds i32, i32* %ptr, i32 1
159  %gep2 = getelementptr inbounds i32, i32* %ptr, i32 2
160  %select = select i1 %cmp, i32* %gep1, i32* %gep2
161  store i32 %value, i32* %select, align 4
162  ret void
163}
164
165; Select between ptr and gep with ptr as base and nonconstant offset
166define void @test_select_onegep_nonconst_offset(i32* %ptr, i32 %value, i32 %off) {
167; CHECK-LABEL: @test_select_onegep_nonconst_offset
168; CHECK-NOT: select i1 %cmp, i32* %ptr, i32* %gep
169; CHECK: select i1 %cmp, i32 0, i32 %off
170entry:
171  %cmp = icmp sgt i32 %value, 0
172  %gep = getelementptr inbounds i32, i32* %ptr, i32 %off
173  %select = select i1 %cmp, i32* %ptr, i32* %gep
174  store i32 %value, i32* %select, align 4
175  ret void
176}
177
178; Select between two geps with same base, different nonconstant offsets
179define void @test_select_twogep_nonconst_offset(i32* %ptr, i32 %value, i32 %off1, i32 %off2) {
180; CHECK-LABEL: @test_select_twogep_nonconst_offset
181; CHECK-NOT: select i1 %cmp, i32* %gep1, i32* %gep2
182; CHECK: select i1 %cmp, i32 %off1, i32 %off2
183entry:
184  %cmp = icmp sgt i32 %value, 0
185  %gep1 = getelementptr inbounds i32, i32* %ptr, i32 %off1
186  %gep2 = getelementptr inbounds i32, i32* %ptr, i32 %off2
187  %select = select i1 %cmp, i32* %gep1, i32* %gep2
188  store i32 %value, i32* %select, align 4
189  ret void
190}
191
192; Select between two geps with different base, same constant offset
193define void @test_select_twogep_base(i32* %ptr1, i32* %ptr2, i32 %value) {
194; CHECK-LABEL: @test_select_twogep_base
195; CHECK-NOT: select i1 %cmp, i32* %gep1, i32* %gep2
196; CHECK: select i1 %cmp, i32* %ptr1, i32* %ptr2
197entry:
198  %cmp = icmp sgt i32 %value, 0
199  %gep1 = getelementptr inbounds i32, i32* %ptr1, i32 1
200  %gep2 = getelementptr inbounds i32, i32* %ptr2, i32 1
201  %select = select i1 %cmp, i32* %gep1, i32* %gep2
202  store i32 %value, i32* %select, align 4
203  ret void
204}
205
206; Select between two geps with different base global variables, same constant offset
207define void @test_select_twogep_base_gv(i32 %value) {
208; CHECK-LABEL: @test_select_twogep_base_gv
209; CHECK-NOT: select i1 %cmp, i32* %gep1, i32* %gep2
210; CHECK: select i1 %cmp, i32* @gv1, i32* @gv2
211entry:
212  %cmp = icmp sgt i32 %value, 0
213  %gep1 = getelementptr inbounds i32, i32* @gv1, i32 1
214  %gep2 = getelementptr inbounds i32, i32* @gv2, i32 1
215  %select = select i1 %cmp, i32* %gep1, i32* %gep2
216  store i32 %value, i32* %select, align 4
217  ret void
218}
219
220; If the phi is in a different block to where the gep will be, the phi goes where
221; the original phi was not where the gep is.
222; CHECK-LABEL: @test_phi_different_block
223; CHECK-LABEL: if1.end
224; CHECK-NOT: phi i32* [ %ptr, %entry ], [ %gep, %if1.then ]
225; CHECK: phi i32 [ 4, %if1.then ], [ 0, %entry ]
226define void @test_phi_different_block(i32* %ptr, i32 %value1, i32 %value2) {
227entry:
228  %cmp1 = icmp sgt i32 %value1, 0
229  br i1 %cmp1, label %if1.then, label %if1.end
230
231if1.then:
232  %gep = getelementptr inbounds i32, i32* %ptr, i32 1
233  br label %if1.end
234
235if1.end:
236  %phi = phi i32* [ %ptr, %entry ], [ %gep, %if1.then ]
237  %cmp2 = icmp sgt i32 %value2, 0
238  br i1 %cmp2, label %if2.then, label %if2.end
239
240if2.then:
241  store i32 %value1, i32* %ptr, align 4
242  br label %if2.end
243
244if2.end:
245  store i32 %value2, i32* %phi, align 4
246  ret void
247}
248
249; A phi with three incoming values should be optimised
250; CHECK-LABEL: @test_phi_threegep
251; CHECK-NOT: phi i32* [ %gep1, %if.then ], [ %gep2, %if.else.then ], [ %gep3, %if.else.else ]
252; CHECK: phi i32 [ 12, %if.else.else ], [ 8, %if.else.then ], [ 4, %if.then ]
253define void @test_phi_threegep(i32* %ptr, i32 %value1, i32 %value2) {
254entry:
255  %cmp1 = icmp sgt i32 %value1, 0
256  br i1 %cmp1, label %if.then, label %if.else
257
258if.then:
259  %gep1 = getelementptr inbounds i32, i32* %ptr, i32 1
260  br label %if.end
261
262if.else:
263  %cmp2 = icmp sgt i32 %value2, 0
264  br i1 %cmp2, label %if.else.then, label %if.else.else
265
266if.else.then:
267  %gep2 = getelementptr inbounds i32, i32* %ptr, i32 2
268  br label %if.end
269
270if.else.else:
271  %gep3 = getelementptr inbounds i32, i32* %ptr, i32 3
272  br label %if.end
273
274if.end:
275  %phi = phi i32* [ %gep1, %if.then ], [ %gep2, %if.else.then ], [ %gep3, %if.else.else ]
276  store i32 %value1, i32* %phi, align 4
277  ret void
278}
279
280; A phi with two incoming values but three geps due to nesting should be
281; optimised
282; CHECK-LABEL: @test_phi_threegep_nested
283; CHECK: %[[PHI:[a-z0-9_]+]] = phi i32 [ 12, %if.else.else ], [ 8, %if.else.then ]
284; CHECK: phi i32 [ %[[PHI]], %if.else.end ], [ 4, %if.then ]
285define void @test_phi_threegep_nested(i32* %ptr, i32 %value1, i32 %value2) {
286entry:
287  %cmp1 = icmp sgt i32 %value1, 0
288  br i1 %cmp1, label %if.then, label %if.else
289
290if.then:
291  %gep1 = getelementptr inbounds i32, i32* %ptr, i32 1
292  br label %if.end
293
294if.else:
295  %cmp2 = icmp sgt i32 %value2, 0
296  br i1 %cmp2, label %if.else.then, label %if.else.else
297
298if.else.then:
299  %gep2 = getelementptr inbounds i32, i32* %ptr, i32 2
300  br label %if.else.end
301
302if.else.else:
303  %gep3 = getelementptr inbounds i32, i32* %ptr, i32 3
304  br label %if.else.end
305
306if.else.end:
307  %gep4 = phi i32* [ %gep2, %if.else.then ], [ %gep3, %if.else.else ]
308  store i32 %value2, i32* %ptr, align 4
309  br label %if.end
310
311if.end:
312  %phi = phi i32* [ %gep1, %if.then ], [ %gep4, %if.else.end ]
313  store i32 %value1, i32* %phi, align 4
314  ret void
315}
316
317; A nested select is expected to be optimised
318; CHECK-LABEL: @test_nested_select
319; CHECK: %[[SELECT:[a-z0-9_]+]] = select i1 %cmp2, i32 4, i32 8
320; CHECK: select i1 %cmp1, i32 4, i32 %[[SELECT]]
321define void @test_nested_select(i32* %ptr, i32 %value1, i32 %value2) {
322entry:
323  %gep1 = getelementptr inbounds i32, i32* %ptr, i32 1
324  %gep2 = getelementptr inbounds i32, i32* %ptr, i32 2
325  %cmp1 = icmp sgt i32 %value1, 0
326  %cmp2 = icmp sgt i32 %value2, 0
327  %select1 = select i1 %cmp2, i32* %gep1, i32* %gep2
328  %select2 = select i1 %cmp1, i32* %gep1, i32* %select1
329  store i32 %value1, i32* %select2, align 4
330  ret void
331}
332
333; Scaling the offset by a different amount is expected not to be optimised
334; CHECK-LABEL: @test_select_different_scale
335; CHECK: select i1 %cmp, i32* %gep1, i32* %castgep
336define void @test_select_different_scale(i32* %ptr, i32 %value, i32 %off) {
337entry:
338  %cmp = icmp sgt i32 %value, 0
339  %castptr = bitcast i32* %ptr to i16*
340  %gep1 = getelementptr inbounds i32, i32* %ptr, i32 %off
341  %gep2 = getelementptr inbounds i16, i16* %castptr, i32 %off
342  %castgep = bitcast i16* %gep2 to i32*
343  %select = select i1 %cmp, i32* %gep1, i32* %castgep
344  store i32 %value, i32* %select, align 4
345  ret void
346}
347
348; A select between two values is already the best we can do
349; CHECK-LABEL: @test_select_trivial
350; CHECK: select i1 %cmp, i32* %ptr1, i32* %ptr2
351define void @test_select_trivial(i32* %ptr1, i32* %ptr2, i32 %value) {
352entey:
353  %cmp = icmp sgt i32 %value, 0
354  %select = select i1 %cmp, i32* %ptr1, i32* %ptr2
355  store i32 %value, i32* %select, align 4
356  ret void
357}
358
359; A select between two global variables is already the best we can do
360; CHECK-LABEL: @test_select_trivial_gv
361; CHECK: select i1 %cmp, i32* @gv1, i32* @gv2
362define void @test_select_trivial_gv(i32 %value) {
363entey:
364  %cmp = icmp sgt i32 %value, 0
365  %select = select i1 %cmp, i32* @gv1, i32* @gv2
366  store i32 %value, i32* %select, align 4
367  ret void
368}
369
370; Same for a select between a value and global variable
371; CHECK-LABEL: @test_select_trivial_ptr_gv
372; CHECK: select i1 %cmp, i32* %ptr, i32* @gv2
373define void @test_select_trivial_ptr_gv(i32* %ptr, i32 %value) {
374entry:
375  %cmp = icmp sgt i32 %value, 0
376  %select = select i1 %cmp, i32* %ptr, i32* @gv2
377  store i32 %value, i32* %select, align 4
378  ret void
379}
380
381; Same for a select between a global variable and null, though the test needs to
382; be a little more complicated to avoid dereferencing a potential null pointer
383; CHECK-LABEL: @test_select_trivial_gv_null
384; CHECK: select i1 %cmp.i, i32* @gv1, i32* null
385define void @test_select_trivial_gv_null(){
386entry:
387  %gv1_val = load i32, i32* @gv1, align 4
388  %cmp.i = icmp eq i32 %gv1_val, 0
389  %spec.select.i = select i1 %cmp.i, i32* @gv1, i32* null
390  br i1 %cmp.i, label %if.then, label %if.end
391
392if.then:
393  %val = load i32, i32* %spec.select.i, align 4
394  %inc = add nsw i32 %val, 1
395  store i32 %inc, i32* %spec.select.i, align 4
396  br label %if.end
397
398if.end:
399  ret void
400}
401
402; Same for a select between a value and null
403; CHECK-LABEL: @test_select_trivial_ptr_null
404; CHECK: select i1 %cmp.i, i32* %ptr, i32* null
405define void @test_select_trivial_ptr_null(i32* %ptr){
406entry:
407  %gv1_val = load i32, i32* %ptr, align 4
408  %cmp.i = icmp eq i32 %gv1_val, 0
409  %spec.select.i = select i1 %cmp.i, i32* %ptr, i32* null
410  br i1 %cmp.i, label %if.then, label %if.end
411
412if.then:
413  %val = load i32, i32* %spec.select.i, align 4
414  %inc = add nsw i32 %val, 1
415  store i32 %inc, i32* %spec.select.i, align 4
416  br label %if.end
417
418if.end:
419  ret void
420}
421