Skip to content

Commit c7ff46c

Browse files
folkertdevtdittr
andcommitted
move cmse ABI validation into its own module
Co-authored-by: Tamme Dittrich <[email protected]>
1 parent 36d2371 commit c7ff46c

File tree

4 files changed

+82
-59
lines changed

4 files changed

+82
-59
lines changed

compiler/rustc_codegen_ssa/src/errors.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -1040,9 +1040,9 @@ pub struct CompilerBuiltinsCannotCall {
10401040
pub struct CmseCallInputsStackSpill {
10411041
#[primary_span]
10421042
#[label(codegen_ssa_call)]
1043-
pub span: Span,
1043+
pub call_site_span: Span,
10441044
#[label]
1045-
pub func_span: Span,
1045+
pub function_definition_span: Span,
10461046
}
10471047

10481048
#[derive(Diagnostic)]
@@ -1051,7 +1051,7 @@ pub struct CmseCallInputsStackSpill {
10511051
pub struct CmseCallOutputStackSpill {
10521052
#[primary_span]
10531053
#[label(codegen_ssa_call)]
1054-
pub span: Span,
1054+
pub call_site_span: Span,
10551055
#[label]
1056-
pub func_span: Span,
1056+
pub function_definition_span: Span,
10571057
}

compiler/rustc_codegen_ssa/src/mir/block.rs

+5-55
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,9 @@ use super::{CachedLlbb, FunctionCx, LocalRef};
55

66
use crate::base;
77
use crate::common::{self, IntPredicate};
8-
use crate::errors::{
9-
CmseCallInputsStackSpill, CmseCallOutputStackSpill, CompilerBuiltinsCannotCall,
10-
};
8+
use crate::errors::CompilerBuiltinsCannotCall;
119
use crate::meth;
10+
use crate::mir::cmse;
1211
use crate::traits::*;
1312
use crate::MemFlags;
1413

@@ -836,58 +835,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
836835
// Create the callee. This is a fn ptr or zero-sized and hence a kind of scalar.
837836
let callee = self.codegen_operand(bx, func);
838837

839-
let fn_sig = callee.layout.ty.fn_sig(bx.tcx()).skip_binder();
840-
841-
if let rustc_target::spec::abi::Abi::CCmseNonSecureCall = fn_sig.abi {
842-
let mut accum = 0u64;
843-
844-
for arg_def in fn_sig.inputs().iter() {
845-
let layout = bx.layout_of(*arg_def);
846-
847-
let align = layout.layout.align().abi.bytes();
848-
let size = layout.layout.size().bytes();
849-
850-
accum += size;
851-
accum = accum.next_multiple_of(Ord::max(4, align));
852-
}
853-
854-
// the available argument space is 16 bytes (4 32-bit registers) in total
855-
let available_space = 16;
856-
857-
if accum > available_space {
858-
let err = CmseCallInputsStackSpill { span, func_span: func.span(self.mir) };
859-
bx.tcx().dcx().emit_err(err);
860-
}
861-
862-
let mut ret_layout = bx.layout_of(fn_sig.output());
863-
864-
// unwrap any `repr(transparent)` wrappers
865-
loop {
866-
if ret_layout.is_transparent::<Bx>() {
867-
match ret_layout.non_1zst_field(bx) {
868-
None => break,
869-
Some((_, layout)) => ret_layout = layout,
870-
}
871-
} else {
872-
break;
873-
}
874-
}
875-
876-
let valid_2register_return_types =
877-
[bx.tcx().types.i64, bx.tcx().types.u64, bx.tcx().types.f64];
878-
879-
// A Composite Type larger than 4 bytes is stored in memory at an address
880-
// passed as an extra argument when the function was called. That is not allowed
881-
// for cmse_nonsecure_entry functions.
882-
let is_valid_output = ret_layout.layout.size().bytes() <= 4
883-
|| valid_2register_return_types.contains(&ret_layout.ty);
884-
885-
if !is_valid_output {
886-
let err = CmseCallOutputStackSpill { span, func_span: func.span(self.mir) };
887-
bx.tcx().dcx().emit_err(err);
888-
}
889-
}
890-
891838
let (instance, mut llfn) = match *callee.layout.ty.kind() {
892839
ty::FnDef(def_id, args) => (
893840
Some(
@@ -923,6 +870,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
923870
let sig = callee.layout.ty.fn_sig(bx.tcx());
924871
let abi = sig.abi();
925872

873+
// emit errors if cmse ABI conditions are violated
874+
cmse::validate_cmse_abi(bx, &sig.skip_binder(), span, func.span(self.mir));
875+
926876
let extra_args = &args[sig.inputs().skip_binder().len()..];
927877
let extra_args = bx.tcx().mk_type_list_from_iter(extra_args.iter().map(|op_arg| {
928878
let op_ty = op_arg.node.ty(self.mir, bx.tcx());
+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
use rustc_middle::ty::FnSig;
2+
use rustc_span::Span;
3+
4+
use crate::errors::{CmseCallInputsStackSpill, CmseCallOutputStackSpill};
5+
use crate::traits::BuilderMethods;
6+
7+
/// Check conditions on inputs and outputs that the cmse ABIs impose: arguments and results MUST be
8+
/// returned via registers (i.e. MUST NOT spill to the stack). LLVM will also validate these
9+
/// conditions, but by checking them here rustc can emit nicer error messages.
10+
pub fn validate_cmse_abi<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
11+
bx: &Bx,
12+
fn_sig: &FnSig<'tcx>,
13+
call_site_span: Span,
14+
function_definition_span: Span,
15+
) {
16+
if let rustc_target::spec::abi::Abi::CCmseNonSecureCall = fn_sig.abi {
17+
if !has_valid_inputs(bx, fn_sig) {
18+
let err = CmseCallInputsStackSpill { call_site_span, function_definition_span };
19+
bx.tcx().dcx().emit_err(err);
20+
}
21+
22+
if !has_valid_output(bx, fn_sig) {
23+
let err = CmseCallOutputStackSpill { call_site_span, function_definition_span };
24+
bx.tcx().dcx().emit_err(err);
25+
}
26+
}
27+
}
28+
29+
/// Returns whether the inputs will fit into the available registers
30+
fn has_valid_inputs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(bx: &Bx, fn_sig: &FnSig<'tcx>) -> bool {
31+
let mut accum = 0u64;
32+
33+
for arg_def in fn_sig.inputs().iter() {
34+
let layout = bx.layout_of(*arg_def);
35+
36+
let align = layout.layout.align().abi.bytes();
37+
let size = layout.layout.size().bytes();
38+
39+
accum += size;
40+
accum = accum.next_multiple_of(Ord::max(4, align));
41+
}
42+
43+
// the available argument space is 16 bytes (4 32-bit registers) in total
44+
let available_space = 16;
45+
46+
accum <= available_space
47+
}
48+
49+
/// Returns whether the output will fit into the available registers
50+
fn has_valid_output<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(bx: &Bx, fn_sig: &FnSig<'tcx>) -> bool {
51+
let mut ret_layout = bx.layout_of(fn_sig.output());
52+
53+
// unwrap any `repr(transparent)` wrappers
54+
loop {
55+
if ret_layout.is_transparent::<Bx>() {
56+
match ret_layout.non_1zst_field(bx) {
57+
None => break,
58+
Some((_, layout)) => ret_layout = layout,
59+
}
60+
} else {
61+
break;
62+
}
63+
}
64+
65+
// Fundamental types of size 8 can be passed via registers according to the ABI
66+
let valid_2register_return_types = [bx.tcx().types.i64, bx.tcx().types.u64, bx.tcx().types.f64];
67+
68+
// A Composite Type larger than 4 bytes is stored in memory at an address
69+
// passed as an extra argument when the function was called. That is not allowed
70+
// for cmse_nonsecure_entry functions.
71+
ret_layout.layout.size().bytes() <= 4 || valid_2register_return_types.contains(&ret_layout.ty)
72+
}

compiler/rustc_codegen_ssa/src/mir/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use std::iter;
1616

1717
mod analyze;
1818
mod block;
19+
mod cmse;
1920
pub mod constant;
2021
pub mod coverageinfo;
2122
pub mod debuginfo;

0 commit comments

Comments
 (0)