xref: /llvm-project/llvm/test/CodeGen/X86/callbr-asm-outputs-indirect-isel.ll (revision d9221da72baee50b24b14b0dc0200a6ddec49ea0)
1; NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
2; FIXME(ndesaulniers): get this test to pass with -verify-machineinstrs
3; enabled. https://github.com/llvm/llvm-project/issues/60827
4; RUN: llc -mtriple=x86_64-linux-gnu %s -o - -stop-after=finalize-isel \
5; RUN:   -verify-machineinstrs=0 -start-before=x86-isel | FileCheck %s
6
7; One virtual register, w/o phi
8define i32 @test0() {
9  ; CHECK-LABEL: name: test0
10  ; CHECK: bb.0 (%ir-block.0):
11  ; CHECK-NEXT:   successors: %bb.1(0x80000000), %bb.2(0x00000000)
12  ; CHECK-NEXT: {{  $}}
13  ; CHECK-NEXT:   INLINEASM_BR &"", 0 /* attdialect */, 2686986 /* regdef:GR32_NOREX2 */, def %1, 13 /* imm */, %bb.2
14  ; CHECK-NEXT:   [[COPY:%[0-9]+]]:gr32 = COPY %1
15  ; CHECK-NEXT:   JMP_1 %bb.1
16  ; CHECK-NEXT: {{  $}}
17  ; CHECK-NEXT: bb.1.cleanup:
18  ; CHECK-NEXT:   [[MOV32ri:%[0-9]+]]:gr32 = MOV32ri 42
19  ; CHECK-NEXT:   $eax = COPY [[MOV32ri]]
20  ; CHECK-NEXT:   RET 0, $eax
21  ; CHECK-NEXT: {{  $}}
22  ; CHECK-NEXT: bb.2.z.split (machine-block-address-taken, inlineasm-br-indirect-target):
23  ; CHECK-NEXT:   $eax = COPY %1
24  ; CHECK-NEXT:   RET 0, $eax
25  %direct = callbr i32 asm "", "=r,!i"()
26          to label %cleanup [label %z.split]
27
28cleanup:
29  ret i32 42
30z.split:
31  %indirect = call i32 @llvm.callbr.landingpad.i32(i32 %direct)
32  ret i32 %indirect
33}
34
35; One virtual register, w/ phi
36define i32 @test1() {
37  ; CHECK-LABEL: name: test1
38  ; CHECK: bb.0.entry:
39  ; CHECK-NEXT:   successors: %bb.2(0x80000000), %bb.1(0x00000000)
40  ; CHECK-NEXT: {{  $}}
41  ; CHECK-NEXT:   [[MOV32ri:%[0-9]+]]:gr32 = MOV32ri 42
42  ; CHECK-NEXT:   INLINEASM_BR &"", 0 /* attdialect */, 2686986 /* regdef:GR32_NOREX2 */, def %4, 13 /* imm */, %bb.1
43  ; CHECK-NEXT:   [[COPY:%[0-9]+]]:gr32 = COPY %4
44  ; CHECK-NEXT:   JMP_1 %bb.2
45  ; CHECK-NEXT: {{  $}}
46  ; CHECK-NEXT: bb.1.z.split (machine-block-address-taken, inlineasm-br-indirect-target):
47  ; CHECK-NEXT:   successors: %bb.2(0x80000000)
48  ; CHECK-NEXT: {{  $}}
49  ; CHECK-NEXT:   [[COPY1:%[0-9]+]]:gr32 = COPY %4
50  ; CHECK-NEXT: {{  $}}
51  ; CHECK-NEXT: bb.2.cleanup:
52  ; CHECK-NEXT:   [[PHI:%[0-9]+]]:gr32 = PHI [[MOV32ri]], %bb.0, [[COPY1]], %bb.1
53  ; CHECK-NEXT:   $eax = COPY [[PHI]]
54  ; CHECK-NEXT:   RET 0, $eax
55entry:
56  %direct = callbr i32 asm "", "=r,!i"()
57          to label %cleanup [label %z.split]
58
59z.split:
60  %indirect = call i32 @llvm.callbr.landingpad.i32(i32 %direct)
61  br label %cleanup
62
63cleanup:
64  %retval.0 = phi i32 [ %indirect, %z.split ], [ 42, %entry ]
65  ret i32 %retval.0
66}
67
68; Two virtual registers
69define i32 @test2() {
70  ; CHECK-LABEL: name: test2
71  ; CHECK: bb.0.entry:
72  ; CHECK-NEXT:   successors: %bb.2(0x80000000), %bb.1(0x00000000)
73  ; CHECK-NEXT: {{  $}}
74  ; CHECK-NEXT:   [[MOV32ri:%[0-9]+]]:gr32 = MOV32ri 42
75  ; CHECK-NEXT:   INLINEASM_BR &"", 0 /* attdialect */, 2686986 /* regdef:GR32_NOREX2 */, def %5, 2686986 /* regdef:GR32_NOREX2 */, def %6, 13 /* imm */, %bb.1
76  ; CHECK-NEXT:   [[COPY:%[0-9]+]]:gr32 = COPY %6
77  ; CHECK-NEXT:   [[COPY1:%[0-9]+]]:gr32 = COPY %5
78  ; CHECK-NEXT:   JMP_1 %bb.2
79  ; CHECK-NEXT: {{  $}}
80  ; CHECK-NEXT: bb.1.z.split (machine-block-address-taken, inlineasm-br-indirect-target):
81  ; CHECK-NEXT:   successors: %bb.2(0x80000000)
82  ; CHECK-NEXT: {{  $}}
83  ; CHECK-NEXT:   [[COPY2:%[0-9]+]]:gr32 = COPY %5
84  ; CHECK-NEXT: {{  $}}
85  ; CHECK-NEXT: bb.2.cleanup:
86  ; CHECK-NEXT:   [[PHI:%[0-9]+]]:gr32 = PHI [[MOV32ri]], %bb.0, [[COPY2]], %bb.1
87  ; CHECK-NEXT:   $eax = COPY [[PHI]]
88  ; CHECK-NEXT:   RET 0, $eax
89entry:
90  %direct = callbr { i32, i32 } asm "", "=r,=r,!i"()
91          to label %cleanup [label %z.split]
92
93z.split:
94  %indirect = call { i32, i32 } @llvm.callbr.landingpad.sl_i32i32s({ i32, i32 } %direct)
95  %asmresult2 = extractvalue { i32, i32 } %indirect, 0
96  br label %cleanup
97
98cleanup:
99  %retval.0 = phi i32 [ %asmresult2, %z.split ], [ 42, %entry ]
100  ret i32 %retval.0
101}
102
103; One physical register
104define i32 @test3() {
105  ; CHECK-LABEL: name: test3
106  ; CHECK: bb.0.entry:
107  ; CHECK-NEXT:   successors: %bb.2(0x80000000), %bb.1(0x00000000)
108  ; CHECK-NEXT: {{  $}}
109  ; CHECK-NEXT:   [[MOV32ri:%[0-9]+]]:gr32 = MOV32ri 42
110  ; CHECK-NEXT:   INLINEASM_BR &"", 0 /* attdialect */, 10 /* regdef */, implicit-def $ebx, 13 /* imm */, %bb.1
111  ; CHECK-NEXT:   [[COPY:%[0-9]+]]:gr32 = COPY $ebx
112  ; CHECK-NEXT:   [[COPY1:%[0-9]+]]:gr32 = COPY [[COPY]]
113  ; CHECK-NEXT:   JMP_1 %bb.2
114  ; CHECK-NEXT: {{  $}}
115  ; CHECK-NEXT: bb.1.z.split (machine-block-address-taken, inlineasm-br-indirect-target):
116  ; CHECK-NEXT:   successors: %bb.2(0x80000000)
117  ; CHECK-NEXT:   liveins: $ebx
118  ; CHECK-NEXT: {{  $}}
119  ; CHECK-NEXT:   [[COPY2:%[0-9]+]]:gr32 = COPY $ebx
120  ; CHECK-NEXT:   [[COPY3:%[0-9]+]]:gr32 = COPY [[COPY2]]
121  ; CHECK-NEXT: {{  $}}
122  ; CHECK-NEXT: bb.2.cleanup:
123  ; CHECK-NEXT:   [[PHI:%[0-9]+]]:gr32 = PHI [[MOV32ri]], %bb.0, [[COPY3]], %bb.1
124  ; CHECK-NEXT:   $eax = COPY [[PHI]]
125  ; CHECK-NEXT:   RET 0, $eax
126entry:
127  %direct = callbr i32 asm "", "={bx},!i"()
128          to label %cleanup [label %z.split]
129
130z.split:
131  %indirect = call i32 @llvm.callbr.landingpad.i32(i32 %direct)
132  br label %cleanup
133
134cleanup:
135  %retval.0 = phi i32 [ %indirect, %z.split ], [ 42, %entry ]
136  ret i32 %retval.0
137}
138
139; Two physical registers
140define i32 @test4() {
141  ; CHECK-LABEL: name: test4
142  ; CHECK: bb.0.entry:
143  ; CHECK-NEXT:   successors: %bb.2(0x80000000), %bb.1(0x00000000)
144  ; CHECK-NEXT: {{  $}}
145  ; CHECK-NEXT:   [[MOV32ri:%[0-9]+]]:gr32 = MOV32ri 42
146  ; CHECK-NEXT:   INLINEASM_BR &"", 0 /* attdialect */, 10 /* regdef */, implicit-def $ebx, 10 /* regdef */, implicit-def $edx, 13 /* imm */, %bb.1
147  ; CHECK-NEXT:   [[COPY:%[0-9]+]]:gr32 = COPY $ebx
148  ; CHECK-NEXT:   [[COPY1:%[0-9]+]]:gr32 = COPY $edx
149  ; CHECK-NEXT:   [[COPY2:%[0-9]+]]:gr32 = COPY [[COPY1]]
150  ; CHECK-NEXT:   [[COPY3:%[0-9]+]]:gr32 = COPY [[COPY]]
151  ; CHECK-NEXT:   JMP_1 %bb.2
152  ; CHECK-NEXT: {{  $}}
153  ; CHECK-NEXT: bb.1.z.split (machine-block-address-taken, inlineasm-br-indirect-target):
154  ; CHECK-NEXT:   successors: %bb.2(0x80000000)
155  ; CHECK-NEXT:   liveins: $ebx, $edx
156  ; CHECK-NEXT: {{  $}}
157  ; CHECK-NEXT:   [[COPY4:%[0-9]+]]:gr32 = COPY $ebx
158  ; CHECK-NEXT:   [[COPY5:%[0-9]+]]:gr32 = COPY [[COPY4]]
159  ; CHECK-NEXT: {{  $}}
160  ; CHECK-NEXT: bb.2.cleanup:
161  ; CHECK-NEXT:   [[PHI:%[0-9]+]]:gr32 = PHI [[MOV32ri]], %bb.0, [[COPY5]], %bb.1
162  ; CHECK-NEXT:   $eax = COPY [[PHI]]
163  ; CHECK-NEXT:   RET 0, $eax
164entry:
165  %direct = callbr { i32, i32 } asm "", "={bx},={dx},!i"()
166          to label %cleanup [label %z.split]
167
168z.split:
169  %indirect = call { i32, i32 } @llvm.callbr.landingpad.sl_i32i32s({ i32, i32 } %direct)
170  %asmresult2 = extractvalue { i32, i32 } %indirect, 0
171  br label %cleanup
172
173cleanup:
174  %retval.0 = phi i32 [ %asmresult2, %z.split ], [ 42, %entry ]
175  ret i32 %retval.0
176}
177
178; Test the same destination appearing in the direct/fallthrough branch as the
179; indirect branch. Physreg.
180define i32 @test5() {
181  ; CHECK-LABEL: name: test5
182  ; CHECK: bb.0.entry:
183  ; CHECK-NEXT:   successors: %bb.1(0x80000000)
184  ; CHECK-NEXT: {{  $}}
185  ; CHECK-NEXT:   INLINEASM_BR &"# $0", 0 /* attdialect */, 10 /* regdef */, implicit-def $ebx, 13 /* imm */, %bb.1
186  ; CHECK-NEXT:   [[COPY:%[0-9]+]]:gr32 = COPY $ebx
187  ; CHECK-NEXT:   [[COPY1:%[0-9]+]]:gr32 = COPY [[COPY]]
188  ; CHECK-NEXT:   JMP_1 %bb.1
189  ; CHECK-NEXT: {{  $}}
190  ; CHECK-NEXT: bb.1.cleanup (machine-block-address-taken, inlineasm-br-indirect-target):
191  ; CHECK-NEXT:   liveins: $ebx
192  ; CHECK-NEXT: {{  $}}
193  ; CHECK-NEXT:   [[COPY2:%[0-9]+]]:gr32 = COPY $ebx
194  ; CHECK-NEXT:   $eax = COPY [[COPY2]]
195  ; CHECK-NEXT:   RET 0, $eax
196entry:
197  %direct = callbr i32 asm "# $0", "={bx},!i"()
198          to label %cleanup [label %cleanup]
199
200cleanup:
201  %indirect = call i32 @llvm.callbr.landingpad.i32(i32 %direct)
202  ret i32 %indirect
203}
204
205; "The Devil's cross" (i.e. two asm goto with conflicting physreg constraints
206; going to the same destination) as expressed by clang.
207define i64 @test6() {
208  ; CHECK-LABEL: name: test6
209  ; CHECK: bb.0.entry:
210  ; CHECK-NEXT:   successors: %bb.1(0x80000000), %bb.3(0x00000000)
211  ; CHECK-NEXT: {{  $}}
212  ; CHECK-NEXT:   INLINEASM_BR &"", 0 /* attdialect */, 10 /* regdef */, implicit-def $rdx, 13 /* imm */, %bb.3
213  ; CHECK-NEXT:   [[COPY:%[0-9]+]]:gr64 = COPY $rdx
214  ; CHECK-NEXT:   [[COPY1:%[0-9]+]]:gr64 = COPY [[COPY]]
215  ; CHECK-NEXT:   JMP_1 %bb.1
216  ; CHECK-NEXT: {{  $}}
217  ; CHECK-NEXT: bb.1.asm.fallthrough:
218  ; CHECK-NEXT:   successors: %bb.2(0x80000000), %bb.4(0x00000000)
219  ; CHECK-NEXT: {{  $}}
220  ; CHECK-NEXT:   INLINEASM_BR &"", 0 /* attdialect */, 10 /* regdef */, implicit-def $rbx, 13 /* imm */, %bb.4
221  ; CHECK-NEXT:   [[COPY2:%[0-9]+]]:gr64 = COPY $rbx
222  ; CHECK-NEXT:   [[COPY3:%[0-9]+]]:gr64 = COPY [[COPY2]]
223  ; CHECK-NEXT:   JMP_1 %bb.2
224  ; CHECK-NEXT: {{  $}}
225  ; CHECK-NEXT: bb.2.foo:
226  ; CHECK-NEXT:   [[PHI:%[0-9]+]]:gr64 = PHI %3, %bb.3, [[COPY3]], %bb.1, %4, %bb.4
227  ; CHECK-NEXT:   $rax = COPY [[PHI]]
228  ; CHECK-NEXT:   RET 0, $rax
229  ; CHECK-NEXT: {{  $}}
230  ; CHECK-NEXT: bb.3.foo.split (machine-block-address-taken, inlineasm-br-indirect-target):
231  ; CHECK-NEXT:   successors: %bb.2(0x80000000)
232  ; CHECK-NEXT:   liveins: $rdx
233  ; CHECK-NEXT: {{  $}}
234  ; CHECK-NEXT:   [[COPY4:%[0-9]+]]:gr64 = COPY $rdx
235  ; CHECK-NEXT:   [[COPY5:%[0-9]+]]:gr64 = COPY [[COPY4]]
236  ; CHECK-NEXT:   JMP_1 %bb.2
237  ; CHECK-NEXT: {{  $}}
238  ; CHECK-NEXT: bb.4.foo.split2 (machine-block-address-taken, inlineasm-br-indirect-target):
239  ; CHECK-NEXT:   successors: %bb.2(0x80000000)
240  ; CHECK-NEXT:   liveins: $rbx
241  ; CHECK-NEXT: {{  $}}
242  ; CHECK-NEXT:   [[COPY6:%[0-9]+]]:gr64 = COPY $rbx
243  ; CHECK-NEXT:   [[COPY7:%[0-9]+]]:gr64 = COPY [[COPY6]]
244  ; CHECK-NEXT:   JMP_1 %bb.2
245entry:
246  %0 = callbr i64 asm "", "={dx},!i"()
247          to label %asm.fallthrough [label %foo.split]
248
249asm.fallthrough:
250  %1 = callbr i64 asm "", "={bx},!i"()
251          to label %foo [label %foo.split2]
252
253foo:
254  %x.0 = phi i64 [ %3, %foo.split2 ], [ %2, %foo.split ], [ %1, %asm.fallthrough ]
255  ret i64 %x.0
256
257foo.split:
258  %2 = call i64 @llvm.callbr.landingpad.i64(i64 %0)
259  br label %foo
260
261foo.split2:
262  %3 = call i64 @llvm.callbr.landingpad.i64(i64 %1)
263  br label %foo
264}
265
266
267; Test a callbr looping back on itself.
268define i32 @test7() {
269  ; CHECK-LABEL: name: test7
270  ; CHECK: bb.0.entry:
271  ; CHECK-NEXT:   successors: %bb.1(0x80000000)
272  ; CHECK-NEXT: {{  $}}
273  ; CHECK-NEXT:   [[DEF:%[0-9]+]]:gr32 = IMPLICIT_DEF
274  ; CHECK-NEXT: {{  $}}
275  ; CHECK-NEXT: bb.1.retry:
276  ; CHECK-NEXT:   successors: %bb.2(0x80000000), %bb.3(0x00000000)
277  ; CHECK-NEXT: {{  $}}
278  ; CHECK-NEXT:   [[PHI:%[0-9]+]]:gr32 = PHI [[DEF]], %bb.0, %2, %bb.3
279  ; CHECK-NEXT:   [[COPY:%[0-9]+]]:gr32 = COPY [[PHI]]
280  ; CHECK-NEXT:   INLINEASM_BR &"", 0 /* attdialect */, 10 /* regdef */, implicit-def $edx, 2147483657 /* reguse tiedto:$0 */, [[COPY]](tied-def 3), 13 /* imm */, %bb.3
281  ; CHECK-NEXT:   [[COPY1:%[0-9]+]]:gr32 = COPY $edx
282  ; CHECK-NEXT:   [[COPY2:%[0-9]+]]:gr32 = COPY [[COPY1]]
283  ; CHECK-NEXT:   JMP_1 %bb.2
284  ; CHECK-NEXT: {{  $}}
285  ; CHECK-NEXT: bb.2.asm.fallthrough:
286  ; CHECK-NEXT:   $eax = COPY [[COPY2]]
287  ; CHECK-NEXT:   RET 0, $eax
288  ; CHECK-NEXT: {{  $}}
289  ; CHECK-NEXT: bb.3.retry.split (machine-block-address-taken, inlineasm-br-indirect-target):
290  ; CHECK-NEXT:   successors: %bb.1(0x80000000)
291  ; CHECK-NEXT:   liveins: $edx
292  ; CHECK-NEXT: {{  $}}
293  ; CHECK-NEXT:   [[COPY3:%[0-9]+]]:gr32 = COPY $edx
294  ; CHECK-NEXT:   [[COPY4:%[0-9]+]]:gr32 = COPY [[COPY3]]
295  ; CHECK-NEXT:   JMP_1 %bb.1
296entry:
297  br label %retry
298
299retry:
300  %x.0 = phi i32 [ undef, %entry ], [ %1, %retry.split ]
301  %0 = callbr i32 asm "", "={dx},0,!i"(i32 %x.0)
302          to label %asm.fallthrough [label %retry.split]
303
304asm.fallthrough:
305  ret i32 %0
306
307retry.split:
308  %1 = call i32 @llvm.callbr.landingpad.i32(i32 %0)
309  br label %retry
310}
311
312; Test the same destination appearing in the direct/fallthrough branch as the
313; indirect branch. Same as test5 but with a virtreg rather than a physreg
314; constraint.
315define i32 @test8() {
316  ; CHECK-LABEL: name: test8
317  ; CHECK: bb.0.entry:
318  ; CHECK-NEXT:   successors: %bb.1(0x80000000)
319  ; CHECK-NEXT: {{  $}}
320  ; CHECK-NEXT:   INLINEASM_BR &"# $0", 0 /* attdialect */, 2686986 /* regdef:GR32_NOREX2 */, def %1, 13 /* imm */, %bb.1
321  ; CHECK-NEXT:   [[COPY:%[0-9]+]]:gr32 = COPY %1
322  ; CHECK-NEXT:   JMP_1 %bb.1
323  ; CHECK-NEXT: {{  $}}
324  ; CHECK-NEXT: bb.1.cleanup (machine-block-address-taken, inlineasm-br-indirect-target):
325  ; CHECK-NEXT:   $eax = COPY %1
326  ; CHECK-NEXT:   RET 0, $eax
327entry:
328  %direct = callbr i32 asm "# $0", "=r,!i"()
329          to label %cleanup [label %cleanup]
330
331cleanup:
332  %indirect = call i32 @llvm.callbr.landingpad.i32(i32 %direct)
333  ret i32 %indirect
334}
335
336define i64 @condition_code() {
337  ; CHECK-LABEL: name: condition_code
338  ; CHECK: bb.0 (%ir-block.0):
339  ; CHECK-NEXT:   successors: %bb.1(0x80000000), %bb.2(0x00000000)
340  ; CHECK-NEXT: {{  $}}
341  ; CHECK-NEXT:   INLINEASM_BR &"", 16 /* maystore attdialect */, 2359306 /* regdef:GR32 */, def %1, 13 /* imm */, %bb.2
342  ; CHECK-NEXT:   [[SETCCr:%[0-9]+]]:gr8 = SETCCr 4, implicit $eflags
343  ; CHECK-NEXT:   [[MOVZX32rr8_:%[0-9]+]]:gr32 = MOVZX32rr8 killed [[SETCCr]]
344  ; CHECK-NEXT:   [[SUBREG_TO_REG:%[0-9]+]]:gr64 = SUBREG_TO_REG 0, killed [[MOVZX32rr8_]], %subreg.sub_32bit
345  ; CHECK-NEXT:   JMP_1 %bb.1
346  ; CHECK-NEXT: {{  $}}
347  ; CHECK-NEXT: bb.1.b:
348  ; CHECK-NEXT:   $rax = COPY [[SUBREG_TO_REG]]
349  ; CHECK-NEXT:   RET 0, $rax
350  ; CHECK-NEXT: {{  $}}
351  ; CHECK-NEXT: bb.2.c (machine-block-address-taken, inlineasm-br-indirect-target):
352  ; CHECK-NEXT:   [[SETCCr1:%[0-9]+]]:gr8 = SETCCr 4, implicit $eflags
353  ; CHECK-NEXT:   [[MOVZX32rr8_1:%[0-9]+]]:gr32 = MOVZX32rr8 killed [[SETCCr1]]
354  ; CHECK-NEXT:   [[SUBREG_TO_REG1:%[0-9]+]]:gr64 = SUBREG_TO_REG 0, killed [[MOVZX32rr8_1]], %subreg.sub_32bit
355  ; CHECK-NEXT:   $rax = COPY [[SUBREG_TO_REG1]]
356  ; CHECK-NEXT:   RET 0, $rax
357  %a = callbr i64 asm "", "={@ccz},!i"()
358       to label %b [label %c]
359
360b:
361  ret i64 %a
362
363c:
364  %1 = call i64 @llvm.callbr.landingpad.i64(i64 %a)
365  ret i64 %1
366}
367
368declare i64 @llvm.callbr.landingpad.i64(i64)
369declare i32 @llvm.callbr.landingpad.i32(i32)
370declare { i32, i32 } @llvm.callbr.landingpad.sl_i32i32s({ i32, i32 })
371