From 50ba821e12f51903aba6902b2b404edbc94011d2 Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 16 Jul 2024 15:57:00 +0200 Subject: [PATCH 01/11] add rust error message for CMSE stack spill when the `C-cmse-nonsecure-call` ABI is used, arguments and return values must be passed via registers. Failing to do so (i.e. spilling to the stack) causes an LLVM error down the line, but now rustc will properly emit an error a bit earlier in the chain --- compiler/rustc_codegen_ssa/messages.ftl | 12 ++++ compiler/rustc_codegen_ssa/src/errors.rs | 22 ++++++++ compiler/rustc_codegen_ssa/src/mir/block.rs | 56 ++++++++++++++++++- .../src/error_codes/E0798.md | 36 ++++++++++++ compiler/rustc_error_codes/src/lib.rs | 1 + .../cmse-nonsecure-call/params-on-stack.rs | 15 ++--- .../params-on-stack.stderr | 12 +++- 7 files changed, 143 insertions(+), 11 deletions(-) create mode 100644 compiler/rustc_error_codes/src/error_codes/E0798.md diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index 000fe2e3ce0f..0aee5e16a537 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -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}` diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index e9d31db92541..13a2dce3e69c 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -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, +} diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 6a5525dc2b34..8011604d5766 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -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; @@ -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::() { + 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( diff --git a/compiler/rustc_error_codes/src/error_codes/E0798.md b/compiler/rustc_error_codes/src/error_codes/E0798.md new file mode 100644 index 000000000000..79ed041004e9 --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0798.md @@ -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) +} +``` diff --git a/compiler/rustc_error_codes/src/lib.rs b/compiler/rustc_error_codes/src/lib.rs index d13d5e1bca21..2a7bc2501c08 100644 --- a/compiler/rustc_error_codes/src/lib.rs +++ b/compiler/rustc_error_codes/src/lib.rs @@ -536,6 +536,7 @@ E0794: 0794, E0795: 0795, E0796: 0796, E0797: 0797, +E0798: 0798, ); ) } diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-stack.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-stack.rs index c225a26c065d..f13e81d00608 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-stack.rs +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-stack.rs @@ -3,10 +3,10 @@ //@ 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 { } +#[lang = "sized"] +pub trait Sized {} +#[lang = "copy"] +pub trait Copy {} impl Copy for u32 {} extern "rust-intrinsic" { @@ -16,12 +16,9 @@ extern "rust-intrinsic" { #[no_mangle] pub fn test(a: u32, b: u32, c: u32, d: u32, e: u32) -> u32 { let non_secure_function = unsafe { - transmute::< - usize, - extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32, u32) -> u32> - ( + transmute:: u32>( 0x10000004, ) }; - non_secure_function(a, b, c, d, e) + non_secure_function(a, b, c, d, e) //~ ERROR [E0798] } diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-stack.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-stack.stderr index a8aced2483e8..ee4effa56d4a 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-stack.stderr +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-stack.stderr @@ -1,4 +1,14 @@ -error: :0:0: in function test i32 (i32, i32, i32, i32, i32): call to non-secure function would require passing arguments on stack +error[E0798]: arguments for `C-cmse-nonsecure-call` function too large to pass via registers + --> $DIR/params-on-stack.rs:23:5 + | +LL | let non_secure_function = unsafe { + | ------------------- this function uses the `C-cmse-nonsecure-call` ABI +... +LL | non_secure_function(a, b, c, d, e) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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 1 previous error +For more information about this error, try `rustc --explain E0798`. From 1e8606408d14e2509f59c0a87c02788cb112b55a Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 16 Jul 2024 16:35:23 +0200 Subject: [PATCH 02/11] add more tests for `cmse-nonsecure-call` stack spills --- .../params-on-registers.rs | 24 ----- .../cmse-nonsecure-call/params-on-stack.rs | 24 ----- .../params-on-stack.stderr | 14 --- .../cmse-nonsecure-call/params-via-stack.rs | 29 ++++++ .../params-via-stack.stderr | 58 +++++++++++ .../cmse-nonsecure-call/return-via-stack.rs | 41 ++++++++ .../return-via-stack.stderr | 97 +++++++++++++++++++ .../cmse-nonsecure-call/via-registers.rs | 55 +++++++++++ 8 files changed, 280 insertions(+), 62 deletions(-) delete mode 100644 tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-registers.rs delete mode 100644 tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-stack.rs delete mode 100644 tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-stack.stderr create mode 100644 tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.rs create mode 100644 tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.stderr create mode 100644 tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.rs create mode 100644 tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.stderr create mode 100644 tests/ui/cmse-nonsecure/cmse-nonsecure-call/via-registers.rs diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-registers.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-registers.rs deleted file mode 100644 index 364d0858afb9..000000000000 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-registers.rs +++ /dev/null @@ -1,24 +0,0 @@ -//@ build-pass -//@ 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 {} - -extern "rust-intrinsic" { - pub fn transmute(e: T) -> U; -} - -#[no_mangle] -pub fn test(a: u32, b: u32, c: u32, d: u32) -> u32 { - let non_secure_function = unsafe { - transmute:: u32>( - 0x10000004, - ) - }; - non_secure_function(a, b, c, d) -} diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-stack.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-stack.rs deleted file mode 100644 index f13e81d00608..000000000000 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-stack.rs +++ /dev/null @@ -1,24 +0,0 @@ -//@ 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 {} - -extern "rust-intrinsic" { - pub fn transmute(e: T) -> U; -} - -#[no_mangle] -pub fn test(a: u32, b: u32, c: u32, d: u32, e: u32) -> u32 { - let non_secure_function = unsafe { - transmute:: u32>( - 0x10000004, - ) - }; - non_secure_function(a, b, c, d, e) //~ ERROR [E0798] -} diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-stack.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-stack.stderr deleted file mode 100644 index ee4effa56d4a..000000000000 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-stack.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error[E0798]: arguments for `C-cmse-nonsecure-call` function too large to pass via registers - --> $DIR/params-on-stack.rs:23:5 - | -LL | let non_secure_function = unsafe { - | ------------------- this function uses the `C-cmse-nonsecure-call` ABI -... -LL | non_secure_function(a, b, c, d, e) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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 1 previous error - -For more information about this error, try `rustc --explain E0798`. diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.rs new file mode 100644 index 000000000000..a4cc74f716f1 --- /dev/null +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.rs @@ -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] +} diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.stderr new file mode 100644 index 000000000000..b161c90d01ac --- /dev/null +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.stderr @@ -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, + | -- 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`. diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.rs new file mode 100644 index 000000000000..c417dddac4ff --- /dev/null +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.rs @@ -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] +} diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.stderr new file mode 100644 index 000000000000..6dea0cf68a0d --- /dev/null +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.stderr @@ -0,0 +1,97 @@ +warning: `extern` fn uses type `u128`, which is not FFI-safe + --> $DIR/return-via-stack.rs:31:9 + | +LL | f6: extern "C-cmse-nonsecure-call" fn() -> u128, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: 128-bit integers don't currently have a known stable ABI + = note: `#[warn(improper_ctypes_definitions)]` on by default + +warning: `extern` fn uses type `i128`, which is not FFI-safe + --> $DIR/return-via-stack.rs:32:9 + | +LL | f7: extern "C-cmse-nonsecure-call" fn() -> i128, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: 128-bit integers don't currently have a known stable ABI + +error[E0798]: return value of `C-cmse-nonsecure-call` function too large to pass via registers + --> $DIR/return-via-stack.rs:34:5 + | +LL | f1: extern "C-cmse-nonsecure-call" fn() -> ReprCU64, + | -- this function uses the `C-cmse-nonsecure-call` ABI +... +LL | f1(); + | ^^^^ 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 + +error[E0798]: return value of `C-cmse-nonsecure-call` function too large to pass via registers + --> $DIR/return-via-stack.rs:35:5 + | +LL | f2: extern "C-cmse-nonsecure-call" fn() -> ReprCBytes, + | -- this function uses the `C-cmse-nonsecure-call` ABI +... +LL | f2(); + | ^^^^ 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 + +error[E0798]: return value of `C-cmse-nonsecure-call` function too large to pass via registers + --> $DIR/return-via-stack.rs:36:5 + | +LL | f3: extern "C-cmse-nonsecure-call" fn() -> U64Compound, + | -- this function uses the `C-cmse-nonsecure-call` ABI +... +LL | f3(); + | ^^^^ 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 + +error[E0798]: return value of `C-cmse-nonsecure-call` function too large to pass via registers + --> $DIR/return-via-stack.rs:37:5 + | +LL | f4: extern "C-cmse-nonsecure-call" fn() -> ReprCAlign16, + | -- this function uses the `C-cmse-nonsecure-call` ABI +... +LL | f4(); + | ^^^^ 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 + +error[E0798]: return value of `C-cmse-nonsecure-call` function too large to pass via registers + --> $DIR/return-via-stack.rs:38:5 + | +LL | f5: extern "C-cmse-nonsecure-call" fn() -> [u8; 5], + | -- this function uses the `C-cmse-nonsecure-call` ABI +... +LL | f5(); + | ^^^^ 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 + +error[E0798]: return value of `C-cmse-nonsecure-call` function too large to pass via registers + --> $DIR/return-via-stack.rs:39:5 + | +LL | f6: extern "C-cmse-nonsecure-call" fn() -> u128, + | -- this function uses the `C-cmse-nonsecure-call` ABI +... +LL | f6(); + | ^^^^ 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 + +error[E0798]: return value of `C-cmse-nonsecure-call` function too large to pass via registers + --> $DIR/return-via-stack.rs:40:5 + | +LL | f7: extern "C-cmse-nonsecure-call" fn() -> i128, + | -- this function uses the `C-cmse-nonsecure-call` ABI +... +LL | f7(); + | ^^^^ 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 + +error: aborting due to 7 previous errors; 2 warnings emitted + +For more information about this error, try `rustc --explain E0798`. diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/via-registers.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/via-registers.rs new file mode 100644 index 000000000000..72b405ef2820 --- /dev/null +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/via-registers.rs @@ -0,0 +1,55 @@ +//@ build-pass +//@ 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(transparent)] +pub struct ReprTransparentU64(u64); + +#[repr(C)] +pub struct U32Compound(u16, u16); + +#[no_mangle] +#[allow(improper_ctypes_definitions)] +pub fn params( + f1: extern "C-cmse-nonsecure-call" fn(), + f2: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32), + f3: extern "C-cmse-nonsecure-call" fn(u64, u64), + f4: extern "C-cmse-nonsecure-call" fn(u128), + f5: extern "C-cmse-nonsecure-call" fn(f64, f32, f32), + f6: extern "C-cmse-nonsecure-call" fn(ReprTransparentU64, U32Compound), + f7: extern "C-cmse-nonsecure-call" fn([u32; 4]), +) { + f1(); + f2(1, 2, 3, 4); + f3(1, 2); + f4(1); + f5(1.0, 2.0, 3.0); + f6(ReprTransparentU64(1), U32Compound(2, 3)); + f7([0xDEADBEEF; 4]); +} + +#[no_mangle] +pub fn returns( + f1: extern "C-cmse-nonsecure-call" fn() -> u32, + f2: extern "C-cmse-nonsecure-call" fn() -> u64, + f3: extern "C-cmse-nonsecure-call" fn() -> i64, + f4: extern "C-cmse-nonsecure-call" fn() -> f64, + f5: extern "C-cmse-nonsecure-call" fn() -> [u8; 4], + f6: extern "C-cmse-nonsecure-call" fn() -> ReprTransparentU64, + f7: extern "C-cmse-nonsecure-call" fn() -> U32Compound, +) { + f1(); + f2(); + f3(); + f4(); + f5(); + f6(); + f7(); +} From 36d23713fb524589be409392472d78b4b3e20da6 Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 16 Jul 2024 18:38:42 +0200 Subject: [PATCH 03/11] make function pub in error_codes markdown file the error is only generated for functions that are actually codegen'd --- compiler/rustc_error_codes/src/error_codes/E0798.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_error_codes/src/error_codes/E0798.md b/compiler/rustc_error_codes/src/error_codes/E0798.md index 79ed041004e9..f63796a67f19 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0798.md +++ b/compiler/rustc_error_codes/src/error_codes/E0798.md @@ -14,7 +14,7 @@ Erroneous code example: ```compile_fail,E0798 #![feature(abi_c_cmse_nonsecure_call)] -fn test( +pub fn test( f: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32, u32) -> u32, ) -> u32 { f(1, 2, 3, 4, 5) @@ -28,7 +28,7 @@ room left for the final `f32` argument ```compile_fail,E0798 #![feature(abi_c_cmse_nonsecure_call)] -fn test( +pub fn test( f: extern "C-cmse-nonsecure-call" fn(u32, u64, f32) -> u32, ) -> u32 { f(1, 2, 3.0) From c7ff46c971ea7c17b67b2b619fd094c4df981863 Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 16 Jul 2024 18:39:30 +0200 Subject: [PATCH 04/11] move cmse ABI validation into its own module Co-authored-by: Tamme Dittrich --- compiler/rustc_codegen_ssa/src/errors.rs | 8 +-- compiler/rustc_codegen_ssa/src/mir/block.rs | 60 ++--------------- compiler/rustc_codegen_ssa/src/mir/cmse.rs | 72 +++++++++++++++++++++ compiler/rustc_codegen_ssa/src/mir/mod.rs | 1 + 4 files changed, 82 insertions(+), 59 deletions(-) create mode 100644 compiler/rustc_codegen_ssa/src/mir/cmse.rs diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index 13a2dce3e69c..af7835633cfd 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -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, } diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 8011604d5766..24d3b8ad264b 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -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::() { - 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()); diff --git a/compiler/rustc_codegen_ssa/src/mir/cmse.rs b/compiler/rustc_codegen_ssa/src/mir/cmse.rs new file mode 100644 index 000000000000..d3583d79c00d --- /dev/null +++ b/compiler/rustc_codegen_ssa/src/mir/cmse.rs @@ -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::() { + 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) +} diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index 61f57c9030a1..ec28def8b83e 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -16,6 +16,7 @@ use std::iter; mod analyze; mod block; +mod cmse; pub mod constant; pub mod coverageinfo; pub mod debuginfo; From 1a7960603f60239a5b91c27b3d63e2b87eb09512 Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 16 Jul 2024 20:36:55 +0200 Subject: [PATCH 05/11] another attempt at fixing the examples in the error codes .md --- compiler/rustc_error_codes/src/error_codes/E0798.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/rustc_error_codes/src/error_codes/E0798.md b/compiler/rustc_error_codes/src/error_codes/E0798.md index f63796a67f19..012c13b96ed4 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0798.md +++ b/compiler/rustc_error_codes/src/error_codes/E0798.md @@ -14,6 +14,7 @@ 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 { @@ -28,6 +29,7 @@ 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 { From 09b620d179713cceb343dc172d7e5633bfd7a824 Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 16 Jul 2024 21:26:21 +0200 Subject: [PATCH 06/11] stop running code samples in the error code .md --- compiler/rustc_error_codes/src/error_codes/E0798.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_error_codes/src/error_codes/E0798.md b/compiler/rustc_error_codes/src/error_codes/E0798.md index 012c13b96ed4..89e6f4d57180 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0798.md +++ b/compiler/rustc_error_codes/src/error_codes/E0798.md @@ -11,7 +11,7 @@ see [arm's aapcs32](https://github.com/ARM-software/abi-aa/releases). Erroneous code example: -```compile_fail,E0798 +```ignore (only fails on supported targets) #![feature(abi_c_cmse_nonsecure_call)] #[no_mangle] @@ -26,7 +26,7 @@ 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 +```ignore (only fails on supported targets) #![feature(abi_c_cmse_nonsecure_call)] #[no_mangle] From 5f0f690bd6d0fbe98e82b5b98036ffe08dff4fbe Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 17 Jul 2024 15:41:16 +0200 Subject: [PATCH 07/11] add test for repr(transparent) enum --- .../cmse-nonsecure-call/via-registers.rs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/via-registers.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/via-registers.rs index 72b405ef2820..43768dd72f70 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/via-registers.rs +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/via-registers.rs @@ -10,7 +10,12 @@ pub trait Copy {} impl Copy for u32 {} #[repr(transparent)] -pub struct ReprTransparentU64(u64); +pub struct ReprTransparentStructU64(u64); + +#[repr(transparent)] +pub enum ReprTransparentEnumU64 { + A(u64), +} #[repr(C)] pub struct U32Compound(u16, u16); @@ -23,7 +28,7 @@ pub fn params( f3: extern "C-cmse-nonsecure-call" fn(u64, u64), f4: extern "C-cmse-nonsecure-call" fn(u128), f5: extern "C-cmse-nonsecure-call" fn(f64, f32, f32), - f6: extern "C-cmse-nonsecure-call" fn(ReprTransparentU64, U32Compound), + f6: extern "C-cmse-nonsecure-call" fn(ReprTransparentStructU64, U32Compound), f7: extern "C-cmse-nonsecure-call" fn([u32; 4]), ) { f1(); @@ -31,7 +36,7 @@ pub fn params( f3(1, 2); f4(1); f5(1.0, 2.0, 3.0); - f6(ReprTransparentU64(1), U32Compound(2, 3)); + f6(ReprTransparentStructU64(1), U32Compound(2, 3)); f7([0xDEADBEEF; 4]); } @@ -42,8 +47,9 @@ pub fn returns( f3: extern "C-cmse-nonsecure-call" fn() -> i64, f4: extern "C-cmse-nonsecure-call" fn() -> f64, f5: extern "C-cmse-nonsecure-call" fn() -> [u8; 4], - f6: extern "C-cmse-nonsecure-call" fn() -> ReprTransparentU64, - f7: extern "C-cmse-nonsecure-call" fn() -> U32Compound, + f6: extern "C-cmse-nonsecure-call" fn() -> ReprTransparentStructU64, + f7: extern "C-cmse-nonsecure-call" fn() -> ReprTransparentEnumU64, + f8: extern "C-cmse-nonsecure-call" fn() -> U32Compound, ) { f1(); f2(); @@ -52,4 +58,5 @@ pub fn returns( f5(); f6(); f7(); + f8(); } From 8a3dd7fb5f9e2c3b6c8411e59ed7fa8d674c5a91 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 17 Jul 2024 17:01:59 +0200 Subject: [PATCH 08/11] add test for unions and repr(transparent) with ZST fields --- .../cmse-nonsecure-call/return-via-stack.rs | 19 ++++++++++ .../return-via-stack.stderr | 38 ++++++++++++++++++- .../cmse-nonsecure-call/via-registers.rs | 12 +++++- 3 files changed, 66 insertions(+), 3 deletions(-) diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.rs index c417dddac4ff..2ea693273920 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.rs +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.rs @@ -39,3 +39,22 @@ pub fn test( f6(); //~ ERROR [E0798] f7(); //~ ERROR [E0798] } + +#[repr(C)] +pub union ReprCUnionU64 { + _unused: u64, +} + +#[repr(Rust)] +pub union ReprRustUnionU64 { + _unused: u64, +} + +#[no_mangle] +pub fn test_union( + f1: extern "C-cmse-nonsecure-call" fn() -> ReprRustUnionU64, //~ WARNING [improper_ctypes_definitions] + f2: extern "C-cmse-nonsecure-call" fn() -> ReprCUnionU64, +) { + f1(); //~ ERROR [E0798] + f2(); //~ ERROR [E0798] +} diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.stderr index 6dea0cf68a0d..99dc7ad4a9fe 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.stderr +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.stderr @@ -15,6 +15,20 @@ LL | f7: extern "C-cmse-nonsecure-call" fn() -> i128, | = note: 128-bit integers don't currently have a known stable ABI +warning: `extern` fn uses type `ReprRustUnionU64`, which is not FFI-safe + --> $DIR/return-via-stack.rs:55:9 + | +LL | f1: extern "C-cmse-nonsecure-call" fn() -> ReprRustUnionU64, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this union + = note: this union has unspecified layout +note: the type is defined here + --> $DIR/return-via-stack.rs:49:1 + | +LL | pub union ReprRustUnionU64 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + error[E0798]: return value of `C-cmse-nonsecure-call` function too large to pass via registers --> $DIR/return-via-stack.rs:34:5 | @@ -92,6 +106,28 @@ LL | f7(); | = note: functions with the `C-cmse-nonsecure-call` ABI must pass their result via the available return registers -error: aborting due to 7 previous errors; 2 warnings emitted +error[E0798]: return value of `C-cmse-nonsecure-call` function too large to pass via registers + --> $DIR/return-via-stack.rs:58:5 + | +LL | f1: extern "C-cmse-nonsecure-call" fn() -> ReprRustUnionU64, + | -- this function uses the `C-cmse-nonsecure-call` ABI +... +LL | f1(); + | ^^^^ 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 + +error[E0798]: return value of `C-cmse-nonsecure-call` function too large to pass via registers + --> $DIR/return-via-stack.rs:59:5 + | +LL | f2: extern "C-cmse-nonsecure-call" fn() -> ReprCUnionU64, + | -- this function uses the `C-cmse-nonsecure-call` ABI +... +LL | f2(); + | ^^^^ 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 + +error: aborting due to 9 previous errors; 3 warnings emitted For more information about this error, try `rustc --explain E0798`. diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/via-registers.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/via-registers.rs index 43768dd72f70..91aadd4fe1d7 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/via-registers.rs +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/via-registers.rs @@ -10,7 +10,12 @@ pub trait Copy {} impl Copy for u32 {} #[repr(transparent)] -pub struct ReprTransparentStructU64(u64); +pub struct ReprTransparentStructU64 { + _marker1: (), + _marker2: (), + field: u64, + _marker3: (), +} #[repr(transparent)] pub enum ReprTransparentEnumU64 { @@ -36,7 +41,10 @@ pub fn params( f3(1, 2); f4(1); f5(1.0, 2.0, 3.0); - f6(ReprTransparentStructU64(1), U32Compound(2, 3)); + f6( + ReprTransparentStructU64 { _marker1: (), _marker2: (), field: 1, _marker3: () }, + U32Compound(2, 3), + ); f7([0xDEADBEEF; 4]); } From 7b6373496107807327094ec990d317b98e34fd63 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 17 Jul 2024 16:47:03 +0200 Subject: [PATCH 09/11] move CMSE validation to hir_analysis --- .../src/error_codes/E0798.md | 1 + compiler/rustc_hir_analysis/messages.ftl | 13 ++ .../rustc_hir_analysis/src/check/check.rs | 7 +- compiler/rustc_hir_analysis/src/errors.rs | 25 +++ .../src/hir_ty_lowering/cmse.rs | 152 ++++++++++++++++++ .../src/hir_ty_lowering/mod.rs | 10 +- .../cmse-nonsecure-call/generics.rs | 21 +++ .../cmse-nonsecure-call/generics.stderr | 47 ++++++ .../cmse-nonsecure-call/params-via-stack.rs | 18 +-- .../params-via-stack.stderr | 65 +++----- .../cmse-nonsecure-call/return-via-stack.rs | 34 ++-- .../return-via-stack.stderr | 136 +++++----------- .../cmse-nonsecure-call/via-registers.rs | 31 +--- 13 files changed, 363 insertions(+), 197 deletions(-) create mode 100644 compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs create mode 100644 tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.rs create mode 100644 tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr diff --git a/compiler/rustc_error_codes/src/error_codes/E0798.md b/compiler/rustc_error_codes/src/error_codes/E0798.md index 89e6f4d57180..da08cde30100 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0798.md +++ b/compiler/rustc_error_codes/src/error_codes/E0798.md @@ -5,6 +5,7 @@ inputs and outputs. 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). diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 24c5377a3b12..836180411339 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -58,6 +58,19 @@ hir_analysis_cannot_capture_late_bound_ty = hir_analysis_closure_implicit_hrtb = implicit types in closure signatures are forbidden when `for<...>` is present .label = `for<...>` is here +hir_analysis_cmse_call_generic = + function pointers with the `"C-cmse-nonsecure-call"` ABI cannot contain generics in their type + +hir_analysis_cmse_call_inputs_stack_spill = + arguments for `"C-cmse-nonsecure-call"` function too large to pass via registers + .label = these 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 + +hir_analysis_cmse_call_output_stack_spill = + return value of `"C-cmse-nonsecure-call"` function too large to pass via registers + .label = this type 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 + hir_analysis_coerce_unsized_may = the trait `{$trait_name}` may only be implemented for a coercion between structures hir_analysis_coerce_unsized_multi = implementing the trait `CoerceUnsized` requires multiple coercions diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index bf8ef18c04fc..aa302e4c25f4 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -1287,9 +1287,10 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) (span, trivial, check_non_exhaustive(tcx, ty).break_value()) }); - let non_trivial_fields = field_infos - .clone() - .filter_map(|(span, trivial, _non_exhaustive)| if !trivial { Some(span) } else { None }); + let non_trivial_fields = + field_infos.clone().filter_map( + |(span, trivial, _non_exhaustive)| if !trivial { Some(span) } else { None }, + ); let non_trivial_count = non_trivial_fields.clone().count(); if non_trivial_count >= 2 { bad_non_zero_sized_fields( diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 0ee87a13e9e3..f6783364f163 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1682,3 +1682,28 @@ pub struct InvalidReceiverTy<'tcx> { #[note] #[help] pub struct EffectsWithoutNextSolver; + +#[derive(Diagnostic)] +#[diag(hir_analysis_cmse_call_inputs_stack_spill, code = E0798)] +#[note] +pub struct CmseCallInputsStackSpill { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_cmse_call_output_stack_spill, code = E0798)] +#[note] +pub struct CmseCallOutputStackSpill { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_cmse_call_generic, code = E0798)] +pub struct CmseCallGeneric { + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs new file mode 100644 index 000000000000..8980173f7381 --- /dev/null +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs @@ -0,0 +1,152 @@ +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; + }; + + // fn(u32, u32, u32, u16, u16) -> u32, + // ^^^^^^^^^^^^^^^^^^^^^^^ ^^^ + let output_span = bare_fn_ty.decl.output.span(); + let inputs_span = match ( + bare_fn_ty.param_names.first(), + bare_fn_ty.decl.inputs.first(), + bare_fn_ty.decl.inputs.last(), + ) { + (Some(ident), Some(ty1), Some(ty2)) => ident.span.to(ty1.span).to(ty2.span), + _ => *bare_fn_span, + }; + + match is_valid_cmse_inputs(tcx, fn_sig) { + Ok(true) => {} + Ok(false) => { + dcx.emit_err(errors::CmseCallInputsStackSpill { span: inputs_span }); + } + Err(layout_err) => { + if let Some(err) = cmse_layout_err(layout_err, inputs_span) { + dcx.emit_err(err); + } + } + } + + match is_valid_cmse_output(tcx, fn_sig) { + Ok(true) => {} + Ok(false) => { + dcx.emit_err(errors::CmseCallOutputStackSpill { span: output_span }); + } + Err(layout_err) => { + if let Some(err) = cmse_layout_err(layout_err, output_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> { + let mut accum = 0u64; + + for arg_def in fn_sig.inputs().iter() { + 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. 4 32-bit registers + Ok(accum <= 16) +} + +/// 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> { + 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 { + 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 + } + } +} diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index a665306f2c6a..5a98e4d79bb0 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -14,6 +14,7 @@ //! trait references and bounds. mod bounds; +mod cmse; pub mod errors; pub mod generics; mod lint; @@ -1748,7 +1749,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { generic_segments.iter().map(|GenericPathSegment(_, index)| index).collect(); let _ = self.prohibit_generic_args( path.segments.iter().enumerate().filter_map(|(index, seg)| { - if !indices.contains(&index) { Some(seg) } else { None } + if !indices.contains(&index) { + Some(seg) + } else { + None + } }), GenericsArgsErrExtend::DefVariant, ); @@ -2324,6 +2329,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let fn_ty = tcx.mk_fn_sig(input_tys, output_ty, decl.c_variadic, safety, abi); let bare_fn_ty = ty::Binder::bind_with_vars(fn_ty, bound_vars); + // reject function types that violate cmse ABI requirements + cmse::validate_cmse_abi(self.tcx(), &self.dcx(), hir_id, abi, bare_fn_ty); + // Find any late-bound regions declared in return type that do // not appear in the arguments. These are not well-formed. // diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.rs new file mode 100644 index 000000000000..e6b0bf3e6860 --- /dev/null +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.rs @@ -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); + +struct Test { + f1: extern "C-cmse-nonsecure-call" fn(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, u32, u32, u32) -> u64, //~ ERROR [E0798] +} diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr new file mode 100644 index 000000000000..b255d2619385 --- /dev/null +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr @@ -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, u32, u32, u32) -> u64, + | ^^^^^^^^^ + +error[E0412]: cannot find type `U` in this scope + --> $DIR/generics.rs:15:52 + | +LL | struct Test { + | - similarly named type parameter `T` defined here +LL | f1: extern "C-cmse-nonsecure-call" fn(U, u32, u32, u32) -> u64, + | ^ + | +help: a type parameter with a similar name exists + | +LL | f1: extern "C-cmse-nonsecure-call" fn(T, u32, u32, u32) -> u64, + | ~ +help: you might be missing a type parameter + | +LL | struct Test { + | +++ + +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:43 + | +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:43 + | +LL | f4: extern "C-cmse-nonsecure-call" fn(Wrapper, 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`. diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.rs index a4cc74f716f1..f8ef44199210 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.rs +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.rs @@ -1,7 +1,6 @@ -//@ 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)] +#![feature(abi_c_cmse_nonsecure_call, no_core, lang_items)] #![no_core] #[lang = "sized"] pub trait Sized {} @@ -15,15 +14,10 @@ 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: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32, u32), //~ ERROR [E0798] + f2: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u16, u16), //~ ERROR [E0798] + f3: extern "C-cmse-nonsecure-call" fn(u32, u64, u32), //~ ERROR [E0798] + f4: extern "C-cmse-nonsecure-call" fn(AlignRelevant, u32), //~ ERROR [E0798] + f5: extern "C-cmse-nonsecure-call" fn([u32; 5]), //~ ERROR [E0798] ) { - 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] } diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.stderr index b161c90d01ac..499122ab2c54 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.stderr +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.stderr @@ -1,57 +1,42 @@ -error[E0798]: arguments for `C-cmse-nonsecure-call` function too large to pass via registers - --> $DIR/params-via-stack.rs:24:5 +error[E0798]: arguments for `"C-cmse-nonsecure-call"` function too large to pass via registers + --> $DIR/params-via-stack.rs:17:43 | -LL | f1: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32, u32) -> u32, - | -- 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 +LL | f1: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32, u32), + | ^^^^^^^^^^^^^^^^^^^^^^^ these 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 + = 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 +error[E0798]: arguments for `"C-cmse-nonsecure-call"` function too large to pass via registers + --> $DIR/params-via-stack.rs:18:43 | -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 +LL | f2: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u16, u16), + | ^^^^^^^^^^^^^^^^^^^^^^^ these 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 + = 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 +error[E0798]: arguments for `"C-cmse-nonsecure-call"` function too large to pass via registers + --> $DIR/params-via-stack.rs:19:43 | -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 +LL | f3: extern "C-cmse-nonsecure-call" fn(u32, u64, u32), + | ^^^^^^^^^^^^^ these 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 + = 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 +error[E0798]: arguments for `"C-cmse-nonsecure-call"` function too large to pass via registers + --> $DIR/params-via-stack.rs:20:43 | -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 +LL | f4: extern "C-cmse-nonsecure-call" fn(AlignRelevant, u32), + | ^^^^^^^^^^^^^^^^^^ these 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 + = 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 +error[E0798]: arguments for `"C-cmse-nonsecure-call"` function too large to pass via registers + --> $DIR/params-via-stack.rs:21:43 | -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 +LL | f5: extern "C-cmse-nonsecure-call" fn([u32; 5]), + | ^^^^^^^^ these 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 + = 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 diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.rs index 2ea693273920..e6af1d60e773 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.rs +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.rs @@ -1,7 +1,6 @@ -//@ 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)] +#![feature(abi_c_cmse_nonsecure_call, no_core, lang_items)] #![no_core] #[lang = "sized"] pub trait Sized {} @@ -23,21 +22,18 @@ 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: extern "C-cmse-nonsecure-call" fn() -> ReprCU64, //~ ERROR [E0798] + f2: extern "C-cmse-nonsecure-call" fn() -> ReprCBytes, //~ ERROR [E0798] + f3: extern "C-cmse-nonsecure-call" fn() -> U64Compound, //~ ERROR [E0798] + f4: extern "C-cmse-nonsecure-call" fn() -> ReprCAlign16, //~ ERROR [E0798] + f5: extern "C-cmse-nonsecure-call" fn() -> [u8; 5], //~ ERROR [E0798] ) { - f1(); //~ ERROR [E0798] - f2(); //~ ERROR [E0798] - f3(); //~ ERROR [E0798] - f4(); //~ ERROR [E0798] - f5(); //~ ERROR [E0798] - f6(); //~ ERROR [E0798] - f7(); //~ ERROR [E0798] +} + +#[allow(improper_ctypes_definitions)] +struct Test { + u128: extern "C-cmse-nonsecure-call" fn() -> u128, //~ ERROR [E0798] + i128: extern "C-cmse-nonsecure-call" fn() -> i128, //~ ERROR [E0798] } #[repr(C)] @@ -52,9 +48,7 @@ pub union ReprRustUnionU64 { #[no_mangle] pub fn test_union( - f1: extern "C-cmse-nonsecure-call" fn() -> ReprRustUnionU64, //~ WARNING [improper_ctypes_definitions] - f2: extern "C-cmse-nonsecure-call" fn() -> ReprCUnionU64, + f1: extern "C-cmse-nonsecure-call" fn() -> ReprRustUnionU64, //~ ERROR [E0798] + f2: extern "C-cmse-nonsecure-call" fn() -> ReprCUnionU64, //~ ERROR [E0798] ) { - f1(); //~ ERROR [E0798] - f2(); //~ ERROR [E0798] } diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.stderr index 99dc7ad4a9fe..c8984e3c0d90 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.stderr +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.stderr @@ -1,133 +1,75 @@ -warning: `extern` fn uses type `u128`, which is not FFI-safe - --> $DIR/return-via-stack.rs:31:9 +error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers + --> $DIR/return-via-stack.rs:35:50 | -LL | f6: extern "C-cmse-nonsecure-call" fn() -> u128, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe +LL | u128: extern "C-cmse-nonsecure-call" fn() -> u128, + | ^^^^ this type doesn't fit in the available registers | - = note: 128-bit integers don't currently have a known stable ABI - = note: `#[warn(improper_ctypes_definitions)]` on by default + = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers -warning: `extern` fn uses type `i128`, which is not FFI-safe - --> $DIR/return-via-stack.rs:32:9 +error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers + --> $DIR/return-via-stack.rs:36:50 | -LL | f7: extern "C-cmse-nonsecure-call" fn() -> i128, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe +LL | i128: extern "C-cmse-nonsecure-call" fn() -> i128, + | ^^^^ this type doesn't fit in the available registers | - = note: 128-bit integers don't currently have a known stable ABI + = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers -warning: `extern` fn uses type `ReprRustUnionU64`, which is not FFI-safe - --> $DIR/return-via-stack.rs:55:9 - | -LL | f1: extern "C-cmse-nonsecure-call" fn() -> ReprRustUnionU64, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe - | - = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this union - = note: this union has unspecified layout -note: the type is defined here - --> $DIR/return-via-stack.rs:49:1 - | -LL | pub union ReprRustUnionU64 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error[E0798]: return value of `C-cmse-nonsecure-call` function too large to pass via registers - --> $DIR/return-via-stack.rs:34:5 +error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers + --> $DIR/return-via-stack.rs:25:48 | LL | f1: extern "C-cmse-nonsecure-call" fn() -> ReprCU64, - | -- this function uses the `C-cmse-nonsecure-call` ABI -... -LL | f1(); - | ^^^^ but its return value doesn't fit in the available registers + | ^^^^^^^^ this type 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 + = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers -error[E0798]: return value of `C-cmse-nonsecure-call` function too large to pass via registers - --> $DIR/return-via-stack.rs:35:5 +error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers + --> $DIR/return-via-stack.rs:26:48 | LL | f2: extern "C-cmse-nonsecure-call" fn() -> ReprCBytes, - | -- this function uses the `C-cmse-nonsecure-call` ABI -... -LL | f2(); - | ^^^^ but its return value doesn't fit in the available registers + | ^^^^^^^^^^ this type 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 + = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers -error[E0798]: return value of `C-cmse-nonsecure-call` function too large to pass via registers - --> $DIR/return-via-stack.rs:36:5 +error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers + --> $DIR/return-via-stack.rs:27:48 | LL | f3: extern "C-cmse-nonsecure-call" fn() -> U64Compound, - | -- this function uses the `C-cmse-nonsecure-call` ABI -... -LL | f3(); - | ^^^^ but its return value doesn't fit in the available registers + | ^^^^^^^^^^^ this type 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 + = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers -error[E0798]: return value of `C-cmse-nonsecure-call` function too large to pass via registers - --> $DIR/return-via-stack.rs:37:5 +error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers + --> $DIR/return-via-stack.rs:28:48 | LL | f4: extern "C-cmse-nonsecure-call" fn() -> ReprCAlign16, - | -- this function uses the `C-cmse-nonsecure-call` ABI -... -LL | f4(); - | ^^^^ but its return value doesn't fit in the available registers + | ^^^^^^^^^^^^ this type 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 + = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers -error[E0798]: return value of `C-cmse-nonsecure-call` function too large to pass via registers - --> $DIR/return-via-stack.rs:38:5 +error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers + --> $DIR/return-via-stack.rs:29:48 | LL | f5: extern "C-cmse-nonsecure-call" fn() -> [u8; 5], - | -- this function uses the `C-cmse-nonsecure-call` ABI -... -LL | f5(); - | ^^^^ but its return value doesn't fit in the available registers + | ^^^^^^^ this type 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 + = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers -error[E0798]: return value of `C-cmse-nonsecure-call` function too large to pass via registers - --> $DIR/return-via-stack.rs:39:5 - | -LL | f6: extern "C-cmse-nonsecure-call" fn() -> u128, - | -- this function uses the `C-cmse-nonsecure-call` ABI -... -LL | f6(); - | ^^^^ 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 - -error[E0798]: return value of `C-cmse-nonsecure-call` function too large to pass via registers - --> $DIR/return-via-stack.rs:40:5 - | -LL | f7: extern "C-cmse-nonsecure-call" fn() -> i128, - | -- this function uses the `C-cmse-nonsecure-call` ABI -... -LL | f7(); - | ^^^^ 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 - -error[E0798]: return value of `C-cmse-nonsecure-call` function too large to pass via registers - --> $DIR/return-via-stack.rs:58:5 +error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers + --> $DIR/return-via-stack.rs:51:48 | LL | f1: extern "C-cmse-nonsecure-call" fn() -> ReprRustUnionU64, - | -- this function uses the `C-cmse-nonsecure-call` ABI -... -LL | f1(); - | ^^^^ but its return value doesn't fit in the available registers + | ^^^^^^^^^^^^^^^^ this type 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 + = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers -error[E0798]: return value of `C-cmse-nonsecure-call` function too large to pass via registers - --> $DIR/return-via-stack.rs:59:5 +error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers + --> $DIR/return-via-stack.rs:52:48 | LL | f2: extern "C-cmse-nonsecure-call" fn() -> ReprCUnionU64, - | -- this function uses the `C-cmse-nonsecure-call` ABI -... -LL | f2(); - | ^^^^ but its return value doesn't fit in the available registers + | ^^^^^^^^^^^^^ this type 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 + = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers -error: aborting due to 9 previous errors; 3 warnings emitted +error: aborting due to 9 previous errors For more information about this error, try `rustc --explain E0798`. diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/via-registers.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/via-registers.rs index 91aadd4fe1d7..9fda55c2a480 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/via-registers.rs +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/via-registers.rs @@ -10,10 +10,10 @@ pub trait Copy {} impl Copy for u32 {} #[repr(transparent)] -pub struct ReprTransparentStructU64 { +pub struct ReprTransparentStruct { _marker1: (), _marker2: (), - field: u64, + field: T, _marker3: (), } @@ -33,19 +33,9 @@ pub fn params( f3: extern "C-cmse-nonsecure-call" fn(u64, u64), f4: extern "C-cmse-nonsecure-call" fn(u128), f5: extern "C-cmse-nonsecure-call" fn(f64, f32, f32), - f6: extern "C-cmse-nonsecure-call" fn(ReprTransparentStructU64, U32Compound), + f6: extern "C-cmse-nonsecure-call" fn(ReprTransparentStruct, U32Compound), f7: extern "C-cmse-nonsecure-call" fn([u32; 4]), ) { - f1(); - f2(1, 2, 3, 4); - f3(1, 2); - f4(1); - f5(1.0, 2.0, 3.0); - f6( - ReprTransparentStructU64 { _marker1: (), _marker2: (), field: 1, _marker3: () }, - U32Compound(2, 3), - ); - f7([0xDEADBEEF; 4]); } #[no_mangle] @@ -55,16 +45,9 @@ pub fn returns( f3: extern "C-cmse-nonsecure-call" fn() -> i64, f4: extern "C-cmse-nonsecure-call" fn() -> f64, f5: extern "C-cmse-nonsecure-call" fn() -> [u8; 4], - f6: extern "C-cmse-nonsecure-call" fn() -> ReprTransparentStructU64, - f7: extern "C-cmse-nonsecure-call" fn() -> ReprTransparentEnumU64, - f8: extern "C-cmse-nonsecure-call" fn() -> U32Compound, + f6: extern "C-cmse-nonsecure-call" fn() -> ReprTransparentStruct, + f7: extern "C-cmse-nonsecure-call" fn() -> ReprTransparentStruct>, + f8: extern "C-cmse-nonsecure-call" fn() -> ReprTransparentEnumU64, + f9: extern "C-cmse-nonsecure-call" fn() -> U32Compound, ) { - f1(); - f2(); - f3(); - f4(); - f5(); - f6(); - f7(); - f8(); } From 6b6b8422bafe310b911832119cbcf4d93f46871e Mon Sep 17 00:00:00 2001 From: Folkert Date: Thu, 18 Jul 2024 12:08:23 +0200 Subject: [PATCH 10/11] remove cmse validation from rustc_codegen_ssa it was moved to hir_analysis in the previous commit --- compiler/rustc_codegen_ssa/messages.ftl | 12 ---- compiler/rustc_codegen_ssa/src/errors.rs | 22 ------ compiler/rustc_codegen_ssa/src/mir/block.rs | 4 -- compiler/rustc_codegen_ssa/src/mir/cmse.rs | 72 ------------------- compiler/rustc_codegen_ssa/src/mir/mod.rs | 1 - .../rustc_hir_analysis/src/check/check.rs | 7 +- .../src/hir_ty_lowering/mod.rs | 6 +- 7 files changed, 4 insertions(+), 120 deletions(-) delete mode 100644 compiler/rustc_codegen_ssa/src/mir/cmse.rs diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index 0aee5e16a537..000fe2e3ce0f 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -16,18 +16,6 @@ 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}` diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index af7835633cfd..e9d31db92541 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -1033,25 +1033,3 @@ 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, -} diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 24d3b8ad264b..6a5525dc2b34 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -7,7 +7,6 @@ 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; @@ -870,9 +869,6 @@ 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()); diff --git a/compiler/rustc_codegen_ssa/src/mir/cmse.rs b/compiler/rustc_codegen_ssa/src/mir/cmse.rs deleted file mode 100644 index d3583d79c00d..000000000000 --- a/compiler/rustc_codegen_ssa/src/mir/cmse.rs +++ /dev/null @@ -1,72 +0,0 @@ -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::() { - 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) -} diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index ec28def8b83e..61f57c9030a1 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -16,7 +16,6 @@ use std::iter; mod analyze; mod block; -mod cmse; pub mod constant; pub mod coverageinfo; pub mod debuginfo; diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index aa302e4c25f4..bf8ef18c04fc 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -1287,10 +1287,9 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) (span, trivial, check_non_exhaustive(tcx, ty).break_value()) }); - let non_trivial_fields = - field_infos.clone().filter_map( - |(span, trivial, _non_exhaustive)| if !trivial { Some(span) } else { None }, - ); + let non_trivial_fields = field_infos + .clone() + .filter_map(|(span, trivial, _non_exhaustive)| if !trivial { Some(span) } else { None }); let non_trivial_count = non_trivial_fields.clone().count(); if non_trivial_count >= 2 { bad_non_zero_sized_fields( diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 5a98e4d79bb0..c118181780a1 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -1749,11 +1749,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { generic_segments.iter().map(|GenericPathSegment(_, index)| index).collect(); let _ = self.prohibit_generic_args( path.segments.iter().enumerate().filter_map(|(index, seg)| { - if !indices.contains(&index) { - Some(seg) - } else { - None - } + if !indices.contains(&index) { Some(seg) } else { None } }), GenericsArgsErrExtend::DefVariant, ); From c2894a4297fd1e824e071fd21a92bfe475c0169a Mon Sep 17 00:00:00 2001 From: Folkert Date: Thu, 18 Jul 2024 14:32:10 +0200 Subject: [PATCH 11/11] improve error reporting --- compiler/rustc_hir_analysis/messages.ftl | 8 ++- compiler/rustc_hir_analysis/src/errors.rs | 4 +- .../src/hir_ty_lowering/cmse.rs | 50 ++++++++++--------- .../src/hir_ty_lowering/mod.rs | 2 +- .../cmse-nonsecure-call/generics.stderr | 8 +-- .../cmse-nonsecure-call/params-via-stack.rs | 10 ++-- .../params-via-stack.stderr | 20 ++++---- .../return-via-stack.stderr | 9 ++++ 8 files changed, 65 insertions(+), 46 deletions(-) diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 836180411339..9e5fa37de322 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -63,13 +63,17 @@ hir_analysis_cmse_call_generic = hir_analysis_cmse_call_inputs_stack_spill = arguments for `"C-cmse-nonsecure-call"` function too large to pass via registers - .label = these arguments don't fit in the available registers + .label = {$plural -> + [false] this argument doesn't + *[true] these 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 hir_analysis_cmse_call_output_stack_spill = return value of `"C-cmse-nonsecure-call"` function too large to pass via registers .label = this type 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 + .note1 = functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers + .note2 = the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size hir_analysis_coerce_unsized_may = the trait `{$trait_name}` may only be implemented for a coercion between structures diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index f6783364f163..a77f967a5caa 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1690,11 +1690,13 @@ pub struct CmseCallInputsStackSpill { #[primary_span] #[label] pub span: Span, + pub plural: bool, } #[derive(Diagnostic)] #[diag(hir_analysis_cmse_call_output_stack_spill, code = E0798)] -#[note] +#[note(hir_analysis_note1)] +#[note(hir_analysis_note2)] pub struct CmseCallOutputStackSpill { #[primary_span] #[label] diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs index 8980173f7381..e99717ce00f8 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs @@ -13,7 +13,7 @@ use crate::errors; /// conditions, but by checking them here rustc can emit nicer error messages. pub fn validate_cmse_abi<'tcx>( tcx: TyCtxt<'tcx>, - dcx: &DiagCtxtHandle<'_>, + dcx: DiagCtxtHandle<'_>, hir_id: HirId, abi: abi::Abi, fn_sig: ty::PolyFnSig<'tcx>, @@ -30,25 +30,20 @@ pub fn validate_cmse_abi<'tcx>( return; }; - // fn(u32, u32, u32, u16, u16) -> u32, - // ^^^^^^^^^^^^^^^^^^^^^^^ ^^^ - let output_span = bare_fn_ty.decl.output.span(); - let inputs_span = match ( - bare_fn_ty.param_names.first(), - bare_fn_ty.decl.inputs.first(), - bare_fn_ty.decl.inputs.last(), - ) { - (Some(ident), Some(ty1), Some(ty2)) => ident.span.to(ty1.span).to(ty2.span), - _ => *bare_fn_span, - }; - match is_valid_cmse_inputs(tcx, fn_sig) { - Ok(true) => {} - Ok(false) => { - dcx.emit_err(errors::CmseCallInputsStackSpill { span: inputs_span }); + 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, inputs_span) { + if let Some(err) = cmse_layout_err(layout_err, *bare_fn_span) { dcx.emit_err(err); } } @@ -57,10 +52,11 @@ pub fn validate_cmse_abi<'tcx>( match is_valid_cmse_output(tcx, fn_sig) { Ok(true) => {} Ok(false) => { - dcx.emit_err(errors::CmseCallOutputStackSpill { span: output_span }); + 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, output_span) { + if let Some(err) = cmse_layout_err(layout_err, *bare_fn_span) { dcx.emit_err(err); } } @@ -72,10 +68,11 @@ pub fn validate_cmse_abi<'tcx>( fn is_valid_cmse_inputs<'tcx>( tcx: TyCtxt<'tcx>, fn_sig: ty::PolyFnSig<'tcx>, -) -> Result> { +) -> Result, &'tcx LayoutError<'tcx>> { + let mut span = None; let mut accum = 0u64; - for arg_def in fn_sig.inputs().iter() { + 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(); @@ -83,10 +80,17 @@ fn is_valid_cmse_inputs<'tcx>( 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)); + } } - // i.e. 4 32-bit registers - Ok(accum <= 16) + match span { + None => Ok(Ok(())), + Some(span) => Ok(Err(span)), + } } /// Returns whether the output will fit into the available registers diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index c118181780a1..a632619aef20 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -2326,7 +2326,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let bare_fn_ty = ty::Binder::bind_with_vars(fn_ty, bound_vars); // reject function types that violate cmse ABI requirements - cmse::validate_cmse_abi(self.tcx(), &self.dcx(), hir_id, abi, bare_fn_ty); + cmse::validate_cmse_abi(self.tcx(), self.dcx(), hir_id, abi, bare_fn_ty); // Find any late-bound regions declared in return type that do // not appear in the arguments. These are not well-formed. diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr index b255d2619385..fa68d95218cc 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr @@ -30,16 +30,16 @@ 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:43 + --> $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:43 + --> $DIR/generics.rs:20:9 | LL | f4: extern "C-cmse-nonsecure-call" fn(Wrapper, u32, u32, u32) -> u64, - | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 5 previous errors diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.rs index f8ef44199210..083a563bd7ba 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.rs +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.rs @@ -14,10 +14,10 @@ pub struct AlignRelevant(u32); #[no_mangle] pub fn test( - f1: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32, u32), //~ ERROR [E0798] - f2: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u16, u16), //~ ERROR [E0798] - f3: extern "C-cmse-nonsecure-call" fn(u32, u64, u32), //~ ERROR [E0798] - f4: extern "C-cmse-nonsecure-call" fn(AlignRelevant, u32), //~ ERROR [E0798] - f5: extern "C-cmse-nonsecure-call" fn([u32; 5]), //~ ERROR [E0798] + f1: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32, x: u32, y: u32), //~ ERROR [E0798] + f2: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u16, u16), //~ ERROR [E0798] + f3: extern "C-cmse-nonsecure-call" fn(u32, u64, u32), //~ ERROR [E0798] + f4: extern "C-cmse-nonsecure-call" fn(AlignRelevant, u32), //~ ERROR [E0798] + f5: extern "C-cmse-nonsecure-call" fn([u32; 5]), //~ ERROR [E0798] ) { } diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.stderr index 499122ab2c54..a5f5e1c3151d 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.stderr +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.stderr @@ -1,32 +1,32 @@ error[E0798]: arguments for `"C-cmse-nonsecure-call"` function too large to pass via registers - --> $DIR/params-via-stack.rs:17:43 + --> $DIR/params-via-stack.rs:17:63 | -LL | f1: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32, u32), - | ^^^^^^^^^^^^^^^^^^^^^^^ these arguments don't fit in the available registers +LL | f1: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32, x: u32, y: u32), + | ^^^^^^^^^^^^^^ these 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:18:43 + --> $DIR/params-via-stack.rs:18:63 | LL | f2: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u16, u16), - | ^^^^^^^^^^^^^^^^^^^^^^^ these arguments don't fit in the available registers + | ^^^ this argument doesn'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:19:43 + --> $DIR/params-via-stack.rs:19:53 | LL | f3: extern "C-cmse-nonsecure-call" fn(u32, u64, u32), - | ^^^^^^^^^^^^^ these arguments don't fit in the available registers + | ^^^ this argument doesn'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:20:43 + --> $DIR/params-via-stack.rs:20:58 | LL | f4: extern "C-cmse-nonsecure-call" fn(AlignRelevant, u32), - | ^^^^^^^^^^^^^^^^^^ these arguments don't fit in the available registers + | ^^^ this argument doesn'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 @@ -34,7 +34,7 @@ error[E0798]: arguments for `"C-cmse-nonsecure-call"` function too large to pass --> $DIR/params-via-stack.rs:21:43 | LL | f5: extern "C-cmse-nonsecure-call" fn([u32; 5]), - | ^^^^^^^^ these arguments don't fit in the available registers + | ^^^^^^^^ this argument doesn'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 diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.stderr index c8984e3c0d90..89f7f159d6e4 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.stderr +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.stderr @@ -5,6 +5,7 @@ LL | u128: extern "C-cmse-nonsecure-call" fn() -> u128, | ^^^^ this type 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 + = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers --> $DIR/return-via-stack.rs:36:50 @@ -13,6 +14,7 @@ LL | i128: extern "C-cmse-nonsecure-call" fn() -> i128, | ^^^^ this type 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 + = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers --> $DIR/return-via-stack.rs:25:48 @@ -21,6 +23,7 @@ LL | f1: extern "C-cmse-nonsecure-call" fn() -> ReprCU64, | ^^^^^^^^ this type 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 + = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers --> $DIR/return-via-stack.rs:26:48 @@ -29,6 +32,7 @@ LL | f2: extern "C-cmse-nonsecure-call" fn() -> ReprCBytes, | ^^^^^^^^^^ this type 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 + = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers --> $DIR/return-via-stack.rs:27:48 @@ -37,6 +41,7 @@ LL | f3: extern "C-cmse-nonsecure-call" fn() -> U64Compound, | ^^^^^^^^^^^ this type 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 + = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers --> $DIR/return-via-stack.rs:28:48 @@ -45,6 +50,7 @@ LL | f4: extern "C-cmse-nonsecure-call" fn() -> ReprCAlign16, | ^^^^^^^^^^^^ this type 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 + = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers --> $DIR/return-via-stack.rs:29:48 @@ -53,6 +59,7 @@ LL | f5: extern "C-cmse-nonsecure-call" fn() -> [u8; 5], | ^^^^^^^ this type 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 + = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers --> $DIR/return-via-stack.rs:51:48 @@ -61,6 +68,7 @@ LL | f1: extern "C-cmse-nonsecure-call" fn() -> ReprRustUnionU64, | ^^^^^^^^^^^^^^^^ this type 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 + = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers --> $DIR/return-via-stack.rs:52:48 @@ -69,6 +77,7 @@ LL | f2: extern "C-cmse-nonsecure-call" fn() -> ReprCUnionU64, | ^^^^^^^^^^^^^ this type 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 + = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size error: aborting due to 9 previous errors