301 lines · plain
1; RUN: llc -verify-machineinstrs -mtriple=x86_64-unknown -mattr=+lvi-cfi < %s | FileCheck %s --check-prefix=X642; RUN: llc -verify-machineinstrs -mtriple=x86_64-unknown -mattr=+lvi-cfi -O0 < %s | FileCheck %s --check-prefix=X64FAST3;4; Note that a lot of this code was lifted from retpoline.ll.5 6declare dso_local void @bar(i32)7 8; Test a simple indirect call and tail call.9define void @icall_reg(ptr %fp, i32 %x) {10entry: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)15 ret void16}17 18; X64-LABEL: icall_reg:19; X64-DAG: movq %rdi, %[[fp:[^ ]*]]20; X64-DAG: movl %esi, %[[x:[^ ]*]]21; X64: movl %esi, %edi22; X64: callq bar23; X64-DAG: movl %[[x]], %edi24; X64-DAG: movq %[[fp]], %r1125; X64: cs26; X64-NEXT: callq __llvm_lvi_thunk_r1127; X64: movl %[[x]], %edi28; X64: callq bar29; X64-DAG: movl %[[x]], %edi30; X64-DAG: movq %[[fp]], %r1131; X64: cs32; X64-NEXT: jmp __llvm_lvi_thunk_r11 # TAILCALL33 34; X64FAST-LABEL: icall_reg:35; X64FAST: callq bar36; X64FAST: cs37; X64FAST-NEXT: callq __llvm_lvi_thunk_r1138; X64FAST: callq bar39; X64FAST: cs40; X64FAST-NEXT: jmp __llvm_lvi_thunk_r11 # TAILCALL41 42 43@global_fp = external dso_local global ptr44 45; Test an indirect call through a global variable.46define void @icall_global_fp(i32 %x, ptr %fpp) #0 {47 %fp1 = load ptr, ptr @global_fp48 call void %fp1(i32 %x)49 %fp2 = load ptr, ptr @global_fp50 tail call void %fp2(i32 %x)51 ret void52}53 54; X64-LABEL: icall_global_fp:55; X64-DAG: movl %edi, %[[x:[^ ]*]]56; X64-DAG: movq global_fp(%rip), %r1157; X64: cs58; X64-NEXT: callq __llvm_lvi_thunk_r1159; X64-DAG: movl %[[x]], %edi60; X64-DAG: movq global_fp(%rip), %r1161; X64: cs62; X64-NEXT: jmp __llvm_lvi_thunk_r11 # TAILCALL63 64; X64FAST-LABEL: icall_global_fp:65; X64FAST: movq global_fp(%rip), %r1166; X64FAST: cs67; X64FAST-NEXT: callq __llvm_lvi_thunk_r1168; X64FAST: movq global_fp(%rip), %r1169; X64FAST: cs70; X64FAST-NEXT: jmp __llvm_lvi_thunk_r11 # TAILCALL71 72 73%struct.Foo = type { ptr }74 75; Test an indirect call through a vtable.76define void @vcall(ptr %obj) #0 {77 %vptr = load ptr, ptr %obj78 %vslot = getelementptr ptr, ptr %vptr, i32 179 %fp = load ptr, ptr %vslot80 tail call void %fp(ptr %obj)81 tail call void %fp(ptr %obj)82 ret void83}84 85; X64-LABEL: vcall:86; X64: movq %rdi, %[[obj:[^ ]*]]87; X64: movq (%rdi), %[[vptr:[^ ]*]]88; X64: movq 8(%[[vptr]]), %[[fp:[^ ]*]]89; X64: movq %[[fp]], %r1190; X64: cs91; X64-NEXT: callq __llvm_lvi_thunk_r1192; X64-DAG: movq %[[obj]], %rdi93; X64-DAG: movq %[[fp]], %r1194; X64: cs95; X64-NEXT: jmp __llvm_lvi_thunk_r11 # TAILCALL96 97; X64FAST-LABEL: vcall:98; X64FAST: cs99; X64FAST-NEXT: callq __llvm_lvi_thunk_r11100; X64FAST: cs101; X64FAST-NEXT: jmp __llvm_lvi_thunk_r11 # TAILCALL102 103 104declare dso_local void @direct_callee()105 106define void @direct_tail() #0 {107 tail call void @direct_callee()108 ret void109}110 111; X64-LABEL: direct_tail:112; X64: jmp direct_callee # TAILCALL113; X64FAST-LABEL: direct_tail:114; X64FAST: jmp direct_callee # TAILCALL115 116 117declare void @nonlazybind_callee() #1118 119define void @nonlazybind_caller() #0 {120 call void @nonlazybind_callee()121 tail call void @nonlazybind_callee()122 ret void123}124 125; X64-LABEL: nonlazybind_caller:126; X64: movq nonlazybind_callee@GOTPCREL(%rip), %[[REG:.*]]127; X64: movq %[[REG]], %r11128; X64: cs129; X64-NEXT: callq __llvm_lvi_thunk_r11130; X64: movq %[[REG]], %r11131; X64: cs132; X64-NEXT: jmp __llvm_lvi_thunk_r11 # TAILCALL133; X64FAST-LABEL: nonlazybind_caller:134; X64FAST: movq nonlazybind_callee@GOTPCREL(%rip), %r11135; X64FAST: cs136; X64FAST-NEXT: callq __llvm_lvi_thunk_r11137; X64FAST: movq nonlazybind_callee@GOTPCREL(%rip), %r11138; X64FAST: cs139; X64FAST-NEXT: jmp __llvm_lvi_thunk_r11 # TAILCALL140 141 142; Check that a switch gets lowered using a jump table143define void @switch_jumptable(ptr %ptr, ptr %sink) #0 {144; X64-LABEL: switch_jumptable:145; X64-NOT: jmpq *146entry:147 br label %header148 149header:150 %i = load volatile i32, ptr %ptr151 switch i32 %i, label %bb0 [152 i32 1, label %bb1153 i32 2, label %bb2154 i32 3, label %bb3155 i32 4, label %bb4156 i32 5, label %bb5157 i32 6, label %bb6158 i32 7, label %bb7159 i32 8, label %bb8160 i32 9, label %bb9161 ]162 163bb0:164 store volatile i64 0, ptr %sink165 br label %header166 167bb1:168 store volatile i64 1, ptr %sink169 br label %header170 171bb2:172 store volatile i64 2, ptr %sink173 br label %header174 175bb3:176 store volatile i64 3, ptr %sink177 br label %header178 179bb4:180 store volatile i64 4, ptr %sink181 br label %header182 183bb5:184 store volatile i64 5, ptr %sink185 br label %header186 187bb6:188 store volatile i64 6, ptr %sink189 br label %header190 191bb7:192 store volatile i64 7, ptr %sink193 br label %header194 195bb8:196 store volatile i64 8, ptr %sink197 br label %header198 199bb9:200 store volatile i64 9, ptr %sink201 br label %header202}203 204 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)]215 216; Check that when thunks are enabled the indirectbr instruction gets217; rewritten to use switch, and that in turn doesn't get lowered as a jump218; table.219define void @indirectbr_rewrite(ptr readonly %p, ptr %sink) #0 {220; X64-LABEL: indirectbr_rewrite:221; X64-NOT: jmpq *222entry:223 %i0 = load i64, ptr %p224 %target.i0 = getelementptr [10 x ptr], ptr @indirectbr_rewrite.targets, i64 0, i64 %i0225 %target0 = load ptr, ptr %target.i0226 indirectbr ptr %target0, [label %bb1, label %bb3]227 228bb0:229 store volatile i64 0, ptr %sink230 br label %latch231 232bb1:233 store volatile i64 1, ptr %sink234 br label %latch235 236bb2:237 store volatile i64 2, ptr %sink238 br label %latch239 240bb3:241 store volatile i64 3, ptr %sink242 br label %latch243 244bb4:245 store volatile i64 4, ptr %sink246 br label %latch247 248bb5:249 store volatile i64 5, ptr %sink250 br label %latch251 252bb6:253 store volatile i64 6, ptr %sink254 br label %latch255 256bb7:257 store volatile i64 7, ptr %sink258 br label %latch259 260bb8:261 store volatile i64 8, ptr %sink262 br label %latch263 264bb9:265 store volatile i64 9, ptr %sink266 br label %latch267 268latch:269 %i.next = load i64, ptr %p270 %target.i.next = getelementptr [10 x ptr], ptr @indirectbr_rewrite.targets, i64 0, i64 %i.next271 %target.next = load ptr, ptr %target.i.next272 ; Potentially hit a full 10 successors here so that even if we rewrite as273 ; a switch it will try to be lowered with a jump table.274 indirectbr ptr %target.next, [label %bb0,275 label %bb1,276 label %bb2,277 label %bb3,278 label %bb4,279 label %bb5,280 label %bb6,281 label %bb7,282 label %bb8,283 label %bb9]284}285 286; Lastly check that the necessary thunks were emitted.287;288; X64-LABEL: .section .text.__llvm_lvi_thunk_r11,{{.*}},__llvm_lvi_thunk_r11,comdat289; X64-NEXT: .hidden __llvm_lvi_thunk_r11290; X64-NEXT: .weak __llvm_lvi_thunk_r11291; X64: __llvm_lvi_thunk_r11:292; X64-NEXT: # {{.*}} # %entry293; X64-NEXT: lfence294; X64-NEXT: jmpq *%r11295 296attributes #1 = { nonlazybind }297 298!llvm.module.flags = !{!0}299 300!0 = !{i32 4, !"indirect_branch_cs_prefix", i32 1}301