Rollup merge of #104121 - Lokathor:mir-opt-when-instruction-set-missing-on-callee, r=tmiasko

Refine `instruction_set` MIR inline rules

Previously an exact match of the `instruction_set` attribute was required for an MIR inline to be considered. This change checks for an exact match *only* if the callee sets an `instruction_set` in the first place. When the callee does not declare an instruction set then it is considered to be platform agnostic code and it's allowed to be inline'd into the caller.

cc ``@oli-obk``

[Edit] Zulip Context: https://rust-lang.zulipchat.com/#narrow/stream/189540-t-compiler.2Fwg-mir-opt/topic/What.20exactly.20does.20the.20MIR.20optimizer.20do.3F
This commit is contained in:
Matthias Krüger 2022-11-26 10:39:10 +01:00 committed by GitHub
commit 4733312e09
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 71 additions and 22 deletions

View File

@ -375,7 +375,12 @@ impl<'tcx> Inliner<'tcx> {
return Err("incompatible sanitizer set"); return Err("incompatible sanitizer set");
} }
if callee_attrs.instruction_set != self.codegen_fn_attrs.instruction_set { // Two functions are compatible if the callee has no attribute (meaning
// that it's codegen agnostic), or sets an attribute that is identical
// to this function's attribute.
if callee_attrs.instruction_set.is_some()
&& callee_attrs.instruction_set != self.codegen_fn_attrs.instruction_set
{
return Err("incompatible instruction set"); return Err("incompatible instruction set");
} }
@ -453,6 +458,15 @@ impl<'tcx> Inliner<'tcx> {
if ty.needs_drop(tcx, self.param_env) && let Some(unwind) = unwind { if ty.needs_drop(tcx, self.param_env) && let Some(unwind) = unwind {
work_list.push(unwind); work_list.push(unwind);
} }
} else if callee_attrs.instruction_set != self.codegen_fn_attrs.instruction_set
&& matches!(term.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.
return Err("Cannot move inline-asm across instruction sets");
} else { } else {
work_list.extend(term.successors()) work_list.extend(term.successors())
} }

View File

@ -6,14 +6,19 @@
let _1: (); // in scope 0 at $DIR/inline_instruction_set.rs:+1:5: +1:26 let _1: (); // in scope 0 at $DIR/inline_instruction_set.rs:+1:5: +1:26
let _2: (); // in scope 0 at $DIR/inline_instruction_set.rs:+2:5: +2:26 let _2: (); // in scope 0 at $DIR/inline_instruction_set.rs:+2:5: +2:26
let _3: (); // in scope 0 at $DIR/inline_instruction_set.rs:+3:5: +3:30 let _3: (); // in scope 0 at $DIR/inline_instruction_set.rs:+3:5: +3:30
+ scope 1 (inlined instruction_set_default) { // at $DIR/inline_instruction_set.rs:53:5: 53:30 let _4: (); // in scope 0 at $DIR/inline_instruction_set.rs:+4:5: +4:41
+ scope 1 (inlined instruction_set_default) { // at $DIR/inline_instruction_set.rs:59:5: 59:30
+ }
+ scope 2 (inlined inline_always_and_using_inline_asm) { // at $DIR/inline_instruction_set.rs:60:5: 60:41
+ scope 3 {
+ }
+ } + }
bb0: { bb0: {
StorageLive(_1); // scope 0 at $DIR/inline_instruction_set.rs:+1:5: +1:26 StorageLive(_1); // scope 0 at $DIR/inline_instruction_set.rs:+1:5: +1:26
_1 = instruction_set_a32() -> bb1; // scope 0 at $DIR/inline_instruction_set.rs:+1:5: +1:26 _1 = instruction_set_a32() -> bb1; // scope 0 at $DIR/inline_instruction_set.rs:+1:5: +1:26
// mir::Constant // mir::Constant
// + span: $DIR/inline_instruction_set.rs:51:5: 51:24 // + span: $DIR/inline_instruction_set.rs:57:5: 57:24
// + literal: Const { ty: fn() {instruction_set_a32}, val: Value(<ZST>) } // + literal: Const { ty: fn() {instruction_set_a32}, val: Value(<ZST>) }
} }
@ -22,7 +27,7 @@
StorageLive(_2); // scope 0 at $DIR/inline_instruction_set.rs:+2:5: +2:26 StorageLive(_2); // scope 0 at $DIR/inline_instruction_set.rs:+2:5: +2:26
_2 = instruction_set_t32() -> bb2; // scope 0 at $DIR/inline_instruction_set.rs:+2:5: +2:26 _2 = instruction_set_t32() -> bb2; // scope 0 at $DIR/inline_instruction_set.rs:+2:5: +2:26
// mir::Constant // mir::Constant
// + span: $DIR/inline_instruction_set.rs:52:5: 52:24 // + span: $DIR/inline_instruction_set.rs:58:5: 58:24
// + literal: Const { ty: fn() {instruction_set_t32}, val: Value(<ZST>) } // + literal: Const { ty: fn() {instruction_set_t32}, val: Value(<ZST>) }
} }
@ -31,14 +36,25 @@
StorageLive(_3); // scope 0 at $DIR/inline_instruction_set.rs:+3:5: +3:30 StorageLive(_3); // scope 0 at $DIR/inline_instruction_set.rs:+3:5: +3:30
- _3 = instruction_set_default() -> bb3; // scope 0 at $DIR/inline_instruction_set.rs:+3:5: +3:30 - _3 = instruction_set_default() -> bb3; // scope 0 at $DIR/inline_instruction_set.rs:+3:5: +3:30
- // mir::Constant - // mir::Constant
- // + span: $DIR/inline_instruction_set.rs:53:5: 53:28 - // + span: $DIR/inline_instruction_set.rs:59:5: 59:28
- // + literal: Const { ty: fn() {instruction_set_default}, val: Value(<ZST>) } - // + literal: Const { ty: fn() {instruction_set_default}, val: Value(<ZST>) }
- } - }
- -
- bb3: { - bb3: {
StorageDead(_3); // scope 0 at $DIR/inline_instruction_set.rs:+3:30: +3:31 StorageDead(_3); // scope 0 at $DIR/inline_instruction_set.rs:+3:30: +3:31
_0 = const (); // scope 0 at $DIR/inline_instruction_set.rs:+0:18: +4:2 StorageLive(_4); // scope 0 at $DIR/inline_instruction_set.rs:+4:5: +4:41
return; // scope 0 at $DIR/inline_instruction_set.rs:+4:2: +4:2 - _4 = inline_always_and_using_inline_asm() -> bb4; // scope 0 at $DIR/inline_instruction_set.rs:+4:5: +4:41
- // mir::Constant
- // + span: $DIR/inline_instruction_set.rs:60:5: 60:39
- // + literal: Const { ty: fn() {inline_always_and_using_inline_asm}, val: Value(<ZST>) }
+ asm!("/* do nothing */", options((empty))) -> bb3; // scope 3 at $DIR/inline_instruction_set.rs:43:14: 43:38
}
- bb4: {
+ bb3: {
StorageDead(_4); // scope 0 at $DIR/inline_instruction_set.rs:+4:41: +4:42
_0 = const (); // scope 0 at $DIR/inline_instruction_set.rs:+0:18: +5:2
return; // scope 0 at $DIR/inline_instruction_set.rs:+5:2: +5:2
} }
} }

View File

@ -1,5 +1,7 @@
// Checks that only functions with the compatible instruction_set attributes are inlined. // Checks that only functions with the compatible instruction_set attributes are inlined.
// //
// A function is "compatible" when the *callee* has the same attribute or no attribute.
//
// compile-flags: --target thumbv4t-none-eabi // compile-flags: --target thumbv4t-none-eabi
// needs-llvm-components: arm // needs-llvm-components: arm
@ -36,14 +38,18 @@ fn instruction_set_t32() {}
#[inline] #[inline]
fn instruction_set_default() {} fn instruction_set_default() {}
#[inline(always)]
fn inline_always_and_using_inline_asm() {
unsafe { asm!("/* do nothing */") };
}
// EMIT_MIR inline_instruction_set.t32.Inline.diff // EMIT_MIR inline_instruction_set.t32.Inline.diff
#[instruction_set(arm::t32)] #[instruction_set(arm::t32)]
pub fn t32() { pub fn t32() {
instruction_set_a32(); instruction_set_a32();
instruction_set_t32(); instruction_set_t32();
// The default instruction set is currently
// conservatively assumed to be incompatible.
instruction_set_default(); instruction_set_default();
inline_always_and_using_inline_asm();
} }
// EMIT_MIR inline_instruction_set.default.Inline.diff // EMIT_MIR inline_instruction_set.default.Inline.diff
@ -51,4 +57,5 @@ pub fn default() {
instruction_set_a32(); instruction_set_a32();
instruction_set_t32(); instruction_set_t32();
instruction_set_default(); instruction_set_default();
inline_always_and_using_inline_asm();
} }

View File

@ -5,15 +5,18 @@
let mut _0: (); // return place in scope 0 at $DIR/inline_instruction_set.rs:+0:14: +0:14 let mut _0: (); // return place in scope 0 at $DIR/inline_instruction_set.rs:+0:14: +0:14
let _1: (); // in scope 0 at $DIR/inline_instruction_set.rs:+1:5: +1:26 let _1: (); // in scope 0 at $DIR/inline_instruction_set.rs:+1:5: +1:26
let _2: (); // in scope 0 at $DIR/inline_instruction_set.rs:+2:5: +2:26 let _2: (); // in scope 0 at $DIR/inline_instruction_set.rs:+2:5: +2:26
let _3: (); // in scope 0 at $DIR/inline_instruction_set.rs:+5:5: +5:30 let _3: (); // in scope 0 at $DIR/inline_instruction_set.rs:+3:5: +3:30
+ scope 1 (inlined instruction_set_t32) { // at $DIR/inline_instruction_set.rs:43:5: 43:26 let _4: (); // in scope 0 at $DIR/inline_instruction_set.rs:+4:5: +4:41
+ scope 1 (inlined instruction_set_t32) { // at $DIR/inline_instruction_set.rs:50:5: 50:26
+ }
+ scope 2 (inlined instruction_set_default) { // at $DIR/inline_instruction_set.rs:51:5: 51:30
+ } + }
bb0: { bb0: {
StorageLive(_1); // scope 0 at $DIR/inline_instruction_set.rs:+1:5: +1:26 StorageLive(_1); // scope 0 at $DIR/inline_instruction_set.rs:+1:5: +1:26
_1 = instruction_set_a32() -> bb1; // scope 0 at $DIR/inline_instruction_set.rs:+1:5: +1:26 _1 = instruction_set_a32() -> bb1; // scope 0 at $DIR/inline_instruction_set.rs:+1:5: +1:26
// mir::Constant // mir::Constant
// + span: $DIR/inline_instruction_set.rs:42:5: 42:24 // + span: $DIR/inline_instruction_set.rs:49:5: 49:24
// + literal: Const { ty: fn() {instruction_set_a32}, val: Value(<ZST>) } // + literal: Const { ty: fn() {instruction_set_a32}, val: Value(<ZST>) }
} }
@ -22,25 +25,34 @@
StorageLive(_2); // scope 0 at $DIR/inline_instruction_set.rs:+2:5: +2:26 StorageLive(_2); // scope 0 at $DIR/inline_instruction_set.rs:+2:5: +2:26
- _2 = instruction_set_t32() -> bb2; // scope 0 at $DIR/inline_instruction_set.rs:+2:5: +2:26 - _2 = instruction_set_t32() -> bb2; // scope 0 at $DIR/inline_instruction_set.rs:+2:5: +2:26
- // mir::Constant - // mir::Constant
- // + span: $DIR/inline_instruction_set.rs:43:5: 43:24 - // + span: $DIR/inline_instruction_set.rs:50:5: 50:24
- // + literal: Const { ty: fn() {instruction_set_t32}, val: Value(<ZST>) } - // + literal: Const { ty: fn() {instruction_set_t32}, val: Value(<ZST>) }
- } - }
- -
- bb2: { - bb2: {
StorageDead(_2); // scope 0 at $DIR/inline_instruction_set.rs:+2:26: +2:27 StorageDead(_2); // scope 0 at $DIR/inline_instruction_set.rs:+2:26: +2:27
StorageLive(_3); // scope 0 at $DIR/inline_instruction_set.rs:+5:5: +5:30 StorageLive(_3); // scope 0 at $DIR/inline_instruction_set.rs:+3:5: +3:30
- _3 = instruction_set_default() -> bb3; // scope 0 at $DIR/inline_instruction_set.rs:+5:5: +5:30 - _3 = instruction_set_default() -> bb3; // scope 0 at $DIR/inline_instruction_set.rs:+3:5: +3:30
+ _3 = instruction_set_default() -> bb2; // scope 0 at $DIR/inline_instruction_set.rs:+5:5: +5:30 - // mir::Constant
- // + span: $DIR/inline_instruction_set.rs:51:5: 51:28
- // + literal: Const { ty: fn() {instruction_set_default}, val: Value(<ZST>) }
- }
-
- bb3: {
StorageDead(_3); // scope 0 at $DIR/inline_instruction_set.rs:+3:30: +3:31
StorageLive(_4); // scope 0 at $DIR/inline_instruction_set.rs:+4:5: +4:41
- _4 = inline_always_and_using_inline_asm() -> bb4; // scope 0 at $DIR/inline_instruction_set.rs:+4:5: +4:41
+ _4 = inline_always_and_using_inline_asm() -> bb2; // scope 0 at $DIR/inline_instruction_set.rs:+4:5: +4:41
// mir::Constant // mir::Constant
// + span: $DIR/inline_instruction_set.rs:46:5: 46:28 // + span: $DIR/inline_instruction_set.rs:52:5: 52:39
// + literal: Const { ty: fn() {instruction_set_default}, val: Value(<ZST>) } // + literal: Const { ty: fn() {inline_always_and_using_inline_asm}, val: Value(<ZST>) }
} }
- bb3: { - bb4: {
+ bb2: { + bb2: {
StorageDead(_3); // scope 0 at $DIR/inline_instruction_set.rs:+5:30: +5:31 StorageDead(_4); // scope 0 at $DIR/inline_instruction_set.rs:+4:41: +4:42
_0 = const (); // scope 0 at $DIR/inline_instruction_set.rs:+0:14: +6:2 _0 = const (); // scope 0 at $DIR/inline_instruction_set.rs:+0:14: +5:2
return; // scope 0 at $DIR/inline_instruction_set.rs:+6:2: +6:2 return; // scope 0 at $DIR/inline_instruction_set.rs:+5:2: +5:2
} }
} }