From c0a811a24ee926431f6e8fea6984430138a5fee0 Mon Sep 17 00:00:00 2001 From: kadmin Date: Thu, 13 Aug 2020 23:46:27 +0000 Subject: [PATCH 1/6] Add regression test for matching on u8 --- src/test/mir-opt/matches_u8.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 src/test/mir-opt/matches_u8.rs diff --git a/src/test/mir-opt/matches_u8.rs b/src/test/mir-opt/matches_u8.rs new file mode 100644 index 0000000000000..15de01fa963e0 --- /dev/null +++ b/src/test/mir-opt/matches_u8.rs @@ -0,0 +1,21 @@ +// EMIT_MIR_FOR_EACH_BIT_WIDTH +// EMIT_MIR matches_u8.exhaustive_match.MatchBranchSimplification.diff + +pub enum E { + A, + B, +} + +// This only breaks on u8's, but probably still have to test i8. +#[no_mangle] +pub fn exhaustive_match(e: E) -> u8 { + match e { + E::A => 0, + E::B => 1, + } +} + +fn main() { + assert_eq!(exhaustive_match(E::A), 0); + assert_eq!(exhaustive_match(E::B), 1); +} From fd740266de93f89195eee78a0feaa770bf90a47e Mon Sep 17 00:00:00 2001 From: kadmin Date: Fri, 14 Aug 2020 00:11:36 +0000 Subject: [PATCH 2/6] Add fix This also explicitly checks that the types are `bool`. `try_eval_bool` also appears to just succeed for `u8`, so this ensures that it actually is a bool before casting. --- src/librustc_mir/transform/match_branches.rs | 16 ++++---- ...match.MatchBranchSimplification.diff.32bit | 40 +++++++++++++++++++ ...match.MatchBranchSimplification.diff.64bit | 40 +++++++++++++++++++ ...ch_i8.MatchBranchSimplification.diff.32bit | 40 +++++++++++++++++++ ...ch_i8.MatchBranchSimplification.diff.64bit | 40 +++++++++++++++++++ src/test/mir-opt/matches_u8.rs | 13 +++++- 6 files changed, 179 insertions(+), 10 deletions(-) create mode 100644 src/test/mir-opt/matches_u8.exhaustive_match.MatchBranchSimplification.diff.32bit create mode 100644 src/test/mir-opt/matches_u8.exhaustive_match.MatchBranchSimplification.diff.64bit create mode 100644 src/test/mir-opt/matches_u8.exhaustive_match_i8.MatchBranchSimplification.diff.32bit create mode 100644 src/test/mir-opt/matches_u8.exhaustive_match_i8.MatchBranchSimplification.diff.64bit diff --git a/src/librustc_mir/transform/match_branches.rs b/src/librustc_mir/transform/match_branches.rs index 74da6d5e629b3..67021ae512b65 100644 --- a/src/librustc_mir/transform/match_branches.rs +++ b/src/librustc_mir/transform/match_branches.rs @@ -48,15 +48,13 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification { ( StatementKind::Assign(box (lhs_f, Rvalue::Use(Operand::Constant(f_c)))), StatementKind::Assign(box (lhs_s, Rvalue::Use(Operand::Constant(s_c)))), - ) if lhs_f == lhs_s => { - if let Some(f_c) = f_c.literal.try_eval_bool(tcx, param_env) { - // This should also be a bool because it's writing to the same place - let s_c = s_c.literal.try_eval_bool(tcx, param_env).unwrap(); - if f_c != s_c { - // have to check this here because f_c & s_c might have - // different spans. - continue; - } + ) if lhs_f == lhs_s && f_c.literal.ty.is_bool() && s_c.literal.ty.is_bool() => { + let f_c = f_c.literal.try_eval_bool(tcx, param_env).unwrap(); + let s_c = s_c.literal.try_eval_bool(tcx, param_env).unwrap(); + if f_c != s_c { + // have to check this here because f_c & s_c might have + // different spans. + continue; } continue 'outer; } diff --git a/src/test/mir-opt/matches_u8.exhaustive_match.MatchBranchSimplification.diff.32bit b/src/test/mir-opt/matches_u8.exhaustive_match.MatchBranchSimplification.diff.32bit new file mode 100644 index 0000000000000..c41bd999dc9fc --- /dev/null +++ b/src/test/mir-opt/matches_u8.exhaustive_match.MatchBranchSimplification.diff.32bit @@ -0,0 +1,40 @@ +- // MIR for `exhaustive_match` before MatchBranchSimplification ++ // MIR for `exhaustive_match` after MatchBranchSimplification + + fn exhaustive_match(_1: E) -> u8 { + debug e => _1; // in scope 0 at $DIR/matches_u8.rs:11:25: 11:26 + let mut _0: u8; // return place in scope 0 at $DIR/matches_u8.rs:11:34: 11:36 + let mut _2: isize; // in scope 0 at $DIR/matches_u8.rs:13:9: 13:13 + + bb0: { + _2 = discriminant(_1); // scope 0 at $DIR/matches_u8.rs:13:9: 13:13 + switchInt(move _2) -> [0_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/matches_u8.rs:13:9: 13:13 + } + + bb1: { + _0 = const 1_u8; // scope 0 at $DIR/matches_u8.rs:14:17: 14:18 + // ty::Const + // + ty: u8 + // + val: Value(Scalar(0x01)) + // mir::Constant + // + span: $DIR/matches_u8.rs:14:17: 14:18 + // + literal: Const { ty: u8, val: Value(Scalar(0x01)) } + goto -> bb3; // scope 0 at $DIR/matches_u8.rs:12:5: 15:6 + } + + bb2: { + _0 = const 0_u8; // scope 0 at $DIR/matches_u8.rs:13:17: 13:18 + // ty::Const + // + ty: u8 + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/matches_u8.rs:13:17: 13:18 + // + literal: Const { ty: u8, val: Value(Scalar(0x00)) } + goto -> bb3; // scope 0 at $DIR/matches_u8.rs:12:5: 15:6 + } + + bb3: { + return; // scope 0 at $DIR/matches_u8.rs:16:2: 16:2 + } + } + diff --git a/src/test/mir-opt/matches_u8.exhaustive_match.MatchBranchSimplification.diff.64bit b/src/test/mir-opt/matches_u8.exhaustive_match.MatchBranchSimplification.diff.64bit new file mode 100644 index 0000000000000..c41bd999dc9fc --- /dev/null +++ b/src/test/mir-opt/matches_u8.exhaustive_match.MatchBranchSimplification.diff.64bit @@ -0,0 +1,40 @@ +- // MIR for `exhaustive_match` before MatchBranchSimplification ++ // MIR for `exhaustive_match` after MatchBranchSimplification + + fn exhaustive_match(_1: E) -> u8 { + debug e => _1; // in scope 0 at $DIR/matches_u8.rs:11:25: 11:26 + let mut _0: u8; // return place in scope 0 at $DIR/matches_u8.rs:11:34: 11:36 + let mut _2: isize; // in scope 0 at $DIR/matches_u8.rs:13:9: 13:13 + + bb0: { + _2 = discriminant(_1); // scope 0 at $DIR/matches_u8.rs:13:9: 13:13 + switchInt(move _2) -> [0_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/matches_u8.rs:13:9: 13:13 + } + + bb1: { + _0 = const 1_u8; // scope 0 at $DIR/matches_u8.rs:14:17: 14:18 + // ty::Const + // + ty: u8 + // + val: Value(Scalar(0x01)) + // mir::Constant + // + span: $DIR/matches_u8.rs:14:17: 14:18 + // + literal: Const { ty: u8, val: Value(Scalar(0x01)) } + goto -> bb3; // scope 0 at $DIR/matches_u8.rs:12:5: 15:6 + } + + bb2: { + _0 = const 0_u8; // scope 0 at $DIR/matches_u8.rs:13:17: 13:18 + // ty::Const + // + ty: u8 + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/matches_u8.rs:13:17: 13:18 + // + literal: Const { ty: u8, val: Value(Scalar(0x00)) } + goto -> bb3; // scope 0 at $DIR/matches_u8.rs:12:5: 15:6 + } + + bb3: { + return; // scope 0 at $DIR/matches_u8.rs:16:2: 16:2 + } + } + diff --git a/src/test/mir-opt/matches_u8.exhaustive_match_i8.MatchBranchSimplification.diff.32bit b/src/test/mir-opt/matches_u8.exhaustive_match_i8.MatchBranchSimplification.diff.32bit new file mode 100644 index 0000000000000..2c4bbc8095e9a --- /dev/null +++ b/src/test/mir-opt/matches_u8.exhaustive_match_i8.MatchBranchSimplification.diff.32bit @@ -0,0 +1,40 @@ +- // MIR for `exhaustive_match_i8` before MatchBranchSimplification ++ // MIR for `exhaustive_match_i8` after MatchBranchSimplification + + fn exhaustive_match_i8(_1: E) -> i8 { + debug e => _1; // in scope 0 at $DIR/matches_u8.rs:19:28: 19:29 + let mut _0: i8; // return place in scope 0 at $DIR/matches_u8.rs:19:37: 19:39 + let mut _2: isize; // in scope 0 at $DIR/matches_u8.rs:21:9: 21:13 + + bb0: { + _2 = discriminant(_1); // scope 0 at $DIR/matches_u8.rs:21:9: 21:13 + switchInt(move _2) -> [0_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/matches_u8.rs:21:9: 21:13 + } + + bb1: { + _0 = const 1_i8; // scope 0 at $DIR/matches_u8.rs:22:17: 22:18 + // ty::Const + // + ty: i8 + // + val: Value(Scalar(0x01)) + // mir::Constant + // + span: $DIR/matches_u8.rs:22:17: 22:18 + // + literal: Const { ty: i8, val: Value(Scalar(0x01)) } + goto -> bb3; // scope 0 at $DIR/matches_u8.rs:20:5: 23:6 + } + + bb2: { + _0 = const 0_i8; // scope 0 at $DIR/matches_u8.rs:21:17: 21:18 + // ty::Const + // + ty: i8 + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/matches_u8.rs:21:17: 21:18 + // + literal: Const { ty: i8, val: Value(Scalar(0x00)) } + goto -> bb3; // scope 0 at $DIR/matches_u8.rs:20:5: 23:6 + } + + bb3: { + return; // scope 0 at $DIR/matches_u8.rs:24:2: 24:2 + } + } + diff --git a/src/test/mir-opt/matches_u8.exhaustive_match_i8.MatchBranchSimplification.diff.64bit b/src/test/mir-opt/matches_u8.exhaustive_match_i8.MatchBranchSimplification.diff.64bit new file mode 100644 index 0000000000000..2c4bbc8095e9a --- /dev/null +++ b/src/test/mir-opt/matches_u8.exhaustive_match_i8.MatchBranchSimplification.diff.64bit @@ -0,0 +1,40 @@ +- // MIR for `exhaustive_match_i8` before MatchBranchSimplification ++ // MIR for `exhaustive_match_i8` after MatchBranchSimplification + + fn exhaustive_match_i8(_1: E) -> i8 { + debug e => _1; // in scope 0 at $DIR/matches_u8.rs:19:28: 19:29 + let mut _0: i8; // return place in scope 0 at $DIR/matches_u8.rs:19:37: 19:39 + let mut _2: isize; // in scope 0 at $DIR/matches_u8.rs:21:9: 21:13 + + bb0: { + _2 = discriminant(_1); // scope 0 at $DIR/matches_u8.rs:21:9: 21:13 + switchInt(move _2) -> [0_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/matches_u8.rs:21:9: 21:13 + } + + bb1: { + _0 = const 1_i8; // scope 0 at $DIR/matches_u8.rs:22:17: 22:18 + // ty::Const + // + ty: i8 + // + val: Value(Scalar(0x01)) + // mir::Constant + // + span: $DIR/matches_u8.rs:22:17: 22:18 + // + literal: Const { ty: i8, val: Value(Scalar(0x01)) } + goto -> bb3; // scope 0 at $DIR/matches_u8.rs:20:5: 23:6 + } + + bb2: { + _0 = const 0_i8; // scope 0 at $DIR/matches_u8.rs:21:17: 21:18 + // ty::Const + // + ty: i8 + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/matches_u8.rs:21:17: 21:18 + // + literal: Const { ty: i8, val: Value(Scalar(0x00)) } + goto -> bb3; // scope 0 at $DIR/matches_u8.rs:20:5: 23:6 + } + + bb3: { + return; // scope 0 at $DIR/matches_u8.rs:24:2: 24:2 + } + } + diff --git a/src/test/mir-opt/matches_u8.rs b/src/test/mir-opt/matches_u8.rs index 15de01fa963e0..78373be48b685 100644 --- a/src/test/mir-opt/matches_u8.rs +++ b/src/test/mir-opt/matches_u8.rs @@ -1,12 +1,12 @@ // EMIT_MIR_FOR_EACH_BIT_WIDTH // EMIT_MIR matches_u8.exhaustive_match.MatchBranchSimplification.diff +// EMIT_MIR matches_u8.exhaustive_match_i8.MatchBranchSimplification.diff pub enum E { A, B, } -// This only breaks on u8's, but probably still have to test i8. #[no_mangle] pub fn exhaustive_match(e: E) -> u8 { match e { @@ -15,7 +15,18 @@ pub fn exhaustive_match(e: E) -> u8 { } } +#[no_mangle] +pub fn exhaustive_match_i8(e: E) -> i8 { + match e { + E::A => 0, + E::B => 1, + } +} + fn main() { assert_eq!(exhaustive_match(E::A), 0); assert_eq!(exhaustive_match(E::B), 1); + + assert_eq!(exhaustive_match_i8(E::A), 0); + assert_eq!(exhaustive_match_i8(E::B), 1); } From 60d7d28948c1a3d5125f12a2254f6edb5077c4c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Fri, 14 Aug 2020 00:00:00 +0000 Subject: [PATCH 3/6] MatchBranchSimplification: optimize when switching on copy operand --- src/librustc_mir/transform/match_branches.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_mir/transform/match_branches.rs b/src/librustc_mir/transform/match_branches.rs index 67021ae512b65..e6c5845cd481a 100644 --- a/src/librustc_mir/transform/match_branches.rs +++ b/src/librustc_mir/transform/match_branches.rs @@ -16,7 +16,7 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification { 'outer: for bb_idx in bbs.indices() { let (discr, val, switch_ty, first, second) = match bbs[bb_idx].terminator().kind { TerminatorKind::SwitchInt { - discr: Operand::Move(ref place), + discr: Operand::Copy(ref place) | Operand::Move(ref place), switch_ty, ref targets, ref values, From 4fae04968eccb8797000b5a42760005b35504136 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Fri, 14 Aug 2020 00:00:00 +0000 Subject: [PATCH 4/6] MatchBranchSimplification: copy discriminant instead of moving it It might be necessary to use its value more than once. --- src/librustc_mir/transform/match_branches.rs | 2 +- ...hes_reduce_branches.foo.MatchBranchSimplification.diff.32bit | 2 +- ...hes_reduce_branches.foo.MatchBranchSimplification.diff.64bit | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/librustc_mir/transform/match_branches.rs b/src/librustc_mir/transform/match_branches.rs index e6c5845cd481a..cdf181aab02dd 100644 --- a/src/librustc_mir/transform/match_branches.rs +++ b/src/librustc_mir/transform/match_branches.rs @@ -78,7 +78,7 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification { ); if let Some(c) = c.literal.try_eval_bool(tcx, param_env) { let op = if c { BinOp::Eq } else { BinOp::Ne }; - *rhs = Rvalue::BinaryOp(op, Operand::Move(discr), const_cmp); + *rhs = Rvalue::BinaryOp(op, Operand::Copy(discr), const_cmp); } } } diff --git a/src/test/mir-opt/matches_reduce_branches.foo.MatchBranchSimplification.diff.32bit b/src/test/mir-opt/matches_reduce_branches.foo.MatchBranchSimplification.diff.32bit index df94c897e92f8..9324762fb0803 100644 --- a/src/test/mir-opt/matches_reduce_branches.foo.MatchBranchSimplification.diff.32bit +++ b/src/test/mir-opt/matches_reduce_branches.foo.MatchBranchSimplification.diff.32bit @@ -11,7 +11,7 @@ StorageLive(_2); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL _3 = discriminant(_1); // scope 0 at $DIR/matches_reduce_branches.rs:5:22: 5:26 - switchInt(move _3) -> [0_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/matches_reduce_branches.rs:5:22: 5:26 -+ _2 = Eq(move _3, const 0_isize); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL ++ _2 = Eq(_3, const 0_isize); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + // ty::Const + // + ty: isize + // + val: Value(Scalar(0x00000000)) diff --git a/src/test/mir-opt/matches_reduce_branches.foo.MatchBranchSimplification.diff.64bit b/src/test/mir-opt/matches_reduce_branches.foo.MatchBranchSimplification.diff.64bit index 06849b4a5d983..144a71228ad69 100644 --- a/src/test/mir-opt/matches_reduce_branches.foo.MatchBranchSimplification.diff.64bit +++ b/src/test/mir-opt/matches_reduce_branches.foo.MatchBranchSimplification.diff.64bit @@ -11,7 +11,7 @@ StorageLive(_2); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL _3 = discriminant(_1); // scope 0 at $DIR/matches_reduce_branches.rs:5:22: 5:26 - switchInt(move _3) -> [0_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/matches_reduce_branches.rs:5:22: 5:26 -+ _2 = Eq(move _3, const 0_isize); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL ++ _2 = Eq(_3, const 0_isize); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + // ty::Const + // + ty: isize + // + val: Value(Scalar(0x0000000000000000)) From 1fe2e2941d93da397c8ace5f410c0ab3dc5a3049 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Sat, 15 Aug 2020 00:00:00 +0000 Subject: [PATCH 5/6] MatchBranchSimplification: fix equal const bool assignments The match branch simplification is applied when target blocks contain statements that are either equal or perform a const bool assignment with different values to the same place. Previously, when constructing new statements, only statements from a single block had been examined. This lead to a misoptimization when statements are equal because the assign the *same* const bool value to the same place. Fix the issue by examining statements from both blocks when deciding on replacement. --- src/librustc_mir/transform/match_branches.rs | 117 +++++++++---- ...s.bar.MatchBranchSimplification.diff.32bit | 156 ++++++++++++++++++ ...s.bar.MatchBranchSimplification.diff.64bit | 156 ++++++++++++++++++ ...s.foo.MatchBranchSimplification.diff.32bit | 24 +-- ...s.foo.MatchBranchSimplification.diff.64bit | 24 +-- src/test/mir-opt/matches_reduce_branches.rs | 29 ++++ 6 files changed, 447 insertions(+), 59 deletions(-) create mode 100644 src/test/mir-opt/matches_reduce_branches.bar.MatchBranchSimplification.diff.32bit create mode 100644 src/test/mir-opt/matches_reduce_branches.bar.MatchBranchSimplification.diff.64bit diff --git a/src/librustc_mir/transform/match_branches.rs b/src/librustc_mir/transform/match_branches.rs index cdf181aab02dd..b78dee753f85f 100644 --- a/src/librustc_mir/transform/match_branches.rs +++ b/src/librustc_mir/transform/match_branches.rs @@ -4,10 +4,37 @@ use rustc_middle::ty::TyCtxt; pub struct MatchBranchSimplification; -// What's the intent of this pass? -// If one block is found that switches between blocks which both go to the same place -// AND both of these blocks set a similar const in their -> -// condense into 1 block based on discriminant AND goto the destination afterwards +/// If a source block is found that switches between two blocks that are exactly +/// the same modulo const bool assignments (e.g., one assigns true another false +/// to the same place), merge a target block statements into the source block, +/// using Eq / Ne comparison with switch value where const bools value differ. +/// +/// For example: +/// +/// ```rust +/// bb0: { +/// switchInt(move _3) -> [42_isize: bb1, otherwise: bb2]; +/// } +/// +/// bb1: { +/// _2 = const true; +/// goto -> bb3; +/// } +/// +/// bb2: { +/// _2 = const false; +/// goto -> bb3; +/// } +/// ``` +/// +/// into: +/// +/// ```rust +/// bb0: { +/// _2 = Eq(move _3, const 42_isize); +/// goto -> bb3; +/// } +/// ``` impl<'tcx> MirPass<'tcx> for MatchBranchSimplification { fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) { @@ -42,48 +69,68 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification { } for (f, s) in first_stmts.iter().zip(scnd_stmts.iter()) { match (&f.kind, &s.kind) { - // If two statements are exactly the same just ignore them. - (f_s, s_s) if f_s == s_s => (), + // If two statements are exactly the same, we can optimize. + (f_s, s_s) if f_s == s_s => {} + // If two statements are const bool assignments to the same place, we can optimize. ( StatementKind::Assign(box (lhs_f, Rvalue::Use(Operand::Constant(f_c)))), StatementKind::Assign(box (lhs_s, Rvalue::Use(Operand::Constant(s_c)))), - ) if lhs_f == lhs_s && f_c.literal.ty.is_bool() && s_c.literal.ty.is_bool() => { - let f_c = f_c.literal.try_eval_bool(tcx, param_env).unwrap(); - let s_c = s_c.literal.try_eval_bool(tcx, param_env).unwrap(); - if f_c != s_c { - // have to check this here because f_c & s_c might have - // different spans. - continue; - } - continue 'outer; - } - // If there are not exclusively assignments, then ignore this + ) if lhs_f == lhs_s + && f_c.literal.ty.is_bool() + && s_c.literal.ty.is_bool() + && f_c.literal.try_eval_bool(tcx, param_env).is_some() + && s_c.literal.try_eval_bool(tcx, param_env).is_some() => {} + + // Otherwise we cannot optimize. Try another block. _ => continue 'outer, } } - // Take owenership of items now that we know we can optimize. + // Take ownership of items now that we know we can optimize. let discr = discr.clone(); - let (from, first) = bbs.pick2_mut(bb_idx, first); - let new_stmts = first.statements.iter().cloned().map(|mut s| { - if let StatementKind::Assign(box (_, ref mut rhs)) = s.kind { - if let Rvalue::Use(Operand::Constant(c)) = rhs { - let size = tcx.layout_of(param_env.and(switch_ty)).unwrap().size; - let const_cmp = Operand::const_from_scalar( - tcx, - switch_ty, - crate::interpret::Scalar::from_uint(val, size), - rustc_span::DUMMY_SP, - ); - if let Some(c) = c.literal.try_eval_bool(tcx, param_env) { - let op = if c { BinOp::Eq } else { BinOp::Ne }; - *rhs = Rvalue::BinaryOp(op, Operand::Copy(discr), const_cmp); + let new_stmts = first_stmts + .iter() + .zip(scnd_stmts.iter()) + .map(|(f, s)| { + match (&f.kind, &s.kind) { + (f_s, s_s) if f_s == s_s => (*f).clone(), + + ( + StatementKind::Assign(box (lhs, Rvalue::Use(Operand::Constant(f_c)))), + StatementKind::Assign(box (_, Rvalue::Use(Operand::Constant(s_c)))), + ) => { + // From earlier loop we know that we are dealing with bool constants only: + let f_b = f_c.literal.try_eval_bool(tcx, param_env).unwrap(); + let s_b = s_c.literal.try_eval_bool(tcx, param_env).unwrap(); + if f_b == s_b { + // Same value in both blocks. Use statement as is. + (*f).clone() + } else { + // Different value between blocks. Make value conditional on switch condition. + let size = tcx.layout_of(param_env.and(switch_ty)).unwrap().size; + let const_cmp = Operand::const_from_scalar( + tcx, + switch_ty, + crate::interpret::Scalar::from_uint(val, size), + rustc_span::DUMMY_SP, + ); + let op = if f_b { BinOp::Eq } else { BinOp::Ne }; + let rhs = + Rvalue::BinaryOp(op, Operand::Copy(discr.clone()), const_cmp); + Statement { + source_info: f.source_info, + kind: StatementKind::Assign(box (*lhs, rhs)), + } + } } + + _ => unreachable!(), } - } - s - }); + }) + .collect::>(); + + let (from, first) = bbs.pick2_mut(bb_idx, first); from.statements.extend(new_stmts); from.terminator_mut().kind = first.terminator().kind.clone(); } diff --git a/src/test/mir-opt/matches_reduce_branches.bar.MatchBranchSimplification.diff.32bit b/src/test/mir-opt/matches_reduce_branches.bar.MatchBranchSimplification.diff.32bit new file mode 100644 index 0000000000000..968890e3a298c --- /dev/null +++ b/src/test/mir-opt/matches_reduce_branches.bar.MatchBranchSimplification.diff.32bit @@ -0,0 +1,156 @@ +- // MIR for `bar` before MatchBranchSimplification ++ // MIR for `bar` after MatchBranchSimplification + + fn bar(_1: i32) -> (bool, bool, bool, bool) { + debug i => _1; // in scope 0 at $DIR/matches_reduce_branches.rs:11:8: 11:9 + let mut _0: (bool, bool, bool, bool); // return place in scope 0 at $DIR/matches_reduce_branches.rs:11:19: 11:43 + let _2: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:12:9: 12:10 + let _6: (); // in scope 0 at $DIR/matches_reduce_branches.rs:17:5: 32:6 + let mut _7: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:34:6: 34:7 + let mut _8: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:34:9: 34:10 + let mut _9: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:34:12: 34:13 + let mut _10: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:34:15: 34:16 + scope 1 { + debug a => _2; // in scope 1 at $DIR/matches_reduce_branches.rs:12:9: 12:10 + let _3: bool; // in scope 1 at $DIR/matches_reduce_branches.rs:13:9: 13:10 + scope 2 { + debug b => _3; // in scope 2 at $DIR/matches_reduce_branches.rs:13:9: 13:10 + let _4: bool; // in scope 2 at $DIR/matches_reduce_branches.rs:14:9: 14:10 + scope 3 { + debug c => _4; // in scope 3 at $DIR/matches_reduce_branches.rs:14:9: 14:10 + let _5: bool; // in scope 3 at $DIR/matches_reduce_branches.rs:15:9: 15:10 + scope 4 { + debug d => _5; // in scope 4 at $DIR/matches_reduce_branches.rs:15:9: 15:10 + } + } + } + } + + bb0: { + StorageLive(_2); // scope 0 at $DIR/matches_reduce_branches.rs:12:9: 12:10 + StorageLive(_3); // scope 1 at $DIR/matches_reduce_branches.rs:13:9: 13:10 + StorageLive(_4); // scope 2 at $DIR/matches_reduce_branches.rs:14:9: 14:10 + StorageLive(_5); // scope 3 at $DIR/matches_reduce_branches.rs:15:9: 15:10 + StorageLive(_6); // scope 4 at $DIR/matches_reduce_branches.rs:17:5: 32:6 +- switchInt(_1) -> [7_i32: bb2, otherwise: bb1]; // scope 4 at $DIR/matches_reduce_branches.rs:18:9: 18:10 ++ _2 = Ne(_1, const 7_i32); // scope 4 at $DIR/matches_reduce_branches.rs:19:13: 19:22 ++ // ty::Const ++ // + ty: i32 ++ // + val: Value(Scalar(0x00000007)) ++ // mir::Constant ++ // + span: $DIR/matches_reduce_branches.rs:1:1: 1:1 ++ // + literal: Const { ty: i32, val: Value(Scalar(0x00000007)) } ++ _3 = Eq(_1, const 7_i32); // scope 4 at $DIR/matches_reduce_branches.rs:20:13: 20:21 ++ // ty::Const ++ // + ty: i32 ++ // + val: Value(Scalar(0x00000007)) ++ // mir::Constant ++ // + span: $DIR/matches_reduce_branches.rs:1:1: 1:1 ++ // + literal: Const { ty: i32, val: Value(Scalar(0x00000007)) } ++ _4 = const false; // scope 4 at $DIR/matches_reduce_branches.rs:21:13: 21:22 ++ // ty::Const ++ // + ty: bool ++ // + val: Value(Scalar(0x00)) ++ // mir::Constant ++ // + span: $DIR/matches_reduce_branches.rs:21:17: 21:22 ++ // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } ++ _5 = const true; // scope 4 at $DIR/matches_reduce_branches.rs:22:13: 22:21 ++ // ty::Const ++ // + ty: bool ++ // + val: Value(Scalar(0x01)) ++ // mir::Constant ++ // + span: $DIR/matches_reduce_branches.rs:22:17: 22:21 ++ // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } ++ goto -> bb3; // scope 4 at $DIR/matches_reduce_branches.rs:18:9: 18:10 + } + + bb1: { + _2 = const true; // scope 4 at $DIR/matches_reduce_branches.rs:26:13: 26:21 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x01)) + // mir::Constant + // + span: $DIR/matches_reduce_branches.rs:26:17: 26:21 + // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + _3 = const false; // scope 4 at $DIR/matches_reduce_branches.rs:27:13: 27:22 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/matches_reduce_branches.rs:27:17: 27:22 + // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + _4 = const false; // scope 4 at $DIR/matches_reduce_branches.rs:28:13: 28:22 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/matches_reduce_branches.rs:28:17: 28:22 + // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + _5 = const true; // scope 4 at $DIR/matches_reduce_branches.rs:29:13: 29:21 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x01)) + // mir::Constant + // + span: $DIR/matches_reduce_branches.rs:29:17: 29:21 + // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + goto -> bb3; // scope 4 at $DIR/matches_reduce_branches.rs:17:5: 32:6 + } + + bb2: { + _2 = const false; // scope 4 at $DIR/matches_reduce_branches.rs:19:13: 19:22 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/matches_reduce_branches.rs:19:17: 19:22 + // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + _3 = const true; // scope 4 at $DIR/matches_reduce_branches.rs:20:13: 20:21 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x01)) + // mir::Constant + // + span: $DIR/matches_reduce_branches.rs:20:17: 20:21 + // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + _4 = const false; // scope 4 at $DIR/matches_reduce_branches.rs:21:13: 21:22 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/matches_reduce_branches.rs:21:17: 21:22 + // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + _5 = const true; // scope 4 at $DIR/matches_reduce_branches.rs:22:13: 22:21 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x01)) + // mir::Constant + // + span: $DIR/matches_reduce_branches.rs:22:17: 22:21 + // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + goto -> bb3; // scope 4 at $DIR/matches_reduce_branches.rs:17:5: 32:6 + } + + bb3: { + StorageDead(_6); // scope 4 at $DIR/matches_reduce_branches.rs:32:6: 32:7 + StorageLive(_7); // scope 4 at $DIR/matches_reduce_branches.rs:34:6: 34:7 + _7 = _2; // scope 4 at $DIR/matches_reduce_branches.rs:34:6: 34:7 + StorageLive(_8); // scope 4 at $DIR/matches_reduce_branches.rs:34:9: 34:10 + _8 = _3; // scope 4 at $DIR/matches_reduce_branches.rs:34:9: 34:10 + StorageLive(_9); // scope 4 at $DIR/matches_reduce_branches.rs:34:12: 34:13 + _9 = _4; // scope 4 at $DIR/matches_reduce_branches.rs:34:12: 34:13 + StorageLive(_10); // scope 4 at $DIR/matches_reduce_branches.rs:34:15: 34:16 + _10 = _5; // scope 4 at $DIR/matches_reduce_branches.rs:34:15: 34:16 + (_0.0: bool) = move _7; // scope 4 at $DIR/matches_reduce_branches.rs:34:5: 34:17 + (_0.1: bool) = move _8; // scope 4 at $DIR/matches_reduce_branches.rs:34:5: 34:17 + (_0.2: bool) = move _9; // scope 4 at $DIR/matches_reduce_branches.rs:34:5: 34:17 + (_0.3: bool) = move _10; // scope 4 at $DIR/matches_reduce_branches.rs:34:5: 34:17 + StorageDead(_10); // scope 4 at $DIR/matches_reduce_branches.rs:34:16: 34:17 + StorageDead(_9); // scope 4 at $DIR/matches_reduce_branches.rs:34:16: 34:17 + StorageDead(_8); // scope 4 at $DIR/matches_reduce_branches.rs:34:16: 34:17 + StorageDead(_7); // scope 4 at $DIR/matches_reduce_branches.rs:34:16: 34:17 + StorageDead(_5); // scope 3 at $DIR/matches_reduce_branches.rs:35:1: 35:2 + StorageDead(_4); // scope 2 at $DIR/matches_reduce_branches.rs:35:1: 35:2 + StorageDead(_3); // scope 1 at $DIR/matches_reduce_branches.rs:35:1: 35:2 + StorageDead(_2); // scope 0 at $DIR/matches_reduce_branches.rs:35:1: 35:2 + return; // scope 0 at $DIR/matches_reduce_branches.rs:35:2: 35:2 + } + } + diff --git a/src/test/mir-opt/matches_reduce_branches.bar.MatchBranchSimplification.diff.64bit b/src/test/mir-opt/matches_reduce_branches.bar.MatchBranchSimplification.diff.64bit new file mode 100644 index 0000000000000..968890e3a298c --- /dev/null +++ b/src/test/mir-opt/matches_reduce_branches.bar.MatchBranchSimplification.diff.64bit @@ -0,0 +1,156 @@ +- // MIR for `bar` before MatchBranchSimplification ++ // MIR for `bar` after MatchBranchSimplification + + fn bar(_1: i32) -> (bool, bool, bool, bool) { + debug i => _1; // in scope 0 at $DIR/matches_reduce_branches.rs:11:8: 11:9 + let mut _0: (bool, bool, bool, bool); // return place in scope 0 at $DIR/matches_reduce_branches.rs:11:19: 11:43 + let _2: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:12:9: 12:10 + let _6: (); // in scope 0 at $DIR/matches_reduce_branches.rs:17:5: 32:6 + let mut _7: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:34:6: 34:7 + let mut _8: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:34:9: 34:10 + let mut _9: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:34:12: 34:13 + let mut _10: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:34:15: 34:16 + scope 1 { + debug a => _2; // in scope 1 at $DIR/matches_reduce_branches.rs:12:9: 12:10 + let _3: bool; // in scope 1 at $DIR/matches_reduce_branches.rs:13:9: 13:10 + scope 2 { + debug b => _3; // in scope 2 at $DIR/matches_reduce_branches.rs:13:9: 13:10 + let _4: bool; // in scope 2 at $DIR/matches_reduce_branches.rs:14:9: 14:10 + scope 3 { + debug c => _4; // in scope 3 at $DIR/matches_reduce_branches.rs:14:9: 14:10 + let _5: bool; // in scope 3 at $DIR/matches_reduce_branches.rs:15:9: 15:10 + scope 4 { + debug d => _5; // in scope 4 at $DIR/matches_reduce_branches.rs:15:9: 15:10 + } + } + } + } + + bb0: { + StorageLive(_2); // scope 0 at $DIR/matches_reduce_branches.rs:12:9: 12:10 + StorageLive(_3); // scope 1 at $DIR/matches_reduce_branches.rs:13:9: 13:10 + StorageLive(_4); // scope 2 at $DIR/matches_reduce_branches.rs:14:9: 14:10 + StorageLive(_5); // scope 3 at $DIR/matches_reduce_branches.rs:15:9: 15:10 + StorageLive(_6); // scope 4 at $DIR/matches_reduce_branches.rs:17:5: 32:6 +- switchInt(_1) -> [7_i32: bb2, otherwise: bb1]; // scope 4 at $DIR/matches_reduce_branches.rs:18:9: 18:10 ++ _2 = Ne(_1, const 7_i32); // scope 4 at $DIR/matches_reduce_branches.rs:19:13: 19:22 ++ // ty::Const ++ // + ty: i32 ++ // + val: Value(Scalar(0x00000007)) ++ // mir::Constant ++ // + span: $DIR/matches_reduce_branches.rs:1:1: 1:1 ++ // + literal: Const { ty: i32, val: Value(Scalar(0x00000007)) } ++ _3 = Eq(_1, const 7_i32); // scope 4 at $DIR/matches_reduce_branches.rs:20:13: 20:21 ++ // ty::Const ++ // + ty: i32 ++ // + val: Value(Scalar(0x00000007)) ++ // mir::Constant ++ // + span: $DIR/matches_reduce_branches.rs:1:1: 1:1 ++ // + literal: Const { ty: i32, val: Value(Scalar(0x00000007)) } ++ _4 = const false; // scope 4 at $DIR/matches_reduce_branches.rs:21:13: 21:22 ++ // ty::Const ++ // + ty: bool ++ // + val: Value(Scalar(0x00)) ++ // mir::Constant ++ // + span: $DIR/matches_reduce_branches.rs:21:17: 21:22 ++ // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } ++ _5 = const true; // scope 4 at $DIR/matches_reduce_branches.rs:22:13: 22:21 ++ // ty::Const ++ // + ty: bool ++ // + val: Value(Scalar(0x01)) ++ // mir::Constant ++ // + span: $DIR/matches_reduce_branches.rs:22:17: 22:21 ++ // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } ++ goto -> bb3; // scope 4 at $DIR/matches_reduce_branches.rs:18:9: 18:10 + } + + bb1: { + _2 = const true; // scope 4 at $DIR/matches_reduce_branches.rs:26:13: 26:21 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x01)) + // mir::Constant + // + span: $DIR/matches_reduce_branches.rs:26:17: 26:21 + // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + _3 = const false; // scope 4 at $DIR/matches_reduce_branches.rs:27:13: 27:22 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/matches_reduce_branches.rs:27:17: 27:22 + // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + _4 = const false; // scope 4 at $DIR/matches_reduce_branches.rs:28:13: 28:22 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/matches_reduce_branches.rs:28:17: 28:22 + // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + _5 = const true; // scope 4 at $DIR/matches_reduce_branches.rs:29:13: 29:21 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x01)) + // mir::Constant + // + span: $DIR/matches_reduce_branches.rs:29:17: 29:21 + // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + goto -> bb3; // scope 4 at $DIR/matches_reduce_branches.rs:17:5: 32:6 + } + + bb2: { + _2 = const false; // scope 4 at $DIR/matches_reduce_branches.rs:19:13: 19:22 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/matches_reduce_branches.rs:19:17: 19:22 + // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + _3 = const true; // scope 4 at $DIR/matches_reduce_branches.rs:20:13: 20:21 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x01)) + // mir::Constant + // + span: $DIR/matches_reduce_branches.rs:20:17: 20:21 + // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + _4 = const false; // scope 4 at $DIR/matches_reduce_branches.rs:21:13: 21:22 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/matches_reduce_branches.rs:21:17: 21:22 + // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + _5 = const true; // scope 4 at $DIR/matches_reduce_branches.rs:22:13: 22:21 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x01)) + // mir::Constant + // + span: $DIR/matches_reduce_branches.rs:22:17: 22:21 + // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + goto -> bb3; // scope 4 at $DIR/matches_reduce_branches.rs:17:5: 32:6 + } + + bb3: { + StorageDead(_6); // scope 4 at $DIR/matches_reduce_branches.rs:32:6: 32:7 + StorageLive(_7); // scope 4 at $DIR/matches_reduce_branches.rs:34:6: 34:7 + _7 = _2; // scope 4 at $DIR/matches_reduce_branches.rs:34:6: 34:7 + StorageLive(_8); // scope 4 at $DIR/matches_reduce_branches.rs:34:9: 34:10 + _8 = _3; // scope 4 at $DIR/matches_reduce_branches.rs:34:9: 34:10 + StorageLive(_9); // scope 4 at $DIR/matches_reduce_branches.rs:34:12: 34:13 + _9 = _4; // scope 4 at $DIR/matches_reduce_branches.rs:34:12: 34:13 + StorageLive(_10); // scope 4 at $DIR/matches_reduce_branches.rs:34:15: 34:16 + _10 = _5; // scope 4 at $DIR/matches_reduce_branches.rs:34:15: 34:16 + (_0.0: bool) = move _7; // scope 4 at $DIR/matches_reduce_branches.rs:34:5: 34:17 + (_0.1: bool) = move _8; // scope 4 at $DIR/matches_reduce_branches.rs:34:5: 34:17 + (_0.2: bool) = move _9; // scope 4 at $DIR/matches_reduce_branches.rs:34:5: 34:17 + (_0.3: bool) = move _10; // scope 4 at $DIR/matches_reduce_branches.rs:34:5: 34:17 + StorageDead(_10); // scope 4 at $DIR/matches_reduce_branches.rs:34:16: 34:17 + StorageDead(_9); // scope 4 at $DIR/matches_reduce_branches.rs:34:16: 34:17 + StorageDead(_8); // scope 4 at $DIR/matches_reduce_branches.rs:34:16: 34:17 + StorageDead(_7); // scope 4 at $DIR/matches_reduce_branches.rs:34:16: 34:17 + StorageDead(_5); // scope 3 at $DIR/matches_reduce_branches.rs:35:1: 35:2 + StorageDead(_4); // scope 2 at $DIR/matches_reduce_branches.rs:35:1: 35:2 + StorageDead(_3); // scope 1 at $DIR/matches_reduce_branches.rs:35:1: 35:2 + StorageDead(_2); // scope 0 at $DIR/matches_reduce_branches.rs:35:1: 35:2 + return; // scope 0 at $DIR/matches_reduce_branches.rs:35:2: 35:2 + } + } + diff --git a/src/test/mir-opt/matches_reduce_branches.foo.MatchBranchSimplification.diff.32bit b/src/test/mir-opt/matches_reduce_branches.foo.MatchBranchSimplification.diff.32bit index 9324762fb0803..a33db001f4438 100644 --- a/src/test/mir-opt/matches_reduce_branches.foo.MatchBranchSimplification.diff.32bit +++ b/src/test/mir-opt/matches_reduce_branches.foo.MatchBranchSimplification.diff.32bit @@ -2,15 +2,15 @@ + // MIR for `foo` after MatchBranchSimplification fn foo(_1: std::option::Option<()>) -> () { - debug bar => _1; // in scope 0 at $DIR/matches_reduce_branches.rs:4:8: 4:11 - let mut _0: (); // return place in scope 0 at $DIR/matches_reduce_branches.rs:4:25: 4:25 + debug bar => _1; // in scope 0 at $DIR/matches_reduce_branches.rs:5:8: 5:11 + let mut _0: (); // return place in scope 0 at $DIR/matches_reduce_branches.rs:5:25: 5:25 let mut _2: bool; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - let mut _3: isize; // in scope 0 at $DIR/matches_reduce_branches.rs:5:22: 5:26 + let mut _3: isize; // in scope 0 at $DIR/matches_reduce_branches.rs:6:22: 6:26 bb0: { StorageLive(_2); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - _3 = discriminant(_1); // scope 0 at $DIR/matches_reduce_branches.rs:5:22: 5:26 -- switchInt(move _3) -> [0_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/matches_reduce_branches.rs:5:22: 5:26 + _3 = discriminant(_1); // scope 0 at $DIR/matches_reduce_branches.rs:6:22: 6:26 +- switchInt(move _3) -> [0_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/matches_reduce_branches.rs:6:22: 6:26 + _2 = Eq(_3, const 0_isize); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + // ty::Const + // + ty: isize @@ -18,7 +18,7 @@ + // mir::Constant + // + span: $DIR/matches_reduce_branches.rs:1:1: 1:1 + // + literal: Const { ty: isize, val: Value(Scalar(0x00000000)) } -+ goto -> bb3; // scope 0 at $DIR/matches_reduce_branches.rs:5:22: 5:26 ++ goto -> bb3; // scope 0 at $DIR/matches_reduce_branches.rs:6:22: 6:26 } bb1: { @@ -44,23 +44,23 @@ } bb3: { - switchInt(_2) -> [false: bb4, otherwise: bb5]; // scope 0 at $DIR/matches_reduce_branches.rs:5:5: 7:6 + switchInt(_2) -> [false: bb4, otherwise: bb5]; // scope 0 at $DIR/matches_reduce_branches.rs:6:5: 8:6 } bb4: { - _0 = const (); // scope 0 at $DIR/matches_reduce_branches.rs:5:5: 7:6 + _0 = const (); // scope 0 at $DIR/matches_reduce_branches.rs:6:5: 8:6 // ty::Const // + ty: () // + val: Value(Scalar()) // mir::Constant - // + span: $DIR/matches_reduce_branches.rs:5:5: 7:6 + // + span: $DIR/matches_reduce_branches.rs:6:5: 8:6 // + literal: Const { ty: (), val: Value(Scalar()) } - goto -> bb5; // scope 0 at $DIR/matches_reduce_branches.rs:5:5: 7:6 + goto -> bb5; // scope 0 at $DIR/matches_reduce_branches.rs:6:5: 8:6 } bb5: { - StorageDead(_2); // scope 0 at $DIR/matches_reduce_branches.rs:8:1: 8:2 - return; // scope 0 at $DIR/matches_reduce_branches.rs:8:2: 8:2 + StorageDead(_2); // scope 0 at $DIR/matches_reduce_branches.rs:9:1: 9:2 + return; // scope 0 at $DIR/matches_reduce_branches.rs:9:2: 9:2 } } diff --git a/src/test/mir-opt/matches_reduce_branches.foo.MatchBranchSimplification.diff.64bit b/src/test/mir-opt/matches_reduce_branches.foo.MatchBranchSimplification.diff.64bit index 144a71228ad69..3eb5b01fbf496 100644 --- a/src/test/mir-opt/matches_reduce_branches.foo.MatchBranchSimplification.diff.64bit +++ b/src/test/mir-opt/matches_reduce_branches.foo.MatchBranchSimplification.diff.64bit @@ -2,15 +2,15 @@ + // MIR for `foo` after MatchBranchSimplification fn foo(_1: std::option::Option<()>) -> () { - debug bar => _1; // in scope 0 at $DIR/matches_reduce_branches.rs:4:8: 4:11 - let mut _0: (); // return place in scope 0 at $DIR/matches_reduce_branches.rs:4:25: 4:25 + debug bar => _1; // in scope 0 at $DIR/matches_reduce_branches.rs:5:8: 5:11 + let mut _0: (); // return place in scope 0 at $DIR/matches_reduce_branches.rs:5:25: 5:25 let mut _2: bool; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - let mut _3: isize; // in scope 0 at $DIR/matches_reduce_branches.rs:5:22: 5:26 + let mut _3: isize; // in scope 0 at $DIR/matches_reduce_branches.rs:6:22: 6:26 bb0: { StorageLive(_2); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - _3 = discriminant(_1); // scope 0 at $DIR/matches_reduce_branches.rs:5:22: 5:26 -- switchInt(move _3) -> [0_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/matches_reduce_branches.rs:5:22: 5:26 + _3 = discriminant(_1); // scope 0 at $DIR/matches_reduce_branches.rs:6:22: 6:26 +- switchInt(move _3) -> [0_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/matches_reduce_branches.rs:6:22: 6:26 + _2 = Eq(_3, const 0_isize); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + // ty::Const + // + ty: isize @@ -18,7 +18,7 @@ + // mir::Constant + // + span: $DIR/matches_reduce_branches.rs:1:1: 1:1 + // + literal: Const { ty: isize, val: Value(Scalar(0x0000000000000000)) } -+ goto -> bb3; // scope 0 at $DIR/matches_reduce_branches.rs:5:22: 5:26 ++ goto -> bb3; // scope 0 at $DIR/matches_reduce_branches.rs:6:22: 6:26 } bb1: { @@ -44,23 +44,23 @@ } bb3: { - switchInt(_2) -> [false: bb4, otherwise: bb5]; // scope 0 at $DIR/matches_reduce_branches.rs:5:5: 7:6 + switchInt(_2) -> [false: bb4, otherwise: bb5]; // scope 0 at $DIR/matches_reduce_branches.rs:6:5: 8:6 } bb4: { - _0 = const (); // scope 0 at $DIR/matches_reduce_branches.rs:5:5: 7:6 + _0 = const (); // scope 0 at $DIR/matches_reduce_branches.rs:6:5: 8:6 // ty::Const // + ty: () // + val: Value(Scalar()) // mir::Constant - // + span: $DIR/matches_reduce_branches.rs:5:5: 7:6 + // + span: $DIR/matches_reduce_branches.rs:6:5: 8:6 // + literal: Const { ty: (), val: Value(Scalar()) } - goto -> bb5; // scope 0 at $DIR/matches_reduce_branches.rs:5:5: 7:6 + goto -> bb5; // scope 0 at $DIR/matches_reduce_branches.rs:6:5: 8:6 } bb5: { - StorageDead(_2); // scope 0 at $DIR/matches_reduce_branches.rs:8:1: 8:2 - return; // scope 0 at $DIR/matches_reduce_branches.rs:8:2: 8:2 + StorageDead(_2); // scope 0 at $DIR/matches_reduce_branches.rs:9:1: 9:2 + return; // scope 0 at $DIR/matches_reduce_branches.rs:9:2: 9:2 } } diff --git a/src/test/mir-opt/matches_reduce_branches.rs b/src/test/mir-opt/matches_reduce_branches.rs index 91b6bfc836bd4..ebc88d2fbd1da 100644 --- a/src/test/mir-opt/matches_reduce_branches.rs +++ b/src/test/mir-opt/matches_reduce_branches.rs @@ -1,5 +1,6 @@ // EMIT_MIR_FOR_EACH_BIT_WIDTH // EMIT_MIR matches_reduce_branches.foo.MatchBranchSimplification.diff +// EMIT_MIR matches_reduce_branches.bar.MatchBranchSimplification.diff fn foo(bar: Option<()>) { if matches!(bar, None) { @@ -7,7 +8,35 @@ fn foo(bar: Option<()>) { } } +fn bar(i: i32) -> (bool, bool, bool, bool) { + let a; + let b; + let c; + let d; + + match i { + 7 => { + a = false; + b = true; + c = false; + d = true; + () + } + _ => { + a = true; + b = false; + c = false; + d = true; + () + } + }; + + (a, b, c, d) +} + + fn main() { let _ = foo(None); let _ = foo(Some(())); + let _ = bar(0); } From af9b9e4ec874c4bc5c771841d491e02d02d5636a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Sat, 15 Aug 2020 00:00:00 +0000 Subject: [PATCH 6/6] MatchBranchSimplification: avoid intermediate vec allocation --- src/librustc_index/vec.rs | 11 +++ src/librustc_mir/transform/match_branches.rs | 73 ++++++++++---------- 2 files changed, 46 insertions(+), 38 deletions(-) diff --git a/src/librustc_index/vec.rs b/src/librustc_index/vec.rs index c5dedab979326..c24f25d652be9 100644 --- a/src/librustc_index/vec.rs +++ b/src/librustc_index/vec.rs @@ -669,6 +669,17 @@ impl IndexVec { } } + /// Returns mutable references to three distinct elements or panics otherwise. + #[inline] + pub fn pick3_mut(&mut self, a: I, b: I, c: I) -> (&mut T, &mut T, &mut T) { + let (ai, bi, ci) = (a.index(), b.index(), c.index()); + assert!(ai != bi && bi != ci && ci != ai); + let len = self.raw.len(); + assert!(ai < len && bi < len && ci < len); + let ptr = self.raw.as_mut_ptr(); + unsafe { (&mut *ptr.add(ai), &mut *ptr.add(bi), &mut *ptr.add(ci)) } + } + pub fn convert_index_type(self) -> IndexVec { IndexVec { raw: self.raw, _marker: PhantomData } } diff --git a/src/librustc_mir/transform/match_branches.rs b/src/librustc_mir/transform/match_branches.rs index b78dee753f85f..c1d574d6ef290 100644 --- a/src/librustc_mir/transform/match_branches.rs +++ b/src/librustc_mir/transform/match_branches.rs @@ -48,7 +48,7 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification { ref targets, ref values, .. - } if targets.len() == 2 && values.len() == 1 => { + } if targets.len() == 2 && values.len() == 1 && targets[0] != targets[1] => { (place, values[0], switch_ty, targets[0], targets[1]) } // Only optimize switch int statements @@ -89,48 +89,45 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification { // Take ownership of items now that we know we can optimize. let discr = discr.clone(); - let new_stmts = first_stmts - .iter() - .zip(scnd_stmts.iter()) - .map(|(f, s)| { - match (&f.kind, &s.kind) { - (f_s, s_s) if f_s == s_s => (*f).clone(), + // We already checked that first and second are different blocks, + // and bb_idx has a different terminator from both of them. + let (from, first, second) = bbs.pick3_mut(bb_idx, first, second); - ( - StatementKind::Assign(box (lhs, Rvalue::Use(Operand::Constant(f_c)))), - StatementKind::Assign(box (_, Rvalue::Use(Operand::Constant(s_c)))), - ) => { - // From earlier loop we know that we are dealing with bool constants only: - let f_b = f_c.literal.try_eval_bool(tcx, param_env).unwrap(); - let s_b = s_c.literal.try_eval_bool(tcx, param_env).unwrap(); - if f_b == s_b { - // Same value in both blocks. Use statement as is. - (*f).clone() - } else { - // Different value between blocks. Make value conditional on switch condition. - let size = tcx.layout_of(param_env.and(switch_ty)).unwrap().size; - let const_cmp = Operand::const_from_scalar( - tcx, - switch_ty, - crate::interpret::Scalar::from_uint(val, size), - rustc_span::DUMMY_SP, - ); - let op = if f_b { BinOp::Eq } else { BinOp::Ne }; - let rhs = - Rvalue::BinaryOp(op, Operand::Copy(discr.clone()), const_cmp); - Statement { - source_info: f.source_info, - kind: StatementKind::Assign(box (*lhs, rhs)), - } + let new_stmts = first.statements.iter().zip(second.statements.iter()).map(|(f, s)| { + match (&f.kind, &s.kind) { + (f_s, s_s) if f_s == s_s => (*f).clone(), + + ( + StatementKind::Assign(box (lhs, Rvalue::Use(Operand::Constant(f_c)))), + StatementKind::Assign(box (_, Rvalue::Use(Operand::Constant(s_c)))), + ) => { + // From earlier loop we know that we are dealing with bool constants only: + let f_b = f_c.literal.try_eval_bool(tcx, param_env).unwrap(); + let s_b = s_c.literal.try_eval_bool(tcx, param_env).unwrap(); + if f_b == s_b { + // Same value in both blocks. Use statement as is. + (*f).clone() + } else { + // Different value between blocks. Make value conditional on switch condition. + let size = tcx.layout_of(param_env.and(switch_ty)).unwrap().size; + let const_cmp = Operand::const_from_scalar( + tcx, + switch_ty, + crate::interpret::Scalar::from_uint(val, size), + rustc_span::DUMMY_SP, + ); + let op = if f_b { BinOp::Eq } else { BinOp::Ne }; + let rhs = Rvalue::BinaryOp(op, Operand::Copy(discr.clone()), const_cmp); + Statement { + source_info: f.source_info, + kind: StatementKind::Assign(box (*lhs, rhs)), } } - - _ => unreachable!(), } - }) - .collect::>(); - let (from, first) = bbs.pick2_mut(bb_idx, first); + _ => unreachable!(), + } + }); from.statements.extend(new_stmts); from.terminator_mut().kind = first.terminator().kind.clone(); }