about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_codegen_ssa/src/errors.rs8
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/block.rs60
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/cmse.rs72
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/mod.rs1
4 files changed, 82 insertions, 59 deletions
diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs
index 13a2dce3e69..af7835633cf 100644
--- a/compiler/rustc_codegen_ssa/src/errors.rs
+++ b/compiler/rustc_codegen_ssa/src/errors.rs
@@ -1040,9 +1040,9 @@ pub struct CompilerBuiltinsCannotCall {
 pub struct CmseCallInputsStackSpill {
     #[primary_span]
     #[label(codegen_ssa_call)]
-    pub span: Span,
+    pub call_site_span: Span,
     #[label]
-    pub func_span: Span,
+    pub function_definition_span: Span,
 }
 
 #[derive(Diagnostic)]
@@ -1051,7 +1051,7 @@ pub struct CmseCallInputsStackSpill {
 pub struct CmseCallOutputStackSpill {
     #[primary_span]
     #[label(codegen_ssa_call)]
-    pub span: Span,
+    pub call_site_span: Span,
     #[label]
-    pub func_span: Span,
+    pub function_definition_span: Span,
 }
diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs
index 8011604d576..24d3b8ad264 100644
--- a/compiler/rustc_codegen_ssa/src/mir/block.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/block.rs
@@ -5,10 +5,9 @@ use super::{CachedLlbb, FunctionCx, LocalRef};
 
 use crate::base;
 use crate::common::{self, IntPredicate};
-use crate::errors::{
-    CmseCallInputsStackSpill, CmseCallOutputStackSpill, CompilerBuiltinsCannotCall,
-};
+use crate::errors::CompilerBuiltinsCannotCall;
 use crate::meth;
+use crate::mir::cmse;
 use crate::traits::*;
 use crate::MemFlags;
 
@@ -836,58 +835,6 @@ 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(
@@ -923,6 +870,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         let sig = callee.layout.ty.fn_sig(bx.tcx());
         let abi = sig.abi();
 
+        // emit errors if cmse ABI conditions are violated
+        cmse::validate_cmse_abi(bx, &sig.skip_binder(), span, func.span(self.mir));
+
         let extra_args = &args[sig.inputs().skip_binder().len()..];
         let extra_args = bx.tcx().mk_type_list_from_iter(extra_args.iter().map(|op_arg| {
             let op_ty = op_arg.node.ty(self.mir, bx.tcx());
diff --git a/compiler/rustc_codegen_ssa/src/mir/cmse.rs b/compiler/rustc_codegen_ssa/src/mir/cmse.rs
new file mode 100644
index 00000000000..d3583d79c00
--- /dev/null
+++ b/compiler/rustc_codegen_ssa/src/mir/cmse.rs
@@ -0,0 +1,72 @@
+use rustc_middle::ty::FnSig;
+use rustc_span::Span;
+
+use crate::errors::{CmseCallInputsStackSpill, CmseCallOutputStackSpill};
+use crate::traits::BuilderMethods;
+
+/// Check conditions on inputs and outputs that the cmse ABIs impose: arguments and results MUST be
+/// returned via registers (i.e. MUST NOT spill to the stack). LLVM will also validate these
+/// conditions, but by checking them here rustc can emit nicer error messages.
+pub fn validate_cmse_abi<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
+    bx: &Bx,
+    fn_sig: &FnSig<'tcx>,
+    call_site_span: Span,
+    function_definition_span: Span,
+) {
+    if let rustc_target::spec::abi::Abi::CCmseNonSecureCall = fn_sig.abi {
+        if !has_valid_inputs(bx, fn_sig) {
+            let err = CmseCallInputsStackSpill { call_site_span, function_definition_span };
+            bx.tcx().dcx().emit_err(err);
+        }
+
+        if !has_valid_output(bx, fn_sig) {
+            let err = CmseCallOutputStackSpill { call_site_span, function_definition_span };
+            bx.tcx().dcx().emit_err(err);
+        }
+    }
+}
+
+/// Returns whether the inputs will fit into the available registers
+fn has_valid_inputs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(bx: &Bx, fn_sig: &FnSig<'tcx>) -> bool {
+    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;
+
+    accum <= available_space
+}
+
+/// Returns whether the output will fit into the available registers
+fn has_valid_output<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(bx: &Bx, fn_sig: &FnSig<'tcx>) -> bool {
+    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;
+        }
+    }
+
+    // Fundamental types of size 8 can be passed via registers according to the ABI
+    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.
+    ret_layout.layout.size().bytes() <= 4 || valid_2register_return_types.contains(&ret_layout.ty)
+}
diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs
index 61f57c9030a..ec28def8b83 100644
--- a/compiler/rustc_codegen_ssa/src/mir/mod.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs
@@ -16,6 +16,7 @@ use std::iter;
 
 mod analyze;
 mod block;
+mod cmse;
 pub mod constant;
 pub mod coverageinfo;
 pub mod debuginfo;