Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

C-cmse-nonsecure-call: improved error messages #127814

Merged
merged 11 commits into from
Jul 19, 2024
12 changes: 12 additions & 0 deletions compiler/rustc_codegen_ssa/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,18 @@ codegen_ssa_cgu_not_recorded =

codegen_ssa_check_installed_visual_studio = please ensure that Visual Studio 2017 or later, or Build Tools for Visual Studio were installed with the Visual C++ option.

codegen_ssa_cmse_call_inputs_stack_spill =
arguments for `C-cmse-nonsecure-call` function too large to pass via registers
.label = this function uses the `C-cmse-nonsecure-call` ABI
.call = but its arguments don't fit in the available registers
.note = functions with the `C-cmse-nonsecure-call` ABI must pass all their arguments via the 4 32-bit available argument registers

codegen_ssa_cmse_call_output_stack_spill =
return value of `C-cmse-nonsecure-call` function too large to pass via registers
.label = this function uses the `C-cmse-nonsecure-call` ABI
.call = but its return value doesn't fit in the available registers
.note = functions with the `C-cmse-nonsecure-call` ABI must pass their result via the available return registers

codegen_ssa_compiler_builtins_cannot_call =
`compiler_builtins` cannot call functions through upstream monomorphizations; encountered invalid call from `{$caller}` to `{$callee}`

Expand Down
22 changes: 22 additions & 0 deletions compiler/rustc_codegen_ssa/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1033,3 +1033,25 @@ pub struct CompilerBuiltinsCannotCall {
pub caller: String,
pub callee: String,
}

#[derive(Diagnostic)]
#[diag(codegen_ssa_cmse_call_inputs_stack_spill, code = E0798)]
#[note]
pub struct CmseCallInputsStackSpill {
#[primary_span]
#[label(codegen_ssa_call)]
pub call_site_span: Span,
#[label]
pub function_definition_span: Span,
}

#[derive(Diagnostic)]
#[diag(codegen_ssa_cmse_call_output_stack_spill, code = E0798)]
#[note]
pub struct CmseCallOutputStackSpill {
#[primary_span]
#[label(codegen_ssa_call)]
pub call_site_span: Span,
#[label]
pub function_definition_span: Span,
}
4 changes: 4 additions & 0 deletions compiler/rustc_codegen_ssa/src/mir/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::base;
use crate::common::{self, IntPredicate};
use crate::errors::CompilerBuiltinsCannotCall;
use crate::meth;
use crate::mir::cmse;
use crate::traits::*;
use crate::MemFlags;

Expand Down Expand Up @@ -869,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());
Expand Down
72 changes: 72 additions & 0 deletions compiler/rustc_codegen_ssa/src/mir/cmse.rs
Original file line number Diff line number Diff line change
@@ -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>>(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in the near future, this will also validate the C-cmse-nonsecure-entry abi which will be added by #127766.

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)
}
1 change: 1 addition & 0 deletions compiler/rustc_codegen_ssa/src/mir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use std::iter;

mod analyze;
mod block;
mod cmse;
pub mod constant;
pub mod coverageinfo;
pub mod debuginfo;
Expand Down
38 changes: 38 additions & 0 deletions compiler/rustc_error_codes/src/error_codes/E0798.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
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`).

For more information,
see [arm's aapcs32](https://github.com/ARM-software/abi-aa/releases).

Erroneous code example:

```compile_fail,E0798
#![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)
}
```
folkertdev marked this conversation as resolved.
Show resolved Hide resolved

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

```compile_fail,E0798
#![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)
}
```
1 change: 1 addition & 0 deletions compiler/rustc_error_codes/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,7 @@ E0794: 0794,
E0795: 0795,
E0796: 0796,
E0797: 0797,
E0798: 0798,
);
)
}
Expand Down
24 changes: 0 additions & 24 deletions tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-registers.rs

This file was deleted.

27 changes: 0 additions & 27 deletions tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-stack.rs

This file was deleted.

This file was deleted.

29 changes: 29 additions & 0 deletions tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//@ build-fail
//@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib
//@ needs-llvm-components: arm
#![feature(abi_c_cmse_nonsecure_call, no_core, lang_items, intrinsics)]
#![no_core]
#[lang = "sized"]
pub trait Sized {}
#[lang = "copy"]
pub trait Copy {}
impl Copy for u32 {}

#[repr(C, align(16))]
#[allow(unused)]
pub struct AlignRelevant(u32);

#[no_mangle]
pub fn test(
f1: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32, u32) -> u32,
f2: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u16, u16) -> u32,
f3: extern "C-cmse-nonsecure-call" fn(u32, u64, u32) -> u32,
f4: extern "C-cmse-nonsecure-call" fn(AlignRelevant, u32) -> u32,
f5: extern "C-cmse-nonsecure-call" fn([u32; 5]) -> u32,
) {
f1(1, 2, 3, 4, 5); //~ ERROR [E0798]
f2(1, 2, 3, 4, 5); //~ ERROR [E0798]
f3(1, 2, 3); //~ ERROR [E0798]
f4(AlignRelevant(1), 2); //~ ERROR [E0798]
f5([0xAA; 5]); //~ ERROR [E0798]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
error[E0798]: arguments for `C-cmse-nonsecure-call` function too large to pass via registers
--> $DIR/params-via-stack.rs:24:5
|
LL | f1: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32, u32) -> u32,
oli-obk marked this conversation as resolved.
Show resolved Hide resolved
| -- this function uses the `C-cmse-nonsecure-call` ABI
...
LL | f1(1, 2, 3, 4, 5);
| ^^^^^^^^^^^^^^^^^ but its arguments don't fit in the available registers
|
= note: functions with the `C-cmse-nonsecure-call` ABI must pass all their arguments via the 4 32-bit available argument registers

error[E0798]: arguments for `C-cmse-nonsecure-call` function too large to pass via registers
--> $DIR/params-via-stack.rs:25:5
|
LL | f2: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u16, u16) -> u32,
| -- this function uses the `C-cmse-nonsecure-call` ABI
...
LL | f2(1, 2, 3, 4, 5);
| ^^^^^^^^^^^^^^^^^ but its arguments don't fit in the available registers
|
= note: functions with the `C-cmse-nonsecure-call` ABI must pass all their arguments via the 4 32-bit available argument registers

error[E0798]: arguments for `C-cmse-nonsecure-call` function too large to pass via registers
--> $DIR/params-via-stack.rs:26:5
|
LL | f3: extern "C-cmse-nonsecure-call" fn(u32, u64, u32) -> u32,
| -- this function uses the `C-cmse-nonsecure-call` ABI
...
LL | f3(1, 2, 3);
| ^^^^^^^^^^^ but its arguments don't fit in the available registers
|
= note: functions with the `C-cmse-nonsecure-call` ABI must pass all their arguments via the 4 32-bit available argument registers

error[E0798]: arguments for `C-cmse-nonsecure-call` function too large to pass via registers
--> $DIR/params-via-stack.rs:27:5
|
LL | f4: extern "C-cmse-nonsecure-call" fn(AlignRelevant, u32) -> u32,
| -- this function uses the `C-cmse-nonsecure-call` ABI
...
LL | f4(AlignRelevant(1), 2);
| ^^^^^^^^^^^^^^^^^^^^^^^ but its arguments don't fit in the available registers
|
= note: functions with the `C-cmse-nonsecure-call` ABI must pass all their arguments via the 4 32-bit available argument registers

error[E0798]: arguments for `C-cmse-nonsecure-call` function too large to pass via registers
--> $DIR/params-via-stack.rs:28:5
|
LL | f5: extern "C-cmse-nonsecure-call" fn([u32; 5]) -> u32,
| -- this function uses the `C-cmse-nonsecure-call` ABI
...
LL | f5([0xAA; 5]);
| ^^^^^^^^^^^^^ but its arguments don't fit in the available registers
|
= note: functions with the `C-cmse-nonsecure-call` ABI must pass all their arguments via the 4 32-bit available argument registers

error: aborting due to 5 previous errors

For more information about this error, try `rustc --explain E0798`.
41 changes: 41 additions & 0 deletions tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//@ build-fail
//@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib
//@ needs-llvm-components: arm
#![feature(abi_c_cmse_nonsecure_call, no_core, lang_items, intrinsics)]
#![no_core]
#[lang = "sized"]
pub trait Sized {}
#[lang = "copy"]
pub trait Copy {}
impl Copy for u32 {}

#[repr(C)]
pub struct ReprCU64(u64);

#[repr(C)]
pub struct ReprCBytes(u8, u8, u8, u8, u8);

#[repr(C)]
pub struct U64Compound(u32, u32);

#[repr(C, align(16))]
pub struct ReprCAlign16(u16);

#[no_mangle]
pub fn test(
f1: extern "C-cmse-nonsecure-call" fn() -> ReprCU64,
f2: extern "C-cmse-nonsecure-call" fn() -> ReprCBytes,
f3: extern "C-cmse-nonsecure-call" fn() -> U64Compound,
f4: extern "C-cmse-nonsecure-call" fn() -> ReprCAlign16,
f5: extern "C-cmse-nonsecure-call" fn() -> [u8; 5],
f6: extern "C-cmse-nonsecure-call" fn() -> u128, //~ WARNING [improper_ctypes_definitions]
f7: extern "C-cmse-nonsecure-call" fn() -> i128, //~ WARNING [improper_ctypes_definitions]
) {
f1(); //~ ERROR [E0798]
f2(); //~ ERROR [E0798]
f3(); //~ ERROR [E0798]
f4(); //~ ERROR [E0798]
f5(); //~ ERROR [E0798]
f6(); //~ ERROR [E0798]
f7(); //~ ERROR [E0798]
}
Loading
Loading