about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatthias Krüger <matthias.krueger@famsik.de>2022-11-26 10:39:10 +0100
committerGitHub <noreply@github.com>2022-11-26 10:39:10 +0100
commit4733312e09c04fec3626ea27083da615d1a1df4d (patch)
tree9a8f2196f26457972c8c84f77c7f13ce39c98d2b
parentf8a2e491ebec34113563cf8683f6fec2646cebb1 (diff)
parentea4794321224964060b931d962a9a6ee7e59ac7c (diff)
downloadrust-4733312e09c04fec3626ea27083da615d1a1df4d.tar.gz
rust-4733312e09c04fec3626ea27083da615d1a1df4d.zip
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
-rw-r--r--compiler/rustc_mir_transform/src/inline.rs16
-rw-r--r--src/test/mir-opt/inline/inline_instruction_set.default.Inline.diff28
-rw-r--r--src/test/mir-opt/inline/inline_instruction_set.rs11
-rw-r--r--src/test/mir-opt/inline/inline_instruction_set.t32.Inline.diff38
4 files changed, 71 insertions, 22 deletions
diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs
index d7dd5fc8528..9174f04887e 100644
--- a/compiler/rustc_mir_transform/src/inline.rs
+++ b/compiler/rustc_mir_transform/src/inline.rs
@@ -375,7 +375,12 @@ impl<'tcx> Inliner<'tcx> {
             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");
         }
 
@@ -453,6 +458,15 @@ impl<'tcx> Inliner<'tcx> {
                 if ty.needs_drop(tcx, self.param_env) && let Some(unwind) = 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 {
                 work_list.extend(term.successors())
             }
diff --git a/src/test/mir-opt/inline/inline_instruction_set.default.Inline.diff b/src/test/mir-opt/inline/inline_instruction_set.default.Inline.diff
index e421428dcdf..f1988ea4bd6 100644
--- a/src/test/mir-opt/inline/inline_instruction_set.default.Inline.diff
+++ b/src/test/mir-opt/inline/inline_instruction_set.default.Inline.diff
@@ -6,14 +6,19 @@
       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 _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: {
           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
                                            // 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>) }
       }
   
@@ -22,7 +27,7 @@
           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
                                            // 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>) }
       }
   
@@ -31,14 +36,25 @@
           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
 -                                          // 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>) }
 -     }
 - 
 -     bb3: {
           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
-          return;                          // scope 0 at $DIR/inline_instruction_set.rs:+4:2: +4:2
+          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
+-                                          // 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
       }
   }
   
diff --git a/src/test/mir-opt/inline/inline_instruction_set.rs b/src/test/mir-opt/inline/inline_instruction_set.rs
index be36ff50c7e..5dfb04943e3 100644
--- a/src/test/mir-opt/inline/inline_instruction_set.rs
+++ b/src/test/mir-opt/inline/inline_instruction_set.rs
@@ -1,5 +1,7 @@
 // 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
 // needs-llvm-components: arm
 
@@ -36,14 +38,18 @@ fn instruction_set_t32() {}
 #[inline]
 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
 #[instruction_set(arm::t32)]
 pub fn t32() {
     instruction_set_a32();
     instruction_set_t32();
-    // The default instruction set is currently
-    // conservatively assumed to be incompatible.
     instruction_set_default();
+    inline_always_and_using_inline_asm();
 }
 
 // EMIT_MIR inline_instruction_set.default.Inline.diff
@@ -51,4 +57,5 @@ pub fn default() {
     instruction_set_a32();
     instruction_set_t32();
     instruction_set_default();
+    inline_always_and_using_inline_asm();
 }
diff --git a/src/test/mir-opt/inline/inline_instruction_set.t32.Inline.diff b/src/test/mir-opt/inline/inline_instruction_set.t32.Inline.diff
index 1ea2b87e53a..e777b2cc29e 100644
--- a/src/test/mir-opt/inline/inline_instruction_set.t32.Inline.diff
+++ b/src/test/mir-opt/inline/inline_instruction_set.t32.Inline.diff
@@ -5,15 +5,18 @@
       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 _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
-+     scope 1 (inlined instruction_set_t32) { // at $DIR/inline_instruction_set.rs:43:5: 43:26
+      let _3: ();                          // in scope 0 at $DIR/inline_instruction_set.rs:+3:5: +3:30
+      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: {
           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
                                            // 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>) }
       }
   
@@ -22,25 +25,34 @@
           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
 -                                          // 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>) }
 -     }
 - 
 -     bb2: {
           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
--         _3 = instruction_set_default() -> bb3; // scope 0 at $DIR/inline_instruction_set.rs:+5:5: +5:30
-+         _3 = instruction_set_default() -> bb2; // 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:+3:5: +3: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
-                                           // + span: $DIR/inline_instruction_set.rs:46:5: 46:28
-                                           // + literal: Const { ty: fn() {instruction_set_default}, val: Value(<ZST>) }
+                                           // + span: $DIR/inline_instruction_set.rs:52:5: 52:39
+                                           // + literal: Const { ty: fn() {inline_always_and_using_inline_asm}, val: Value(<ZST>) }
       }
   
--     bb3: {
+-     bb4: {
 +     bb2: {
-          StorageDead(_3);                 // scope 0 at $DIR/inline_instruction_set.rs:+5:30: +5:31
-          _0 = const ();                   // scope 0 at $DIR/inline_instruction_set.rs:+0:14: +6:2
-          return;                          // scope 0 at $DIR/inline_instruction_set.rs:+6:2: +6:2
+          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: +5:2
+          return;                          // scope 0 at $DIR/inline_instruction_set.rs:+5:2: +5:2
       }
   }