cmse: more accurate spans for excess parameters

Use the span of the types that don't fit, rather than the span of the
whole signature.
This commit is contained in:
Folkert de Vries
2025-10-08 13:40:42 +02:00
parent f62be66e94
commit f4d8bd3b47
4 changed files with 43 additions and 58 deletions

View File

@@ -1616,7 +1616,7 @@ pub(crate) struct InvalidGenericReceiverTy<'tcx> {
pub(crate) struct CmseInputsStackSpill {
#[primary_span]
#[label]
pub span: Span,
pub spans: Vec<Span>,
pub plural: bool,
pub abi: ExternAbi,
}

View File

@@ -44,20 +44,8 @@ pub(crate) fn validate_cmse_abi<'tcx>(
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 = if let Some(ident) = fn_ptr_ty.param_idents[index] {
ident.span.to(fn_ptr_ty.decl.inputs[index].span)
} else {
fn_ptr_ty.decl.inputs[index].span
}
.to(fn_ptr_ty.decl.inputs.last().unwrap().span);
let plural = fn_ptr_ty.param_idents.len() - index != 1;
dcx.emit_err(errors::CmseInputsStackSpill { span, plural, abi });
}
match is_valid_cmse_inputs(tcx, dcx, fn_sig, fn_ptr_ty.decl, abi) {
Ok(()) => {}
Err(layout_err) => {
if should_emit_generic_error(abi, layout_err) {
dcx.emit_err(errors::CmseCallGeneric { span: *fn_ptr_span });
@@ -65,18 +53,11 @@ pub(crate) fn validate_cmse_abi<'tcx>(
}
}
match is_valid_cmse_output(tcx, fn_sig) {
Ok(true) => {}
Ok(false) => {
let span = fn_ptr_ty.decl.output.span();
dcx.emit_err(errors::CmseOutputStackSpill { span, abi });
if let Err(layout_err) = is_valid_cmse_output(tcx, dcx, fn_sig, fn_ptr_ty.decl, abi) {
if should_emit_generic_error(abi, layout_err) {
dcx.emit_err(errors::CmseCallGeneric { span: *fn_ptr_span });
}
Err(layout_err) => {
if should_emit_generic_error(abi, layout_err) {
dcx.emit_err(errors::CmseCallGeneric { span: *fn_ptr_span });
}
}
};
}
}
ExternAbi::CmseNonSecureEntry => {
let hir_node = tcx.hir_node(hir_id);
@@ -91,15 +72,8 @@ pub(crate) fn validate_cmse_abi<'tcx>(
return;
}
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 });
}
match is_valid_cmse_inputs(tcx, dcx, fn_sig, decl, abi) {
Ok(()) => {}
Err(layout_err) => {
if should_emit_generic_error(abi, layout_err) {
dcx.emit_err(errors::CmseEntryGeneric { span: *fn_sig_span });
@@ -107,18 +81,11 @@ pub(crate) fn validate_cmse_abi<'tcx>(
}
}
match is_valid_cmse_output(tcx, fn_sig) {
Ok(true) => {}
Ok(false) => {
let span = decl.output.span();
dcx.emit_err(errors::CmseOutputStackSpill { span, abi });
if let Err(layout_err) = is_valid_cmse_output(tcx, dcx, fn_sig, decl, abi) {
if should_emit_generic_error(abi, layout_err) {
dcx.emit_err(errors::CmseEntryGeneric { span: *fn_sig_span });
}
Err(layout_err) => {
if should_emit_generic_error(abi, layout_err) {
dcx.emit_err(errors::CmseEntryGeneric { span: *fn_sig_span });
}
}
};
}
}
_ => (),
}
@@ -127,16 +94,19 @@ pub(crate) fn validate_cmse_abi<'tcx>(
/// Returns whether the inputs will fit into the available registers
fn is_valid_cmse_inputs<'tcx>(
tcx: TyCtxt<'tcx>,
dcx: DiagCtxtHandle<'_>,
fn_sig: ty::PolyFnSig<'tcx>,
) -> Result<Result<(), usize>, &'tcx LayoutError<'tcx>> {
let mut span = None;
fn_decl: &hir::FnDecl<'tcx>,
abi: ExternAbi,
) -> Result<(), &'tcx LayoutError<'tcx>> {
let mut accum = 0u64;
let mut excess_argument_spans = Vec::new();
// this type is only used for layout computation, which does not rely on regions
let fn_sig = tcx.instantiate_bound_regions_with_erased(fn_sig);
let fn_sig = tcx.erase_and_anonymize_regions(fn_sig);
for (index, ty) in fn_sig.inputs().iter().enumerate() {
for (ty, hir_ty) in fn_sig.inputs().iter().zip(fn_decl.inputs) {
let layout = tcx.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(*ty))?;
let align = layout.layout.align().bytes();
@@ -147,21 +117,28 @@ fn is_valid_cmse_inputs<'tcx>(
// i.e. exceeds 4 32-bit registers
if accum > 16 {
span = span.or(Some(index));
excess_argument_spans.push(hir_ty.span);
}
}
match span {
None => Ok(Ok(())),
Some(span) => Ok(Err(span)),
if !excess_argument_spans.is_empty() {
// fn f(x: u32, y: u32, z: u32, w: u16, q: u16) -> u32,
// ^^^^^^
let plural = excess_argument_spans.len() != 1;
dcx.emit_err(errors::CmseInputsStackSpill { spans: excess_argument_spans, plural, abi });
}
Ok(())
}
/// Returns whether the output will fit into the available registers
fn is_valid_cmse_output<'tcx>(
tcx: TyCtxt<'tcx>,
dcx: DiagCtxtHandle<'_>,
fn_sig: ty::PolyFnSig<'tcx>,
) -> Result<bool, &'tcx LayoutError<'tcx>> {
fn_decl: &hir::FnDecl<'tcx>,
abi: ExternAbi,
) -> Result<(), &'tcx LayoutError<'tcx>> {
// this type is only used for layout computation, which does not rely on regions
let fn_sig = tcx.instantiate_bound_regions_with_erased(fn_sig);
let fn_sig = tcx.erase_and_anonymize_regions(fn_sig);
@@ -183,7 +160,11 @@ fn is_valid_cmse_output<'tcx>(
let typing_env = ty::TypingEnv::fully_monomorphized();
let layout = tcx.layout_of(typing_env.as_query_input(return_type))?;
Ok(is_valid_cmse_output_layout(layout))
if !is_valid_cmse_output_layout(layout) {
dcx.emit_err(errors::CmseOutputStackSpill { span: fn_decl.output.span(), abi });
}
Ok(())
}
/// Returns whether the output will fit into the available registers

View File

@@ -1,8 +1,10 @@
error[E0798]: arguments for `"cmse-nonsecure-call"` function too large to pass via registers
--> $DIR/params-via-stack.rs:16:61
--> $DIR/params-via-stack.rs:16:64
|
LL | f1: extern "cmse-nonsecure-call" fn(u32, u32, u32, u32, x: u32, y: u32),
| ^^^^^^^^^^^^^^ these arguments don't fit in the available registers
| ^^^ ^^^ these arguments don't fit in the available registers
| |
| these arguments don't fit in the available registers
|
= note: functions with the `"cmse-nonsecure-call"` ABI must pass all their arguments via the 4 32-bit available argument registers

View File

@@ -2,7 +2,9 @@ error[E0798]: arguments for `"cmse-nonsecure-entry"` function too large to pass
--> $DIR/params-via-stack.rs:15:76
|
LL | pub extern "cmse-nonsecure-entry" fn f1(_: u32, _: u32, _: u32, _: u32, _: u32, _: u32) {}
| ^^^^^^^^^^^ these arguments don't fit in the available registers
| ^^^ ^^^ these arguments don't fit in the available registers
| |
| these arguments don't fit in the available registers
|
= note: functions with the `"cmse-nonsecure-entry"` ABI must pass all their arguments via the 4 32-bit available argument registers