xref: /llvm-project/llvm/test/CodeGen/X86/tailcall-extract.ll (revision 5b4759f9fd1419abc69e656c40f04a0fd9483d2a)
1; RUN: llc -mtriple=x86_64-linux < %s | FileCheck %s
2; RUN: opt -passes='require<profile-summary>,function(codegenprepare)' -S -mtriple=x86_64-linux < %s | FileCheck %s --check-prefix OPT
3
4
5; The exit block containing extractvalue can be duplicated into the BB
6; containing call. And later tail call can be generated.
7
8; CHECK-LABEL: test1:
9; CHECK:       je foo # TAILCALL
10; CHECK:       jmp bar # TAILCALL
11
12; OPT-LABEL:   test1
13; OPT:         if.then.i:
14; OPT-NEXT:    tail call { ptr, i64 } @bar
15; OPT-NEXT:    extractvalue
16; OPT-NEXT:    ret
17;
18; OPT:         if.end.i:
19; OPT-NEXT:    tail call { ptr, i64 } @foo
20; OPT-NEXT:    extractvalue
21; OPT-NEXT:    ret
22
23define ptr @test1(i64 %size) {
24entry:
25  %cmp.i.i = icmp ugt i64 %size, 16384
26  %add.i.i = add i64 %size, 7
27  %div.i.i = lshr i64 %add.i.i, 3
28  %phitmp.i.i = trunc i64 %div.i.i to i32
29  %cmp1.i = icmp eq i32 %phitmp.i.i, 0
30  %cmp.i = or i1 %cmp.i.i, %cmp1.i
31  br i1 %cmp.i, label %if.end.i, label %if.then.i
32  if.then.i:                                        ; preds = %entry
33  %call1.i = tail call { ptr, i64 } @bar(i64 %size)
34  br label %exit
35
36if.end.i:                                         ; preds = %entry
37  %call2.i = tail call { ptr, i64 } @foo(i64 %size)
38  br label %exit
39
40exit:
41  %call1.i.sink = phi { ptr, i64 } [ %call1.i, %if.then.i ], [ %call2.i, %if.end.i ]
42  %ev = extractvalue { ptr, i64 } %call1.i.sink, 0
43  ret ptr %ev
44}
45
46
47; The extractvalue extracts a field with non-zero offset, so the exit block
48; can't be duplicated.
49
50; CHECK-LABEL: test2:
51; CHECK:       callq bar
52; CHECK:       callq foo
53
54; OPT-LABEL:   test2
55; OPT:         if.then.i:
56; OPT-NEXT:    tail call { ptr, i64 } @bar
57; OPT-NEXT:    br label %exit
58;
59; OPT:         if.end.i:
60; OPT-NEXT:    tail call { ptr, i64 } @foo
61; OPT-NEXT:    br label %exit
62;
63; OPT:         exit:
64; OPT-NEXT:    phi
65; OPT-NEXT:    extractvalue
66; OPT-NEXT:    ret
67
68define i64 @test2(i64 %size) {
69entry:
70  %cmp.i.i = icmp ugt i64 %size, 16384
71  %add.i.i = add i64 %size, 7
72  %div.i.i = lshr i64 %add.i.i, 3
73  %phitmp.i.i = trunc i64 %div.i.i to i32
74  %cmp1.i = icmp eq i32 %phitmp.i.i, 0
75  %cmp.i = or i1 %cmp.i.i, %cmp1.i
76  br i1 %cmp.i, label %if.end.i, label %if.then.i
77  if.then.i:                                        ; preds = %entry
78  %call1.i = tail call { ptr, i64 } @bar(i64 %size)
79  br label %exit
80
81if.end.i:                                         ; preds = %entry
82  %call2.i = tail call { ptr, i64 } @foo(i64 %size)
83  br label %exit
84
85exit:
86  %call1.i.sink = phi { ptr, i64 } [ %call1.i, %if.then.i ], [ %call2.i, %if.end.i ]
87  %ev = extractvalue { ptr, i64 } %call1.i.sink, 1
88  ret i64 %ev
89}
90
91
92; The extractvalue accesses a nest struct type, the extracted field has zero
93; offset, so the exit block can still be duplicated, and tail call generated.
94
95; CHECK-LABEL: test3:
96; CHECK:       je qux # TAILCALL
97; CHECK:       jmp baz # TAILCALL
98
99; OPT-LABEL:   test3
100; OPT:         if.then.i:
101; OPT-NEXT:    tail call { { ptr, i64 }, i64 } @baz
102; OPT-NEXT:    extractvalue
103; OPT-NEXT:    ret
104;
105; OPT:         if.end.i:
106; OPT-NEXT:    tail call { { ptr, i64 }, i64 } @qux
107; OPT-NEXT:    extractvalue
108; OPT-NEXT:    ret
109
110define ptr @test3(i64 %size) {
111entry:
112  %cmp.i.i = icmp ugt i64 %size, 16384
113  %add.i.i = add i64 %size, 7
114  %div.i.i = lshr i64 %add.i.i, 3
115  %phitmp.i.i = trunc i64 %div.i.i to i32
116  %cmp1.i = icmp eq i32 %phitmp.i.i, 0
117  %cmp.i = or i1 %cmp.i.i, %cmp1.i
118  br i1 %cmp.i, label %if.end.i, label %if.then.i
119
120if.then.i:                                        ; preds = %entry
121  %call1.i = tail call { {ptr, i64}, i64 } @baz(i64 %size)
122  br label %exit
123
124if.end.i:                                         ; preds = %entry
125  %call2.i = tail call { {ptr, i64}, i64 } @qux(i64 %size)
126  br label %exit
127
128exit:
129  %call1.i.sink = phi { {ptr, i64}, i64 } [ %call1.i, %if.then.i ], [ %call2.i, %if.end.i ]
130  %ev = extractvalue { {ptr, i64}, i64 } %call1.i.sink, 0, 0
131  ret ptr %ev
132}
133
134
135; The extractvalue accesses a nest struct with non-zero offset, so the exit
136; block can't be duplicated.
137
138; CHECK-LABEL: test4:
139; CHECK:       callq baz
140; CHECK:       callq qux
141
142; OPT-LABEL:   test4
143; OPT:         if.then.i:
144; OPT-NEXT:    tail call { { ptr, i64 }, i64 } @baz
145; OPT-NEXT:    br label %exit
146;
147; OPT:         if.end.i:
148; OPT-NEXT:    tail call { { ptr, i64 }, i64 } @qux
149; OPT-NEXT:    br label %exit
150;
151; OPT:         exit:
152; OPT-NEXT:    phi
153; OPT-NEXT:    extractvalue
154; OPT-NEXT:    ret
155
156define i64 @test4(i64 %size) {
157entry:
158  %cmp.i.i = icmp ugt i64 %size, 16384
159  %add.i.i = add i64 %size, 7
160  %div.i.i = lshr i64 %add.i.i, 3
161  %phitmp.i.i = trunc i64 %div.i.i to i32
162  %cmp1.i = icmp eq i32 %phitmp.i.i, 0
163  %cmp.i = or i1 %cmp.i.i, %cmp1.i
164  br i1 %cmp.i, label %if.end.i, label %if.then.i
165
166if.then.i:                                        ; preds = %entry
167  %call1.i = tail call { {ptr, i64}, i64 } @baz(i64 %size)
168  br label %exit
169
170if.end.i:                                         ; preds = %entry
171  %call2.i = tail call { {ptr, i64}, i64 } @qux(i64 %size)
172  br label %exit
173
174exit:
175  %call1.i.sink = phi { {ptr, i64}, i64 } [ %call1.i, %if.then.i ], [ %call2.i, %if.end.i ]
176  %ev = extractvalue { {ptr, i64}, i64 } %call1.i.sink, 0, 1
177  ret i64 %ev
178}
179
180
181declare dso_local { ptr, i64 } @foo(i64)
182declare dso_local { ptr, i64 } @bar(i64)
183declare dso_local { {ptr, i64}, i64 } @baz(i64)
184declare dso_local { {ptr, i64}, i64 } @qux(i64)
185