-
Notifications
You must be signed in to change notification settings - Fork 12.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Rollup merge of #127814 - folkertdev:c-cmse-nonsecure-call-error-mess…
…ages, r=oli-obk `C-cmse-nonsecure-call`: improved error messages tracking issue: #81391 issue for the error messages (partially implemented by this PR): #81347 related, in that it also deals with CMSE: #127766 When using the `C-cmse-nonsecure-call` ABI, both the arguments and return value must be passed via registers. Previously, when violating this constraint, an ugly LLVM error would be shown. Now, the rust compiler itself will print a pretty message and link to more information.
- Loading branch information
Showing
16 changed files
with
569 additions
and
55 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
Functions marked as `C-cmse-nonsecure-call` place restrictions on their | ||
inputs and outputs. | ||
|
||
- inputs must fit in the 4 available 32-bit argument registers. Alignment | ||
is relevant. | ||
- outputs must either fit in 4 bytes, or be a foundational type of | ||
size 8 (`i64`, `u64`, `f64`). | ||
- no generics can be used in the signature | ||
|
||
For more information, | ||
see [arm's aapcs32](https://github.com/ARM-software/abi-aa/releases). | ||
|
||
Erroneous code example: | ||
|
||
```ignore (only fails on supported targets) | ||
#![feature(abi_c_cmse_nonsecure_call)] | ||
#[no_mangle] | ||
pub fn test( | ||
f: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32, u32) -> u32, | ||
) -> u32 { | ||
f(1, 2, 3, 4, 5) | ||
} | ||
``` | ||
|
||
Arguments' alignment is respected. In the example below, padding is inserted | ||
so that the `u64` argument is passed in registers r2 and r3. There is then no | ||
room left for the final `f32` argument | ||
|
||
```ignore (only fails on supported targets) | ||
#![feature(abi_c_cmse_nonsecure_call)] | ||
#[no_mangle] | ||
pub fn test( | ||
f: extern "C-cmse-nonsecure-call" fn(u32, u64, f32) -> u32, | ||
) -> u32 { | ||
f(1, 2, 3.0) | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -536,6 +536,7 @@ E0794: 0794, | |
E0795: 0795, | ||
E0796: 0796, | ||
E0797: 0797, | ||
E0798: 0798, | ||
); | ||
) | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
156 changes: 156 additions & 0 deletions
156
compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
use rustc_errors::DiagCtxtHandle; | ||
use rustc_hir as hir; | ||
use rustc_hir::HirId; | ||
use rustc_middle::ty::layout::LayoutError; | ||
use rustc_middle::ty::{self, ParamEnv, TyCtxt}; | ||
use rustc_span::Span; | ||
use rustc_target::spec::abi; | ||
|
||
use crate::errors; | ||
|
||
/// 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<'tcx>( | ||
tcx: TyCtxt<'tcx>, | ||
dcx: DiagCtxtHandle<'_>, | ||
hir_id: HirId, | ||
abi: abi::Abi, | ||
fn_sig: ty::PolyFnSig<'tcx>, | ||
) { | ||
if let abi::Abi::CCmseNonSecureCall = abi { | ||
let hir_node = tcx.hir_node(hir_id); | ||
let hir::Node::Ty(hir::Ty { | ||
span: bare_fn_span, | ||
kind: hir::TyKind::BareFn(bare_fn_ty), | ||
.. | ||
}) = hir_node | ||
else { | ||
// might happen when this ABI is used incorrectly. That will be handled elsewhere | ||
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 = bare_fn_ty.param_names[index] | ||
.span | ||
.to(bare_fn_ty.decl.inputs[index].span) | ||
.to(bare_fn_ty.decl.inputs.last().unwrap().span); | ||
let plural = bare_fn_ty.param_names.len() - index != 1; | ||
dcx.emit_err(errors::CmseCallInputsStackSpill { span, plural }); | ||
} | ||
Err(layout_err) => { | ||
if let Some(err) = cmse_layout_err(layout_err, *bare_fn_span) { | ||
dcx.emit_err(err); | ||
} | ||
} | ||
} | ||
|
||
match is_valid_cmse_output(tcx, fn_sig) { | ||
Ok(true) => {} | ||
Ok(false) => { | ||
let span = bare_fn_ty.decl.output.span(); | ||
dcx.emit_err(errors::CmseCallOutputStackSpill { span }); | ||
} | ||
Err(layout_err) => { | ||
if let Some(err) = cmse_layout_err(layout_err, *bare_fn_span) { | ||
dcx.emit_err(err); | ||
} | ||
} | ||
}; | ||
} | ||
} | ||
|
||
/// Returns whether the inputs will fit into the available registers | ||
fn is_valid_cmse_inputs<'tcx>( | ||
tcx: TyCtxt<'tcx>, | ||
fn_sig: ty::PolyFnSig<'tcx>, | ||
) -> Result<Result<(), usize>, &'tcx LayoutError<'tcx>> { | ||
let mut span = None; | ||
let mut accum = 0u64; | ||
|
||
for (index, arg_def) in fn_sig.inputs().iter().enumerate() { | ||
let layout = tcx.layout_of(ParamEnv::reveal_all().and(*arg_def.skip_binder()))?; | ||
|
||
let align = layout.layout.align().abi.bytes(); | ||
let size = layout.layout.size().bytes(); | ||
|
||
accum += size; | ||
accum = accum.next_multiple_of(Ord::max(4, align)); | ||
|
||
// i.e. exceeds 4 32-bit registers | ||
if accum > 16 { | ||
span = span.or(Some(index)); | ||
} | ||
} | ||
|
||
match span { | ||
None => Ok(Ok(())), | ||
Some(span) => Ok(Err(span)), | ||
} | ||
} | ||
|
||
/// Returns whether the output will fit into the available registers | ||
fn is_valid_cmse_output<'tcx>( | ||
tcx: TyCtxt<'tcx>, | ||
fn_sig: ty::PolyFnSig<'tcx>, | ||
) -> Result<bool, &'tcx LayoutError<'tcx>> { | ||
let mut ret_ty = fn_sig.output().skip_binder(); | ||
let layout = tcx.layout_of(ParamEnv::reveal_all().and(ret_ty))?; | ||
let size = layout.layout.size().bytes(); | ||
|
||
if size <= 4 { | ||
return Ok(true); | ||
} else if size > 8 { | ||
return Ok(false); | ||
} | ||
|
||
// next we need to peel any repr(transparent) layers off | ||
'outer: loop { | ||
let ty::Adt(adt_def, args) = ret_ty.kind() else { | ||
break; | ||
}; | ||
|
||
if !adt_def.repr().transparent() { | ||
break; | ||
} | ||
|
||
// the first field with non-trivial size and alignment must be the data | ||
for variant_def in adt_def.variants() { | ||
for field_def in variant_def.fields.iter() { | ||
let ty = field_def.ty(tcx, args); | ||
let layout = tcx.layout_of(ParamEnv::reveal_all().and(ty))?; | ||
|
||
if !layout.layout.is_1zst() { | ||
ret_ty = ty; | ||
continue 'outer; | ||
} | ||
} | ||
} | ||
} | ||
|
||
Ok(ret_ty == tcx.types.i64 || ret_ty == tcx.types.u64 || ret_ty == tcx.types.f64) | ||
} | ||
|
||
fn cmse_layout_err<'tcx>( | ||
layout_err: &'tcx LayoutError<'tcx>, | ||
span: Span, | ||
) -> Option<crate::errors::CmseCallGeneric> { | ||
use LayoutError::*; | ||
|
||
match layout_err { | ||
Unknown(ty) => { | ||
if ty.is_impl_trait() { | ||
None // prevent double reporting of this error | ||
} else { | ||
Some(errors::CmseCallGeneric { span }) | ||
} | ||
} | ||
SizeOverflow(..) | NormalizationFailure(..) | ReferencesError(..) | Cycle(..) => { | ||
None // not our job to report these | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
//@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib | ||
//@ needs-llvm-components: arm | ||
#![feature(abi_c_cmse_nonsecure_call, no_core, lang_items)] | ||
#![no_core] | ||
#[lang = "sized"] | ||
pub trait Sized {} | ||
#[lang = "copy"] | ||
pub trait Copy {} | ||
impl Copy for u32 {} | ||
|
||
#[repr(C)] | ||
struct Wrapper<T>(T); | ||
|
||
struct Test<T: Copy> { | ||
f1: extern "C-cmse-nonsecure-call" fn<U: Copy>(U, u32, u32, u32) -> u64, //~ ERROR cannot find type `U` in this scope | ||
//~^ ERROR function pointer types may not have generic parameters | ||
f2: extern "C-cmse-nonsecure-call" fn(impl Copy, u32, u32, u32) -> u64, | ||
//~^ ERROR `impl Trait` is not allowed in `fn` pointer parameters | ||
f3: extern "C-cmse-nonsecure-call" fn(T, u32, u32, u32) -> u64, //~ ERROR [E0798] | ||
f4: extern "C-cmse-nonsecure-call" fn(Wrapper<T>, u32, u32, u32) -> u64, //~ ERROR [E0798] | ||
} |
47 changes: 47 additions & 0 deletions
47
tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
error: function pointer types may not have generic parameters | ||
--> $DIR/generics.rs:15:42 | ||
| | ||
LL | f1: extern "C-cmse-nonsecure-call" fn<U: Copy>(U, u32, u32, u32) -> u64, | ||
| ^^^^^^^^^ | ||
|
||
error[E0412]: cannot find type `U` in this scope | ||
--> $DIR/generics.rs:15:52 | ||
| | ||
LL | struct Test<T: Copy> { | ||
| - similarly named type parameter `T` defined here | ||
LL | f1: extern "C-cmse-nonsecure-call" fn<U: Copy>(U, u32, u32, u32) -> u64, | ||
| ^ | ||
| | ||
help: a type parameter with a similar name exists | ||
| | ||
LL | f1: extern "C-cmse-nonsecure-call" fn<U: Copy>(T, u32, u32, u32) -> u64, | ||
| ~ | ||
help: you might be missing a type parameter | ||
| | ||
LL | struct Test<T: Copy, U> { | ||
| +++ | ||
|
||
error[E0562]: `impl Trait` is not allowed in `fn` pointer parameters | ||
--> $DIR/generics.rs:17:43 | ||
| | ||
LL | f2: extern "C-cmse-nonsecure-call" fn(impl Copy, u32, u32, u32) -> u64, | ||
| ^^^^^^^^^ | ||
| | ||
= note: `impl Trait` is only allowed in arguments and return types of functions and methods | ||
|
||
error[E0798]: function pointers with the `"C-cmse-nonsecure-call"` ABI cannot contain generics in their type | ||
--> $DIR/generics.rs:19:9 | ||
| | ||
LL | f3: extern "C-cmse-nonsecure-call" fn(T, u32, u32, u32) -> u64, | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
|
||
error[E0798]: function pointers with the `"C-cmse-nonsecure-call"` ABI cannot contain generics in their type | ||
--> $DIR/generics.rs:20:9 | ||
| | ||
LL | f4: extern "C-cmse-nonsecure-call" fn(Wrapper<T>, u32, u32, u32) -> u64, | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
|
||
error: aborting due to 5 previous errors | ||
|
||
Some errors have detailed explanations: E0412, E0562, E0798. | ||
For more information about an error, try `rustc --explain E0412`. |
24 changes: 0 additions & 24 deletions
24
tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-registers.rs
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.