216 lines · plain
1; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 62; RUN: llc -mtriple=thumbv6m-none-eabi < %s | FileCheck %s3 4; This test verifies that Thumb1 (ARMv6-M) generates correct code for backend KCFI.5; Thumb1 uses the backend KCFI implementation with Thumb1-specific instructions.6 7; Test function without KCFI annotation8; CHECK-LABEL: .globl nosan9; CHECK-NEXT: .p2align 110; CHECK-NEXT: .type nosan,%function11; CHECK-NEXT: .code 1612; CHECK-NEXT: .thumb_func13define dso_local void @nosan() nounwind {14; CHECK-LABEL: nosan:15; CHECK: @ %bb.0:16; CHECK-NEXT: bx lr17 ret void18}19 20; Test function with KCFI annotation - verifies type hash emission21;; The alignment is at least 4 to avoid unaligned type hash loads when this22;; instrumented function is indirectly called.23; CHECK-LABEL: .globl target_func24; CHECK-NEXT: .p2align 225; CHECK-NEXT: .type target_func,%function26; CHECK-NEXT: .long 317046893227; CHECK-NEXT: .code 1628; CHECK-NEXT: .thumb_func29define void @target_func() !kcfi_type !1 {30; CHECK-LABEL: target_func:31; CHECK: @ %bb.0:32; CHECK-NEXT: bx lr33 ret void34}35 36; Test indirect call with KCFI check using operand bundles37; CHECK-LABEL: .globl f138; CHECK: .p2align 239; CHECK-NEXT: .type f1,%function40; CHECK-NEXT: .long 317046893241; CHECK-NEXT: .code 1642; CHECK-NEXT: .thumb_func43define void @f1(ptr noundef %x) !kcfi_type !1 {44; CHECK-LABEL: f1:45; CHECK: @ %bb.0:46; CHECK-NEXT: .save {r7, lr}47; CHECK-NEXT: push {r7, lr}48; CHECK-NEXT: movs r3, #149; CHECK-NEXT: mov r2, r050; CHECK-NEXT: bics r2, r351; CHECK-NEXT: subs r2, #452; CHECK-NEXT: ldr r2, [r2]53; CHECK-NEXT: movs r3, #18854; CHECK-NEXT: lsls r3, r3, #855; CHECK-NEXT: adds r3, #24956; CHECK-NEXT: lsls r3, r3, #857; CHECK-NEXT: adds r3, #13258; CHECK-NEXT: lsls r3, r3, #859; CHECK-NEXT: adds r3, #6860; CHECK-NEXT: cmp r2, r361; CHECK-NEXT: beq .Ltmp062; CHECK-NEXT: bkpt #063; CHECK-NEXT: .Ltmp0:64; CHECK-NEXT: blx r065; CHECK-NEXT: pop {r7, pc}66 call void %x() [ "kcfi"(i32 -1124498364) ]67 ret void68}69 70; Test with tail call - backend KCFI supports tail calls71define void @f2(ptr noundef %x) !kcfi_type !1 {72; CHECK-LABEL: f2:73; CHECK: @ %bb.0:74; CHECK-NEXT: .save {r7, lr}75; CHECK-NEXT: push {r7, lr}76; CHECK-NEXT: movs r3, #177; CHECK-NEXT: mov r2, r078; CHECK-NEXT: bics r2, r379; CHECK-NEXT: subs r2, #480; CHECK-NEXT: ldr r2, [r2]81; CHECK-NEXT: movs r3, #18882; CHECK-NEXT: lsls r3, r3, #883; CHECK-NEXT: adds r3, #24984; CHECK-NEXT: lsls r3, r3, #885; CHECK-NEXT: adds r3, #13286; CHECK-NEXT: lsls r3, r3, #887; CHECK-NEXT: adds r3, #6888; CHECK-NEXT: cmp r2, r389; CHECK-NEXT: beq .Ltmp190; CHECK-NEXT: bkpt #091; CHECK-NEXT: .Ltmp1:92; CHECK-NEXT: blx r093; CHECK-NEXT: pop {r7, pc}94 tail call void %x() [ "kcfi"(i32 -1124498364) ]95 ret void96}97 98; Test with R2 live (3 arguments) - compiler shuffles args, no spilling needed99define void @f3_r2_live(ptr noundef %x, i32 %a, i32 %b, i32 %c) !kcfi_type !1 {100; CHECK-LABEL: f3_r2_live:101; CHECK: @ %bb.0:102; CHECK-NEXT: .save {r4, lr}103; CHECK-NEXT: push {r4, lr}104; CHECK-NEXT: mov r4, r0105; CHECK-NEXT: mov r0, r1106; CHECK-NEXT: mov r1, r2107; CHECK-NEXT: mov r2, r3108; CHECK-NEXT: push {r2}109; CHECK-NEXT: movs r3, #1110; CHECK-NEXT: mov r2, r4111; CHECK-NEXT: bics r2, r3112; CHECK-NEXT: subs r2, #4113; CHECK-NEXT: ldr r2, [r2]114; CHECK-NEXT: movs r3, #188115; CHECK-NEXT: lsls r3, r3, #8116; CHECK-NEXT: adds r3, #249117; CHECK-NEXT: lsls r3, r3, #8118; CHECK-NEXT: adds r3, #132119; CHECK-NEXT: lsls r3, r3, #8120; CHECK-NEXT: adds r3, #68121; CHECK-NEXT: cmp r2, r3122; CHECK-NEXT: pop {r2}123; CHECK-NEXT: beq .Ltmp2124; CHECK-NEXT: bkpt #0125; CHECK-NEXT: .Ltmp2:126; CHECK-NEXT: blx r4127; CHECK-NEXT: pop {r4, pc}128; Compiler shuffles: target→r4, c→r2, a→r0, b→r1129; R2 is live (3rd arg), so we push it, then uses R3 as temp, R2 as scratch130 call void %x(i32 %a, i32 %b, i32 %c) [ "kcfi"(i32 -1124498364) ]131 ret void132}133 134; Test with both R2 and R3 live (4 arguments) - compiler moves to r5/r4, uses R3 temp and R12 scratch135define void @f4_r2_r3_live(ptr noundef %x, i32 %a, i32 %b, i32 %c, i32 %d) !kcfi_type !1 {136; CHECK-LABEL: f4_r2_r3_live:137; CHECK: @ %bb.0:138; CHECK-NEXT: .save {r4, r5, r7, lr}139; CHECK-NEXT: push {r4, r5, r7, lr}140; CHECK-NEXT: mov r5, r3141; CHECK-NEXT: mov r4, r0142; CHECK-NEXT: ldr r3, [sp, #16]143; CHECK-NEXT: mov r0, r1144; CHECK-NEXT: mov r1, r2145; CHECK-NEXT: mov r2, r5146; CHECK-NEXT: push {r3}147; CHECK-NEXT: push {r2}148; CHECK-NEXT: movs r3, #1149; CHECK-NEXT: mov r2, r4150; CHECK-NEXT: bics r2, r3151; CHECK-NEXT: subs r2, #4152; CHECK-NEXT: ldr r2, [r2]153; CHECK-NEXT: movs r3, #188154; CHECK-NEXT: lsls r3, r3, #8155; CHECK-NEXT: adds r3, #249156; CHECK-NEXT: lsls r3, r3, #8157; CHECK-NEXT: adds r3, #132158; CHECK-NEXT: lsls r3, r3, #8159; CHECK-NEXT: adds r3, #68160; CHECK-NEXT: cmp r2, r3161; CHECK-NEXT: pop {r2}162; CHECK-NEXT: pop {r3}163; CHECK-NEXT: beq .Ltmp3164; CHECK-NEXT: bkpt #0165; CHECK-NEXT: .Ltmp3:166; CHECK-NEXT: blx r4167; CHECK-NEXT: pop {r4, r5, r7, pc}168; Compiler shuffles: r3→r5, target→r4, d→r3 (from stack), a→r0, b→r1, c→r2169; Then pushes r3 (d value), then r2, uses R3 as temp, R2 as scratch170 call void %x(i32 %a, i32 %b, i32 %c, i32 %d) [ "kcfi"(i32 -1124498364) ]171 ret void172}173 174; Test where target ends up in R12, forcing R2 as scratch, with both R2 and R3 live175; This uses inline asm to force target into R12, with 4 call arguments to make R2/R3 live176define void @f5_r12_target_r2_r3_live(i32 %a, i32 %b, i32 %c, i32 %d) !kcfi_type !1 {177; CHECK-LABEL: f5_r12_target_r2_r3_live:178; CHECK: @ %bb.0:179; CHECK-NEXT: .save {r7, lr}180; CHECK-NEXT: push {r7, lr}181; CHECK-NEXT: @APP182; CHECK-NEXT: @NO_APP183; CHECK-NEXT: push {r3}184; CHECK-NEXT: push {r2}185; CHECK-NEXT: movs r3, #1186; CHECK-NEXT: mov r2, r12187; CHECK-NEXT: bics r2, r3188; CHECK-NEXT: subs r2, #4189; CHECK-NEXT: ldr r2, [r2]190; CHECK-NEXT: movs r3, #188191; CHECK-NEXT: lsls r3, r3, #8192; CHECK-NEXT: adds r3, #249193; CHECK-NEXT: lsls r3, r3, #8194; CHECK-NEXT: adds r3, #132195; CHECK-NEXT: lsls r3, r3, #8196; CHECK-NEXT: adds r3, #68197; CHECK-NEXT: cmp r2, r3198; CHECK-NEXT: pop {r2}199; CHECK-NEXT: pop {r3}200; CHECK-NEXT: beq .Ltmp4201; CHECK-NEXT: bkpt #0202; CHECK-NEXT: .Ltmp4:203; CHECK-NEXT: blx r12204; CHECK-NEXT: pop {r7, pc}205; Use inline asm to get function pointer into R12206; With 4 arguments (r0-r3), both R2 and R3 are live207; Target in R12 means R2 is scratch, R3 is temp, and both need spilling208 %target = call ptr asm "", "={r12}"()209 call void %target(i32 %a, i32 %b, i32 %c, i32 %d) [ "kcfi"(i32 -1124498364) ]210 ret void211}212 213!llvm.module.flags = !{!0}214!0 = !{i32 4, !"kcfi", i32 1}215!1 = !{i32 -1124498364}216