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 span: Span,
#[label]
pub func_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 span: Span,
#[label]
pub func_span: Span,
}
56 changes: 55 additions & 1 deletion compiler/rustc_codegen_ssa/src/mir/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ use super::{CachedLlbb, FunctionCx, LocalRef};

use crate::base;
use crate::common::{self, IntPredicate};
use crate::errors::CompilerBuiltinsCannotCall;
use crate::errors::{
CmseCallInputsStackSpill, CmseCallOutputStackSpill, CompilerBuiltinsCannotCall,
};
use crate::meth;
use crate::traits::*;
use crate::MemFlags;
Expand Down Expand Up @@ -834,6 +836,58 @@ 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);
}
folkertdev marked this conversation as resolved.
Show resolved Hide resolved
}

let (instance, mut llfn) = match *callee.layout.ty.kind() {
ty::FnDef(def_id, args) => (
Some(
Expand Down
36 changes: 36 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,36 @@
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)]

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

```compile_fail,E0798
#![feature(abi_c_cmse_nonsecure_call)]

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