From e4bae91be11c81b8c68a7bb521dfa17ccef3fc24 Mon Sep 17 00:00:00 2001 From: David Wood Date: Tue, 10 Dec 2024 12:06:57 +0000 Subject: [PATCH] inline: re-introduce some callee body checks --- compiler/rustc_mir_transform/src/inline.rs | 31 +++++++++- tests/ui/force-inlining/asm.rs | 68 ++++++++++++++++++++++ tests/ui/force-inlining/asm.stderr | 34 +++++++++++ 3 files changed, 131 insertions(+), 2 deletions(-) create mode 100644 tests/ui/force-inlining/asm.rs create mode 100644 tests/ui/force-inlining/asm.stderr diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index cdb0ed761cf..a98901a6688 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -195,10 +195,37 @@ impl<'tcx> Inliner<'tcx> for ForceInliner<'tcx> { &self, _: &CallSite<'tcx>, callee_body: &Body<'tcx>, - _: &CodegenFnAttrs, + callee_attrs: &CodegenFnAttrs, _: bool, ) -> Result<(), &'static str> { - if let Some(_) = callee_body.tainted_by_errors { Err("body has errors") } else { Ok(()) } + if callee_body.tainted_by_errors.is_some() { + return Err("body has errors"); + } + + let caller_attrs = self.tcx().codegen_fn_attrs(self.caller_def_id()); + if callee_attrs.instruction_set != caller_attrs.instruction_set + && callee_body + .basic_blocks + .iter() + .any(|bb| matches!(bb.terminator().kind, TerminatorKind::InlineAsm { .. })) + { + // During the attribute checking stage we allow a callee with no + // instruction_set assigned to count as compatible with a function that does + // assign one. However, during this stage we require an exact match when any + // inline-asm is detected. LLVM will still possibly do an inline later on + // if the no-attribute function ends up with the same instruction set anyway. + Err("cannot move inline-asm across instruction sets") + } else if callee_body + .basic_blocks + .iter() + .any(|bb| matches!(bb.terminator().kind, TerminatorKind::TailCall { .. })) + { + // FIXME(explicit_tail_calls): figure out how exactly functions containing tail + // calls can be inlined (and if they even should) + Err("can't inline functions with tail calls") + } else { + Ok(()) + } } fn inline_limit_for_block(&self) -> Option { diff --git a/tests/ui/force-inlining/asm.rs b/tests/ui/force-inlining/asm.rs new file mode 100644 index 00000000000..85014ffa515 --- /dev/null +++ b/tests/ui/force-inlining/asm.rs @@ -0,0 +1,68 @@ +//@ build-fail +//@ compile-flags: --crate-type=lib --target thumbv4t-none-eabi +//@ needs-llvm-components: arm + +// Checks that forced inlining won't mix asm with incompatible instruction sets. + +#![crate_type = "lib"] +#![feature(rustc_attrs)] +#![feature(no_core, lang_items)] +#![no_core] + +#[lang = "sized"] +pub trait Sized {} +#[lang = "copy"] +pub trait Copy {} +#[lang = "freeze"] +pub unsafe trait Freeze {} + +#[lang = "start"] +fn start(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize { + 0 +} + +#[rustc_builtin_macro] +#[macro_export] +macro_rules! asm { + ("assembly template", + $(operands,)* + $(options($(option),*))? + ) => { + /* compiler built-in */ + }; +} + +#[instruction_set(arm::a32)] +#[rustc_force_inline] +fn instruction_set_a32() {} + +#[instruction_set(arm::t32)] +#[rustc_force_inline] +fn instruction_set_t32() {} + +#[rustc_force_inline] +fn instruction_set_default() {} + +#[rustc_force_inline] +fn inline_always_and_using_inline_asm() { + unsafe { asm!("/* do nothing */") }; +} + +#[instruction_set(arm::t32)] +pub fn t32() { + instruction_set_a32(); +//~^ ERROR `instruction_set_a32` could not be inlined into `t32` but is required to be inlined + instruction_set_t32(); + instruction_set_default(); + inline_always_and_using_inline_asm(); +//~^ ERROR `inline_always_and_using_inline_asm` could not be inlined into `t32` but is required to be inlined +} + +pub fn default() { + instruction_set_a32(); +//~^ ERROR `instruction_set_a32` could not be inlined into `default` but is required to be inlined + instruction_set_t32(); +//~^ ERROR `instruction_set_t32` could not be inlined into `default` but is required to be inlined + instruction_set_default(); + inline_always_and_using_inline_asm(); +} diff --git a/tests/ui/force-inlining/asm.stderr b/tests/ui/force-inlining/asm.stderr new file mode 100644 index 00000000000..ef04688965b --- /dev/null +++ b/tests/ui/force-inlining/asm.stderr @@ -0,0 +1,34 @@ +error: `instruction_set_a32` could not be inlined into `t32` but is required to be inlined + --> $DIR/asm.rs:53:5 + | +LL | instruction_set_a32(); + | ^^^^^^^^^^^^^^^^^^^^^ ...`instruction_set_a32` called here + | + = note: could not be inlined due to: incompatible instruction set + +error: `inline_always_and_using_inline_asm` could not be inlined into `t32` but is required to be inlined + --> $DIR/asm.rs:57:5 + | +LL | inline_always_and_using_inline_asm(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...`inline_always_and_using_inline_asm` called here + | + = note: could not be inlined due to: cannot move inline-asm across instruction sets + +error: `instruction_set_a32` could not be inlined into `default` but is required to be inlined + --> $DIR/asm.rs:62:5 + | +LL | instruction_set_a32(); + | ^^^^^^^^^^^^^^^^^^^^^ ...`instruction_set_a32` called here + | + = note: could not be inlined due to: incompatible instruction set + +error: `instruction_set_t32` could not be inlined into `default` but is required to be inlined + --> $DIR/asm.rs:64:5 + | +LL | instruction_set_t32(); + | ^^^^^^^^^^^^^^^^^^^^^ ...`instruction_set_t32` called here + | + = note: could not be inlined due to: incompatible instruction set + +error: aborting due to 4 previous errors +