about summary refs log tree commit diff
path: root/compiler/rustc_codegen_ssa/src
diff options
context:
space:
mode:
authorFolkert <folkert@folkertdev.nl>2024-07-16 15:57:00 +0200
committerFolkert <folkert@folkertdev.nl>2024-07-16 15:58:33 +0200
commit50ba821e12f51903aba6902b2b404edbc94011d2 (patch)
tree75b79e1400de2bc59d62b764ed7d8db27061b057 /compiler/rustc_codegen_ssa/src
parentd3dd34a1d4a7e25a92a0d65994bb01a5d17c59ee (diff)
downloadrust-50ba821e12f51903aba6902b2b404edbc94011d2.tar.gz
rust-50ba821e12f51903aba6902b2b404edbc94011d2.zip
add rust error message for CMSE stack spill
when the `C-cmse-nonsecure-call` ABI is used, arguments and return values must be passed via registers. Failing to do so (i.e. spilling to the stack) causes an LLVM error down the line, but now rustc will properly emit an error a bit earlier in the chain
Diffstat (limited to 'compiler/rustc_codegen_ssa/src')
-rw-r--r--compiler/rustc_codegen_ssa/src/errors.rs22
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/block.rs56
2 files changed, 77 insertions, 1 deletions
diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs
index e9d31db9254..13a2dce3e69 100644
--- a/compiler/rustc_codegen_ssa/src/errors.rs
+++ b/compiler/rustc_codegen_ssa/src/errors.rs
@@ -1033,3 +1033,25 @@ pub struct CompilerBuiltinsCannotCall {
     pub caller: String,
     pub callee: String,
 }
+
+#[derive(Diagnostic)]
+#[diag(codegen_ssa_cmse_call_inputs_stack_spill, code = E0798)]
+#[note]
+pub struct CmseCallInputsStackSpill {
+    #[primary_span]
+    #[label(codegen_ssa_call)]
+    pub span: Span,
+    #[label]
+    pub func_span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(codegen_ssa_cmse_call_output_stack_spill, code = E0798)]
+#[note]
+pub struct CmseCallOutputStackSpill {
+    #[primary_span]
+    #[label(codegen_ssa_call)]
+    pub span: Span,
+    #[label]
+    pub func_span: Span,
+}
diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs
index 6a5525dc2b3..8011604d576 100644
--- a/compiler/rustc_codegen_ssa/src/mir/block.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/block.rs
@@ -5,7 +5,9 @@ use super::{CachedLlbb, FunctionCx, LocalRef};
 
 use crate::base;
 use crate::common::{self, IntPredicate};
-use crate::errors::CompilerBuiltinsCannotCall;
+use crate::errors::{
+    CmseCallInputsStackSpill, CmseCallOutputStackSpill, CompilerBuiltinsCannotCall,
+};
 use crate::meth;
 use crate::traits::*;
 use crate::MemFlags;
@@ -834,6 +836,58 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         // Create the callee. This is a fn ptr or zero-sized and hence a kind of scalar.
         let callee = self.codegen_operand(bx, func);
 
+        let fn_sig = callee.layout.ty.fn_sig(bx.tcx()).skip_binder();
+
+        if let rustc_target::spec::abi::Abi::CCmseNonSecureCall = fn_sig.abi {
+            let mut accum = 0u64;
+
+            for arg_def in fn_sig.inputs().iter() {
+                let layout = bx.layout_of(*arg_def);
+
+                let align = layout.layout.align().abi.bytes();
+                let size = layout.layout.size().bytes();
+
+                accum += size;
+                accum = accum.next_multiple_of(Ord::max(4, align));
+            }
+
+            // the available argument space is 16 bytes (4 32-bit registers) in total
+            let available_space = 16;
+
+            if accum > available_space {
+                let err = CmseCallInputsStackSpill { span, func_span: func.span(self.mir) };
+                bx.tcx().dcx().emit_err(err);
+            }
+
+            let mut ret_layout = bx.layout_of(fn_sig.output());
+
+            // unwrap any `repr(transparent)` wrappers
+            loop {
+                if ret_layout.is_transparent::<Bx>() {
+                    match ret_layout.non_1zst_field(bx) {
+                        None => break,
+                        Some((_, layout)) => ret_layout = layout,
+                    }
+                } else {
+                    break;
+                }
+            }
+
+            let valid_2register_return_types =
+                [bx.tcx().types.i64, bx.tcx().types.u64, bx.tcx().types.f64];
+
+            // A Composite Type larger than 4 bytes is stored in memory at an address
+            // passed as an extra argument when the function was called. That is not allowed
+            // for cmse_nonsecure_entry functions.
+            let is_valid_output = ret_layout.layout.size().bytes() <= 4
+                || valid_2register_return_types.contains(&ret_layout.ty);
+
+            if !is_valid_output {
+                let err = CmseCallOutputStackSpill { span, func_span: func.span(self.mir) };
+                bx.tcx().dcx().emit_err(err);
+            }
+        }
+
         let (instance, mut llfn) = match *callee.layout.ty.kind() {
             ty::FnDef(def_id, args) => (
                 Some(