1 ; RUN: llc -verify-machineinstrs -mtriple=x86_64-unknown -mattr=+lvi-cfi < %s | FileCheck %s --check-prefix=X64
2 ; RUN: llc -verify-machineinstrs -mtriple=x86_64-unknown -mattr=+lvi-cfi -O0 < %s | FileCheck %s --check-prefix=X64FAST
4 ; Note that a lot of this code was lifted from retpoline.ll.
6 declare dso_local void @bar(i32)
8 ; Test a simple indirect call and tail call.
9 define void @icall_reg(ptr %fp, i32 %x) {
11 tail call void @bar(i32 %x)
12 tail call void %fp(i32 %x)
13 tail call void @bar(i32 %x)
14 tail call void %fp(i32 %x)
18 ; X64-LABEL: icall_reg:
19 ; X64-DAG: movq %rdi, %[[fp:[^ ]*]]
20 ; X64-DAG: movl %esi, %[[x:[^ ]*]]
21 ; X64: movl %esi, %edi
23 ; X64-DAG: movl %[[x]], %edi
24 ; X64-DAG: movq %[[fp]], %r11
26 ; X64-NEXT: callq __llvm_lvi_thunk_r11
27 ; X64: movl %[[x]], %edi
29 ; X64-DAG: movl %[[x]], %edi
30 ; X64-DAG: movq %[[fp]], %r11
32 ; X64-NEXT: jmp __llvm_lvi_thunk_r11 # TAILCALL
34 ; X64FAST-LABEL: icall_reg:
37 ; X64FAST-NEXT: callq __llvm_lvi_thunk_r11
40 ; X64FAST-NEXT: jmp __llvm_lvi_thunk_r11 # TAILCALL
43 @global_fp = external dso_local global ptr
45 ; Test an indirect call through a global variable.
46 define void @icall_global_fp(i32 %x, ptr %fpp) #0 {
47 %fp1 = load ptr, ptr @global_fp
48 call void %fp1(i32 %x)
49 %fp2 = load ptr, ptr @global_fp
50 tail call void %fp2(i32 %x)
54 ; X64-LABEL: icall_global_fp:
55 ; X64-DAG: movl %edi, %[[x:[^ ]*]]
56 ; X64-DAG: movq global_fp(%rip), %r11
58 ; X64-NEXT: callq __llvm_lvi_thunk_r11
59 ; X64-DAG: movl %[[x]], %edi
60 ; X64-DAG: movq global_fp(%rip), %r11
62 ; X64-NEXT: jmp __llvm_lvi_thunk_r11 # TAILCALL
64 ; X64FAST-LABEL: icall_global_fp:
65 ; X64FAST: movq global_fp(%rip), %r11
67 ; X64FAST-NEXT: callq __llvm_lvi_thunk_r11
68 ; X64FAST: movq global_fp(%rip), %r11
70 ; X64FAST-NEXT: jmp __llvm_lvi_thunk_r11 # TAILCALL
73 %struct.Foo = type { ptr }
75 ; Test an indirect call through a vtable.
76 define void @vcall(ptr %obj) #0 {
77 %vptr = load ptr, ptr %obj
78 %vslot = getelementptr ptr, ptr %vptr, i32 1
79 %fp = load ptr, ptr %vslot
80 tail call void %fp(ptr %obj)
81 tail call void %fp(ptr %obj)
86 ; X64: movq %rdi, %[[obj:[^ ]*]]
87 ; X64: movq (%rdi), %[[vptr:[^ ]*]]
88 ; X64: movq 8(%[[vptr]]), %[[fp:[^ ]*]]
89 ; X64: movq %[[fp]], %r11
91 ; X64-NEXT: callq __llvm_lvi_thunk_r11
92 ; X64-DAG: movq %[[obj]], %rdi
93 ; X64-DAG: movq %[[fp]], %r11
95 ; X64-NEXT: jmp __llvm_lvi_thunk_r11 # TAILCALL
97 ; X64FAST-LABEL: vcall:
99 ; X64FAST-NEXT: callq __llvm_lvi_thunk_r11
101 ; X64FAST-NEXT: jmp __llvm_lvi_thunk_r11 # TAILCALL
104 declare dso_local void @direct_callee()
106 define void @direct_tail() #0 {
107 tail call void @direct_callee()
111 ; X64-LABEL: direct_tail:
112 ; X64: jmp direct_callee # TAILCALL
113 ; X64FAST-LABEL: direct_tail:
114 ; X64FAST: jmp direct_callee # TAILCALL
117 declare void @nonlazybind_callee() #1
119 define void @nonlazybind_caller() #0 {
120 call void @nonlazybind_callee()
121 tail call void @nonlazybind_callee()
125 ; X64-LABEL: nonlazybind_caller:
126 ; X64: movq nonlazybind_callee@GOTPCREL(%rip), %[[REG:.*]]
127 ; X64: movq %[[REG]], %r11
129 ; X64-NEXT: callq __llvm_lvi_thunk_r11
130 ; X64: movq %[[REG]], %r11
132 ; X64-NEXT: jmp __llvm_lvi_thunk_r11 # TAILCALL
133 ; X64FAST-LABEL: nonlazybind_caller:
134 ; X64FAST: movq nonlazybind_callee@GOTPCREL(%rip), %r11
136 ; X64FAST-NEXT: callq __llvm_lvi_thunk_r11
137 ; X64FAST: movq nonlazybind_callee@GOTPCREL(%rip), %r11
139 ; X64FAST-NEXT: jmp __llvm_lvi_thunk_r11 # TAILCALL
142 ; Check that a switch gets lowered using a jump table
143 define void @switch_jumptable(ptr %ptr, ptr %sink) #0 {
144 ; X64-LABEL: switch_jumptable:
150 %i = load volatile i32, ptr %ptr
151 switch i32 %i, label %bb0 [
164 store volatile i64 0, ptr %sink
168 store volatile i64 1, ptr %sink
172 store volatile i64 2, ptr %sink
176 store volatile i64 3, ptr %sink
180 store volatile i64 4, ptr %sink
184 store volatile i64 5, ptr %sink
188 store volatile i64 6, ptr %sink
192 store volatile i64 7, ptr %sink
196 store volatile i64 8, ptr %sink
200 store volatile i64 9, ptr %sink
205 @indirectbr_rewrite.targets = constant [10 x ptr] [ptr blockaddress(@indirectbr_rewrite, %bb0),
206 ptr blockaddress(@indirectbr_rewrite, %bb1),
207 ptr blockaddress(@indirectbr_rewrite, %bb2),
208 ptr blockaddress(@indirectbr_rewrite, %bb3),
209 ptr blockaddress(@indirectbr_rewrite, %bb4),
210 ptr blockaddress(@indirectbr_rewrite, %bb5),
211 ptr blockaddress(@indirectbr_rewrite, %bb6),
212 ptr blockaddress(@indirectbr_rewrite, %bb7),
213 ptr blockaddress(@indirectbr_rewrite, %bb8),
214 ptr blockaddress(@indirectbr_rewrite, %bb9)]
216 ; Check that when thunks are enabled the indirectbr instruction gets
217 ; rewritten to use switch, and that in turn doesn't get lowered as a jump
219 define void @indirectbr_rewrite(ptr readonly %p, ptr %sink) #0 {
220 ; X64-LABEL: indirectbr_rewrite:
223 %i0 = load i64, ptr %p
224 %target.i0 = getelementptr [10 x ptr], ptr @indirectbr_rewrite.targets, i64 0, i64 %i0
225 %target0 = load ptr, ptr %target.i0
226 indirectbr ptr %target0, [label %bb1, label %bb3]
229 store volatile i64 0, ptr %sink
233 store volatile i64 1, ptr %sink
237 store volatile i64 2, ptr %sink
241 store volatile i64 3, ptr %sink
245 store volatile i64 4, ptr %sink
249 store volatile i64 5, ptr %sink
253 store volatile i64 6, ptr %sink
257 store volatile i64 7, ptr %sink
261 store volatile i64 8, ptr %sink
265 store volatile i64 9, ptr %sink
269 %i.next = load i64, ptr %p
270 %target.i.next = getelementptr [10 x ptr], ptr @indirectbr_rewrite.targets, i64 0, i64 %i.next
271 %target.next = load ptr, ptr %target.i.next
272 ; Potentially hit a full 10 successors here so that even if we rewrite as
273 ; a switch it will try to be lowered with a jump table.
274 indirectbr ptr %target.next, [label %bb0,
286 ; Lastly check that the necessary thunks were emitted.
288 ; X64-LABEL: .section .text.__llvm_lvi_thunk_r11,{{.*}},__llvm_lvi_thunk_r11,comdat
289 ; X64-NEXT: .hidden __llvm_lvi_thunk_r11
290 ; X64-NEXT: .weak __llvm_lvi_thunk_r11
291 ; X64: __llvm_lvi_thunk_r11:
292 ; X64-NEXT: # {{.*}} # %entry
294 ; X64-NEXT: jmpq *%r11
296 attributes #1 = { nonlazybind }
298 !llvm.module.flags = !{!0}
300 !0 = !{i32 4, !"indirect_branch_cs_prefix", i32 1}