From 3d168c28f61565856f5d4726bf42235346848d5b Mon Sep 17 00:00:00 2001
From: Tamme Dittrich <tamme@tweedegolf.com>
Date: Mon, 23 Sep 2024 15:20:23 +0200
Subject: [PATCH] Improve assembly test for CMSE ABIs

This ensures the code-gen for these ABIs does not change silently.

Co-authored-by: Folkert <folkert@folkertdev.nl>
---
 tests/assembly/cmse.rs | 86 +++++++++++++++++++++++++++++++++++++++---
 1 file changed, 81 insertions(+), 5 deletions(-)

diff --git a/tests/assembly/cmse.rs b/tests/assembly/cmse.rs
index acad77b251357..e0ada8dc2f15c 100644
--- a/tests/assembly/cmse.rs
+++ b/tests/assembly/cmse.rs
@@ -1,6 +1,9 @@
+//@ revisions: hard soft
 //@ assembly-output: emit-asm
-//@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib -Copt-level=1
-//@ needs-llvm-components: arm
+//@ [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_c_cmse_nonsecure_call, cmse_nonsecure_entry, no_core, lang_items)]
 #![no_core]
@@ -9,15 +12,88 @@ pub trait Sized {}
 #[lang = "copy"]
 pub trait Copy {}
 
-// CHECK-LABEL: __acle_se_entry_point
-// CHECK: bxns
+// 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 "C-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
-// CHECK: blxns
+// 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 "C-cmse-nonsecure-call" fn(u32, u32, u32, u32) -> u64,