Skip to content

Commit

Permalink
inline: re-introduce some callee body checks
Browse files Browse the repository at this point in the history
  • Loading branch information
davidtwco committed Jan 10, 2025
1 parent 4507939 commit e4bae91
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 2 deletions.
31 changes: 29 additions & 2 deletions compiler/rustc_mir_transform/src/inline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<usize> {
Expand Down
68 changes: 68 additions & 0 deletions tests/ui/force-inlining/asm.rs
Original file line number Diff line number Diff line change
@@ -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<T>(_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();
}
34 changes: 34 additions & 0 deletions tests/ui/force-inlining/asm.stderr
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit e4bae91

Please sign in to comment.