summary refs log tree commit diff
path: root/tests/assembly-llvm/cmse.rs
diff options
context:
space:
mode:
Diffstat (limited to 'tests/assembly-llvm/cmse.rs')
-rw-r--r--tests/assembly-llvm/cmse.rs100
1 files changed, 100 insertions, 0 deletions
diff --git a/tests/assembly-llvm/cmse.rs b/tests/assembly-llvm/cmse.rs
new file mode 100644
index 00000000000..a68ee99eac6
--- /dev/null
+++ b/tests/assembly-llvm/cmse.rs
@@ -0,0 +1,100 @@
+//@ add-core-stubs
+//@ revisions: hard soft
+//@ assembly-output: emit-asm
+//@ [hard] compile-flags: --target thumbv8m.main-none-eabihf --crate-type lib -Copt-level=1
+//@ [soft] compile-flags: --target thumbv8m.main-none-eabi --crate-type lib -Copt-level=1
+//@ [hard] needs-llvm-components: arm
+//@ [soft] needs-llvm-components: arm
+#![crate_type = "lib"]
+#![feature(abi_cmse_nonsecure_call, cmse_nonsecure_entry, no_core, lang_items)]
+#![no_core]
+
+extern crate minicore;
+use minicore::*;
+
+// CHECK-LABEL: __acle_se_entry_point:
+// CHECK-NEXT: entry_point:
+//
+// Write return argument (two registers since 64bit integer)
+// CHECK: movs r0, #0
+// CHECK: movs r1, #0
+//
+// If we are using hard-float:
+// * Check if the float registers were touched (bit 3 in CONTROL)
+// hard: mrs     [[REG:r[0-9]+]], control
+// hard: tst.w   [[REG]], #8
+// hard: beq     [[LABEL:[\.a-zA-Z0-9_]+]]
+//
+// * If touched clear all float registers (d0..=d7)
+// hard: vmov    d0,
+// hard: vmov    d1,
+// hard: vmov    d2,
+// hard: vmov    d3,
+// hard: vmov    d4,
+// hard: vmov    d5,
+// hard: vmov    d6,
+// hard: vmov    d7,
+//
+// * If touched clear FPU status register
+// hard: vmrs    [[REG:r[0-9]+]], fpscr
+// hard: bic     [[REG]], [[REG]], #159
+// hard: bic     [[REG]], [[REG]], #4026531840
+// hard: vmsr    fpscr, [[REG]]
+// hard: [[LABEL]]:
+//
+// Clear all other registers that might have been used
+// CHECK: mov r2,
+// CHECK: mov r3,
+// CHECK: mov r12,
+//
+// Clear the flags
+// CHECK: msr apsr_nzcvq,
+//
+// Branch back to non-secure side
+// CHECK: bxns lr
+#[no_mangle]
+pub extern "cmse-nonsecure-entry" fn entry_point() -> i64 {
+    0
+}
+
+// NOTE for future codegen changes:
+// The specific register assignment is not important, however:
+// * all registers must be cleared before `blxns` is executed
+//     (either by writing arguments or any other value)
+// * the lowest bit on the address of the callee must be cleared
+// * the flags need to be overwritten
+// * `blxns` needs to be called with the callee address
+//     (with the lowest bit cleared)
+//
+// CHECK-LABEL: call_nonsecure
+// Save callee pointer
+// CHECK: mov r12, r0
+//
+// All arguments are written to (writes r0..=r3)
+// CHECK: movs r0, #0
+// CHECK: movs r1, #1
+// CHECK: movs r2, #2
+// CHECK: movs r3, #3
+//
+// Lowest bit gets cleared on callee address
+// CHECK: bic r12, r12, #1
+//
+// Ununsed registers get cleared (r4..=r11)
+// CHECK: mov r4,
+// CHECK: mov r5,
+// CHECK: mov r6,
+// CHECK: mov r7,
+// CHECK: mov r8,
+// CHECK: mov r9,
+// CHECK: mov r10,
+// CHECK: mov r11,
+//
+// Flags get cleared
+// CHECK: msr apsr_nzcvq,
+//
+// Call to non-secure
+// CHECK: blxns r12
+#[no_mangle]
+pub fn call_nonsecure(f: unsafe extern "cmse-nonsecure-call" fn(u32, u32, u32, u32) -> u64) -> u64 {
+    unsafe { f(0, 1, 2, 3) }
+}