blob: 7fd2ee4a9e16da6f0866dd14331c617bc30088eb [file] [edit]
; RUN: llc -mtriple=hexagon -verify-machineinstrs < %s | FileCheck %s --check-prefix=ASM
; RUN: llc -mtriple=hexagon -verify-machineinstrs -stop-after=finalize-isel < %s \
; RUN: | FileCheck %s --check-prefix=ISEL
; RUN: llc -mtriple=hexagon -verify-machineinstrs -stop-after=kcfi < %s \
; RUN: | FileCheck %s --check-prefix=KCFI
; Verify KCFI type hash is emitted before the function.
; ASM: .word 12345678
; ASM-LABEL: f1:
define void @f1(ptr noundef %x) !kcfi_type !1 {
; Load and type-hash materialization are combined in one packet.
; ASM: r{{[0-9]+}} = memw(r0+#-4)
; ASM-NEXT: r{{[0-9]+}} = ##12345678
; ASM-NEXT: }
; ASM-NEXT: {
; ASM-NEXT: p0 = cmp.eq(r{{[0-9]+}},r{{[0-9]+}})
; ASM-NEXT: if (p0.new) jump:t
; ASM-NEXT: }
; ASM: r{{[0-9]+}}:{{[0-9]+}} = memd(##3134984174)
; After ISel, the call should carry a cfi-type.
; ISEL-LABEL: name: f1
; ISEL: J2_callr %0,{{.*}} cfi-type 12345678
; After the KCFI pass, the check and call are bundled.
; KCFI-LABEL: name: f1
; KCFI: BUNDLE{{.*}} {
; KCFI-NEXT: KCFI_CHECK $r0, 12345678
; KCFI-NEXT: J2_callr killed $r0
; KCFI-NEXT: }
call void %x() [ "kcfi"(i32 12345678) ]
ret void
}
; Test with a second call using a different type hash.
define void @f2(ptr noundef %x) !kcfi_type !2 {
; ASM-LABEL: f2:
; ASM: r{{[0-9]+}} = memw(r0+#-4)
; ASM-NEXT: r{{[0-9]+}} = ##1234
; ASM-NEXT: }
; ASM-NEXT: {
; ASM-NEXT: p0 = cmp.eq(r{{[0-9]+}},r{{[0-9]+}})
; ASM-NEXT: if (p0.new) jump:t
; ASM-NEXT: }
; ASM: r{{[0-9]+}}:{{[0-9]+}} = memd(##3134984174)
call void %x() [ "kcfi"(i32 1234) ]
ret void
}
; Test with patchable-function-entry (nops placed after the label,
; so the KCFI offset is still -4).
define void @f3(ptr noundef %x) #0 {
; ASM-LABEL: f3:
; ASM: nop
; ASM: nop
; ASM: r{{[0-9]+}} = memw(r0+#-4)
; ASM-NEXT: r{{[0-9]+}} = ##12345678
; ASM-NEXT: }
; ASM-NEXT: {
; ASM-NEXT: p0 = cmp.eq(r{{[0-9]+}},r{{[0-9]+}})
; ASM-NEXT: if (p0.new) jump:t
; ASM-NEXT: }
; ASM: r{{[0-9]+}}:{{[0-9]+}} = memd(##3134984174)
call void %x() [ "kcfi"(i32 12345678) ]
ret void
}
;; Test patchable-function-prefix: nops are placed before the function entry
;; (after the type hash), so the KCFI load offset is adjusted from -4 to
;; -(PrefixNops*4 + 4).
define void @f4_prefix(ptr noundef %x) #1 !kcfi_type !1 {
; ASM-LABEL: f4_prefix:
; ASM: r{{[0-9]+}} = memw(r0+#-12)
; ASM-NEXT: r{{[0-9]+}} = ##12345678
; ASM-NEXT: }
; ASM-NEXT: {
; ASM-NEXT: p0 = cmp.eq(r{{[0-9]+}},r{{[0-9]+}})
; ASM-NEXT: if (p0.new) jump:t
; ASM-NEXT: }
; ASM: r{{[0-9]+}}:{{[0-9]+}} = memd(##3134984174)
call void %x() [ "kcfi"(i32 12345678) ]
ret void
}
;; Test patchable-function-prefix with 3 nops: offset = -(3*4+4) = -16.
define void @f5_prefix3(ptr noundef %x) #2 !kcfi_type !1 {
; ASM-LABEL: f5_prefix3:
; ASM: r{{[0-9]+}} = memw(r0+#-16)
; ASM-NEXT: r{{[0-9]+}} = ##12345678
; ASM-NEXT: }
; ASM-NEXT: {
; ASM-NEXT: p0 = cmp.eq(r{{[0-9]+}},r{{[0-9]+}})
; ASM-NEXT: if (p0.new) jump:t
; ASM-NEXT: }
; ASM: r{{[0-9]+}}:{{[0-9]+}} = memd(##3134984174)
call void %x() [ "kcfi"(i32 12345678) ]
ret void
}
;; Test scratch register conflict: call target is R6. The default scratch
;; registers are R6/R7, so when the target occupies R6, the load scratch
;; must use R8 instead.
define void @f6_target_r6() {
; ASM-LABEL: f6_target_r6:
; ASM: r8 = memw(r6+#-4)
; ASM-NEXT: r7 = ##12345678
; ASM-NEXT: }
; ASM-NEXT: {
; ASM-NEXT: p0 = cmp.eq(r8,r7)
; ASM-NEXT: if (p0.new) jump:t
; ASM-NEXT: }
; ASM: r{{[0-9]+}}:{{[0-9]+}} = memd(##3134984174)
; KCFI-LABEL: name: f6_target_r6
; KCFI: BUNDLE{{.*}} {
; KCFI-NEXT: KCFI_CHECK $r6, 12345678
; KCFI-NEXT: J2_callr{{.*}}killed $r6
; KCFI-NEXT: }
%target = call ptr asm sideeffect "", "={r6}"()
call void %target() [ "kcfi"(i32 12345678) ]
ret void
}
;; Test scratch register conflict: call target is R7. The type-hash scratch
;; must use R8 instead.
define void @f7_target_r7() {
; ASM-LABEL: f7_target_r7:
; ASM: r6 = memw(r7+#-4)
; ASM-NEXT: r8 = ##12345678
; ASM-NEXT: }
; ASM-NEXT: {
; ASM-NEXT: p0 = cmp.eq(r6,r8)
; ASM-NEXT: if (p0.new) jump:t
; ASM-NEXT: }
; ASM: r{{[0-9]+}}:{{[0-9]+}} = memd(##3134984174)
; KCFI-LABEL: name: f7_target_r7
; KCFI: BUNDLE{{.*}} {
; KCFI-NEXT: KCFI_CHECK $r7, 12345678
; KCFI-NEXT: J2_callr{{.*}}killed $r7
; KCFI-NEXT: }
%target = call ptr asm sideeffect "", "={r7}"()
call void %target() [ "kcfi"(i32 12345678) ]
ret void
}
;; Test noreturn indirect call with KCFI (uses PS_callr_nr opcode).
define void @f8_noreturn(ptr noundef %x) {
; ASM-LABEL: f8_noreturn:
; ASM: r{{[0-9]+}} = memw(r0+#-4)
; ASM-NEXT: r{{[0-9]+}} = ##12345678
; ASM-NEXT: }
; ASM-NEXT: {
; ASM-NEXT: p0 = cmp.eq(r{{[0-9]+}},r{{[0-9]+}})
; ASM-NEXT: if (p0.new) jump:t
; ASM-NEXT: }
; ASM: r{{[0-9]+}}:{{[0-9]+}} = memd(##3134984174)
; ISEL-LABEL: name: f8_noreturn
; ISEL: PS_callr_nr %0,{{.*}} cfi-type 12345678
; KCFI-LABEL: name: f8_noreturn
; KCFI: BUNDLE{{.*}} {
; KCFI-NEXT: KCFI_CHECK $r0, 12345678
; KCFI-NEXT: PS_callr_nr killed $r0
; KCFI-NEXT: }
call void %x() #3 [ "kcfi"(i32 12345678) ]
unreachable
}
; Verify the .kcfi_traps section is emitted.
; ASM: .section .kcfi_traps
attributes #0 = { "patchable-function-entry"="2" }
attributes #1 = { "patchable-function-prefix"="2" }
attributes #2 = { "patchable-function-prefix"="3" }
attributes #3 = { noreturn }
!llvm.module.flags = !{!0}
!0 = !{i32 4, !"kcfi", i32 1}
!1 = !{i32 12345678}
!2 = !{i32 1234}