xref: /llvm-project/llvm/test/CodeGen/X86/tailcall-cgp-dup.ll (revision e6bf48d11047e970cb24554a01b65b566d6b5d22)
1; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
2; RUN: llc < %s -mtriple=x86_64-apple-darwin | FileCheck %s
3; RUN: opt -S -passes='require<profile-summary>,function(codegenprepare)' %s -mtriple=x86_64-apple-darwin -o - | FileCheck %s --check-prefix OPT
4
5; Teach CGP to dup returns to enable tail call optimization.
6; rdar://9147433
7
8define i32 @foo(i32 %x) nounwind ssp {
9; CHECK-LABEL: foo:
10; CHECK:       ## %bb.0: ## %entry
11; CHECK-NEXT:    ## kill: def $edi killed $edi def $rdi
12; CHECK-NEXT:    decl %edi
13; CHECK-NEXT:    cmpl $5, %edi
14; CHECK-NEXT:    ja LBB0_8
15; CHECK-NEXT:  ## %bb.1: ## %entry
16; CHECK-NEXT:    leaq LJTI0_0(%rip), %rax
17; CHECK-NEXT:    movslq (%rax,%rdi,4), %rcx
18; CHECK-NEXT:    addq %rax, %rcx
19; CHECK-NEXT:    jmpq *%rcx
20; CHECK-NEXT:  LBB0_2: ## %sw.bb
21; CHECK-NEXT:    jmp _f1 ## TAILCALL
22; CHECK-NEXT:  LBB0_6: ## %sw.bb7
23; CHECK-NEXT:    jmp _f5 ## TAILCALL
24; CHECK-NEXT:  LBB0_4: ## %sw.bb3
25; CHECK-NEXT:    jmp _f3 ## TAILCALL
26; CHECK-NEXT:  LBB0_5: ## %sw.bb5
27; CHECK-NEXT:    jmp _f4 ## TAILCALL
28; CHECK-NEXT:  LBB0_3: ## %sw.bb1
29; CHECK-NEXT:    jmp _f2 ## TAILCALL
30; CHECK-NEXT:  LBB0_7: ## %sw.bb9
31; CHECK-NEXT:    jmp _f6 ## TAILCALL
32; CHECK-NEXT:  LBB0_8: ## %return
33; CHECK-NEXT:    xorl %eax, %eax
34; CHECK-NEXT:    retq
35; CHECK-NEXT:    .p2align 2
36; CHECK-NEXT:    .data_region jt32
37; CHECK-NEXT:  .set L0_0_set_2, LBB0_2-LJTI0_0
38; CHECK-NEXT:  .set L0_0_set_3, LBB0_3-LJTI0_0
39; CHECK-NEXT:  .set L0_0_set_4, LBB0_4-LJTI0_0
40; CHECK-NEXT:  .set L0_0_set_5, LBB0_5-LJTI0_0
41; CHECK-NEXT:  .set L0_0_set_6, LBB0_6-LJTI0_0
42; CHECK-NEXT:  .set L0_0_set_7, LBB0_7-LJTI0_0
43; CHECK-NEXT:  LJTI0_0:
44; CHECK-NEXT:    .long L0_0_set_2
45; CHECK-NEXT:    .long L0_0_set_3
46; CHECK-NEXT:    .long L0_0_set_4
47; CHECK-NEXT:    .long L0_0_set_5
48; CHECK-NEXT:    .long L0_0_set_6
49; CHECK-NEXT:    .long L0_0_set_7
50; CHECK-NEXT:    .end_data_region
51entry:
52  switch i32 %x, label %return [
53    i32 1, label %sw.bb
54    i32 2, label %sw.bb1
55    i32 3, label %sw.bb3
56    i32 4, label %sw.bb5
57    i32 5, label %sw.bb7
58    i32 6, label %sw.bb9
59  ]
60
61sw.bb:                                            ; preds = %entry
62  %call = tail call i32 @f1() nounwind
63  br label %return
64
65sw.bb1:                                           ; preds = %entry
66  %call2 = tail call i32 @f2() nounwind
67  br label %return
68
69sw.bb3:                                           ; preds = %entry
70  %call4 = tail call i32 @f3() nounwind
71  br label %return
72
73sw.bb5:                                           ; preds = %entry
74  %call6 = tail call i32 @f4() nounwind
75  br label %return
76
77sw.bb7:                                           ; preds = %entry
78  %call8 = tail call i32 @f5() nounwind
79  br label %return
80
81sw.bb9:                                           ; preds = %entry
82  %call10 = tail call i32 @f6() nounwind
83  br label %return
84
85return:                                           ; preds = %entry, %sw.bb9, %sw.bb7, %sw.bb5, %sw.bb3, %sw.bb1, %sw.bb
86  %retval.0 = phi i32 [ %call10, %sw.bb9 ], [ %call8, %sw.bb7 ], [ %call6, %sw.bb5 ], [ %call4, %sw.bb3 ], [ %call2, %sw.bb1 ], [ %call, %sw.bb ], [ 0, %entry ]
87  ret i32 %retval.0
88}
89
90declare i32 @f1()
91
92declare i32 @f2()
93
94declare i32 @f3()
95
96declare i32 @f4()
97
98declare i32 @f5()
99
100declare i32 @f6()
101
102; rdar://11958338
103%0 = type opaque
104
105declare ptr @bar(ptr) uwtable optsize noinline ssp
106
107define hidden ptr @thingWithValue(ptr %self) uwtable ssp {
108; CHECK-LABEL: thingWithValue:
109; CHECK:       ## %bb.0: ## %entry
110; CHECK-NEXT:    xorl %eax, %eax
111; CHECK-NEXT:    testb %al, %al
112; CHECK-NEXT:    je _bar ## TAILCALL
113; CHECK-NEXT:  ## %bb.1: ## %someThingWithValue.exit
114; CHECK-NEXT:    retq
115entry:
116  br i1 undef, label %if.then.i, label %if.else.i
117
118if.then.i:                                        ; preds = %entry
119  br label %someThingWithValue.exit
120
121if.else.i:                                        ; preds = %entry
122  %call4.i = tail call ptr @bar(ptr undef) optsize
123  br label %someThingWithValue.exit
124
125someThingWithValue.exit:                          ; preds = %if.else.i, %if.then.i
126  %retval.0.in.i = phi ptr [ undef, %if.then.i ], [ %call4.i, %if.else.i ]
127  ret ptr %retval.0.in.i
128}
129
130
131; Correctly handle zext returns.
132declare zeroext i1 @foo_i1()
133
134define zeroext i1 @zext_i1(i1 %k) {
135; CHECK-LABEL: zext_i1:
136; CHECK:       ## %bb.0: ## %entry
137; CHECK-NEXT:    testb $1, %dil
138; CHECK-NEXT:    je _foo_i1 ## TAILCALL
139; CHECK-NEXT:  ## %bb.1: ## %land.end
140; CHECK-NEXT:    xorl %eax, %eax
141; CHECK-NEXT:    retq
142entry:
143  br i1 %k, label %land.end, label %land.rhs
144
145land.rhs:                                         ; preds = %entry
146  %call1 = tail call zeroext i1 @foo_i1()
147  br label %land.end
148
149land.end:                                         ; preds = %entry, %land.rhs
150  %0 = phi i1 [ false, %entry ], [ %call1, %land.rhs ]
151  ret i1 %0
152}
153
154; We need to look through bitcasts when looking for tail calls in phi incoming
155; values.
156declare ptr @g_ret32()
157define ptr @f_ret8(ptr %obj) nounwind {
158; OPT-LABEL: @f_ret8(
159; OPT-NEXT:  entry:
160; OPT-NEXT:    [[CMP:%.*]] = icmp eq ptr [[OBJ:%.*]], null
161; OPT-NEXT:    br i1 [[CMP]], label [[RETURN:%.*]], label [[IF_THEN:%.*]]
162; OPT:       if.then:
163; OPT-NEXT:    [[PTR:%.*]] = tail call ptr @g_ret32()
164; OPT-NEXT:    ret ptr [[PTR]]
165; OPT:       return:
166; OPT-NEXT:    ret ptr [[OBJ]]
167;
168; CHECK-LABEL: f_ret8:
169; CHECK:       ## %bb.0: ## %entry
170; CHECK-NEXT:    testq %rdi, %rdi
171; CHECK-NEXT:    jne _g_ret32 ## TAILCALL
172; CHECK-NEXT:  ## %bb.1: ## %return
173; CHECK-NEXT:    movq %rdi, %rax
174; CHECK-NEXT:    retq
175entry:
176  %cmp = icmp eq ptr %obj, null
177  br i1 %cmp, label %return, label %if.then
178
179if.then:
180  %ptr = tail call ptr @g_ret32()
181  br label %return
182
183return:
184  %retval = phi ptr [ %ptr, %if.then ], [ %obj, %entry ]
185  ret ptr %retval
186}
187
188define ptr @memset_tailc(ptr %ret_val, i64 %sz) nounwind {
189; CHECK-LABEL: memset_tailc:
190; CHECK:       ## %bb.0: ## %entry
191; CHECK-NEXT:    testq %rdi, %rdi
192; CHECK-NEXT:    je LBB4_1
193; CHECK-NEXT:  ## %bb.2: ## %if.then
194; CHECK-NEXT:    movq %rsi, %rdx
195; CHECK-NEXT:    xorl %esi, %esi
196; CHECK-NEXT:    jmp _memset ## TAILCALL
197; CHECK-NEXT:  LBB4_1: ## %return
198; CHECK-NEXT:    movq %rdi, %rax
199; CHECK-NEXT:    retq
200entry:
201  %cmp = icmp eq ptr %ret_val, null
202  br i1 %cmp, label %return, label %if.then
203
204if.then:
205  tail call void @llvm.memset.p0.i64(ptr nonnull align 1 %ret_val, i8 0, i64 %sz, i1 false)
206  br label %return
207
208return:
209  ret ptr %ret_val
210}
211
212define ptr @memcpy_tailc(ptr %ret_val, i64 %sz, ptr %src) nounwind {
213; CHECK-LABEL: memcpy_tailc:
214; CHECK:       ## %bb.0: ## %entry
215; CHECK-NEXT:    testq %rsi, %rsi
216; CHECK-NEXT:    je LBB5_1
217; CHECK-NEXT:  ## %bb.2: ## %if.then
218; CHECK-NEXT:    movq %rsi, %rax
219; CHECK-NEXT:    movq %rdx, %rsi
220; CHECK-NEXT:    movq %rax, %rdx
221; CHECK-NEXT:    jmp _memcpy ## TAILCALL
222; CHECK-NEXT:  LBB5_1: ## %return
223; CHECK-NEXT:    movq %rdx, %rax
224; CHECK-NEXT:    retq
225entry:
226  %cmp = icmp eq i64 %sz, 0
227  br i1 %cmp, label %return, label %if.then
228
229if.then:
230  tail call void @llvm.memcpy.p0.p0.i64(ptr align 1 %ret_val, ptr align 1 %src, i64 %sz, i1 false)
231  br label %return
232
233return:
234  %phi = phi ptr [ %ret_val, %if.then ], [ %src, %entry ]
235  ret ptr %phi
236}
237
238define ptr @strcpy_legal_and_baz_illegal(ptr %arg, i64 %sz, ptr %2) nounwind {
239; CHECK-LABEL: strcpy_legal_and_baz_illegal:
240; CHECK:       ## %bb.0: ## %entry
241; CHECK-NEXT:    pushq %r15
242; CHECK-NEXT:    pushq %r14
243; CHECK-NEXT:    pushq %rbx
244; CHECK-NEXT:    movq %rdx, %rbx
245; CHECK-NEXT:    movq %rsi, %r15
246; CHECK-NEXT:    movq %rdi, %r14
247; CHECK-NEXT:    movq %rsi, %rdi
248; CHECK-NEXT:    callq _malloc
249; CHECK-NEXT:    testq %r15, %r15
250; CHECK-NEXT:    je LBB6_1
251; CHECK-NEXT:  ## %bb.2: ## %if.then
252; CHECK-NEXT:    movq %rax, %rdi
253; CHECK-NEXT:    movq %rbx, %rsi
254; CHECK-NEXT:    popq %rbx
255; CHECK-NEXT:    popq %r14
256; CHECK-NEXT:    popq %r15
257; CHECK-NEXT:    jmp _strcpy ## TAILCALL
258; CHECK-NEXT:  LBB6_1: ## %if.else
259; CHECK-NEXT:    movq %r14, %rdi
260; CHECK-NEXT:    movq %rbx, %rsi
261; CHECK-NEXT:    callq _baz
262; CHECK-NEXT:    movq %r14, %rax
263; CHECK-NEXT:    popq %rbx
264; CHECK-NEXT:    popq %r14
265; CHECK-NEXT:    popq %r15
266; CHECK-NEXT:    retq
267entry:
268  %strcpy_ret_val = tail call noalias ptr @malloc(i64 %sz)
269  %cmp = icmp eq i64 %sz, 0
270  br i1 %cmp, label %if.else, label %if.then
271
272if.then:
273  %rv_unused = tail call ptr @strcpy(ptr dereferenceable(1) %strcpy_ret_val, ptr dereferenceable(1) %2)
274  br label %return
275
276if.else:
277  %rv_unused_2 = tail call ptr @baz(ptr %arg, ptr %2)
278  br label %return
279
280return:
281  %phi = phi ptr [ %strcpy_ret_val, %if.then ], [ %arg, %if.else ]
282  ret ptr %phi
283}
284
285define ptr @baz_illegal_tailc(ptr %ret_val, ptr %arg) nounwind {
286; CHECK-LABEL: baz_illegal_tailc:
287; CHECK:       ## %bb.0: ## %entry
288; CHECK-NEXT:    pushq %rbx
289; CHECK-NEXT:    movq %rdi, %rbx
290; CHECK-NEXT:    testq %rdi, %rdi
291; CHECK-NEXT:    je LBB7_2
292; CHECK-NEXT:  ## %bb.1: ## %if.then
293; CHECK-NEXT:    movq %rbx, %rdi
294; CHECK-NEXT:    callq _baz
295; CHECK-NEXT:  LBB7_2: ## %return
296; CHECK-NEXT:    movq %rbx, %rax
297; CHECK-NEXT:    popq %rbx
298; CHECK-NEXT:    retq
299entry:
300  %cmp = icmp eq ptr %ret_val, null
301  br i1 %cmp, label %return, label %if.then
302
303if.then:
304  %rv = tail call ptr @baz(ptr %ret_val, ptr %arg)
305  br label %return
306
307return:
308  ret ptr %ret_val
309}
310
311define ptr @memset_illegal_tailc(ptr %arg, i64 %sz, ptr %ret_val_1, ptr %ret_val_2) nounwind {
312; CHECK-LABEL: memset_illegal_tailc:
313; CHECK:       ## %bb.0: ## %entry
314; CHECK-NEXT:    movq %rdx, %rax
315; CHECK-NEXT:    testq %rsi, %rsi
316; CHECK-NEXT:    je LBB8_2
317; CHECK-NEXT:  ## %bb.1: ## %if.then
318; CHECK-NEXT:    pushq %rbx
319; CHECK-NEXT:    movq %rcx, %rbx
320; CHECK-NEXT:    movq %rsi, %rdx
321; CHECK-NEXT:    xorl %esi, %esi
322; CHECK-NEXT:    callq _memset
323; CHECK-NEXT:    movq %rbx, %rax
324; CHECK-NEXT:    popq %rbx
325; CHECK-NEXT:  LBB8_2: ## %return
326; CHECK-NEXT:    retq
327entry:
328  %cmp = icmp eq i64 %sz, 0
329  br i1 %cmp, label %return, label %if.then
330
331if.then:
332  tail call void @llvm.memset.p0.i64(ptr align 1 %arg, i8 0, i64 %sz, i1 false)
333  br label %return
334
335return:
336  %phi = phi ptr [ %ret_val_2, %if.then ], [ %ret_val_1, %entry ]
337  ret ptr %phi
338}
339
340define ptr @strcpy_illegal_tailc(ptr %dest, i64 %sz, ptr readonly returned %src) nounwind {
341; CHECK-LABEL: strcpy_illegal_tailc:
342; CHECK:       ## %bb.0: ## %entry
343; CHECK-NEXT:    pushq %rbx
344; CHECK-NEXT:    movq %rdx, %rbx
345; CHECK-NEXT:    testq %rsi, %rsi
346; CHECK-NEXT:    je LBB9_2
347; CHECK-NEXT:  ## %bb.1: ## %if.then
348; CHECK-NEXT:    movq %rbx, %rsi
349; CHECK-NEXT:    callq _strcpy
350; CHECK-NEXT:  LBB9_2: ## %return
351; CHECK-NEXT:    movq %rbx, %rax
352; CHECK-NEXT:    popq %rbx
353; CHECK-NEXT:    retq
354entry:
355  %cmp = icmp eq i64 %sz, 0
356  br i1 %cmp, label %return, label %if.then
357
358if.then:
359  %6 = tail call ptr @strcpy(ptr dereferenceable(1) %dest, ptr dereferenceable(1) %src)
360  br label %return
361
362return:
363  ret ptr %src
364}
365
366@i = global i32 0, align 4
367
368define i32 @undef_tailc() nounwind {
369; CHECK-LABEL: undef_tailc:
370; CHECK:       ## %bb.0: ## %entry
371; CHECK-NEXT:    cmpl $0, _i(%rip)
372; CHECK-NEXT:    jne _qux ## TAILCALL
373; CHECK-NEXT:  ## %bb.1: ## %return
374; CHECK-NEXT:    retq
375entry:
376  %val = load i32, ptr @i, align 4
377  %cmp = icmp eq i32 %val, 0
378  br i1 %cmp, label %return, label %if.then
379
380if.then:
381  %rv_unused = tail call i32 @qux()
382  br label %return
383
384return:
385  ret i32 undef
386}
387
388define i32 @undef_and_known_tailc() nounwind {
389; CHECK-LABEL: undef_and_known_tailc:
390; CHECK:       ## %bb.0: ## %entry
391; CHECK-NEXT:    movl _i(%rip), %eax
392; CHECK-NEXT:    cmpl $5, %eax
393; CHECK-NEXT:    je _qux ## TAILCALL
394; CHECK-NEXT:  ## %bb.1: ## %entry
395; CHECK-NEXT:    cmpl $2, %eax
396; CHECK-NEXT:    je _quux ## TAILCALL
397; CHECK-NEXT:  ## %bb.2: ## %return
398; CHECK-NEXT:    retq
399entry:
400  %val = load i32, ptr @i, align 4
401  switch i32 %val, label %return [
402    i32 2, label %case_2
403    i32 5, label %case_5
404  ]
405
406case_2:
407  %rv_unused = tail call i32 @quux()
408  br label %return
409
410case_5:
411  %rv = tail call i32 @qux()
412  br label %return
413
414return:
415  %phi = phi i32 [ undef, %case_2 ], [ %rv, %case_5 ], [ undef, %entry ]
416  ret i32 %phi
417}
418
419declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1)
420declare void @llvm.memset.p0.i64(ptr nocapture writeonly, i8, i64, i1)
421declare noalias ptr @malloc(i64)
422declare ptr @strcpy(ptr noalias returned writeonly, ptr noalias nocapture readonly)
423declare ptr @baz(ptr, ptr)
424declare i32 @qux()
425declare i32 @quux()
426