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(void (i32)* %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
25 ; X64: callq __llvm_lvi_thunk_r11
26 ; X64: movl %[[x]], %edi
28 ; X64-DAG: movl %[[x]], %edi
29 ; X64-DAG: movq %[[fp]], %r11
30 ; X64: jmp __llvm_lvi_thunk_r11 # TAILCALL
32 ; X64FAST-LABEL: icall_reg:
34 ; X64FAST: callq __llvm_lvi_thunk_r11
36 ; X64FAST: jmp __llvm_lvi_thunk_r11 # TAILCALL
39 @global_fp = external dso_local global void (i32)*
41 ; Test an indirect call through a global variable.
42 define void @icall_global_fp(i32 %x, void (i32)** %fpp) #0 {
43 %fp1 = load void (i32)*, void (i32)** @global_fp
44 call void %fp1(i32 %x)
45 %fp2 = load void (i32)*, void (i32)** @global_fp
46 tail call void %fp2(i32 %x)
50 ; X64-LABEL: icall_global_fp:
51 ; X64-DAG: movl %edi, %[[x:[^ ]*]]
52 ; X64-DAG: movq global_fp(%rip), %r11
53 ; X64: callq __llvm_lvi_thunk_r11
54 ; X64-DAG: movl %[[x]], %edi
55 ; X64-DAG: movq global_fp(%rip), %r11
56 ; X64: jmp __llvm_lvi_thunk_r11 # TAILCALL
58 ; X64FAST-LABEL: icall_global_fp:
59 ; X64FAST: movq global_fp(%rip), %r11
60 ; X64FAST: callq __llvm_lvi_thunk_r11
61 ; X64FAST: movq global_fp(%rip), %r11
62 ; X64FAST: jmp __llvm_lvi_thunk_r11 # TAILCALL
65 %struct.Foo = type { void (%struct.Foo*)** }
67 ; Test an indirect call through a vtable.
68 define void @vcall(%struct.Foo* %obj) #0 {
69 %vptr_field = getelementptr %struct.Foo, %struct.Foo* %obj, i32 0, i32 0
70 %vptr = load void (%struct.Foo*)**, void (%struct.Foo*)*** %vptr_field
71 %vslot = getelementptr void(%struct.Foo*)*, void(%struct.Foo*)** %vptr, i32 1
72 %fp = load void(%struct.Foo*)*, void(%struct.Foo*)** %vslot
73 tail call void %fp(%struct.Foo* %obj)
74 tail call void %fp(%struct.Foo* %obj)
79 ; X64: movq %rdi, %[[obj:[^ ]*]]
80 ; X64: movq (%rdi), %[[vptr:[^ ]*]]
81 ; X64: movq 8(%[[vptr]]), %[[fp:[^ ]*]]
82 ; X64: movq %[[fp]], %r11
83 ; X64: callq __llvm_lvi_thunk_r11
84 ; X64-DAG: movq %[[obj]], %rdi
85 ; X64-DAG: movq %[[fp]], %r11
86 ; X64: jmp __llvm_lvi_thunk_r11 # TAILCALL
88 ; X64FAST-LABEL: vcall:
89 ; X64FAST: callq __llvm_lvi_thunk_r11
90 ; X64FAST: jmp __llvm_lvi_thunk_r11 # TAILCALL
93 declare dso_local void @direct_callee()
95 define void @direct_tail() #0 {
96 tail call void @direct_callee()
100 ; X64-LABEL: direct_tail:
101 ; X64: jmp direct_callee # TAILCALL
102 ; X64FAST-LABEL: direct_tail:
103 ; X64FAST: jmp direct_callee # TAILCALL
106 declare void @nonlazybind_callee() #1
108 define void @nonlazybind_caller() #0 {
109 call void @nonlazybind_callee()
110 tail call void @nonlazybind_callee()
114 ; X64-LABEL: nonlazybind_caller:
115 ; X64: movq nonlazybind_callee@GOTPCREL(%rip), %[[REG:.*]]
116 ; X64: movq %[[REG]], %r11
117 ; X64: callq __llvm_lvi_thunk_r11
118 ; X64: movq %[[REG]], %r11
119 ; X64: jmp __llvm_lvi_thunk_r11 # TAILCALL
120 ; X64FAST-LABEL: nonlazybind_caller:
121 ; X64FAST: movq nonlazybind_callee@GOTPCREL(%rip), %r11
122 ; X64FAST: callq __llvm_lvi_thunk_r11
123 ; X64FAST: movq nonlazybind_callee@GOTPCREL(%rip), %r11
124 ; X64FAST: jmp __llvm_lvi_thunk_r11 # TAILCALL
127 ; Check that a switch gets lowered using a jump table
128 define void @switch_jumptable(i32* %ptr, i64* %sink) #0 {
129 ; X64-LABEL: switch_jumptable:
135 %i = load volatile i32, i32* %ptr
136 switch i32 %i, label %bb0 [
149 store volatile i64 0, i64* %sink
153 store volatile i64 1, i64* %sink
157 store volatile i64 2, i64* %sink
161 store volatile i64 3, i64* %sink
165 store volatile i64 4, i64* %sink
169 store volatile i64 5, i64* %sink
173 store volatile i64 6, i64* %sink
177 store volatile i64 7, i64* %sink
181 store volatile i64 8, i64* %sink
185 store volatile i64 9, i64* %sink
190 @indirectbr_rewrite.targets = constant [10 x i8*] [i8* blockaddress(@indirectbr_rewrite, %bb0),
191 i8* blockaddress(@indirectbr_rewrite, %bb1),
192 i8* blockaddress(@indirectbr_rewrite, %bb2),
193 i8* blockaddress(@indirectbr_rewrite, %bb3),
194 i8* blockaddress(@indirectbr_rewrite, %bb4),
195 i8* blockaddress(@indirectbr_rewrite, %bb5),
196 i8* blockaddress(@indirectbr_rewrite, %bb6),
197 i8* blockaddress(@indirectbr_rewrite, %bb7),
198 i8* blockaddress(@indirectbr_rewrite, %bb8),
199 i8* blockaddress(@indirectbr_rewrite, %bb9)]
201 ; Check that when thunks are enabled the indirectbr instruction gets
202 ; rewritten to use switch, and that in turn doesn't get lowered as a jump
204 define void @indirectbr_rewrite(i64* readonly %p, i64* %sink) #0 {
205 ; X64-LABEL: indirectbr_rewrite:
208 %i0 = load i64, i64* %p
209 %target.i0 = getelementptr [10 x i8*], [10 x i8*]* @indirectbr_rewrite.targets, i64 0, i64 %i0
210 %target0 = load i8*, i8** %target.i0
211 indirectbr i8* %target0, [label %bb1, label %bb3]
214 store volatile i64 0, i64* %sink
218 store volatile i64 1, i64* %sink
222 store volatile i64 2, i64* %sink
226 store volatile i64 3, i64* %sink
230 store volatile i64 4, i64* %sink
234 store volatile i64 5, i64* %sink
238 store volatile i64 6, i64* %sink
242 store volatile i64 7, i64* %sink
246 store volatile i64 8, i64* %sink
250 store volatile i64 9, i64* %sink
254 %i.next = load i64, i64* %p
255 %target.i.next = getelementptr [10 x i8*], [10 x i8*]* @indirectbr_rewrite.targets, i64 0, i64 %i.next
256 %target.next = load i8*, i8** %target.i.next
257 ; Potentially hit a full 10 successors here so that even if we rewrite as
258 ; a switch it will try to be lowered with a jump table.
259 indirectbr i8* %target.next, [label %bb0,
271 ; Lastly check that the necessary thunks were emitted.
273 ; X64-LABEL: .section .text.__llvm_lvi_thunk_r11,{{.*}},__llvm_lvi_thunk_r11,comdat
274 ; X64-NEXT: .hidden __llvm_lvi_thunk_r11
275 ; X64-NEXT: .weak __llvm_lvi_thunk_r11
276 ; X64: __llvm_lvi_thunk_r11:
277 ; X64-NEXT: # {{.*}} # %entry
279 ; X64-NEXT: jmpq *%r11
281 attributes #1 = { nonlazybind }