diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 820998eed1005..f88be5de50d1b 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -90,14 +90,16 @@ use std::hash::{Hash, Hasher}; use either::Either; use hashbrown::hash_table::{Entry, HashTable}; use itertools::Itertools as _; -use rustc_abi::{self as abi, BackendRepr, FIRST_VARIANT, FieldIdx, Primitive, Size, VariantIdx}; +use rustc_abi::{ + self as abi, BackendRepr, FIRST_VARIANT, FieldIdx, Primitive, Size, VariantIdx, WrappingRange, +}; use rustc_arena::DroplessArena; use rustc_const_eval::const_eval::DummyMachine; use rustc_const_eval::interpret::{ ImmTy, Immediate, InterpCx, MemPlaceMeta, MemoryKind, OpTy, Projectable, Scalar, intern_const_alloc_for_constprop, }; -use rustc_data_structures::fx::FxHasher; +use rustc_data_structures::fx::{FxHashMap, FxHasher}; use rustc_data_structures::graph::dominators::Dominators; use rustc_hir::def::DefKind; use rustc_index::bit_set::DenseBitSet; @@ -130,10 +132,25 @@ impl<'tcx> crate::MirPass<'tcx> for GVN { // Clone dominators because we need them while mutating the body. let dominators = body.basic_blocks.dominators().clone(); let maybe_loop_headers = loops::maybe_loop_headers(body); + let predecessors = body.basic_blocks.predecessors(); + let mut unique_predecessors = DenseBitSet::new_empty(body.basic_blocks.len()); + for (bb, _) in body.basic_blocks.iter_enumerated() { + if predecessors[bb].len() == 1 { + unique_predecessors.insert(bb); + } + } let arena = DroplessArena::default(); - let mut state = - VnState::new(tcx, body, typing_env, &ssa, dominators, &body.local_decls, &arena); + let mut state = VnState::new( + tcx, + body, + typing_env, + &ssa, + dominators, + &body.local_decls, + &arena, + unique_predecessors, + ); for local in body.args_iter().filter(|&local| ssa.is_ssa(local)) { let opaque = state.new_opaque(body.local_decls[local].ty); @@ -380,6 +397,10 @@ struct VnState<'body, 'a, 'tcx> { dominators: Dominators, reused_locals: DenseBitSet, arena: &'a DroplessArena, + /// Known ranges at each locations. + ranges: IndexVec>, + /// Determines if the basic block has a single unique predecessor. + unique_predecessors: DenseBitSet, } impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { @@ -391,6 +412,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { dominators: Dominators, local_decls: &'body LocalDecls<'tcx>, arena: &'a DroplessArena, + unique_predecessors: DenseBitSet, ) -> Self { // Compute a rough estimate of the number of values in the body from the number of // statements. This is meant to reduce the number of allocations, but it's all right if @@ -413,6 +435,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { dominators, reused_locals: DenseBitSet::new_empty(local_decls.len()), arena, + ranges: IndexVec::with_capacity(num_values), + unique_predecessors, } } @@ -430,6 +454,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { debug_assert_eq!(index, _index); let _index = self.rev_locals.push(SmallVec::new()); debug_assert_eq!(index, _index); + let _index = self.ranges.push(Vec::new()); + debug_assert_eq!(index, _index); index } @@ -442,6 +468,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { debug_assert_eq!(index, _index); let _index = self.rev_locals.push(SmallVec::new()); debug_assert_eq!(index, _index); + let _index = self.ranges.push(Vec::new()); + debug_assert_eq!(index, _index); } index } @@ -523,6 +551,20 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { self.rev_locals[value].push(local); } + /// Create a new known range at the location. + fn insert_range(&mut self, value: VnIndex, location: Location, range: WrappingRange) { + self.ranges[value].push((location, range)); + } + + /// Get the known range at the location. + fn get_range(&self, value: VnIndex, location: Location) -> Option { + // FIXME: This should use the intersection of all valid ranges. + let (_, range) = self.ranges[value] + .iter() + .find(|(range_loc, _)| range_loc.dominates(location, &self.dominators))?; + Some(*range) + } + fn insert_bool(&mut self, flag: bool) -> VnIndex { // Booleans are deterministic. let value = Const::from_bool(self.tcx, flag); @@ -1011,7 +1053,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { Operand::Constant(ref constant) => Some(self.insert_constant(constant.const_)), Operand::Copy(ref mut place) | Operand::Move(ref mut place) => { let value = self.simplify_place_value(place, location)?; - if let Some(const_) = self.try_as_constant(value) { + if let Some(const_) = self.try_as_constant(value, location) { *operand = Operand::Constant(Box::new(const_)); } else if let Value::RuntimeChecks(c) = self.get(value) { *operand = Operand::RuntimeChecks(c); @@ -1782,7 +1824,7 @@ impl<'tcx> VnState<'_, '_, 'tcx> { /// If either [`Self::try_as_constant`] as [`Self::try_as_place`] succeeds, /// returns that result as an [`Operand`]. fn try_as_operand(&mut self, index: VnIndex, location: Location) -> Option> { - if let Some(const_) = self.try_as_constant(index) { + if let Some(const_) = self.try_as_constant(index, location) { Some(Operand::Constant(Box::new(const_))) } else if let Value::RuntimeChecks(c) = self.get(index) { Some(Operand::RuntimeChecks(c)) @@ -1795,7 +1837,11 @@ impl<'tcx> VnState<'_, '_, 'tcx> { } /// If `index` is a `Value::Constant`, return the `Constant` to be put in the MIR. - fn try_as_constant(&mut self, index: VnIndex) -> Option> { + fn try_as_constant( + &mut self, + index: VnIndex, + location: Location, + ) -> Option> { // This was already constant in MIR, do not change it. If the constant is not // deterministic, adding an additional mention of it in MIR will not give the same value as // the former mention. @@ -1804,21 +1850,34 @@ impl<'tcx> VnState<'_, '_, 'tcx> { return Some(ConstOperand { span: DUMMY_SP, user_ty: None, const_: value }); } - let op = self.eval_to_const(index)?; - if op.layout.is_unsized() { - // Do not attempt to propagate unsized locals. - return None; - } + if let Some(op) = self.eval_to_const(index) { + if op.layout.is_unsized() { + // Do not attempt to propagate unsized locals. + return None; + } + + let value = op_to_prop_const(&mut self.ecx, op)?; - let value = op_to_prop_const(&mut self.ecx, op)?; + // Check that we do not leak a pointer. + // Those pointers may lose part of their identity in codegen. + // FIXME: remove this hack once https://github.com/rust-lang/rust/issues/79738 is fixed. + assert!(!value.may_have_provenance(self.tcx, op.layout.size)); - // Check that we do not leak a pointer. - // Those pointers may lose part of their identity in codegen. - // FIXME: remove this hack once https://github.com/rust-lang/rust/issues/79738 is fixed. - assert!(!value.may_have_provenance(self.tcx, op.layout.size)); + let const_ = Const::Val(value, op.layout.ty); + return Some(ConstOperand { span: DUMMY_SP, user_ty: None, const_ }); + } - let const_ = Const::Val(value, op.layout.ty); - Some(ConstOperand { span: DUMMY_SP, user_ty: None, const_ }) + if let Some(range) = self.get_range(index, location) + && range.start == range.end + { + let ty = self.ty(index); + let layout = self.ecx.layout_of(ty).ok()?; + let value = ConstValue::Scalar(Scalar::from_uint(range.start, layout.size)); + let const_ = Const::Val(value, self.ty(index)); + return Some(ConstOperand { span: DUMMY_SP, user_ty: None, const_ }); + } + + None } /// Construct a place which holds the same value as `index` and for which all locals strictly @@ -1895,7 +1954,7 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, '_, 'tcx> { let value = self.simplify_rvalue(lhs, rvalue, location); if let Some(value) = value { - if let Some(const_) = self.try_as_constant(value) { + if let Some(const_) = self.try_as_constant(value, location) { *rvalue = Rvalue::Use(Operand::Constant(Box::new(const_))); } else if let Some(place) = self.try_as_place(value, location, false) && *rvalue != Rvalue::Use(Operand::Move(place)) @@ -1924,14 +1983,73 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, '_, 'tcx> { } fn visit_terminator(&mut self, terminator: &mut Terminator<'tcx>, location: Location) { - if let Terminator { kind: TerminatorKind::Call { destination, .. }, .. } = terminator { - if let Some(local) = destination.as_local() - && self.ssa.is_ssa(local) - { - let ty = self.local_decls[local].ty; - let opaque = self.new_opaque(ty); - self.assign(local, opaque); + match &mut terminator.kind { + TerminatorKind::Call { destination, .. } => { + if let Some(local) = destination.as_local() + && self.ssa.is_ssa(local) + { + let ty = self.local_decls[local].ty; + let opaque = self.new_opaque(ty); + self.assign(local, opaque); + } + } + TerminatorKind::Assert { cond, expected, target, .. } => { + if let Some(value) = self.simplify_operand(cond, location) + && !matches!(self.get(value), Value::Constant { .. }) + { + let successor = Location { block: *target, statement_index: 0 }; + if location.block != successor.block + && self.unique_predecessors.contains(successor.block) + { + let val = *expected as u128; + let range = WrappingRange { start: val, end: val }; + self.insert_range(value, successor, range); + } + } + } + TerminatorKind::SwitchInt { discr, targets } => { + if let Some(value) = self.simplify_operand(discr, location) + && targets.all_targets().len() < 16 + && !matches!(self.get(value), Value::Constant { .. }) + { + let mut distinct_targets: FxHashMap = FxHashMap::default(); + for (_, target) in targets.iter() { + let targets = distinct_targets.entry(target).or_default(); + if *targets == 0 { + *targets = 1; + } else { + *targets = 2; + } + } + for (val, target) in targets.iter() { + if distinct_targets[&target] != 1 { + continue; + } + let successor = Location { block: target, statement_index: 0 }; + if location.block != successor.block + && self.unique_predecessors.contains(successor.block) + { + let range = WrappingRange { start: val, end: val }; + self.insert_range(value, successor, range); + } + } + + let otherwise = Location { block: targets.otherwise(), statement_index: 0 }; + if self.ty(value).is_bool() + && let [val] = targets.all_values() + && location.block != otherwise.block + && self.unique_predecessors.contains(otherwise.block) + { + let range = if val.get() == 0 { + WrappingRange { start: 1, end: 1 } + } else { + WrappingRange { start: 0, end: 0 } + }; + self.insert_range(value, otherwise, range); + } + } } + _ => {} } // Terminators that can write to memory may invalidate (nested) derefs. if terminator.kind.can_write_to_memory() { diff --git a/tests/mir-opt/early_otherwise_branch_unwind.poll.EarlyOtherwiseBranch.diff b/tests/mir-opt/early_otherwise_branch_unwind.poll.EarlyOtherwiseBranch.diff index b6450e43b09e6..96de2b627bba0 100644 --- a/tests/mir-opt/early_otherwise_branch_unwind.poll.EarlyOtherwiseBranch.diff +++ b/tests/mir-opt/early_otherwise_branch_unwind.poll.EarlyOtherwiseBranch.diff @@ -39,7 +39,7 @@ bb3: { _2 = discriminant(((((_1 as Ready).0: std::result::Result>, u8>) as Ok).0: std::option::Option>)); - switchInt(copy _2) -> [0: bb5, 1: bb7, otherwise: bb1]; + switchInt(move _2) -> [0: bb5, 1: bb7, otherwise: bb1]; } bb4: { @@ -112,7 +112,7 @@ } bb18 (cleanup): { - switchInt(copy _3) -> [0: bb19, otherwise: bb9]; + switchInt(const 0_isize) -> [0: bb19, otherwise: bb9]; } bb19 (cleanup): { @@ -120,7 +120,7 @@ } bb20 (cleanup): { - switchInt(copy _4) -> [0: bb18, otherwise: bb9]; + switchInt(const 0_isize) -> [0: bb18, otherwise: bb9]; } } diff --git a/tests/mir-opt/early_otherwise_branch_unwind.unwind.EarlyOtherwiseBranch.diff b/tests/mir-opt/early_otherwise_branch_unwind.unwind.EarlyOtherwiseBranch.diff index 2b2c007e082a7..c2e6ab04eb8b7 100644 --- a/tests/mir-opt/early_otherwise_branch_unwind.unwind.EarlyOtherwiseBranch.diff +++ b/tests/mir-opt/early_otherwise_branch_unwind.unwind.EarlyOtherwiseBranch.diff @@ -35,7 +35,7 @@ bb3: { _2 = discriminant(((((_1 as Some).0: std::option::Option>) as Some).0: std::option::Option)); - switchInt(copy _2) -> [0: bb6, 1: bb7, otherwise: bb1]; + switchInt(move _2) -> [0: bb6, 1: bb7, otherwise: bb1]; } bb4: { @@ -105,7 +105,7 @@ } bb18 (cleanup): { - switchInt(copy _3) -> [1: bb19, otherwise: bb9]; + switchInt(const 1_isize) -> [1: bb19, otherwise: bb9]; } bb19 (cleanup): { @@ -113,7 +113,7 @@ } bb20 (cleanup): { - switchInt(copy _4) -> [1: bb18, otherwise: bb9]; + switchInt(const 1_isize) -> [1: bb18, otherwise: bb9]; } } diff --git a/tests/mir-opt/gvn.arithmetic.GVN.panic-abort.diff b/tests/mir-opt/gvn.arithmetic.GVN.panic-abort.diff index f980645b1d092..f853eaa61c686 100644 --- a/tests/mir-opt/gvn.arithmetic.GVN.panic-abort.diff +++ b/tests/mir-opt/gvn.arithmetic.GVN.panic-abort.diff @@ -222,8 +222,8 @@ _32 = copy _1; - _33 = Eq(copy _32, const 0_u64); - assert(!move _33, "attempt to divide `{}` by zero", const 1_u64) -> [success: bb12, unwind unreachable]; -+ _33 = copy _29; -+ assert(!copy _29, "attempt to divide `{}` by zero", const 1_u64) -> [success: bb12, unwind unreachable]; ++ _33 = const false; ++ assert(!const false, "attempt to divide `{}` by zero", const 1_u64) -> [success: bb12, unwind unreachable]; } bb12: { @@ -283,8 +283,8 @@ _44 = copy _1; - _45 = Eq(copy _44, const 0_u64); - assert(!move _45, "attempt to calculate the remainder of `{}` with a divisor of zero", const 0_u64) -> [success: bb18, unwind unreachable]; -+ _45 = copy _29; -+ assert(!copy _29, "attempt to calculate the remainder of `{}` with a divisor of zero", const 0_u64) -> [success: bb18, unwind unreachable]; ++ _45 = const false; ++ assert(!const false, "attempt to calculate the remainder of `{}` with a divisor of zero", const 0_u64) -> [success: bb18, unwind unreachable]; } bb18: { @@ -304,8 +304,8 @@ _48 = copy _1; - _49 = Eq(copy _48, const 0_u64); - assert(!move _49, "attempt to calculate the remainder of `{}` with a divisor of zero", const 1_u64) -> [success: bb20, unwind unreachable]; -+ _49 = copy _29; -+ assert(!copy _29, "attempt to calculate the remainder of `{}` with a divisor of zero", const 1_u64) -> [success: bb20, unwind unreachable]; ++ _49 = const false; ++ assert(!const false, "attempt to calculate the remainder of `{}` with a divisor of zero", const 1_u64) -> [success: bb20, unwind unreachable]; } bb20: { diff --git a/tests/mir-opt/gvn.arithmetic.GVN.panic-unwind.diff b/tests/mir-opt/gvn.arithmetic.GVN.panic-unwind.diff index b8e4967fe8b18..10b9e3b538df8 100644 --- a/tests/mir-opt/gvn.arithmetic.GVN.panic-unwind.diff +++ b/tests/mir-opt/gvn.arithmetic.GVN.panic-unwind.diff @@ -222,8 +222,8 @@ _32 = copy _1; - _33 = Eq(copy _32, const 0_u64); - assert(!move _33, "attempt to divide `{}` by zero", const 1_u64) -> [success: bb12, unwind continue]; -+ _33 = copy _29; -+ assert(!copy _29, "attempt to divide `{}` by zero", const 1_u64) -> [success: bb12, unwind continue]; ++ _33 = const false; ++ assert(!const false, "attempt to divide `{}` by zero", const 1_u64) -> [success: bb12, unwind continue]; } bb12: { @@ -283,8 +283,8 @@ _44 = copy _1; - _45 = Eq(copy _44, const 0_u64); - assert(!move _45, "attempt to calculate the remainder of `{}` with a divisor of zero", const 0_u64) -> [success: bb18, unwind continue]; -+ _45 = copy _29; -+ assert(!copy _29, "attempt to calculate the remainder of `{}` with a divisor of zero", const 0_u64) -> [success: bb18, unwind continue]; ++ _45 = const false; ++ assert(!const false, "attempt to calculate the remainder of `{}` with a divisor of zero", const 0_u64) -> [success: bb18, unwind continue]; } bb18: { @@ -304,8 +304,8 @@ _48 = copy _1; - _49 = Eq(copy _48, const 0_u64); - assert(!move _49, "attempt to calculate the remainder of `{}` with a divisor of zero", const 1_u64) -> [success: bb20, unwind continue]; -+ _49 = copy _29; -+ assert(!copy _29, "attempt to calculate the remainder of `{}` with a divisor of zero", const 1_u64) -> [success: bb20, unwind continue]; ++ _49 = const false; ++ assert(!const false, "attempt to calculate the remainder of `{}` with a divisor of zero", const 1_u64) -> [success: bb20, unwind continue]; } bb20: { diff --git a/tests/mir-opt/gvn.subexpression_elimination.GVN.panic-abort.diff b/tests/mir-opt/gvn.subexpression_elimination.GVN.panic-abort.diff index e872e011542b3..037c1a7f719d9 100644 --- a/tests/mir-opt/gvn.subexpression_elimination.GVN.panic-abort.diff +++ b/tests/mir-opt/gvn.subexpression_elimination.GVN.panic-abort.diff @@ -283,8 +283,8 @@ _24 = copy _2; - _25 = Eq(copy _24, const 0_u64); - assert(!move _25, "attempt to calculate the remainder of `{}` with a divisor of zero", copy _23) -> [success: bb6, unwind unreachable]; -+ _25 = copy _20; -+ assert(!copy _20, "attempt to calculate the remainder of `{}` with a divisor of zero", copy _1) -> [success: bb6, unwind unreachable]; ++ _25 = const false; ++ assert(!const false, "attempt to calculate the remainder of `{}` with a divisor of zero", copy _1) -> [success: bb6, unwind unreachable]; } bb6: { @@ -520,8 +520,8 @@ _74 = copy _2; - _75 = Eq(copy _74, const 0_u64); - assert(!move _75, "attempt to divide `{}` by zero", copy _73) -> [success: bb20, unwind unreachable]; -+ _75 = copy _20; -+ assert(!copy _20, "attempt to divide `{}` by zero", copy _1) -> [success: bb20, unwind unreachable]; ++ _75 = const false; ++ assert(!const false, "attempt to divide `{}` by zero", copy _1) -> [success: bb20, unwind unreachable]; } bb20: { @@ -544,8 +544,8 @@ _79 = copy _2; - _80 = Eq(copy _79, const 0_u64); - assert(!move _80, "attempt to calculate the remainder of `{}` with a divisor of zero", copy _78) -> [success: bb22, unwind unreachable]; -+ _80 = copy _20; -+ assert(!copy _20, "attempt to calculate the remainder of `{}` with a divisor of zero", copy _1) -> [success: bb22, unwind unreachable]; ++ _80 = const false; ++ assert(!const false, "attempt to calculate the remainder of `{}` with a divisor of zero", copy _1) -> [success: bb22, unwind unreachable]; } bb22: { diff --git a/tests/mir-opt/gvn.subexpression_elimination.GVN.panic-unwind.diff b/tests/mir-opt/gvn.subexpression_elimination.GVN.panic-unwind.diff index 3996dab27a343..b77d1acf756c2 100644 --- a/tests/mir-opt/gvn.subexpression_elimination.GVN.panic-unwind.diff +++ b/tests/mir-opt/gvn.subexpression_elimination.GVN.panic-unwind.diff @@ -283,8 +283,8 @@ _24 = copy _2; - _25 = Eq(copy _24, const 0_u64); - assert(!move _25, "attempt to calculate the remainder of `{}` with a divisor of zero", copy _23) -> [success: bb6, unwind continue]; -+ _25 = copy _20; -+ assert(!copy _20, "attempt to calculate the remainder of `{}` with a divisor of zero", copy _1) -> [success: bb6, unwind continue]; ++ _25 = const false; ++ assert(!const false, "attempt to calculate the remainder of `{}` with a divisor of zero", copy _1) -> [success: bb6, unwind continue]; } bb6: { @@ -520,8 +520,8 @@ _74 = copy _2; - _75 = Eq(copy _74, const 0_u64); - assert(!move _75, "attempt to divide `{}` by zero", copy _73) -> [success: bb20, unwind continue]; -+ _75 = copy _20; -+ assert(!copy _20, "attempt to divide `{}` by zero", copy _1) -> [success: bb20, unwind continue]; ++ _75 = const false; ++ assert(!const false, "attempt to divide `{}` by zero", copy _1) -> [success: bb20, unwind continue]; } bb20: { @@ -544,8 +544,8 @@ _79 = copy _2; - _80 = Eq(copy _79, const 0_u64); - assert(!move _80, "attempt to calculate the remainder of `{}` with a divisor of zero", copy _78) -> [success: bb22, unwind continue]; -+ _80 = copy _20; -+ assert(!copy _20, "attempt to calculate the remainder of `{}` with a divisor of zero", copy _1) -> [success: bb22, unwind continue]; ++ _80 = const false; ++ assert(!const false, "attempt to calculate the remainder of `{}` with a divisor of zero", copy _1) -> [success: bb22, unwind continue]; } bb22: { diff --git a/tests/mir-opt/gvn_range.on_assert.GVN.diff b/tests/mir-opt/gvn_range.on_assert.GVN.diff new file mode 100644 index 0000000000000..8ce960d0846b5 --- /dev/null +++ b/tests/mir-opt/gvn_range.on_assert.GVN.diff @@ -0,0 +1,79 @@ +- // MIR for `on_assert` before GVN ++ // MIR for `on_assert` after GVN + + fn on_assert(_1: usize, _2: &[u8]) -> u8 { + debug i => _1; + debug v => _2; + let mut _0: u8; + let _3: (); + let mut _4: bool; + let mut _5: usize; + let mut _6: usize; + let mut _7: &[u8]; + let mut _8: !; + let _9: usize; + let mut _10: usize; + let mut _11: bool; + scope 1 (inlined core::slice::::len) { + scope 2 (inlined std::ptr::metadata::<[u8]>) { + } + } + + bb0: { + StorageLive(_3); +- StorageLive(_4); ++ nop; + StorageLive(_5); + _5 = copy _1; +- StorageLive(_6); ++ nop; + StorageLive(_7); + _7 = &(*_2); +- _6 = PtrMetadata(copy _7); ++ _6 = PtrMetadata(copy _2); + StorageDead(_7); +- _4 = Lt(move _5, move _6); +- switchInt(move _4) -> [0: bb2, otherwise: bb1]; ++ _4 = Lt(copy _1, copy _6); ++ switchInt(copy _4) -> [0: bb2, otherwise: bb1]; + } + + bb1: { +- StorageDead(_6); ++ nop; + StorageDead(_5); + _3 = const (); +- StorageDead(_4); ++ nop; + StorageDead(_3); + StorageLive(_9); + _9 = copy _1; +- _10 = PtrMetadata(copy _2); +- _11 = Lt(copy _9, copy _10); +- assert(move _11, "index out of bounds: the length is {} but the index is {}", move _10, copy _9) -> [success: bb3, unwind unreachable]; ++ _10 = copy _6; ++ _11 = const true; ++ assert(const true, "index out of bounds: the length is {} but the index is {}", copy _6, copy _1) -> [success: bb3, unwind unreachable]; + } + + bb2: { +- StorageDead(_6); ++ nop; + StorageDead(_5); + StorageLive(_8); + _8 = panic(const "assertion failed: i < v.len()") -> unwind unreachable; + } + + bb3: { +- _0 = copy (*_2)[_9]; ++ _0 = copy (*_2)[_1]; + StorageDead(_9); + return; + } + } + + ALLOC0 (size: 29, align: 1) { + 0x00 │ 61 73 73 65 72 74 69 6f 6e 20 66 61 69 6c 65 64 │ assertion failed + 0x10 │ 3a 20 69 20 3c 20 76 2e 6c 65 6e 28 29 │ : i < v.len() + } + diff --git a/tests/mir-opt/gvn_range.on_if.GVN.diff b/tests/mir-opt/gvn_range.on_if.GVN.diff new file mode 100644 index 0000000000000..70e221453563c --- /dev/null +++ b/tests/mir-opt/gvn_range.on_if.GVN.diff @@ -0,0 +1,73 @@ +- // MIR for `on_if` before GVN ++ // MIR for `on_if` after GVN + + fn on_if(_1: usize, _2: &[u8]) -> u8 { + debug i => _1; + debug v => _2; + let mut _0: u8; + let mut _3: bool; + let mut _4: usize; + let mut _5: usize; + let mut _6: &[u8]; + let _7: usize; + let mut _8: usize; + let mut _9: bool; + scope 1 (inlined core::slice::::len) { + scope 2 (inlined std::ptr::metadata::<[u8]>) { + } + } + + bb0: { +- StorageLive(_3); ++ nop; + StorageLive(_4); + _4 = copy _1; +- StorageLive(_5); ++ nop; + StorageLive(_6); + _6 = &(*_2); +- _5 = PtrMetadata(copy _6); ++ _5 = PtrMetadata(copy _2); + StorageDead(_6); +- _3 = Lt(move _4, move _5); +- switchInt(move _3) -> [0: bb3, otherwise: bb1]; ++ _3 = Lt(copy _1, copy _5); ++ switchInt(copy _3) -> [0: bb3, otherwise: bb1]; + } + + bb1: { +- StorageDead(_5); ++ nop; + StorageDead(_4); + StorageLive(_7); + _7 = copy _1; +- _8 = PtrMetadata(copy _2); +- _9 = Lt(copy _7, copy _8); +- assert(move _9, "index out of bounds: the length is {} but the index is {}", move _8, copy _7) -> [success: bb2, unwind unreachable]; ++ _8 = copy _5; ++ _9 = const true; ++ assert(const true, "index out of bounds: the length is {} but the index is {}", copy _5, copy _1) -> [success: bb2, unwind unreachable]; + } + + bb2: { +- _0 = copy (*_2)[_7]; ++ _0 = copy (*_2)[_1]; + StorageDead(_7); + goto -> bb4; + } + + bb3: { +- StorageDead(_5); ++ nop; + StorageDead(_4); + _0 = const 0_u8; + goto -> bb4; + } + + bb4: { +- StorageDead(_3); ++ nop; + return; + } + } + diff --git a/tests/mir-opt/gvn_range.on_if_2.GVN.diff b/tests/mir-opt/gvn_range.on_if_2.GVN.diff new file mode 100644 index 0000000000000..fb36fc352d28b --- /dev/null +++ b/tests/mir-opt/gvn_range.on_if_2.GVN.diff @@ -0,0 +1,20 @@ +- // MIR for `on_if_2` before GVN ++ // MIR for `on_if_2` after GVN + + fn on_if_2(_1: bool) -> bool { + let mut _0: bool; + + bb0: { + switchInt(copy _1) -> [1: bb2, otherwise: bb1]; + } + + bb1: { + goto -> bb2; + } + + bb2: { + _0 = copy _1; + return; + } + } + diff --git a/tests/mir-opt/gvn_range.on_match.GVN.diff b/tests/mir-opt/gvn_range.on_match.GVN.diff new file mode 100644 index 0000000000000..0cfe08a49e13e --- /dev/null +++ b/tests/mir-opt/gvn_range.on_match.GVN.diff @@ -0,0 +1,33 @@ +- // MIR for `on_match` before GVN ++ // MIR for `on_match` after GVN + + fn on_match(_1: u8) -> u8 { + debug i => _1; + let mut _0: u8; + + bb0: { + switchInt(copy _1) -> [1: bb3, 2: bb2, otherwise: bb1]; + } + + bb1: { + _0 = const 0_u8; + goto -> bb4; + } + + bb2: { +- _0 = copy _1; ++ _0 = const 2_u8; + goto -> bb4; + } + + bb3: { +- _0 = copy _1; ++ _0 = const 1_u8; + goto -> bb4; + } + + bb4: { + return; + } + } + diff --git a/tests/mir-opt/gvn_range.on_match_2.GVN.diff b/tests/mir-opt/gvn_range.on_match_2.GVN.diff new file mode 100644 index 0000000000000..82d3d1ee4fbbf --- /dev/null +++ b/tests/mir-opt/gvn_range.on_match_2.GVN.diff @@ -0,0 +1,26 @@ +- // MIR for `on_match_2` before GVN ++ // MIR for `on_match_2` after GVN + + fn on_match_2(_1: u8) -> u8 { + debug i => _1; + let mut _0: u8; + + bb0: { + switchInt(copy _1) -> [1: bb2, 2: bb2, otherwise: bb1]; + } + + bb1: { + _0 = const 0_u8; + goto -> bb3; + } + + bb2: { + _0 = copy _1; + goto -> bb3; + } + + bb3: { + return; + } + } + diff --git a/tests/mir-opt/gvn_range.rs b/tests/mir-opt/gvn_range.rs new file mode 100644 index 0000000000000..49c3dd71c4ab4 --- /dev/null +++ b/tests/mir-opt/gvn_range.rs @@ -0,0 +1,70 @@ +//@ test-mir-pass: GVN +//@ compile-flags: -Zinline-mir --crate-type=lib -Cpanic=abort + +#![feature(custom_mir, core_intrinsics)] + +use std::intrinsics::mir::*; + +// EMIT_MIR gvn_range.on_if.GVN.diff +pub fn on_if(i: usize, v: &[u8]) -> u8 { + // CHECK-LABEL: fn on_if( + // CHECK: assert(const true + if i < v.len() { v[i] } else { 0 } +} + +// EMIT_MIR gvn_range.on_assert.GVN.diff +pub fn on_assert(i: usize, v: &[u8]) -> u8 { + // CHECK-LABEL: fn on_assert( + // CHECK: assert(const true + assert!(i < v.len()); + v[i] +} + +// EMIT_MIR gvn_range.on_match.GVN.diff +pub fn on_match(i: u8) -> u8 { + // CHECK-LABEL: fn on_match( + // CHECK: switchInt(copy _1) -> [1: [[BB_V1:bb.*]], 2: [[BB_V2:bb.*]], + // CHECK: [[BB_V2]]: { + // CHECK-NEXT: _0 = const 2_u8; + // CHECK: [[BB_V1]]: { + // CHECK-NEXT: _0 = const 1_u8; + match i { + 1 => i, + 2 => i, + _ => 0, + } +} + +// EMIT_MIR gvn_range.on_match_2.GVN.diff +pub fn on_match_2(i: u8) -> u8 { + // CHECK-LABEL: fn on_match_2( + // CHECK: switchInt(copy _1) -> [1: [[BB:bb.*]], 2: [[BB]], + // CHECK: [[BB]]: { + // CHECK-NEXT: _0 = copy _1; + match i { + 1 | 2 => i, + _ => 0, + } +} + +// EMIT_MIR gvn_range.on_if_2.GVN.diff +#[custom_mir(dialect = "runtime", phase = "post-cleanup")] +pub fn on_if_2(a: bool) -> bool { + // CHECK-LABEL: fn on_if_2( + // CHECK: _0 = copy _1; + mir! { + { + match a { + true => bb2, + _ => bb1 + } + } + bb1 = { + Goto(bb2) + } + bb2 = { + RET = a; + Return() + } + } +} diff --git a/tests/mir-opt/lower_array_len.array_bound.GVN.panic-abort.diff b/tests/mir-opt/lower_array_len.array_bound.GVN.panic-abort.diff index 98c5e868046b5..e10b1601b1cbb 100644 --- a/tests/mir-opt/lower_array_len.array_bound.GVN.panic-abort.diff +++ b/tests/mir-opt/lower_array_len.array_bound.GVN.panic-abort.diff @@ -44,8 +44,8 @@ _8 = copy _1; - _9 = Lt(copy _8, const N); - assert(move _9, "index out of bounds: the length is {} but the index is {}", const N, copy _8) -> [success: bb3, unwind unreachable]; -+ _9 = copy _3; -+ assert(copy _3, "index out of bounds: the length is {} but the index is {}", const N, copy _1) -> [success: bb3, unwind unreachable]; ++ _9 = const true; ++ assert(const true, "index out of bounds: the length is {} but the index is {}", const N, copy _1) -> [success: bb3, unwind unreachable]; } bb3: { diff --git a/tests/mir-opt/lower_array_len.array_bound.GVN.panic-unwind.diff b/tests/mir-opt/lower_array_len.array_bound.GVN.panic-unwind.diff index 72c7313786996..b3e7a22e279bf 100644 --- a/tests/mir-opt/lower_array_len.array_bound.GVN.panic-unwind.diff +++ b/tests/mir-opt/lower_array_len.array_bound.GVN.panic-unwind.diff @@ -44,8 +44,8 @@ _8 = copy _1; - _9 = Lt(copy _8, const N); - assert(move _9, "index out of bounds: the length is {} but the index is {}", const N, copy _8) -> [success: bb3, unwind continue]; -+ _9 = copy _3; -+ assert(copy _3, "index out of bounds: the length is {} but the index is {}", const N, copy _1) -> [success: bb3, unwind continue]; ++ _9 = const true; ++ assert(const true, "index out of bounds: the length is {} but the index is {}", const N, copy _1) -> [success: bb3, unwind continue]; } bb3: { diff --git a/tests/mir-opt/lower_array_len.array_bound_mut.GVN.panic-abort.diff b/tests/mir-opt/lower_array_len.array_bound_mut.GVN.panic-abort.diff index 9ffaf44c02bd2..aaffcbf5967f3 100644 --- a/tests/mir-opt/lower_array_len.array_bound_mut.GVN.panic-abort.diff +++ b/tests/mir-opt/lower_array_len.array_bound_mut.GVN.panic-abort.diff @@ -46,8 +46,8 @@ _8 = copy _1; - _9 = Lt(copy _8, const N); - assert(move _9, "index out of bounds: the length is {} but the index is {}", const N, copy _8) -> [success: bb3, unwind unreachable]; -+ _9 = copy _3; -+ assert(copy _3, "index out of bounds: the length is {} but the index is {}", const N, copy _1) -> [success: bb3, unwind unreachable]; ++ _9 = const true; ++ assert(const true, "index out of bounds: the length is {} but the index is {}", const N, copy _1) -> [success: bb3, unwind unreachable]; } bb3: { diff --git a/tests/mir-opt/lower_array_len.array_bound_mut.GVN.panic-unwind.diff b/tests/mir-opt/lower_array_len.array_bound_mut.GVN.panic-unwind.diff index 08008e463357f..691414a612536 100644 --- a/tests/mir-opt/lower_array_len.array_bound_mut.GVN.panic-unwind.diff +++ b/tests/mir-opt/lower_array_len.array_bound_mut.GVN.panic-unwind.diff @@ -46,8 +46,8 @@ _8 = copy _1; - _9 = Lt(copy _8, const N); - assert(move _9, "index out of bounds: the length is {} but the index is {}", const N, copy _8) -> [success: bb3, unwind continue]; -+ _9 = copy _3; -+ assert(copy _3, "index out of bounds: the length is {} but the index is {}", const N, copy _1) -> [success: bb3, unwind continue]; ++ _9 = const true; ++ assert(const true, "index out of bounds: the length is {} but the index is {}", const N, copy _1) -> [success: bb3, unwind continue]; } bb3: { diff --git a/tests/mir-opt/pre-codegen/two_unwrap_unchecked.two_unwrap_unchecked.GVN.diff b/tests/mir-opt/pre-codegen/two_unwrap_unchecked.two_unwrap_unchecked.GVN.diff index 5b063e6762e07..afa081ca25299 100644 --- a/tests/mir-opt/pre-codegen/two_unwrap_unchecked.two_unwrap_unchecked.GVN.diff +++ b/tests/mir-opt/pre-codegen/two_unwrap_unchecked.two_unwrap_unchecked.GVN.diff @@ -74,8 +74,8 @@ StorageLive(_9); - _9 = discriminant(_5); - switchInt(move _9) -> [0: bb4, 1: bb5, otherwise: bb1]; -+ _9 = copy _8; -+ switchInt(copy _8) -> [0: bb4, 1: bb5, otherwise: bb1]; ++ _9 = const 1_isize; ++ switchInt(const 1_isize) -> [0: bb4, 1: bb5, otherwise: bb1]; } bb4: {