about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_mir_transform/src/instsimplify.rs30
-rw-r--r--tests/mir-opt/instsimplify/aggregate_array.const_items.InstSimplify-after-simplifycfg.diff13
-rw-r--r--tests/mir-opt/instsimplify/aggregate_array.equal_referents.InstSimplify-after-simplifycfg.diff12
-rw-r--r--tests/mir-opt/instsimplify/aggregate_array.literals.InstSimplify-after-simplifycfg.diff13
-rw-r--r--tests/mir-opt/instsimplify/aggregate_array.local.InstSimplify-after-simplifycfg.diff39
-rw-r--r--tests/mir-opt/instsimplify/aggregate_array.rs56
-rw-r--r--tests/mir-opt/instsimplify/aggregate_array.strs.InstSimplify-after-simplifycfg.diff13
7 files changed, 176 insertions, 0 deletions
diff --git a/compiler/rustc_mir_transform/src/instsimplify.rs b/compiler/rustc_mir_transform/src/instsimplify.rs
index 9e024508c58..f74a577c598 100644
--- a/compiler/rustc_mir_transform/src/instsimplify.rs
+++ b/compiler/rustc_mir_transform/src/instsimplify.rs
@@ -48,6 +48,7 @@ impl<'tcx> crate::MirPass<'tcx> for InstSimplify {
                         ctx.simplify_ref_deref(rvalue);
                         ctx.simplify_ptr_aggregate(rvalue);
                         ctx.simplify_cast(rvalue);
+                        ctx.simplify_repeated_aggregate(rvalue);
                     }
                     _ => {}
                 }
@@ -68,6 +69,35 @@ struct InstSimplifyContext<'a, 'tcx> {
 }
 
 impl<'tcx> InstSimplifyContext<'_, 'tcx> {
+    /// Transform aggregates like [0, 0, 0, 0, 0] into [0; 5].
+    /// GVN can also do this optimization, but GVN is only run at mir-opt-level 2 so having this in
+    /// InstSimplify helps unoptimized builds.
+    fn simplify_repeated_aggregate(&self, rvalue: &mut Rvalue<'tcx>) {
+        let Rvalue::Aggregate(box AggregateKind::Array(_), fields) = rvalue else {
+            return;
+        };
+        if fields.len() < 5 {
+            return;
+        }
+        let first = &fields[rustc_abi::FieldIdx::ZERO];
+        let Operand::Constant(first) = first else {
+            return;
+        };
+        let Ok(first_val) = first.const_.eval(self.tcx, self.typing_env, first.span) else {
+            return;
+        };
+        if fields.iter().all(|field| {
+            let Operand::Constant(field) = field else {
+                return false;
+            };
+            let field = field.const_.eval(self.tcx, self.typing_env, field.span);
+            field == Ok(first_val)
+        }) {
+            let len = ty::Const::from_target_usize(self.tcx, fields.len().try_into().unwrap());
+            *rvalue = Rvalue::Repeat(Operand::Constant(first.clone()), len);
+        }
+    }
+
     /// Transform boolean comparisons into logical operations.
     fn simplify_bool_cmp(&self, rvalue: &mut Rvalue<'tcx>) {
         match rvalue {
diff --git a/tests/mir-opt/instsimplify/aggregate_array.const_items.InstSimplify-after-simplifycfg.diff b/tests/mir-opt/instsimplify/aggregate_array.const_items.InstSimplify-after-simplifycfg.diff
new file mode 100644
index 00000000000..bdeabee2e46
--- /dev/null
+++ b/tests/mir-opt/instsimplify/aggregate_array.const_items.InstSimplify-after-simplifycfg.diff
@@ -0,0 +1,13 @@
+- // MIR for `const_items` before InstSimplify-after-simplifycfg
++ // MIR for `const_items` after InstSimplify-after-simplifycfg
+  
+  fn const_items() -> [u8; 5] {
+      let mut _0: [u8; 5];
+  
+      bb0: {
+-         _0 = [const const_items::A, const const_items::B, const const_items::C, const const_items::D, const const_items::E];
++         _0 = [const const_items::A; 5];
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/instsimplify/aggregate_array.equal_referents.InstSimplify-after-simplifycfg.diff b/tests/mir-opt/instsimplify/aggregate_array.equal_referents.InstSimplify-after-simplifycfg.diff
new file mode 100644
index 00000000000..86e0860ccfa
--- /dev/null
+++ b/tests/mir-opt/instsimplify/aggregate_array.equal_referents.InstSimplify-after-simplifycfg.diff
@@ -0,0 +1,12 @@
+- // MIR for `equal_referents` before InstSimplify-after-simplifycfg
++ // MIR for `equal_referents` after InstSimplify-after-simplifycfg
+  
+  fn equal_referents() -> [&u8; 5] {
+      let mut _0: [&u8; 5];
+  
+      bb0: {
+          _0 = [const equal_referents::A, const equal_referents::B, const equal_referents::C, const equal_referents::D, const equal_referents::E];
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/instsimplify/aggregate_array.literals.InstSimplify-after-simplifycfg.diff b/tests/mir-opt/instsimplify/aggregate_array.literals.InstSimplify-after-simplifycfg.diff
new file mode 100644
index 00000000000..2f6963ad016
--- /dev/null
+++ b/tests/mir-opt/instsimplify/aggregate_array.literals.InstSimplify-after-simplifycfg.diff
@@ -0,0 +1,13 @@
+- // MIR for `literals` before InstSimplify-after-simplifycfg
++ // MIR for `literals` after InstSimplify-after-simplifycfg
+  
+  fn literals() -> [u8; 5] {
+      let mut _0: [u8; 5];
+  
+      bb0: {
+-         _0 = [const 0_u8, const 0_u8, const 0_u8, const 0_u8, const 0_u8];
++         _0 = [const 0_u8; 5];
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/instsimplify/aggregate_array.local.InstSimplify-after-simplifycfg.diff b/tests/mir-opt/instsimplify/aggregate_array.local.InstSimplify-after-simplifycfg.diff
new file mode 100644
index 00000000000..2943aa54061
--- /dev/null
+++ b/tests/mir-opt/instsimplify/aggregate_array.local.InstSimplify-after-simplifycfg.diff
@@ -0,0 +1,39 @@
+- // MIR for `local` before InstSimplify-after-simplifycfg
++ // MIR for `local` after InstSimplify-after-simplifycfg
+  
+  fn local() -> [u8; 5] {
+      let mut _0: [u8; 5];
+      let _1: u8;
+      let mut _2: u8;
+      let mut _3: u8;
+      let mut _4: u8;
+      let mut _5: u8;
+      let mut _6: u8;
+      scope 1 {
+          debug val => _1;
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          _1 = const 0_u8;
+          StorageLive(_2);
+          _2 = copy _1;
+          StorageLive(_3);
+          _3 = copy _1;
+          StorageLive(_4);
+          _4 = copy _1;
+          StorageLive(_5);
+          _5 = copy _1;
+          StorageLive(_6);
+          _6 = copy _1;
+          _0 = [move _2, move _3, move _4, move _5, move _6];
+          StorageDead(_6);
+          StorageDead(_5);
+          StorageDead(_4);
+          StorageDead(_3);
+          StorageDead(_2);
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/instsimplify/aggregate_array.rs b/tests/mir-opt/instsimplify/aggregate_array.rs
new file mode 100644
index 00000000000..8dd0d80b459
--- /dev/null
+++ b/tests/mir-opt/instsimplify/aggregate_array.rs
@@ -0,0 +1,56 @@
+//@ test-mir-pass: InstSimplify-after-simplifycfg
+#![crate_type = "lib"]
+
+// This is the easy case, and the most plausible to run into in real code.
+// EMIT_MIR aggregate_array.literals.InstSimplify-after-simplifycfg.diff
+pub fn literals() -> [u8; 5] {
+    // CHECK-LABEL: fn literals(
+    // CHECK: _0 = [const 0_u8; 5];
+    [0, 0, 0, 0, 0]
+}
+
+// Check that hiding the const value behind a const item doesn't prevent the optimization
+// EMIT_MIR aggregate_array.const_items.InstSimplify-after-simplifycfg.diff
+pub fn const_items() -> [u8; 5] {
+    const A: u8 = 0;
+    const B: u8 = 0;
+    const C: u8 = 0;
+    const D: u8 = 0;
+    const E: u8 = 0;
+
+    // CHECK-LABEL: fn const_items(
+    // CHECK: _0 = [const const_items::A; 5];
+    [A, B, C, D, E]
+}
+
+// EMIT_MIR aggregate_array.strs.InstSimplify-after-simplifycfg.diff
+pub fn strs() -> [&'static str; 5] {
+    // CHECK-LABEL: fn strs(
+    // CHECK: _0 = [const "a"; 5];
+    ["a", "a", "a", "a", "a"]
+}
+
+// InstSimplify isn't able to see through the move operands, but GVN can.
+// EMIT_MIR aggregate_array.local.InstSimplify-after-simplifycfg.diff
+pub fn local() -> [u8; 5] {
+    // CHECK-LABEL: fn local(
+    // CHECK: _0 = [move _2, move _3, move _4, move _5, move _6];
+    let val = 0;
+    [val, val, val, val, val]
+}
+
+// All of these consts refer to the same value, but the addresses are all different.
+// It would be wrong to apply the optimization here.
+// EMIT_MIR aggregate_array.equal_referents.InstSimplify-after-simplifycfg.diff
+pub fn equal_referents() -> [&'static u8; 5] {
+    const DATA: &[u8] = &[0, 0, 0, 0, 0];
+    const A: &u8 = &DATA[0];
+    const B: &u8 = &DATA[1];
+    const C: &u8 = &DATA[2];
+    const D: &u8 = &DATA[3];
+    const E: &u8 = &DATA[4];
+
+    // CHECK-LABEL: fn equal_referents(
+    // CHECK: _0 = [const equal_referents::A, const equal_referents::B, const equal_referents::C, const equal_referents::D, const equal_referents::E];
+    [A, B, C, D, E]
+}
diff --git a/tests/mir-opt/instsimplify/aggregate_array.strs.InstSimplify-after-simplifycfg.diff b/tests/mir-opt/instsimplify/aggregate_array.strs.InstSimplify-after-simplifycfg.diff
new file mode 100644
index 00000000000..f8884881756
--- /dev/null
+++ b/tests/mir-opt/instsimplify/aggregate_array.strs.InstSimplify-after-simplifycfg.diff
@@ -0,0 +1,13 @@
+- // MIR for `strs` before InstSimplify-after-simplifycfg
++ // MIR for `strs` after InstSimplify-after-simplifycfg
+  
+  fn strs() -> [&str; 5] {
+      let mut _0: [&str; 5];
+  
+      bb0: {
+-         _0 = [const "a", const "a", const "a", const "a", const "a"];
++         _0 = [const "a"; 5];
+          return;
+      }
+  }
+