about summary refs log tree commit diff
diff options
context:
space:
mode:
authorFolkert de Vries <folkert@folkertdev.nl>2024-10-14 22:32:32 +0200
committerFolkert de Vries <folkert@folkertdev.nl>2024-10-14 22:32:32 +0200
commit10aa255541964bceddaac8f49d7a11f1d736e24d (patch)
tree3e7c5f9f3c4a91e36dfd5bc264b3af74d3dd7a49
parent9322d183f45e0fd5a509820874cc5ff27744a479 (diff)
downloadrust-10aa255541964bceddaac8f49d7a11f1d736e24d.tar.gz
rust-10aa255541964bceddaac8f49d7a11f1d736e24d.zip
improve error messages for `C-cmse-nonsecure-entry` functions
-rw-r--r--compiler/rustc_hir_analysis/messages.ftl15
-rw-r--r--compiler/rustc_hir_analysis/src/errors.rs17
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs157
-rw-r--r--tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs77
-rw-r--r--tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr78
-rw-r--r--tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-on-registers.rs16
-rw-r--r--tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-on-stack.rs21
-rw-r--r--tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-on-stack.stderr4
-rw-r--r--tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-via-stack.rs26
-rw-r--r--tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-via-stack.stderr43
-rw-r--r--tests/ui/cmse-nonsecure/cmse-nonsecure-entry/return-via-stack.rs84
-rw-r--r--tests/ui/cmse-nonsecure/cmse-nonsecure-entry/return-via-stack.stderr84
-rw-r--r--tests/ui/cmse-nonsecure/cmse-nonsecure-entry/trustzone-only.stderr9
-rw-r--r--tests/ui/cmse-nonsecure/cmse-nonsecure-entry/via-registers.rs89
14 files changed, 612 insertions, 108 deletions
diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl
index da814cd2d69..a9f30ffd6da 100644
--- a/compiler/rustc_hir_analysis/messages.ftl
+++ b/compiler/rustc_hir_analysis/messages.ftl
@@ -68,18 +68,21 @@ hir_analysis_closure_implicit_hrtb = implicit types in closure signatures are fo
 hir_analysis_cmse_call_generic =
     function pointers with the `"C-cmse-nonsecure-call"` ABI cannot contain generics in their type
 
-hir_analysis_cmse_call_inputs_stack_spill =
-    arguments for `"C-cmse-nonsecure-call"` function too large to pass via registers
+hir_analysis_cmse_entry_generic =
+    functions with the `"C-cmse-nonsecure-entry"` ABI cannot contain generics in their type
+
+hir_analysis_cmse_inputs_stack_spill =
+    arguments for `"{$abi_name}"` function too large to pass via registers
     .label = {$plural ->
         [false] this argument doesn't
         *[true] these arguments don't
     } fit in the available registers
-    .note = functions with the `"C-cmse-nonsecure-call"` ABI must pass all their arguments via the 4 32-bit available argument registers
+    .note = functions with the `"{$abi_name}"` ABI must pass all their arguments via the 4 32-bit available argument registers
 
-hir_analysis_cmse_call_output_stack_spill =
-    return value of `"C-cmse-nonsecure-call"` function too large to pass via registers
+hir_analysis_cmse_output_stack_spill =
+    return value of `"{$abi_name}"` function too large to pass via registers
     .label = this type doesn't fit in the available registers
-    .note1 = functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers
+    .note1 = functions with the `"{$abi_name}"` ABI must pass their result via the available return registers
     .note2 = the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
 
 hir_analysis_coerce_unsized_may = the trait `{$trait_name}` may only be implemented for a coercion between structures
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index 9099703e812..af4445a7fd4 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -1627,23 +1627,25 @@ pub(crate) struct InvalidReceiverTy<'tcx> {
 pub(crate) struct EffectsWithoutNextSolver;
 
 #[derive(Diagnostic)]
-#[diag(hir_analysis_cmse_call_inputs_stack_spill, code = E0798)]
+#[diag(hir_analysis_cmse_inputs_stack_spill, code = E0798)]
 #[note]
-pub(crate) struct CmseCallInputsStackSpill {
+pub(crate) struct CmseInputsStackSpill {
     #[primary_span]
     #[label]
     pub span: Span,
     pub plural: bool,
+    pub abi_name: &'static str,
 }
 
 #[derive(Diagnostic)]
-#[diag(hir_analysis_cmse_call_output_stack_spill, code = E0798)]
+#[diag(hir_analysis_cmse_output_stack_spill, code = E0798)]
 #[note(hir_analysis_note1)]
 #[note(hir_analysis_note2)]
-pub(crate) struct CmseCallOutputStackSpill {
+pub(crate) struct CmseOutputStackSpill {
     #[primary_span]
     #[label]
     pub span: Span,
+    pub abi_name: &'static str,
 }
 
 #[derive(Diagnostic)]
@@ -1659,3 +1661,10 @@ pub(crate) struct BadReturnTypeNotation {
     #[primary_span]
     pub span: Span,
 }
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_cmse_entry_generic, code = E0798)]
+pub(crate) struct CmseEntryGeneric {
+    #[primary_span]
+    pub span: Span,
+}
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs
index a562759da11..672dc8ddeda 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs
@@ -1,8 +1,8 @@
 use rustc_errors::{DiagCtxtHandle, E0781, struct_span_code_err};
 use rustc_hir::{self as hir, HirId};
+use rustc_middle::bug;
 use rustc_middle::ty::layout::LayoutError;
 use rustc_middle::ty::{self, ParamEnv, TyCtxt};
-use rustc_span::Span;
 use rustc_target::spec::abi;
 
 use crate::errors;
@@ -17,61 +17,104 @@ pub(crate) fn validate_cmse_abi<'tcx>(
     abi: abi::Abi,
     fn_sig: ty::PolyFnSig<'tcx>,
 ) {
-    if let abi::Abi::CCmseNonSecureCall = abi {
-        let hir_node = tcx.hir_node(hir_id);
-        let hir::Node::Ty(hir::Ty {
-            span: bare_fn_span,
-            kind: hir::TyKind::BareFn(bare_fn_ty),
-            ..
-        }) = hir_node
-        else {
-            let span = match tcx.parent_hir_node(hir_id) {
-                hir::Node::Item(hir::Item {
-                    kind: hir::ItemKind::ForeignMod { .. }, span, ..
-                }) => *span,
-                _ => tcx.hir().span(hir_id),
+    let abi_name = abi.name();
+
+    match abi {
+        abi::Abi::CCmseNonSecureCall => {
+            let hir_node = tcx.hir_node(hir_id);
+            let hir::Node::Ty(hir::Ty {
+                span: bare_fn_span,
+                kind: hir::TyKind::BareFn(bare_fn_ty),
+                ..
+            }) = hir_node
+            else {
+                let span = match tcx.parent_hir_node(hir_id) {
+                    hir::Node::Item(hir::Item {
+                        kind: hir::ItemKind::ForeignMod { .. },
+                        span,
+                        ..
+                    }) => *span,
+                    _ => tcx.hir().span(hir_id),
+                };
+                struct_span_code_err!(
+                    tcx.dcx(),
+                    span,
+                    E0781,
+                    "the `\"C-cmse-nonsecure-call\"` ABI is only allowed on function pointers"
+                )
+                .emit();
+                return;
             };
-            struct_span_code_err!(
-                tcx.dcx(),
-                span,
-                E0781,
-                "the `\"C-cmse-nonsecure-call\"` ABI is only allowed on function pointers"
-            )
-            .emit();
-            return;
-        };
 
-        match is_valid_cmse_inputs(tcx, fn_sig) {
-            Ok(Ok(())) => {}
-            Ok(Err(index)) => {
-                // fn(x: u32, u32, u32, u16, y: u16) -> u32,
-                //                           ^^^^^^
-                let span = bare_fn_ty.param_names[index]
-                    .span
-                    .to(bare_fn_ty.decl.inputs[index].span)
-                    .to(bare_fn_ty.decl.inputs.last().unwrap().span);
-                let plural = bare_fn_ty.param_names.len() - index != 1;
-                dcx.emit_err(errors::CmseCallInputsStackSpill { span, plural });
-            }
-            Err(layout_err) => {
-                if let Some(err) = cmse_layout_err(layout_err, *bare_fn_span) {
-                    dcx.emit_err(err);
+            match is_valid_cmse_inputs(tcx, fn_sig) {
+                Ok(Ok(())) => {}
+                Ok(Err(index)) => {
+                    // fn(x: u32, u32, u32, u16, y: u16) -> u32,
+                    //                           ^^^^^^
+                    let span = bare_fn_ty.param_names[index]
+                        .span
+                        .to(bare_fn_ty.decl.inputs[index].span)
+                        .to(bare_fn_ty.decl.inputs.last().unwrap().span);
+                    let plural = bare_fn_ty.param_names.len() - index != 1;
+                    dcx.emit_err(errors::CmseInputsStackSpill { span, plural, abi_name });
+                }
+                Err(layout_err) => {
+                    if should_emit_generic_error(abi, layout_err) {
+                        dcx.emit_err(errors::CmseCallGeneric { span: *bare_fn_span });
+                    }
                 }
             }
+
+            match is_valid_cmse_output(tcx, fn_sig) {
+                Ok(true) => {}
+                Ok(false) => {
+                    let span = bare_fn_ty.decl.output.span();
+                    dcx.emit_err(errors::CmseOutputStackSpill { span, abi_name });
+                }
+                Err(layout_err) => {
+                    if should_emit_generic_error(abi, layout_err) {
+                        dcx.emit_err(errors::CmseCallGeneric { span: *bare_fn_span });
+                    }
+                }
+            };
         }
+        abi::Abi::CCmseNonSecureEntry => {
+            let hir_node = tcx.hir_node(hir_id);
+            let Some(hir::FnSig { decl, span: fn_sig_span, .. }) = hir_node.fn_sig() else {
+                // might happen when this ABI is used incorrectly. That will be handled elsewhere
+                return;
+            };
 
-        match is_valid_cmse_output(tcx, fn_sig) {
-            Ok(true) => {}
-            Ok(false) => {
-                let span = bare_fn_ty.decl.output.span();
-                dcx.emit_err(errors::CmseCallOutputStackSpill { span });
-            }
-            Err(layout_err) => {
-                if let Some(err) = cmse_layout_err(layout_err, *bare_fn_span) {
-                    dcx.emit_err(err);
+            match is_valid_cmse_inputs(tcx, fn_sig) {
+                Ok(Ok(())) => {}
+                Ok(Err(index)) => {
+                    // fn f(x: u32, y: u32, z: u32, w: u16, q: u16) -> u32,
+                    //                                      ^^^^^^
+                    let span = decl.inputs[index].span.to(decl.inputs.last().unwrap().span);
+                    let plural = decl.inputs.len() - index != 1;
+                    dcx.emit_err(errors::CmseInputsStackSpill { span, plural, abi_name });
+                }
+                Err(layout_err) => {
+                    if should_emit_generic_error(abi, layout_err) {
+                        dcx.emit_err(errors::CmseEntryGeneric { span: *fn_sig_span });
+                    }
                 }
             }
-        };
+
+            match is_valid_cmse_output(tcx, fn_sig) {
+                Ok(true) => {}
+                Ok(false) => {
+                    let span = decl.output.span();
+                    dcx.emit_err(errors::CmseOutputStackSpill { span, abi_name });
+                }
+                Err(layout_err) => {
+                    if should_emit_generic_error(abi, layout_err) {
+                        dcx.emit_err(errors::CmseEntryGeneric { span: *fn_sig_span });
+                    }
+                }
+            };
+        }
+        _ => (),
     }
 }
 
@@ -152,22 +195,22 @@ fn is_valid_cmse_output<'tcx>(
     Ok(ret_ty == tcx.types.i64 || ret_ty == tcx.types.u64 || ret_ty == tcx.types.f64)
 }
 
-fn cmse_layout_err<'tcx>(
-    layout_err: &'tcx LayoutError<'tcx>,
-    span: Span,
-) -> Option<crate::errors::CmseCallGeneric> {
+fn should_emit_generic_error<'tcx>(abi: abi::Abi, layout_err: &'tcx LayoutError<'tcx>) -> bool {
     use LayoutError::*;
 
     match layout_err {
         Unknown(ty) => {
-            if ty.is_impl_trait() {
-                None // prevent double reporting of this error
-            } else {
-                Some(errors::CmseCallGeneric { span })
+            match abi {
+                abi::Abi::CCmseNonSecureCall => {
+                    // prevent double reporting of this error
+                    !ty.is_impl_trait()
+                }
+                abi::Abi::CCmseNonSecureEntry => true,
+                _ => bug!("invalid ABI: {abi}"),
             }
         }
         SizeOverflow(..) | NormalizationFailure(..) | ReferencesError(..) | Cycle(..) => {
-            None // not our job to report these
+            false // not our job to report these
         }
     }
 }
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs
new file mode 100644
index 00000000000..a264bba6f3c
--- /dev/null
+++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs
@@ -0,0 +1,77 @@
+//@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib
+//@ needs-llvm-components: arm
+#![feature(cmse_nonsecure_entry, c_variadic, no_core, lang_items)]
+#![no_core]
+#[lang = "sized"]
+pub trait Sized {}
+#[lang = "copy"]
+pub trait Copy {}
+impl Copy for u32 {}
+
+#[repr(C)]
+struct Wrapper<T>(T);
+
+impl<T: Copy> Wrapper<T> {
+    extern "C-cmse-nonsecure-entry" fn ambient_generic(_: T, _: u32, _: u32, _: u32) -> u64 {
+        //~^ ERROR [E0798]
+        0
+    }
+
+    extern "C-cmse-nonsecure-entry" fn ambient_generic_nested(
+        //~^ ERROR [E0798]
+        _: Wrapper<T>,
+        _: u32,
+        _: u32,
+        _: u32,
+    ) -> u64 {
+        0
+    }
+}
+
+extern "C-cmse-nonsecure-entry" fn introduced_generic<U: Copy>(
+    //~^ ERROR [E0798]
+    _: U,
+    _: u32,
+    _: u32,
+    _: u32,
+) -> u64 {
+    0
+}
+
+extern "C-cmse-nonsecure-entry" fn impl_trait(_: impl Copy, _: u32, _: u32, _: u32) -> u64 {
+    //~^ ERROR [E0798]
+    0
+}
+
+extern "C-cmse-nonsecure-entry" fn reference(x: &usize) -> usize {
+    *x
+}
+
+trait Trait {}
+
+extern "C-cmse-nonsecure-entry" fn trait_object(x: &dyn Trait) -> &dyn Trait {
+    //~^ ERROR return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers [E0798]
+    x
+}
+
+extern "C-cmse-nonsecure-entry" fn static_trait_object(
+    x: &'static dyn Trait,
+) -> &'static dyn Trait {
+    //~^ ERROR return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers [E0798]
+    x
+}
+
+#[repr(transparent)]
+struct WrapperTransparent<'a>(&'a dyn Trait);
+
+extern "C-cmse-nonsecure-entry" fn wrapped_trait_object(
+    x: WrapperTransparent,
+) -> WrapperTransparent {
+    //~^ ERROR return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers [E0798]
+    x
+}
+
+extern "C-cmse-nonsecure-entry" fn c_variadic(_: u32, _: ...) {
+    //~^ ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
+    //~| ERROR requires `va_list` lang_item
+}
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr
new file mode 100644
index 00000000000..9e67f881f75
--- /dev/null
+++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr
@@ -0,0 +1,78 @@
+error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
+  --> $DIR/generics.rs:74:55
+   |
+LL | extern "C-cmse-nonsecure-entry" fn c_variadic(_: u32, _: ...) {
+   |                                                       ^^^^^^
+
+error[E0798]: functions with the `"C-cmse-nonsecure-entry"` ABI cannot contain generics in their type
+  --> $DIR/generics.rs:31:1
+   |
+LL | / extern "C-cmse-nonsecure-entry" fn introduced_generic<U: Copy>(
+LL | |
+LL | |     _: U,
+LL | |     _: u32,
+LL | |     _: u32,
+LL | |     _: u32,
+LL | | ) -> u64 {
+   | |________^
+
+error[E0798]: functions with the `"C-cmse-nonsecure-entry"` ABI cannot contain generics in their type
+  --> $DIR/generics.rs:41:1
+   |
+LL | extern "C-cmse-nonsecure-entry" fn impl_trait(_: impl Copy, _: u32, _: u32, _: u32) -> u64 {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0798]: functions with the `"C-cmse-nonsecure-entry"` ABI cannot contain generics in their type
+  --> $DIR/generics.rs:15:5
+   |
+LL |     extern "C-cmse-nonsecure-entry" fn ambient_generic(_: T, _: u32, _: u32, _: u32) -> u64 {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0798]: functions with the `"C-cmse-nonsecure-entry"` ABI cannot contain generics in their type
+  --> $DIR/generics.rs:20:5
+   |
+LL | /     extern "C-cmse-nonsecure-entry" fn ambient_generic_nested(
+LL | |
+LL | |         _: Wrapper<T>,
+LL | |         _: u32,
+LL | |         _: u32,
+LL | |         _: u32,
+LL | |     ) -> u64 {
+   | |____________^
+
+error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers
+  --> $DIR/generics.rs:52:67
+   |
+LL | extern "C-cmse-nonsecure-entry" fn trait_object(x: &dyn Trait) -> &dyn Trait {
+   |                                                                   ^^^^^^^^^^ this type doesn't fit in the available registers
+   |
+   = note: functions with the `"C-cmse-nonsecure-entry"` ABI must pass their result via the available return registers
+   = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
+
+error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers
+  --> $DIR/generics.rs:59:6
+   |
+LL | ) -> &'static dyn Trait {
+   |      ^^^^^^^^^^^^^^^^^^ this type doesn't fit in the available registers
+   |
+   = note: functions with the `"C-cmse-nonsecure-entry"` ABI must pass their result via the available return registers
+   = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
+
+error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers
+  --> $DIR/generics.rs:69:6
+   |
+LL | ) -> WrapperTransparent {
+   |      ^^^^^^^^^^^^^^^^^^ this type doesn't fit in the available registers
+   |
+   = note: functions with the `"C-cmse-nonsecure-entry"` ABI must pass their result via the available return registers
+   = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
+
+error: requires `va_list` lang_item
+  --> $DIR/generics.rs:74:55
+   |
+LL | extern "C-cmse-nonsecure-entry" fn c_variadic(_: u32, _: ...) {
+   |                                                       ^^^^^^
+
+error: aborting due to 9 previous errors
+
+For more information about this error, try `rustc --explain E0798`.
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-on-registers.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-on-registers.rs
deleted file mode 100644
index de6888fae62..00000000000
--- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-on-registers.rs
+++ /dev/null
@@ -1,16 +0,0 @@
-//@ build-pass
-//@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib
-//@ needs-llvm-components: arm
-#![feature(cmse_nonsecure_entry, no_core, lang_items)]
-#![no_core]
-#![crate_type = "lib"]
-#[lang = "sized"]
-trait Sized {}
-#[lang = "copy"]
-trait Copy {}
-impl Copy for u32 {}
-
-#[no_mangle]
-pub extern "C-cmse-nonsecure-entry" fn entry_function(_: u32, _: u32, _: u32, d: u32) -> u32 {
-    d
-}
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-on-stack.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-on-stack.rs
deleted file mode 100644
index 4413c461c04..00000000000
--- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-on-stack.rs
+++ /dev/null
@@ -1,21 +0,0 @@
-//@ build-fail
-//@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib
-//@ needs-llvm-components: arm
-#![feature(cmse_nonsecure_entry, no_core, lang_items)]
-#![no_core]
-#[lang = "sized"]
-trait Sized {}
-#[lang = "copy"]
-trait Copy {}
-impl Copy for u32 {}
-
-#[no_mangle]
-pub extern "C-cmse-nonsecure-entry" fn entry_function(
-    _: u32,
-    _: u32,
-    _: u32,
-    _: u32,
-    e: u32,
-) -> u32 {
-    e
-}
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-on-stack.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-on-stack.stderr
deleted file mode 100644
index cfbdda509e5..00000000000
--- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-on-stack.stderr
+++ /dev/null
@@ -1,4 +0,0 @@
-error: <unknown>:0:0: in function entry_function i32 (i32, i32, i32, i32, i32): secure entry function requires arguments on stack
-
-error: aborting due to 1 previous error
-
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-via-stack.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-via-stack.rs
new file mode 100644
index 00000000000..572d792d5a5
--- /dev/null
+++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-via-stack.rs
@@ -0,0 +1,26 @@
+//@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib
+//@ needs-llvm-components: arm
+#![feature(cmse_nonsecure_entry, no_core, lang_items)]
+#![no_core]
+#[lang = "sized"]
+trait Sized {}
+#[lang = "copy"]
+trait Copy {}
+impl Copy for u32 {}
+
+#[repr(C, align(16))]
+#[allow(unused)]
+pub struct AlignRelevant(u32);
+
+#[no_mangle]
+pub extern "C-cmse-nonsecure-entry" fn f1(_: u32, _: u32, _: u32, _: u32, _: u32, _: u32) {} //~ ERROR [E0798]
+#[no_mangle]
+pub extern "C-cmse-nonsecure-entry" fn f2(_: u32, _: u32, _: u32, _: u16, _: u16) {} //~ ERROR [E0798]
+#[no_mangle]
+pub extern "C-cmse-nonsecure-entry" fn f3(_: u32, _: u64, _: u32) {} //~ ERROR [E0798]
+#[no_mangle]
+pub extern "C-cmse-nonsecure-entry" fn f4(_: AlignRelevant, _: u32) {} //~ ERROR [E0798]
+
+#[no_mangle]
+#[allow(improper_ctypes_definitions)]
+pub extern "C-cmse-nonsecure-entry" fn f5(_: [u32; 5]) {} //~ ERROR [E0798]
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-via-stack.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-via-stack.stderr
new file mode 100644
index 00000000000..b77e64c6bfb
--- /dev/null
+++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-via-stack.stderr
@@ -0,0 +1,43 @@
+error[E0798]: arguments for `"C-cmse-nonsecure-entry"` function too large to pass via registers
+  --> $DIR/params-via-stack.rs:16:78
+   |
+LL | pub extern "C-cmse-nonsecure-entry" fn f1(_: u32, _: u32, _: u32, _: u32, _: u32, _: u32) {}
+   |                                                                              ^^^^^^^^^^^ these arguments don't fit in the available registers
+   |
+   = note: functions with the `"C-cmse-nonsecure-entry"` ABI must pass all their arguments via the 4 32-bit available argument registers
+
+error[E0798]: arguments for `"C-cmse-nonsecure-entry"` function too large to pass via registers
+  --> $DIR/params-via-stack.rs:18:78
+   |
+LL | pub extern "C-cmse-nonsecure-entry" fn f2(_: u32, _: u32, _: u32, _: u16, _: u16) {}
+   |                                                                              ^^^ this argument doesn't fit in the available registers
+   |
+   = note: functions with the `"C-cmse-nonsecure-entry"` ABI must pass all their arguments via the 4 32-bit available argument registers
+
+error[E0798]: arguments for `"C-cmse-nonsecure-entry"` function too large to pass via registers
+  --> $DIR/params-via-stack.rs:20:62
+   |
+LL | pub extern "C-cmse-nonsecure-entry" fn f3(_: u32, _: u64, _: u32) {}
+   |                                                              ^^^ this argument doesn't fit in the available registers
+   |
+   = note: functions with the `"C-cmse-nonsecure-entry"` ABI must pass all their arguments via the 4 32-bit available argument registers
+
+error[E0798]: arguments for `"C-cmse-nonsecure-entry"` function too large to pass via registers
+  --> $DIR/params-via-stack.rs:22:64
+   |
+LL | pub extern "C-cmse-nonsecure-entry" fn f4(_: AlignRelevant, _: u32) {}
+   |                                                                ^^^ this argument doesn't fit in the available registers
+   |
+   = note: functions with the `"C-cmse-nonsecure-entry"` ABI must pass all their arguments via the 4 32-bit available argument registers
+
+error[E0798]: arguments for `"C-cmse-nonsecure-entry"` function too large to pass via registers
+  --> $DIR/params-via-stack.rs:26:46
+   |
+LL | pub extern "C-cmse-nonsecure-entry" fn f5(_: [u32; 5]) {}
+   |                                              ^^^^^^^^ this argument doesn't fit in the available registers
+   |
+   = note: functions with the `"C-cmse-nonsecure-entry"` ABI must pass all their arguments via the 4 32-bit available argument registers
+
+error: aborting due to 5 previous errors
+
+For more information about this error, try `rustc --explain E0798`.
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/return-via-stack.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/return-via-stack.rs
new file mode 100644
index 00000000000..5746d14f9b1
--- /dev/null
+++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/return-via-stack.rs
@@ -0,0 +1,84 @@
+//@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib
+//@ needs-llvm-components: arm
+#![feature(cmse_nonsecure_entry, no_core, lang_items)]
+#![no_core]
+#[lang = "sized"]
+pub trait Sized {}
+#[lang = "copy"]
+pub trait Copy {}
+impl Copy for u32 {}
+impl Copy for u8 {}
+
+#[repr(C)]
+pub struct ReprCU64(u64);
+
+#[repr(C)]
+pub struct ReprCBytes(u8, u8, u8, u8, u8);
+
+#[repr(C)]
+pub struct U64Compound(u32, u32);
+
+#[repr(C, align(16))]
+pub struct ReprCAlign16(u16);
+
+#[no_mangle]
+pub extern "C-cmse-nonsecure-entry" fn f1() -> ReprCU64 {
+    //~^ ERROR [E0798]
+    ReprCU64(0)
+}
+#[no_mangle]
+pub extern "C-cmse-nonsecure-entry" fn f2() -> ReprCBytes {
+    //~^ ERROR [E0798]
+    ReprCBytes(0, 1, 2, 3, 4)
+}
+#[no_mangle]
+pub extern "C-cmse-nonsecure-entry" fn f3() -> U64Compound {
+    //~^ ERROR [E0798]
+    U64Compound(2, 3)
+}
+#[no_mangle]
+pub extern "C-cmse-nonsecure-entry" fn f4() -> ReprCAlign16 {
+    //~^ ERROR [E0798]
+    ReprCAlign16(4)
+}
+
+#[no_mangle]
+#[allow(improper_ctypes_definitions)]
+pub extern "C-cmse-nonsecure-entry" fn f5() -> [u8; 5] {
+    //~^ ERROR [E0798]
+    [0xAA; 5]
+}
+#[no_mangle]
+#[allow(improper_ctypes_definitions)]
+pub extern "C-cmse-nonsecure-entry" fn u128() -> u128 {
+    //~^ ERROR [E0798]
+    123
+}
+#[no_mangle]
+#[allow(improper_ctypes_definitions)]
+pub extern "C-cmse-nonsecure-entry" fn i128() -> i128 {
+    //~^ ERROR [E0798]
+    456
+}
+
+#[repr(Rust)]
+pub union ReprRustUnionU64 {
+    _unused: u64,
+}
+
+#[repr(C)]
+pub union ReprCUnionU64 {
+    _unused: u64,
+}
+
+#[no_mangle]
+#[allow(improper_ctypes_definitions)]
+pub extern "C-cmse-nonsecure-entry" fn union_rust() -> ReprRustUnionU64 {
+    //~^ ERROR [E0798]
+    ReprRustUnionU64 { _unused: 1 }
+}
+#[no_mangle]
+pub extern "C-cmse-nonsecure-entry" fn union_c() -> ReprCUnionU64 {
+    //~^ ERROR [E0798]
+    ReprCUnionU64 { _unused: 2 }
+}
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/return-via-stack.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/return-via-stack.stderr
new file mode 100644
index 00000000000..9c885d95318
--- /dev/null
+++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/return-via-stack.stderr
@@ -0,0 +1,84 @@
+error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers
+  --> $DIR/return-via-stack.rs:25:48
+   |
+LL | pub extern "C-cmse-nonsecure-entry" fn f1() -> ReprCU64 {
+   |                                                ^^^^^^^^ this type doesn't fit in the available registers
+   |
+   = note: functions with the `"C-cmse-nonsecure-entry"` ABI must pass their result via the available return registers
+   = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
+
+error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers
+  --> $DIR/return-via-stack.rs:30:48
+   |
+LL | pub extern "C-cmse-nonsecure-entry" fn f2() -> ReprCBytes {
+   |                                                ^^^^^^^^^^ this type doesn't fit in the available registers
+   |
+   = note: functions with the `"C-cmse-nonsecure-entry"` ABI must pass their result via the available return registers
+   = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
+
+error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers
+  --> $DIR/return-via-stack.rs:35:48
+   |
+LL | pub extern "C-cmse-nonsecure-entry" fn f3() -> U64Compound {
+   |                                                ^^^^^^^^^^^ this type doesn't fit in the available registers
+   |
+   = note: functions with the `"C-cmse-nonsecure-entry"` ABI must pass their result via the available return registers
+   = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
+
+error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers
+  --> $DIR/return-via-stack.rs:40:48
+   |
+LL | pub extern "C-cmse-nonsecure-entry" fn f4() -> ReprCAlign16 {
+   |                                                ^^^^^^^^^^^^ this type doesn't fit in the available registers
+   |
+   = note: functions with the `"C-cmse-nonsecure-entry"` ABI must pass their result via the available return registers
+   = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
+
+error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers
+  --> $DIR/return-via-stack.rs:47:48
+   |
+LL | pub extern "C-cmse-nonsecure-entry" fn f5() -> [u8; 5] {
+   |                                                ^^^^^^^ this type doesn't fit in the available registers
+   |
+   = note: functions with the `"C-cmse-nonsecure-entry"` ABI must pass their result via the available return registers
+   = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
+
+error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers
+  --> $DIR/return-via-stack.rs:53:50
+   |
+LL | pub extern "C-cmse-nonsecure-entry" fn u128() -> u128 {
+   |                                                  ^^^^ this type doesn't fit in the available registers
+   |
+   = note: functions with the `"C-cmse-nonsecure-entry"` ABI must pass their result via the available return registers
+   = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
+
+error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers
+  --> $DIR/return-via-stack.rs:59:50
+   |
+LL | pub extern "C-cmse-nonsecure-entry" fn i128() -> i128 {
+   |                                                  ^^^^ this type doesn't fit in the available registers
+   |
+   = note: functions with the `"C-cmse-nonsecure-entry"` ABI must pass their result via the available return registers
+   = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
+
+error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers
+  --> $DIR/return-via-stack.rs:76:56
+   |
+LL | pub extern "C-cmse-nonsecure-entry" fn union_rust() -> ReprRustUnionU64 {
+   |                                                        ^^^^^^^^^^^^^^^^ this type doesn't fit in the available registers
+   |
+   = note: functions with the `"C-cmse-nonsecure-entry"` ABI must pass their result via the available return registers
+   = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
+
+error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers
+  --> $DIR/return-via-stack.rs:81:53
+   |
+LL | pub extern "C-cmse-nonsecure-entry" fn union_c() -> ReprCUnionU64 {
+   |                                                     ^^^^^^^^^^^^^ this type doesn't fit in the available registers
+   |
+   = note: functions with the `"C-cmse-nonsecure-entry"` ABI must pass their result via the available return registers
+   = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
+
+error: aborting due to 9 previous errors
+
+For more information about this error, try `rustc --explain E0798`.
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/trustzone-only.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/trustzone-only.stderr
new file mode 100644
index 00000000000..77379f7049d
--- /dev/null
+++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/trustzone-only.stderr
@@ -0,0 +1,9 @@
+error[E0570]: `"C-cmse-nonsecure-entry"` is not a supported ABI for the current target
+  --> $DIR/trustzone-only.rs:5:1
+   |
+LL | pub extern "C-cmse-nonsecure-entry" fn entry_function(input: u32) -> u32 {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0570`.
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/via-registers.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/via-registers.rs
new file mode 100644
index 00000000000..8978b35d356
--- /dev/null
+++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/via-registers.rs
@@ -0,0 +1,89 @@
+//@ build-pass
+//@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib
+//@ needs-llvm-components: arm
+#![feature(cmse_nonsecure_entry, no_core, lang_items)]
+#![no_core]
+#![crate_type = "lib"]
+#[lang = "sized"]
+pub trait Sized {}
+#[lang = "copy"]
+trait Copy {}
+impl Copy for u32 {}
+impl Copy for u8 {}
+
+#[repr(transparent)]
+pub struct ReprTransparentStruct<T> {
+    _marker1: (),
+    _marker2: (),
+    field: T,
+    _marker3: (),
+}
+
+#[repr(transparent)]
+pub enum ReprTransparentEnumU64 {
+    A(u64),
+}
+
+#[repr(C)]
+pub struct U32Compound(u16, u16);
+
+#[no_mangle]
+pub extern "C-cmse-nonsecure-entry" fn inputs1() {}
+#[no_mangle]
+pub extern "C-cmse-nonsecure-entry" fn inputs2(_: u32, _: u32, _: u32, _: u32) {}
+#[no_mangle]
+pub extern "C-cmse-nonsecure-entry" fn inputs3(_: u64, _: u64) {}
+#[no_mangle]
+#[allow(improper_ctypes_definitions)]
+pub extern "C-cmse-nonsecure-entry" fn inputs4(_: u128) {}
+#[no_mangle]
+pub extern "C-cmse-nonsecure-entry" fn inputs5(_: f64, _: f32, _: f32) {}
+#[no_mangle]
+pub extern "C-cmse-nonsecure-entry" fn inputs6(_: ReprTransparentStruct<u64>, _: U32Compound) {}
+#[no_mangle]
+#[allow(improper_ctypes_definitions)]
+pub extern "C-cmse-nonsecure-entry" fn inputs7(_: [u32; 4]) {}
+
+#[no_mangle]
+pub extern "C-cmse-nonsecure-entry" fn outputs1() -> u32 {
+    0
+}
+#[no_mangle]
+pub extern "C-cmse-nonsecure-entry" fn outputs2() -> u64 {
+    0
+}
+#[no_mangle]
+pub extern "C-cmse-nonsecure-entry" fn outputs3() -> i64 {
+    0
+}
+#[no_mangle]
+pub extern "C-cmse-nonsecure-entry" fn outputs4() -> f64 {
+    0.0
+}
+#[no_mangle]
+#[allow(improper_ctypes_definitions)]
+pub extern "C-cmse-nonsecure-entry" fn outputs5() -> [u8; 4] {
+    [0xAA; 4]
+}
+#[no_mangle]
+pub extern "C-cmse-nonsecure-entry" fn outputs6() -> ReprTransparentStruct<u64> {
+    ReprTransparentStruct { _marker1: (), _marker2: (), field: 0xAA, _marker3: () }
+}
+#[no_mangle]
+pub extern "C-cmse-nonsecure-entry" fn outputs7(
+) -> ReprTransparentStruct<ReprTransparentStruct<u64>> {
+    ReprTransparentStruct {
+        _marker1: (),
+        _marker2: (),
+        field: ReprTransparentStruct { _marker1: (), _marker2: (), field: 0xAA, _marker3: () },
+        _marker3: (),
+    }
+}
+#[no_mangle]
+pub extern "C-cmse-nonsecure-entry" fn outputs8() -> ReprTransparentEnumU64 {
+    ReprTransparentEnumU64::A(0)
+}
+#[no_mangle]
+pub extern "C-cmse-nonsecure-entry" fn outputs9() -> U32Compound {
+    U32Compound(1, 2)
+}