mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 08:13:41 +00:00
move cmse ABI validation into its own module
Co-authored-by: Tamme Dittrich <tamme@tweedegolf.com>
This commit is contained in:
parent
36d23713fb
commit
c7ff46c971
@ -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,
|
||||
}
|
||||
|
@ -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());
|
||||
|
72
compiler/rustc_codegen_ssa/src/mir/cmse.rs
Normal file
72
compiler/rustc_codegen_ssa/src/mir/cmse.rs
Normal file
@ -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)
|
||||
}
|
@ -16,6 +16,7 @@ use std::iter;
|
||||
|
||||
mod analyze;
|
||||
mod block;
|
||||
mod cmse;
|
||||
pub mod constant;
|
||||
pub mod coverageinfo;
|
||||
pub mod debuginfo;
|
||||
|
Loading…
Reference in New Issue
Block a user