//@ 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_c_cmse_nonsecure_call, cmse_nonsecure_entry, no_core, lang_items)]
#![no_core]
#[lang = "sized"]
pub trait Sized {}
#[lang = "copy"]
pub trait Copy {}

// 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
// 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,
) -> u64 {
    unsafe { f(0, 1, 2, 3) }
}
