about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2020-04-16 14:26:43 +0000
committerbors <bors@rust-lang.org>2020-04-16 14:26:43 +0000
commit7fb5187d0423f4cd0441526571b8cd61927123c9 (patch)
tree7a8e48718bf3f9d686e4e27dca987e7730079b6b /src
parent4e4d49d60fd696c4036d438292673a2d7fd34519 (diff)
parent9666d31bcf57190864fc61a95d2dab7ae3e51cdf (diff)
downloadrust-7fb5187d0423f4cd0441526571b8cd61927123c9.tar.gz
rust-7fb5187d0423f4cd0441526571b8cd61927123c9.zip
Auto merge of #70755 - wesleywiser:simplify_locals_2_electric_boogaloo, r=oli-obk
[mir-opt] Run SimplifyLocals to a fixedpoint and handle most rvalues

Follow up to review feedback left on #70595.
Diffstat (limited to 'src')
-rw-r--r--src/librustc_mir/transform/simplify.rs206
-rw-r--r--src/test/mir-opt/const_allocation3/32bit/rustc.main.ConstProp.after.mir16
-rw-r--r--src/test/mir-opt/const_allocation3/64bit/rustc.main.ConstProp.after.mir14
-rw-r--r--src/test/mir-opt/const_prop/optimizes_into_variable/32bit/rustc.main.SimplifyLocals.after.mir60
-rw-r--r--src/test/mir-opt/const_prop/optimizes_into_variable/64bit/rustc.main.SimplifyLocals.after.mir60
-rw-r--r--src/test/mir-opt/simplify-locals-fixedpoint.rs15
-rw-r--r--src/test/mir-opt/simplify-locals-fixedpoint/rustc.foo.SimplifyLocals.diff102
7 files changed, 319 insertions, 154 deletions
diff --git a/src/librustc_mir/transform/simplify.rs b/src/librustc_mir/transform/simplify.rs
index c0da2c446d6..c4971b25655 100644
--- a/src/librustc_mir/transform/simplify.rs
+++ b/src/librustc_mir/transform/simplify.rs
@@ -306,49 +306,82 @@ pub struct SimplifyLocals;
 impl<'tcx> MirPass<'tcx> for SimplifyLocals {
     fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut BodyAndCache<'tcx>) {
         trace!("running SimplifyLocals on {:?}", source);
-        let locals = {
+
+        // First, we're going to get a count of *actual* uses for every `Local`.
+        // Take a look at `DeclMarker::visit_local()` to see exactly what is ignored.
+        let mut used_locals = {
             let read_only_cache = read_only!(body);
-            let mut marker = DeclMarker { locals: BitSet::new_empty(body.local_decls.len()), body };
+            let mut marker = DeclMarker::new(body);
             marker.visit_body(&read_only_cache);
-            // Return pointer and arguments are always live
-            marker.locals.insert(RETURN_PLACE);
-            for arg in body.args_iter() {
-                marker.locals.insert(arg);
-            }
 
-            marker.locals
+            marker.local_counts
         };
 
-        let map = make_local_map(&mut body.local_decls, locals);
-        // Update references to all vars and tmps now
-        LocalUpdater { map, tcx }.visit_body(body);
-        body.local_decls.shrink_to_fit();
+        let arg_count = body.arg_count;
+
+        // Next, we're going to remove any `Local` with zero actual uses. When we remove those
+        // `Locals`, we're also going to subtract any uses of other `Locals` from the `used_locals`
+        // count. For example, if we removed `_2 = discriminant(_1)`, then we'll subtract one from
+        // `use_counts[_1]`. That in turn might make `_1` unused, so we loop until we hit a
+        // fixedpoint where there are no more unused locals.
+        loop {
+            let mut remove_statements = RemoveStatements::new(&mut used_locals, arg_count, tcx);
+            remove_statements.visit_body(body);
+
+            if !remove_statements.modified {
+                break;
+            }
+        }
+
+        // Finally, we'll actually do the work of shrinking `body.local_decls` and remapping the `Local`s.
+        let map = make_local_map(&mut body.local_decls, used_locals, arg_count);
+
+        // Only bother running the `LocalUpdater` if we actually found locals to remove.
+        if map.iter().any(Option::is_none) {
+            // Update references to all vars and tmps now
+            let mut updater = LocalUpdater { map, tcx };
+            updater.visit_body(body);
+
+            body.local_decls.shrink_to_fit();
+        }
     }
 }
 
 /// Construct the mapping while swapping out unused stuff out from the `vec`.
 fn make_local_map<V>(
-    vec: &mut IndexVec<Local, V>,
-    mask: BitSet<Local>,
+    local_decls: &mut IndexVec<Local, V>,
+    used_locals: IndexVec<Local, usize>,
+    arg_count: usize,
 ) -> IndexVec<Local, Option<Local>> {
-    let mut map: IndexVec<Local, Option<Local>> = IndexVec::from_elem(None, &*vec);
+    let mut map: IndexVec<Local, Option<Local>> = IndexVec::from_elem(None, &*local_decls);
     let mut used = Local::new(0);
-    for alive_index in mask.iter() {
+    for (alive_index, count) in used_locals.iter_enumerated() {
+        // The `RETURN_PLACE` and arguments are always live.
+        if alive_index.as_usize() > arg_count && *count == 0 {
+            continue;
+        }
+
         map[alive_index] = Some(used);
         if alive_index != used {
-            vec.swap(alive_index, used);
+            local_decls.swap(alive_index, used);
         }
         used.increment_by(1);
     }
-    vec.truncate(used.index());
+    local_decls.truncate(used.index());
     map
 }
 
 struct DeclMarker<'a, 'tcx> {
-    pub locals: BitSet<Local>,
+    pub local_counts: IndexVec<Local, usize>,
     pub body: &'a Body<'tcx>,
 }
 
+impl<'a, 'tcx> DeclMarker<'a, 'tcx> {
+    pub fn new(body: &'a Body<'tcx>) -> Self {
+        Self { local_counts: IndexVec::from_elem(0, &body.local_decls), body }
+    }
+}
+
 impl<'a, 'tcx> Visitor<'tcx> for DeclMarker<'a, 'tcx> {
     fn visit_local(&mut self, local: &Local, ctx: PlaceContext, location: Location) {
         // Ignore storage markers altogether, they get removed along with their otherwise unused
@@ -368,21 +401,39 @@ impl<'a, 'tcx> Visitor<'tcx> for DeclMarker<'a, 'tcx> {
             if location.statement_index != block.statements.len() {
                 let stmt = &block.statements[location.statement_index];
 
+                fn can_skip_constant(c: &ty::Const<'tcx>) -> bool {
+                    // Keep assignments from unevaluated constants around, since the
+                    // evaluation may report errors, even if the use of the constant
+                    // is dead code.
+                    !matches!(c.val, ty::ConstKind::Unevaluated(..))
+                }
+
+                fn can_skip_operand(o: &Operand<'_>) -> bool {
+                    match o {
+                        Operand::Copy(_) | Operand::Move(_) => true,
+                        Operand::Constant(c) => can_skip_constant(c.literal),
+                    }
+                }
+
                 if let StatementKind::Assign(box (dest, rvalue)) = &stmt.kind {
                     if !dest.is_indirect() && dest.local == *local {
-                        if let Rvalue::Use(Operand::Constant(c)) = rvalue {
-                            match c.literal.val {
-                                // Keep assignments from unevaluated constants around, since the
-                                // evaluation may report errors, even if the use of the constant
-                                // is dead code.
-                                ty::ConstKind::Unevaluated(..) => {}
-                                _ => {
-                                    trace!("skipping store of const value {:?} to {:?}", c, dest);
-                                    return;
-                                }
+                        let can_skip = match rvalue {
+                            Rvalue::Use(op) => can_skip_operand(op),
+                            Rvalue::Discriminant(_) => true,
+                            Rvalue::BinaryOp(_, l, r) | Rvalue::CheckedBinaryOp(_, l, r) => {
+                                can_skip_operand(l) && can_skip_operand(r)
                             }
-                        } else if let Rvalue::Discriminant(d) = rvalue {
-                            trace!("skipping store of discriminant value {:?} to {:?}", d, dest);
+                            Rvalue::Repeat(op, c) => can_skip_operand(op) && can_skip_constant(c),
+                            Rvalue::AddressOf(_, _) => true,
+                            Rvalue::Len(_) => true,
+                            Rvalue::UnaryOp(_, op) => can_skip_operand(op),
+                            Rvalue::Aggregate(_, operands) => operands.iter().all(can_skip_operand),
+
+                            _ => false,
+                        };
+
+                        if can_skip {
+                            trace!("skipping store of {:?} to {:?}", rvalue, dest);
                             return;
                         }
                     }
@@ -390,29 +441,106 @@ impl<'a, 'tcx> Visitor<'tcx> for DeclMarker<'a, 'tcx> {
             }
         }
 
-        self.locals.insert(*local);
+        self.local_counts[*local] += 1;
     }
 }
 
-struct LocalUpdater<'tcx> {
-    map: IndexVec<Local, Option<Local>>,
+struct StatementDeclMarker<'a, 'tcx> {
+    used_locals: &'a mut IndexVec<Local, usize>,
+    statement: &'a Statement<'tcx>,
+}
+
+impl<'a, 'tcx> StatementDeclMarker<'a, 'tcx> {
+    pub fn new(
+        used_locals: &'a mut IndexVec<Local, usize>,
+        statement: &'a Statement<'tcx>,
+    ) -> Self {
+        Self { used_locals, statement }
+    }
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for StatementDeclMarker<'a, 'tcx> {
+    fn visit_local(&mut self, local: &Local, context: PlaceContext, _location: Location) {
+        // Skip the lvalue for assignments
+        if let StatementKind::Assign(box (p, _)) = self.statement.kind {
+            if p.local == *local && context.is_place_assignment() {
+                return;
+            }
+        }
+
+        let use_count = &mut self.used_locals[*local];
+        // If this is the local we're removing...
+        if *use_count != 0 {
+            *use_count -= 1;
+        }
+    }
+}
+
+struct RemoveStatements<'a, 'tcx> {
+    used_locals: &'a mut IndexVec<Local, usize>,
+    arg_count: usize,
     tcx: TyCtxt<'tcx>,
+    modified: bool,
 }
 
-impl<'tcx> MutVisitor<'tcx> for LocalUpdater<'tcx> {
+impl<'a, 'tcx> RemoveStatements<'a, 'tcx> {
+    fn new(
+        used_locals: &'a mut IndexVec<Local, usize>,
+        arg_count: usize,
+        tcx: TyCtxt<'tcx>,
+    ) -> Self {
+        Self { used_locals, arg_count, tcx, modified: false }
+    }
+
+    fn keep_local(&self, l: Local) -> bool {
+        trace!("keep_local({:?}): count: {:?}", l, self.used_locals[l]);
+        l.as_usize() <= self.arg_count || self.used_locals[l] != 0
+    }
+}
+
+impl<'a, 'tcx> MutVisitor<'tcx> for RemoveStatements<'a, 'tcx> {
     fn tcx(&self) -> TyCtxt<'tcx> {
         self.tcx
     }
 
     fn visit_basic_block_data(&mut self, block: BasicBlock, data: &mut BasicBlockData<'tcx>) {
         // Remove unnecessary StorageLive and StorageDead annotations.
-        data.statements.retain(|stmt| match &stmt.kind {
-            StatementKind::StorageLive(l) | StatementKind::StorageDead(l) => self.map[*l].is_some(),
-            StatementKind::Assign(box (place, _)) => self.map[place.local].is_some(),
-            _ => true,
+        let mut i = 0usize;
+        data.statements.retain(|stmt| {
+            let keep = match &stmt.kind {
+                StatementKind::StorageLive(l) | StatementKind::StorageDead(l) => {
+                    self.keep_local(*l)
+                }
+                StatementKind::Assign(box (place, _)) => self.keep_local(place.local),
+                _ => true,
+            };
+
+            if !keep {
+                trace!("removing statement {:?}", stmt);
+                self.modified = true;
+
+                let mut visitor = StatementDeclMarker::new(self.used_locals, stmt);
+                visitor.visit_statement(stmt, Location { block, statement_index: i });
+            }
+
+            i += 1;
+
+            keep
         });
+
         self.super_basic_block_data(block, data);
     }
+}
+
+struct LocalUpdater<'tcx> {
+    map: IndexVec<Local, Option<Local>>,
+    tcx: TyCtxt<'tcx>,
+}
+
+impl<'tcx> MutVisitor<'tcx> for LocalUpdater<'tcx> {
+    fn tcx(&self) -> TyCtxt<'tcx> {
+        self.tcx
+    }
 
     fn visit_local(&mut self, l: &mut Local, _: PlaceContext, _: Location) {
         *l = self.map[*l].unwrap();
diff --git a/src/test/mir-opt/const_allocation3/32bit/rustc.main.ConstProp.after.mir b/src/test/mir-opt/const_allocation3/32bit/rustc.main.ConstProp.after.mir
index 323134553c1..63e6b8358a5 100644
--- a/src/test/mir-opt/const_allocation3/32bit/rustc.main.ConstProp.after.mir
+++ b/src/test/mir-opt/const_allocation3/32bit/rustc.main.ConstProp.after.mir
@@ -30,30 +30,30 @@ fn main() -> () {
 }
 
 alloc0 (static: FOO, size: 4, align: 4) {
-    ╾alloc10+0╼                                     │ ╾──╼
+    ╾alloc9+0─╼                                     │ ╾──╼
 }
 
-alloc10 (size: 168, align: 1) {
+alloc9 (size: 168, align: 1) {
     0x00 │ ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab │ ................
-    0x10 │ ab ab ab ab ab ab ab ab ab ab ab ab ╾alloc5+0─╼ │ ............╾──╼
+    0x10 │ ab ab ab ab ab ab ab ab ab ab ab ab ╾alloc4+0─╼ │ ............╾──╼
     0x20 │ 01 ef cd ab 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
     0x30 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
     0x40 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
     0x50 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
     0x60 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
     0x70 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
-    0x80 │ 00 00 00 00 00 00 00 00 00 00 ╾alloc7+0─╼ 00 00 │ ..........╾──╼..
-    0x90 │ ╾alloc8+99╼ 00 00 00 00 00 00 00 00 00 00 00 00 │ ╾──╼............
+    0x80 │ 00 00 00 00 00 00 00 00 00 00 ╾alloc6+0─╼ 00 00 │ ..........╾──╼..
+    0x90 │ ╾alloc7+99╼ 00 00 00 00 00 00 00 00 00 00 00 00 │ ╾──╼............
     0xa0 │ 00 00 00 00 00 00 00 00                         │ ........
 }
 
-alloc5 (size: 4, align: 4) {
+alloc4 (size: 4, align: 4) {
     2a 00 00 00                                     │ *...
 }
 
-alloc7 (fn: main)
+alloc6 (fn: main)
 
-alloc8 (size: 100, align: 1) {
+alloc7 (size: 100, align: 1) {
     0x00 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
     0x10 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
     0x20 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
diff --git a/src/test/mir-opt/const_allocation3/64bit/rustc.main.ConstProp.after.mir b/src/test/mir-opt/const_allocation3/64bit/rustc.main.ConstProp.after.mir
index 952fe8336cd..7dea5c664d8 100644
--- a/src/test/mir-opt/const_allocation3/64bit/rustc.main.ConstProp.after.mir
+++ b/src/test/mir-opt/const_allocation3/64bit/rustc.main.ConstProp.after.mir
@@ -30,12 +30,12 @@ fn main() -> () {
 }
 
 alloc0 (static: FOO, size: 8, align: 8) {
-    ╾──────alloc10+0──────╼                         │ ╾──────╼
+    ╾──────alloc9+0───────╼                         │ ╾──────╼
 }
 
-alloc10 (size: 180, align: 1) {
+alloc9 (size: 180, align: 1) {
     0x00 │ ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab │ ................
-    0x10 │ ab ab ab ab ab ab ab ab ab ab ab ab ╾─alloc5+0─ │ ............╾───
+    0x10 │ ab ab ab ab ab ab ab ab ab ab ab ab ╾─alloc4+0─ │ ............╾───
     0x20 │ ──────────╼ 01 ef cd ab 00 00 00 00 00 00 00 00 │ ───╼............
     0x30 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
     0x40 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
@@ -43,18 +43,18 @@ alloc10 (size: 180, align: 1) {
     0x60 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
     0x70 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
     0x80 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ╾──── │ ..............╾─
-    0x90 │ ────alloc7+0────╼ 00 00 ╾──────alloc8+99──────╼ │ ─────╼..╾──────╼
+    0x90 │ ────alloc6+0────╼ 00 00 ╾──────alloc7+99──────╼ │ ─────╼..╾──────╼
     0xa0 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
     0xb0 │ 00 00 00 00                                     │ ....
 }
 
-alloc5 (size: 4, align: 4) {
+alloc4 (size: 4, align: 4) {
     2a 00 00 00                                     │ *...
 }
 
-alloc7 (fn: main)
+alloc6 (fn: main)
 
-alloc8 (size: 100, align: 1) {
+alloc7 (size: 100, align: 1) {
     0x00 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
     0x10 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
     0x20 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
diff --git a/src/test/mir-opt/const_prop/optimizes_into_variable/32bit/rustc.main.SimplifyLocals.after.mir b/src/test/mir-opt/const_prop/optimizes_into_variable/32bit/rustc.main.SimplifyLocals.after.mir
index 5e2a8af060b..f8aea27df66 100644
--- a/src/test/mir-opt/const_prop/optimizes_into_variable/32bit/rustc.main.SimplifyLocals.after.mir
+++ b/src/test/mir-opt/const_prop/optimizes_into_variable/32bit/rustc.main.SimplifyLocals.after.mir
@@ -3,15 +3,14 @@
 fn main() -> () {
     let mut _0: ();                      // return place in scope 0 at $DIR/optimizes_into_variable.rs:11:11: 11:11
     let _1: i32;                         // in scope 0 at $DIR/optimizes_into_variable.rs:12:9: 12:10
-    let mut _3: [i32; 6];                // in scope 0 at $DIR/optimizes_into_variable.rs:13:13: 13:31
     scope 1 {
         debug x => _1;                   // in scope 1 at $DIR/optimizes_into_variable.rs:12:9: 12:10
         let _2: i32;                     // in scope 1 at $DIR/optimizes_into_variable.rs:13:9: 13:10
         scope 2 {
             debug y => _2;               // in scope 2 at $DIR/optimizes_into_variable.rs:13:9: 13:10
-            let _4: u32;                 // in scope 2 at $DIR/optimizes_into_variable.rs:14:9: 14:10
+            let _3: u32;                 // in scope 2 at $DIR/optimizes_into_variable.rs:14:9: 14:10
             scope 3 {
-                debug z => _4;           // in scope 3 at $DIR/optimizes_into_variable.rs:14:9: 14:10
+                debug z => _3;           // in scope 3 at $DIR/optimizes_into_variable.rs:14:9: 14:10
             }
         }
     }
@@ -26,70 +25,31 @@ fn main() -> () {
                                          // + span: $DIR/optimizes_into_variable.rs:12:13: 12:18
                                          // + literal: Const { ty: i32, val: Value(Scalar(0x00000004)) }
         StorageLive(_2);                 // bb0[2]: scope 1 at $DIR/optimizes_into_variable.rs:13:9: 13:10
-        StorageLive(_3);                 // bb0[3]: scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:31
-        _3 = [const 0i32, const 1i32, const 2i32, const 3i32, const 4i32, const 5i32]; // bb0[4]: scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:31
-                                         // ty::Const
-                                         // + ty: i32
-                                         // + val: Value(Scalar(0x00000000))
-                                         // mir::Constant
-                                         // + span: $DIR/optimizes_into_variable.rs:13:14: 13:15
-                                         // + literal: Const { ty: i32, val: Value(Scalar(0x00000000)) }
-                                         // ty::Const
-                                         // + ty: i32
-                                         // + val: Value(Scalar(0x00000001))
-                                         // mir::Constant
-                                         // + span: $DIR/optimizes_into_variable.rs:13:17: 13:18
-                                         // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) }
-                                         // ty::Const
-                                         // + ty: i32
-                                         // + val: Value(Scalar(0x00000002))
-                                         // mir::Constant
-                                         // + span: $DIR/optimizes_into_variable.rs:13:20: 13:21
-                                         // + literal: Const { ty: i32, val: Value(Scalar(0x00000002)) }
-                                         // ty::Const
-                                         // + ty: i32
-                                         // + val: Value(Scalar(0x00000003))
-                                         // mir::Constant
-                                         // + span: $DIR/optimizes_into_variable.rs:13:23: 13:24
-                                         // + literal: Const { ty: i32, val: Value(Scalar(0x00000003)) }
-                                         // ty::Const
-                                         // + ty: i32
-                                         // + val: Value(Scalar(0x00000004))
-                                         // mir::Constant
-                                         // + span: $DIR/optimizes_into_variable.rs:13:26: 13:27
-                                         // + literal: Const { ty: i32, val: Value(Scalar(0x00000004)) }
-                                         // ty::Const
-                                         // + ty: i32
-                                         // + val: Value(Scalar(0x00000005))
-                                         // mir::Constant
-                                         // + span: $DIR/optimizes_into_variable.rs:13:29: 13:30
-                                         // + literal: Const { ty: i32, val: Value(Scalar(0x00000005)) }
-        _2 = const 3i32;                 // bb0[5]: scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34
+        _2 = const 3i32;                 // bb0[3]: scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34
                                          // ty::Const
                                          // + ty: i32
                                          // + val: Value(Scalar(0x00000003))
                                          // mir::Constant
                                          // + span: $DIR/optimizes_into_variable.rs:13:13: 13:34
                                          // + literal: Const { ty: i32, val: Value(Scalar(0x00000003)) }
-        StorageDead(_3);                 // bb0[6]: scope 1 at $DIR/optimizes_into_variable.rs:13:34: 13:35
-        StorageLive(_4);                 // bb0[7]: scope 2 at $DIR/optimizes_into_variable.rs:14:9: 14:10
-        _4 = const 42u32;                // bb0[8]: scope 2 at $DIR/optimizes_into_variable.rs:14:13: 14:38
+        StorageLive(_3);                 // bb0[4]: scope 2 at $DIR/optimizes_into_variable.rs:14:9: 14:10
+        _3 = const 42u32;                // bb0[5]: scope 2 at $DIR/optimizes_into_variable.rs:14:13: 14:38
                                          // ty::Const
                                          // + ty: u32
                                          // + val: Value(Scalar(0x0000002a))
                                          // mir::Constant
                                          // + span: $DIR/optimizes_into_variable.rs:14:13: 14:38
                                          // + literal: Const { ty: u32, val: Value(Scalar(0x0000002a)) }
-        _0 = const ();                   // bb0[9]: scope 0 at $DIR/optimizes_into_variable.rs:11:11: 15:2
+        _0 = const ();                   // bb0[6]: scope 0 at $DIR/optimizes_into_variable.rs:11:11: 15:2
                                          // ty::Const
                                          // + ty: ()
                                          // + val: Value(Scalar(<ZST>))
                                          // mir::Constant
                                          // + span: $DIR/optimizes_into_variable.rs:11:11: 15:2
                                          // + literal: Const { ty: (), val: Value(Scalar(<ZST>)) }
-        StorageDead(_4);                 // bb0[10]: scope 2 at $DIR/optimizes_into_variable.rs:15:1: 15:2
-        StorageDead(_2);                 // bb0[11]: scope 1 at $DIR/optimizes_into_variable.rs:15:1: 15:2
-        StorageDead(_1);                 // bb0[12]: scope 0 at $DIR/optimizes_into_variable.rs:15:1: 15:2
-        return;                          // bb0[13]: scope 0 at $DIR/optimizes_into_variable.rs:15:2: 15:2
+        StorageDead(_3);                 // bb0[7]: scope 2 at $DIR/optimizes_into_variable.rs:15:1: 15:2
+        StorageDead(_2);                 // bb0[8]: scope 1 at $DIR/optimizes_into_variable.rs:15:1: 15:2
+        StorageDead(_1);                 // bb0[9]: scope 0 at $DIR/optimizes_into_variable.rs:15:1: 15:2
+        return;                          // bb0[10]: scope 0 at $DIR/optimizes_into_variable.rs:15:2: 15:2
     }
 }
diff --git a/src/test/mir-opt/const_prop/optimizes_into_variable/64bit/rustc.main.SimplifyLocals.after.mir b/src/test/mir-opt/const_prop/optimizes_into_variable/64bit/rustc.main.SimplifyLocals.after.mir
index 5e2a8af060b..f8aea27df66 100644
--- a/src/test/mir-opt/const_prop/optimizes_into_variable/64bit/rustc.main.SimplifyLocals.after.mir
+++ b/src/test/mir-opt/const_prop/optimizes_into_variable/64bit/rustc.main.SimplifyLocals.after.mir
@@ -3,15 +3,14 @@
 fn main() -> () {
     let mut _0: ();                      // return place in scope 0 at $DIR/optimizes_into_variable.rs:11:11: 11:11
     let _1: i32;                         // in scope 0 at $DIR/optimizes_into_variable.rs:12:9: 12:10
-    let mut _3: [i32; 6];                // in scope 0 at $DIR/optimizes_into_variable.rs:13:13: 13:31
     scope 1 {
         debug x => _1;                   // in scope 1 at $DIR/optimizes_into_variable.rs:12:9: 12:10
         let _2: i32;                     // in scope 1 at $DIR/optimizes_into_variable.rs:13:9: 13:10
         scope 2 {
             debug y => _2;               // in scope 2 at $DIR/optimizes_into_variable.rs:13:9: 13:10
-            let _4: u32;                 // in scope 2 at $DIR/optimizes_into_variable.rs:14:9: 14:10
+            let _3: u32;                 // in scope 2 at $DIR/optimizes_into_variable.rs:14:9: 14:10
             scope 3 {
-                debug z => _4;           // in scope 3 at $DIR/optimizes_into_variable.rs:14:9: 14:10
+                debug z => _3;           // in scope 3 at $DIR/optimizes_into_variable.rs:14:9: 14:10
             }
         }
     }
@@ -26,70 +25,31 @@ fn main() -> () {
                                          // + span: $DIR/optimizes_into_variable.rs:12:13: 12:18
                                          // + literal: Const { ty: i32, val: Value(Scalar(0x00000004)) }
         StorageLive(_2);                 // bb0[2]: scope 1 at $DIR/optimizes_into_variable.rs:13:9: 13:10
-        StorageLive(_3);                 // bb0[3]: scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:31
-        _3 = [const 0i32, const 1i32, const 2i32, const 3i32, const 4i32, const 5i32]; // bb0[4]: scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:31
-                                         // ty::Const
-                                         // + ty: i32
-                                         // + val: Value(Scalar(0x00000000))
-                                         // mir::Constant
-                                         // + span: $DIR/optimizes_into_variable.rs:13:14: 13:15
-                                         // + literal: Const { ty: i32, val: Value(Scalar(0x00000000)) }
-                                         // ty::Const
-                                         // + ty: i32
-                                         // + val: Value(Scalar(0x00000001))
-                                         // mir::Constant
-                                         // + span: $DIR/optimizes_into_variable.rs:13:17: 13:18
-                                         // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) }
-                                         // ty::Const
-                                         // + ty: i32
-                                         // + val: Value(Scalar(0x00000002))
-                                         // mir::Constant
-                                         // + span: $DIR/optimizes_into_variable.rs:13:20: 13:21
-                                         // + literal: Const { ty: i32, val: Value(Scalar(0x00000002)) }
-                                         // ty::Const
-                                         // + ty: i32
-                                         // + val: Value(Scalar(0x00000003))
-                                         // mir::Constant
-                                         // + span: $DIR/optimizes_into_variable.rs:13:23: 13:24
-                                         // + literal: Const { ty: i32, val: Value(Scalar(0x00000003)) }
-                                         // ty::Const
-                                         // + ty: i32
-                                         // + val: Value(Scalar(0x00000004))
-                                         // mir::Constant
-                                         // + span: $DIR/optimizes_into_variable.rs:13:26: 13:27
-                                         // + literal: Const { ty: i32, val: Value(Scalar(0x00000004)) }
-                                         // ty::Const
-                                         // + ty: i32
-                                         // + val: Value(Scalar(0x00000005))
-                                         // mir::Constant
-                                         // + span: $DIR/optimizes_into_variable.rs:13:29: 13:30
-                                         // + literal: Const { ty: i32, val: Value(Scalar(0x00000005)) }
-        _2 = const 3i32;                 // bb0[5]: scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34
+        _2 = const 3i32;                 // bb0[3]: scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34
                                          // ty::Const
                                          // + ty: i32
                                          // + val: Value(Scalar(0x00000003))
                                          // mir::Constant
                                          // + span: $DIR/optimizes_into_variable.rs:13:13: 13:34
                                          // + literal: Const { ty: i32, val: Value(Scalar(0x00000003)) }
-        StorageDead(_3);                 // bb0[6]: scope 1 at $DIR/optimizes_into_variable.rs:13:34: 13:35
-        StorageLive(_4);                 // bb0[7]: scope 2 at $DIR/optimizes_into_variable.rs:14:9: 14:10
-        _4 = const 42u32;                // bb0[8]: scope 2 at $DIR/optimizes_into_variable.rs:14:13: 14:38
+        StorageLive(_3);                 // bb0[4]: scope 2 at $DIR/optimizes_into_variable.rs:14:9: 14:10
+        _3 = const 42u32;                // bb0[5]: scope 2 at $DIR/optimizes_into_variable.rs:14:13: 14:38
                                          // ty::Const
                                          // + ty: u32
                                          // + val: Value(Scalar(0x0000002a))
                                          // mir::Constant
                                          // + span: $DIR/optimizes_into_variable.rs:14:13: 14:38
                                          // + literal: Const { ty: u32, val: Value(Scalar(0x0000002a)) }
-        _0 = const ();                   // bb0[9]: scope 0 at $DIR/optimizes_into_variable.rs:11:11: 15:2
+        _0 = const ();                   // bb0[6]: scope 0 at $DIR/optimizes_into_variable.rs:11:11: 15:2
                                          // ty::Const
                                          // + ty: ()
                                          // + val: Value(Scalar(<ZST>))
                                          // mir::Constant
                                          // + span: $DIR/optimizes_into_variable.rs:11:11: 15:2
                                          // + literal: Const { ty: (), val: Value(Scalar(<ZST>)) }
-        StorageDead(_4);                 // bb0[10]: scope 2 at $DIR/optimizes_into_variable.rs:15:1: 15:2
-        StorageDead(_2);                 // bb0[11]: scope 1 at $DIR/optimizes_into_variable.rs:15:1: 15:2
-        StorageDead(_1);                 // bb0[12]: scope 0 at $DIR/optimizes_into_variable.rs:15:1: 15:2
-        return;                          // bb0[13]: scope 0 at $DIR/optimizes_into_variable.rs:15:2: 15:2
+        StorageDead(_3);                 // bb0[7]: scope 2 at $DIR/optimizes_into_variable.rs:15:1: 15:2
+        StorageDead(_2);                 // bb0[8]: scope 1 at $DIR/optimizes_into_variable.rs:15:1: 15:2
+        StorageDead(_1);                 // bb0[9]: scope 0 at $DIR/optimizes_into_variable.rs:15:1: 15:2
+        return;                          // bb0[10]: scope 0 at $DIR/optimizes_into_variable.rs:15:2: 15:2
     }
 }
diff --git a/src/test/mir-opt/simplify-locals-fixedpoint.rs b/src/test/mir-opt/simplify-locals-fixedpoint.rs
new file mode 100644
index 00000000000..aa5bc345359
--- /dev/null
+++ b/src/test/mir-opt/simplify-locals-fixedpoint.rs
@@ -0,0 +1,15 @@
+// compile-flags: -Zmir-opt-level=1
+
+fn foo<T>() {
+    if let (Some(a), None) = (Option::<u8>::None, Option::<T>::None) {
+        if a > 42u8 {
+
+        }
+    }
+}
+
+fn main() {
+    foo::<()>();
+}
+
+// EMIT_MIR rustc.foo.SimplifyLocals.diff
diff --git a/src/test/mir-opt/simplify-locals-fixedpoint/rustc.foo.SimplifyLocals.diff b/src/test/mir-opt/simplify-locals-fixedpoint/rustc.foo.SimplifyLocals.diff
new file mode 100644
index 00000000000..bc59be48eee
--- /dev/null
+++ b/src/test/mir-opt/simplify-locals-fixedpoint/rustc.foo.SimplifyLocals.diff
@@ -0,0 +1,102 @@
+- // MIR for `foo` before SimplifyLocals
++ // MIR for `foo` after SimplifyLocals
+  
+  fn foo() -> () {
+      let mut _0: ();                      // return place in scope 0 at $DIR/simplify-locals-fixedpoint.rs:3:13: 3:13
+      let mut _1: (std::option::Option<u8>, std::option::Option<T>); // in scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:30: 4:69
+      let mut _2: std::option::Option<u8>; // in scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:31: 4:49
+      let mut _3: std::option::Option<T>;  // in scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:51: 4:68
+      let mut _4: isize;                   // in scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:22: 4:26
+      let mut _5: isize;                   // in scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:13: 4:20
+      let _6: u8;                          // in scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:18: 4:19
+      let mut _7: bool;                    // in scope 0 at $DIR/simplify-locals-fixedpoint.rs:5:12: 5:20
+      let mut _8: u8;                      // in scope 0 at $DIR/simplify-locals-fixedpoint.rs:5:12: 5:13
+      scope 1 {
+          debug a => _6;                   // in scope 1 at $DIR/simplify-locals-fixedpoint.rs:4:18: 4:19
+      }
+  
+      bb0: {
+          StorageLive(_1);                 // bb0[0]: scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:30: 4:69
+          StorageLive(_2);                 // bb0[1]: scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:31: 4:49
+          discriminant(_2) = 0;            // bb0[2]: scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:31: 4:49
+          StorageLive(_3);                 // bb0[3]: scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:51: 4:68
+          discriminant(_3) = 0;            // bb0[4]: scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:51: 4:68
+          (_1.0: std::option::Option<u8>) = move _2; // bb0[5]: scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:30: 4:69
+          (_1.1: std::option::Option<T>) = move _3; // bb0[6]: scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:30: 4:69
+          StorageDead(_3);                 // bb0[7]: scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:68: 4:69
+          StorageDead(_2);                 // bb0[8]: scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:68: 4:69
+          _5 = discriminant((_1.0: std::option::Option<u8>)); // bb0[9]: scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:13: 4:20
+          switchInt(move _5) -> [1isize: bb2, otherwise: bb1]; // bb0[10]: scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:13: 4:20
+      }
+  
+      bb1: {
+          _0 = const ();                   // bb1[0]: scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:5: 8:6
+                                           // ty::Const
+                                           // + ty: ()
+                                           // + val: Value(Scalar(<ZST>))
+                                           // mir::Constant
+                                           // + span: $DIR/simplify-locals-fixedpoint.rs:4:5: 8:6
+                                           // + literal: Const { ty: (), val: Value(Scalar(<ZST>)) }
+          goto -> bb7;                     // bb1[1]: scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:5: 8:6
+      }
+  
+      bb2: {
+          _4 = discriminant((_1.1: std::option::Option<T>)); // bb2[0]: scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:22: 4:26
+          switchInt(move _4) -> [0isize: bb3, otherwise: bb1]; // bb2[1]: scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:22: 4:26
+      }
+  
+      bb3: {
+          StorageLive(_6);                 // bb3[0]: scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:18: 4:19
+          _6 = (((_1.0: std::option::Option<u8>) as Some).0: u8); // bb3[1]: scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:18: 4:19
+          StorageLive(_7);                 // bb3[2]: scope 1 at $DIR/simplify-locals-fixedpoint.rs:5:12: 5:20
+          StorageLive(_8);                 // bb3[3]: scope 1 at $DIR/simplify-locals-fixedpoint.rs:5:12: 5:13
+          _8 = _6;                         // bb3[4]: scope 1 at $DIR/simplify-locals-fixedpoint.rs:5:12: 5:13
+          _7 = Gt(move _8, const 42u8);    // bb3[5]: scope 1 at $DIR/simplify-locals-fixedpoint.rs:5:12: 5:20
+                                           // ty::Const
+                                           // + ty: u8
+                                           // + val: Value(Scalar(0x2a))
+                                           // mir::Constant
+                                           // + span: $DIR/simplify-locals-fixedpoint.rs:5:16: 5:20
+                                           // + literal: Const { ty: u8, val: Value(Scalar(0x2a)) }
+          StorageDead(_8);                 // bb3[6]: scope 1 at $DIR/simplify-locals-fixedpoint.rs:5:19: 5:20
+          switchInt(_7) -> [false: bb4, otherwise: bb5]; // bb3[7]: scope 1 at $DIR/simplify-locals-fixedpoint.rs:5:9: 7:10
+      }
+  
+      bb4: {
+          _0 = const ();                   // bb4[0]: scope 1 at $DIR/simplify-locals-fixedpoint.rs:5:9: 7:10
+                                           // ty::Const
+                                           // + ty: ()
+                                           // + val: Value(Scalar(<ZST>))
+                                           // mir::Constant
+                                           // + span: $DIR/simplify-locals-fixedpoint.rs:5:9: 7:10
+                                           // + literal: Const { ty: (), val: Value(Scalar(<ZST>)) }
+          goto -> bb6;                     // bb4[1]: scope 1 at $DIR/simplify-locals-fixedpoint.rs:5:9: 7:10
+      }
+  
+      bb5: {
+          _0 = const ();                   // bb5[0]: scope 1 at $DIR/simplify-locals-fixedpoint.rs:5:21: 7:10
+                                           // ty::Const
+                                           // + ty: ()
+                                           // + val: Value(Scalar(<ZST>))
+                                           // mir::Constant
+                                           // + span: $DIR/simplify-locals-fixedpoint.rs:5:21: 7:10
+                                           // + literal: Const { ty: (), val: Value(Scalar(<ZST>)) }
+          goto -> bb6;                     // bb5[1]: scope 1 at $DIR/simplify-locals-fixedpoint.rs:5:9: 7:10
+      }
+  
+      bb6: {
+          StorageDead(_7);                 // bb6[0]: scope 1 at $DIR/simplify-locals-fixedpoint.rs:8:5: 8:6
+          StorageDead(_6);                 // bb6[1]: scope 0 at $DIR/simplify-locals-fixedpoint.rs:8:5: 8:6
+          goto -> bb7;                     // bb6[2]: scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:5: 8:6
+      }
+  
+      bb7: {
+          drop(_1) -> bb8;                 // bb7[0]: scope 0 at $DIR/simplify-locals-fixedpoint.rs:9:1: 9:2
+      }
+  
+      bb8: {
+          StorageDead(_1);                 // bb8[0]: scope 0 at $DIR/simplify-locals-fixedpoint.rs:9:1: 9:2
+          return;                          // bb8[1]: scope 0 at $DIR/simplify-locals-fixedpoint.rs:9:2: 9:2
+      }
+  }
+