diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index abf89e550b1..43d5b445258 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -549,13 +549,13 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { &[ &reveal_all::RevealAll, // has to be done before inlining, since inlined code is in RevealAll mode. &lower_slice_len::LowerSliceLenCalls, // has to be done before inlining, otherwise actual call will be almost always inlined. Also simple, so can just do first - &normalize_array_len::NormalizeArrayLen, // has to run after `slice::len` lowering &unreachable_prop::UnreachablePropagation, &uninhabited_enum_branching::UninhabitedEnumBranching, &o1(simplify::SimplifyCfg::new("after-uninhabited-enum-branching")), &inline::Inline, &remove_storage_markers::RemoveStorageMarkers, &remove_zsts::RemoveZsts, + &normalize_array_len::NormalizeArrayLen, // has to run after `slice::len` lowering &const_goto::ConstGoto, &remove_unneeded_drops::RemoveUnneededDrops, &sroa::ScalarReplacementOfAggregates, diff --git a/compiler/rustc_mir_transform/src/normalize_array_len.rs b/compiler/rustc_mir_transform/src/normalize_array_len.rs index 1708b287e56..8894e729502 100644 --- a/compiler/rustc_mir_transform/src/normalize_array_len.rs +++ b/compiler/rustc_mir_transform/src/normalize_array_len.rs @@ -1,288 +1,105 @@ //! This pass eliminates casting of arrays into slices when their length //! is taken using `.len()` method. Handy to preserve information in MIR for const prop +use crate::ssa::SsaLocals; use crate::MirPass; -use rustc_data_structures::fx::FxIndexMap; -use rustc_data_structures::intern::Interned; -use rustc_index::bit_set::BitSet; use rustc_index::vec::IndexVec; +use rustc_middle::mir::visit::*; use rustc_middle::mir::*; -use rustc_middle::ty::{self, ReErased, Region, TyCtxt}; - -const MAX_NUM_BLOCKS: usize = 800; -const MAX_NUM_LOCALS: usize = 3000; +use rustc_middle::ty::{self, TyCtxt}; +use rustc_mir_dataflow::impls::borrowed_locals; pub struct NormalizeArrayLen; impl<'tcx> MirPass<'tcx> for NormalizeArrayLen { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { // See #105929 - sess.mir_opt_level() >= 4 && sess.opts.unstable_opts.unsound_mir_opts + sess.mir_opt_level() >= 3 } + #[instrument(level = "trace", skip(self, tcx, body))] fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - // early returns for edge cases of highly unrolled functions - if body.basic_blocks.len() > MAX_NUM_BLOCKS { - return; - } - if body.local_decls.len() > MAX_NUM_LOCALS { - return; - } + debug!(def_id = ?body.source.def_id()); normalize_array_len_calls(tcx, body) } } -pub fn normalize_array_len_calls<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - // We don't ever touch terminators, so no need to invalidate the CFG cache - let basic_blocks = body.basic_blocks.as_mut_preserves_cfg(); - let local_decls = &mut body.local_decls; +fn normalize_array_len_calls<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + let param_env = tcx.param_env_reveal_all_normalized(body.source.def_id()); + let borrowed_locals = borrowed_locals(body); + let ssa = SsaLocals::new(tcx, param_env, body, &borrowed_locals); - // do a preliminary analysis to see if we ever have locals of type `[T;N]` or `&[T;N]` - let mut interesting_locals = BitSet::new_empty(local_decls.len()); - for (local, decl) in local_decls.iter_enumerated() { - match decl.ty.kind() { - ty::Array(..) => { - interesting_locals.insert(local); - } - ty::Ref(.., ty, Mutability::Not) => match ty.kind() { - ty::Array(..) => { - interesting_locals.insert(local); - } - _ => {} - }, - _ => {} - } - } - if interesting_locals.is_empty() { - // we have found nothing to analyze - return; - } - let num_intesting_locals = interesting_locals.count(); - let mut state = FxIndexMap::with_capacity_and_hasher(num_intesting_locals, Default::default()); - let mut patches_scratchpad = - FxIndexMap::with_capacity_and_hasher(num_intesting_locals, Default::default()); - let mut replacements_scratchpad = - FxIndexMap::with_capacity_and_hasher(num_intesting_locals, Default::default()); - for block in basic_blocks { - // make length calls for arrays [T; N] not to decay into length calls for &[T] - // that forbids constant propagation - normalize_array_len_call( - tcx, - block, - local_decls, - &interesting_locals, - &mut state, - &mut patches_scratchpad, - &mut replacements_scratchpad, - ); - state.clear(); - patches_scratchpad.clear(); - replacements_scratchpad.clear(); - } + let slice_lengths = compute_slice_length(tcx, &ssa, body); + debug!(?slice_lengths); + + Replacer { tcx, slice_lengths }.visit_body_preserves_cfg(body); } -struct Patcher<'a, 'tcx> { +fn compute_slice_length<'tcx>( tcx: TyCtxt<'tcx>, - patches_scratchpad: &'a FxIndexMap, - replacements_scratchpad: &'a mut FxIndexMap, - local_decls: &'a mut IndexVec>, - statement_idx: usize, -} + ssa: &SsaLocals, + body: &Body<'tcx>, +) -> IndexVec>> { + let mut slice_lengths = IndexVec::from_elem(None, &body.local_decls); -impl<'tcx> Patcher<'_, 'tcx> { - fn patch_expand_statement( - &mut self, - statement: &mut Statement<'tcx>, - ) -> Option>> { - let idx = self.statement_idx; - if let Some(len_statemnt_idx) = self.patches_scratchpad.get(&idx).copied() { - let mut statements = Vec::with_capacity(2); - - // we are at statement that performs a cast. The only sound way is - // to create another local that performs a similar copy without a cast and then - // use this copy in the Len operation - - match &statement.kind { - StatementKind::Assign(box ( - .., - Rvalue::Cast( - CastKind::Pointer(ty::adjustment::PointerCast::Unsize), - operand, - _, - ), - )) => { - match operand { - Operand::Copy(place) | Operand::Move(place) => { - // create new local - let ty = operand.ty(self.local_decls, self.tcx); - let local_decl = LocalDecl::with_source_info(ty, statement.source_info); - let local = self.local_decls.push(local_decl); - // make it live - let mut make_live_statement = statement.clone(); - make_live_statement.kind = StatementKind::StorageLive(local); - statements.push(make_live_statement); - // copy into it - - let operand = Operand::Copy(*place); - let mut make_copy_statement = statement.clone(); - let assign_to = Place::from(local); - let rvalue = Rvalue::Use(operand); - make_copy_statement.kind = - StatementKind::Assign(Box::new((assign_to, rvalue))); - statements.push(make_copy_statement); - - // to reorder we have to copy and make NOP - statements.push(statement.clone()); - statement.make_nop(); - - self.replacements_scratchpad.insert(len_statemnt_idx, local); - } - _ => { - unreachable!("it's a bug in the implementation") - } - } - } - _ => { - unreachable!("it's a bug in the implementation") + for (local, rvalue) in ssa.assignments(body) { + match rvalue { + Rvalue::Cast( + CastKind::Pointer(ty::adjustment::PointerCast::Unsize), + operand, + cast_ty, + ) => { + let operand_ty = operand.ty(body, tcx); + debug!(?operand_ty); + if let Some(operand_ty) = operand_ty.builtin_deref(true) + && let ty::Array(_, len) = operand_ty.ty.kind() + && let Some(cast_ty) = cast_ty.builtin_deref(true) + && let ty::Slice(..) = cast_ty.ty.kind() + { + slice_lengths[local] = Some(*len); } } - - self.statement_idx += 1; - - Some(statements.into_iter()) - } else if let Some(local) = self.replacements_scratchpad.get(&idx).copied() { - let mut statements = Vec::with_capacity(2); - - match &statement.kind { - StatementKind::Assign(box (into, Rvalue::Len(place))) => { - let add_deref = if let Some(..) = place.as_local() { - false - } else if let Some(..) = place.local_or_deref_local() { - true - } else { - unreachable!("it's a bug in the implementation") - }; - // replace len statement - let mut len_statement = statement.clone(); - let mut place = Place::from(local); - if add_deref { - place = self.tcx.mk_place_deref(place); - } - len_statement.kind = - StatementKind::Assign(Box::new((*into, Rvalue::Len(place)))); - statements.push(len_statement); - - // make temporary dead - let mut make_dead_statement = statement.clone(); - make_dead_statement.kind = StatementKind::StorageDead(local); - statements.push(make_dead_statement); - - // make original statement NOP - statement.make_nop(); - } - _ => { - unreachable!("it's a bug in the implementation") + // The length information is stored in the fat pointer, so we treat `operand` as a value. + Rvalue::Use(operand) => { + if let Some(rhs) = operand.place() && let Some(rhs) = rhs.as_local() { + slice_lengths[local] = slice_lengths[rhs]; } } - - self.statement_idx += 1; - - Some(statements.into_iter()) - } else { - self.statement_idx += 1; - None - } - } -} - -fn normalize_array_len_call<'tcx>( - tcx: TyCtxt<'tcx>, - block: &mut BasicBlockData<'tcx>, - local_decls: &mut IndexVec>, - interesting_locals: &BitSet, - state: &mut FxIndexMap, - patches_scratchpad: &mut FxIndexMap, - replacements_scratchpad: &mut FxIndexMap, -) { - for (statement_idx, statement) in block.statements.iter_mut().enumerate() { - match &mut statement.kind { - StatementKind::Assign(box (place, rvalue)) => { - match rvalue { - Rvalue::Cast( - CastKind::Pointer(ty::adjustment::PointerCast::Unsize), - operand, - cast_ty, - ) => { - let Some(local) = place.as_local() else { return }; - match operand { - Operand::Copy(place) | Operand::Move(place) => { - let Some(operand_local) = place.local_or_deref_local() else { return; }; - if !interesting_locals.contains(operand_local) { - return; - } - let operand_ty = local_decls[operand_local].ty; - match (operand_ty.kind(), cast_ty.kind()) { - (ty::Array(of_ty_src, ..), ty::Slice(of_ty_dst)) => { - if of_ty_src == of_ty_dst { - // this is a cast from [T; N] into [T], so we are good - state.insert(local, statement_idx); - } - } - // current way of patching doesn't allow to work with `mut` - ( - ty::Ref( - Region(Interned(ReErased, _)), - operand_ty, - Mutability::Not, - ), - ty::Ref( - Region(Interned(ReErased, _)), - cast_ty, - Mutability::Not, - ), - ) => { - match (operand_ty.kind(), cast_ty.kind()) { - // current way of patching doesn't allow to work with `mut` - (ty::Array(of_ty_src, ..), ty::Slice(of_ty_dst)) => { - if of_ty_src == of_ty_dst { - // this is a cast from [T; N] into [T], so we are good - state.insert(local, statement_idx); - } - } - _ => {} - } - } - _ => {} - } - } - _ => {} - } - } - Rvalue::Len(place) => { - let Some(local) = place.local_or_deref_local() else { - return; - }; - if let Some(cast_statement_idx) = state.get(&local).copied() { - patches_scratchpad.insert(cast_statement_idx, statement_idx); - } - } - _ => { - // invalidate - state.remove(&place.local); - } + // The length information is stored in the fat pointer. + // Reborrowing copies length information from one pointer to the other. + Rvalue::Ref(_, _, rhs) | Rvalue::AddressOf(_, rhs) => { + if let [PlaceElem::Deref] = rhs.projection[..] { + slice_lengths[local] = slice_lengths[rhs.local]; } } _ => {} } } - let mut patcher = Patcher { - tcx, - patches_scratchpad: &*patches_scratchpad, - replacements_scratchpad, - local_decls, - statement_idx: 0, - }; - - block.expand_statements(|st| patcher.patch_expand_statement(st)); + slice_lengths +} + +struct Replacer<'tcx> { + tcx: TyCtxt<'tcx>, + slice_lengths: IndexVec>>, +} + +impl<'tcx> MutVisitor<'tcx> for Replacer<'tcx> { + fn tcx(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn visit_rvalue(&mut self, rvalue: &mut Rvalue<'tcx>, loc: Location) { + if let Rvalue::Len(place) = rvalue + && let [PlaceElem::Deref] = &place.projection[..] + && let Some(len) = self.slice_lengths[place.local] + { + *rvalue = Rvalue::Use(Operand::Constant(Box::new(Constant { + span: rustc_span::DUMMY_SP, + user_ty: None, + literal: ConstantKind::from_const(len, self.tcx), + }))); + } + self.super_rvalue(rvalue, loc); + } } diff --git a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.32bit.diff b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.32bit.diff index 4bd0aa09872..38d402b8f21 100644 --- a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.32bit.diff +++ b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.32bit.diff @@ -33,11 +33,11 @@ StorageLive(_4); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:+3:13: +3:15 StorageLive(_5); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:+3:23: +3:24 _5 = const 3_usize; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:+3:23: +3:24 - _6 = Len((*_1)); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:+3:18: +3:25 + _6 = const 3_usize; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:+3:18: +3:25 - _7 = Lt(_5, _6); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:+3:18: +3:25 - assert(move _7, "index out of bounds: the length is {} but the index is {}", move _6, _5) -> bb1; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:+3:18: +3:25 -+ _7 = Lt(const 3_usize, _6); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:+3:18: +3:25 -+ assert(move _7, "index out of bounds: the length is {} but the index is {}", move _6, const 3_usize) -> bb1; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:+3:18: +3:25 ++ _7 = const false; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:+3:18: +3:25 ++ assert(const false, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 3_usize) -> bb1; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:+3:18: +3:25 } bb1: { diff --git a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.64bit.diff b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.64bit.diff index 4bd0aa09872..38d402b8f21 100644 --- a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.64bit.diff +++ b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.64bit.diff @@ -33,11 +33,11 @@ StorageLive(_4); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:+3:13: +3:15 StorageLive(_5); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:+3:23: +3:24 _5 = const 3_usize; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:+3:23: +3:24 - _6 = Len((*_1)); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:+3:18: +3:25 + _6 = const 3_usize; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:+3:18: +3:25 - _7 = Lt(_5, _6); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:+3:18: +3:25 - assert(move _7, "index out of bounds: the length is {} but the index is {}", move _6, _5) -> bb1; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:+3:18: +3:25 -+ _7 = Lt(const 3_usize, _6); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:+3:18: +3:25 -+ assert(move _7, "index out of bounds: the length is {} but the index is {}", move _6, const 3_usize) -> bb1; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:+3:18: +3:25 ++ _7 = const false; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:+3:18: +3:25 ++ assert(const false, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 3_usize) -> bb1; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:+3:18: +3:25 } bb1: { diff --git a/tests/mir-opt/issue_76432.rs b/tests/mir-opt/issue_76432.rs index c8b405ca8ea..fbbfd4ceb11 100644 --- a/tests/mir-opt/issue_76432.rs +++ b/tests/mir-opt/issue_76432.rs @@ -1,3 +1,4 @@ +// compile-flags: -Zmir-enable-passes=-NormalizeArrayLen // Check that we do not insert StorageDead at each target if StorageDead was never seen // EMIT_MIR issue_76432.test.SimplifyComparisonIntegral.diff diff --git a/tests/mir-opt/lower_array_len.array_bound.NormalizeArrayLen.diff b/tests/mir-opt/lower_array_len.array_bound.NormalizeArrayLen.diff index 9bc7060e958..59de067f4a4 100644 --- a/tests/mir-opt/lower_array_len.array_bound.NormalizeArrayLen.diff +++ b/tests/mir-opt/lower_array_len.array_bound.NormalizeArrayLen.diff @@ -13,7 +13,6 @@ let _8: usize; // in scope 0 at $DIR/lower_array_len.rs:+2:15: +2:20 let mut _9: usize; // in scope 0 at $DIR/lower_array_len.rs:+2:9: +2:21 let mut _10: bool; // in scope 0 at $DIR/lower_array_len.rs:+2:9: +2:21 -+ let mut _11: &[u8; N]; // in scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27 bb0: { StorageLive(_3); // scope 0 at $DIR/lower_array_len.rs:+1:8: +1:27 @@ -23,13 +22,10 @@ StorageLive(_6); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27 StorageLive(_7); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27 _7 = &(*_2); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27 -+ StorageLive(_11); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27 -+ _11 = _7; // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27 _6 = move _7 as &[u8] (Pointer(Unsize)); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27 StorageDead(_7); // scope 0 at $DIR/lower_array_len.rs:+1:20: +1:21 - _5 = Len((*_6)); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27 -+ _5 = Len((*_11)); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27 -+ StorageDead(_11); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27 ++ _5 = const N; // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27 goto -> bb1; // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27 } diff --git a/tests/mir-opt/lower_array_len.array_bound_mut.NormalizeArrayLen.diff b/tests/mir-opt/lower_array_len.array_bound_mut.NormalizeArrayLen.diff index cf427cfd1e6..17574b1b635 100644 --- a/tests/mir-opt/lower_array_len.array_bound_mut.NormalizeArrayLen.diff +++ b/tests/mir-opt/lower_array_len.array_bound_mut.NormalizeArrayLen.diff @@ -16,7 +16,6 @@ let _11: usize; // in scope 0 at $DIR/lower_array_len.rs:+4:15: +4:16 let mut _12: usize; // in scope 0 at $DIR/lower_array_len.rs:+4:9: +4:17 let mut _13: bool; // in scope 0 at $DIR/lower_array_len.rs:+4:9: +4:17 -+ let mut _14: &[u8; N]; // in scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27 bb0: { StorageLive(_3); // scope 0 at $DIR/lower_array_len.rs:+1:8: +1:27 @@ -26,13 +25,10 @@ StorageLive(_6); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27 StorageLive(_7); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27 _7 = &(*_2); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27 -+ StorageLive(_14); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27 -+ _14 = _7; // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27 _6 = move _7 as &[u8] (Pointer(Unsize)); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27 StorageDead(_7); // scope 0 at $DIR/lower_array_len.rs:+1:20: +1:21 - _5 = Len((*_6)); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27 -+ _5 = Len((*_14)); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27 -+ StorageDead(_14); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27 ++ _5 = const N; // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27 goto -> bb1; // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27 } diff --git a/tests/mir-opt/lower_array_len.array_len.NormalizeArrayLen.diff b/tests/mir-opt/lower_array_len.array_len.NormalizeArrayLen.diff index 3ed68f5f725..66feff62f42 100644 --- a/tests/mir-opt/lower_array_len.array_len.NormalizeArrayLen.diff +++ b/tests/mir-opt/lower_array_len.array_len.NormalizeArrayLen.diff @@ -6,19 +6,15 @@ let mut _0: usize; // return place in scope 0 at $DIR/lower_array_len.rs:+0:52: +0:57 let mut _2: &[u8]; // in scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14 let mut _3: &[u8; N]; // in scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14 -+ let mut _4: &[u8; N]; // in scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14 bb0: { StorageLive(_2); // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14 StorageLive(_3); // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14 _3 = &(*_1); // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14 -+ StorageLive(_4); // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14 -+ _4 = _3; // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14 _2 = move _3 as &[u8] (Pointer(Unsize)); // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14 StorageDead(_3); // scope 0 at $DIR/lower_array_len.rs:+1:7: +1:8 - _0 = Len((*_2)); // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14 -+ _0 = Len((*_4)); // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14 -+ StorageDead(_4); // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14 ++ _0 = const N; // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14 goto -> bb1; // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14 } diff --git a/tests/mir-opt/lower_array_len.array_len_by_value.NormalizeArrayLen.diff b/tests/mir-opt/lower_array_len.array_len_by_value.NormalizeArrayLen.diff index f0e0cdcfdc0..c0a277edc46 100644 --- a/tests/mir-opt/lower_array_len.array_len_by_value.NormalizeArrayLen.diff +++ b/tests/mir-opt/lower_array_len.array_len_by_value.NormalizeArrayLen.diff @@ -6,19 +6,15 @@ let mut _0: usize; // return place in scope 0 at $DIR/lower_array_len.rs:+0:60: +0:65 let mut _2: &[u8]; // in scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14 let mut _3: &[u8; N]; // in scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14 -+ let mut _4: &[u8; N]; // in scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14 bb0: { StorageLive(_2); // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14 StorageLive(_3); // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14 _3 = &_1; // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14 -+ StorageLive(_4); // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14 -+ _4 = _3; // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14 _2 = move _3 as &[u8] (Pointer(Unsize)); // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14 StorageDead(_3); // scope 0 at $DIR/lower_array_len.rs:+1:7: +1:8 - _0 = Len((*_2)); // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14 -+ _0 = Len((*_4)); // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14 -+ StorageDead(_4); // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14 ++ _0 = const N; // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14 goto -> bb1; // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14 } diff --git a/tests/mir-opt/lower_array_len.array_len_raw.NormalizeArrayLen.diff b/tests/mir-opt/lower_array_len.array_len_raw.NormalizeArrayLen.diff new file mode 100644 index 00000000000..8b35fd57fa0 --- /dev/null +++ b/tests/mir-opt/lower_array_len.array_len_raw.NormalizeArrayLen.diff @@ -0,0 +1,50 @@ +- // MIR for `array_len_raw` before NormalizeArrayLen ++ // MIR for `array_len_raw` after NormalizeArrayLen + + fn array_len_raw(_1: [u8; N]) -> usize { + debug arr => _1; // in scope 0 at $DIR/lower_array_len.rs:+0:38: +0:41 + let mut _0: usize; // return place in scope 0 at $DIR/lower_array_len.rs:+0:55: +0:60 + let _2: &[u8]; // in scope 0 at $DIR/lower_array_len.rs:+1:9: +1:12 + let mut _3: &[u8; N]; // in scope 0 at $DIR/lower_array_len.rs:+1:21: +1:25 + let _4: &[u8; N]; // in scope 0 at $DIR/lower_array_len.rs:+1:21: +1:25 + let mut _6: &[u8]; // in scope 0 at $DIR/lower_array_len.rs:+3:5: +3:27 + let _7: &[u8]; // in scope 0 at $DIR/lower_array_len.rs:+3:14: +3:19 + scope 1 { + debug arr => _2; // in scope 1 at $DIR/lower_array_len.rs:+1:9: +1:12 + let _5: *const [u8]; // in scope 1 at $DIR/lower_array_len.rs:+2:9: +2:12 + scope 2 { + debug arr => _5; // in scope 2 at $DIR/lower_array_len.rs:+2:9: +2:12 + scope 3 { + } + } + } + + bb0: { + StorageLive(_2); // scope 0 at $DIR/lower_array_len.rs:+1:9: +1:12 + StorageLive(_3); // scope 0 at $DIR/lower_array_len.rs:+1:21: +1:25 + StorageLive(_4); // scope 0 at $DIR/lower_array_len.rs:+1:21: +1:25 + _4 = &_1; // scope 0 at $DIR/lower_array_len.rs:+1:21: +1:25 + _3 = &(*_4); // scope 0 at $DIR/lower_array_len.rs:+1:21: +1:25 + _2 = move _3 as &[u8] (Pointer(Unsize)); // scope 0 at $DIR/lower_array_len.rs:+1:21: +1:25 + StorageDead(_3); // scope 0 at $DIR/lower_array_len.rs:+1:24: +1:25 + StorageDead(_4); // scope 0 at $DIR/lower_array_len.rs:+1:25: +1:26 + StorageLive(_5); // scope 1 at $DIR/lower_array_len.rs:+2:9: +2:12 + _5 = &raw const (*_2); // scope 1 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + StorageLive(_6); // scope 2 at $DIR/lower_array_len.rs:+3:5: +3:27 + StorageLive(_7); // scope 2 at $DIR/lower_array_len.rs:+3:14: +3:19 + _7 = &(*_5); // scope 3 at $DIR/lower_array_len.rs:+3:14: +3:19 + _6 = &(*_7); // scope 2 at $DIR/lower_array_len.rs:+3:5: +3:27 +- _0 = Len((*_6)); // scope 2 at $DIR/lower_array_len.rs:+3:5: +3:27 ++ _0 = const N; // scope 2 at $DIR/lower_array_len.rs:+3:5: +3:27 + goto -> bb1; // scope 2 at $DIR/lower_array_len.rs:+3:5: +3:27 + } + + bb1: { + StorageDead(_6); // scope 2 at $DIR/lower_array_len.rs:+3:26: +3:27 + StorageDead(_5); // scope 1 at $DIR/lower_array_len.rs:+4:1: +4:2 + StorageDead(_2); // scope 0 at $DIR/lower_array_len.rs:+4:1: +4:2 + StorageDead(_7); // scope 0 at $DIR/lower_array_len.rs:+4:1: +4:2 + return; // scope 0 at $DIR/lower_array_len.rs:+4:2: +4:2 + } + } + diff --git a/tests/mir-opt/lower_array_len.array_len_reborrow.NormalizeArrayLen.diff b/tests/mir-opt/lower_array_len.array_len_reborrow.NormalizeArrayLen.diff new file mode 100644 index 00000000000..8bdd2ede6bc --- /dev/null +++ b/tests/mir-opt/lower_array_len.array_len_reborrow.NormalizeArrayLen.diff @@ -0,0 +1,44 @@ +- // MIR for `array_len_reborrow` before NormalizeArrayLen ++ // MIR for `array_len_reborrow` after NormalizeArrayLen + + fn array_len_reborrow(_1: [u8; N]) -> usize { + debug arr => _1; // in scope 0 at $DIR/lower_array_len.rs:+0:43: +0:50 + let mut _0: usize; // return place in scope 0 at $DIR/lower_array_len.rs:+0:64: +0:69 + let _2: &mut [u8]; // in scope 0 at $DIR/lower_array_len.rs:+1:9: +1:12 + let mut _3: &mut [u8; N]; // in scope 0 at $DIR/lower_array_len.rs:+1:25: +1:33 + let mut _4: &mut [u8; N]; // in scope 0 at $DIR/lower_array_len.rs:+1:25: +1:33 + let mut _6: &[u8]; // in scope 0 at $DIR/lower_array_len.rs:+3:5: +3:14 + scope 1 { + debug arr => _2; // in scope 1 at $DIR/lower_array_len.rs:+1:9: +1:12 + let _5: &[u8]; // in scope 1 at $DIR/lower_array_len.rs:+2:9: +2:12 + scope 2 { + debug arr => _5; // in scope 2 at $DIR/lower_array_len.rs:+2:9: +2:12 + } + } + + bb0: { + StorageLive(_2); // scope 0 at $DIR/lower_array_len.rs:+1:9: +1:12 + StorageLive(_3); // scope 0 at $DIR/lower_array_len.rs:+1:25: +1:33 + StorageLive(_4); // scope 0 at $DIR/lower_array_len.rs:+1:25: +1:33 + _4 = &mut _1; // scope 0 at $DIR/lower_array_len.rs:+1:25: +1:33 + _3 = &mut (*_4); // scope 0 at $DIR/lower_array_len.rs:+1:25: +1:33 + _2 = move _3 as &mut [u8] (Pointer(Unsize)); // scope 0 at $DIR/lower_array_len.rs:+1:25: +1:33 + StorageDead(_3); // scope 0 at $DIR/lower_array_len.rs:+1:32: +1:33 + StorageDead(_4); // scope 0 at $DIR/lower_array_len.rs:+1:33: +1:34 + StorageLive(_5); // scope 1 at $DIR/lower_array_len.rs:+2:9: +2:12 + _5 = &(*_2); // scope 1 at $DIR/lower_array_len.rs:+2:15: +2:20 + StorageLive(_6); // scope 2 at $DIR/lower_array_len.rs:+3:5: +3:14 + _6 = &(*_5); // scope 2 at $DIR/lower_array_len.rs:+3:5: +3:14 +- _0 = Len((*_6)); // scope 2 at $DIR/lower_array_len.rs:+3:5: +3:14 ++ _0 = const N; // scope 2 at $DIR/lower_array_len.rs:+3:5: +3:14 + goto -> bb1; // scope 2 at $DIR/lower_array_len.rs:+3:5: +3:14 + } + + bb1: { + StorageDead(_6); // scope 2 at $DIR/lower_array_len.rs:+3:13: +3:14 + StorageDead(_5); // scope 1 at $DIR/lower_array_len.rs:+4:1: +4:2 + StorageDead(_2); // scope 0 at $DIR/lower_array_len.rs:+4:1: +4:2 + return; // scope 0 at $DIR/lower_array_len.rs:+4:2: +4:2 + } + } + diff --git a/tests/mir-opt/lower_array_len.rs b/tests/mir-opt/lower_array_len.rs index ea0224b21d7..972d46cb8e2 100644 --- a/tests/mir-opt/lower_array_len.rs +++ b/tests/mir-opt/lower_array_len.rs @@ -31,10 +31,26 @@ pub fn array_len_by_value(arr: [u8; N]) -> usize { arr.len() } +// EMIT_MIR lower_array_len.array_len_reborrow.NormalizeArrayLen.diff +pub fn array_len_reborrow(mut arr: [u8; N]) -> usize { + let arr: &mut [_] = &mut arr; + let arr = &*arr; + arr.len() +} + +// EMIT_MIR lower_array_len.array_len_raw.NormalizeArrayLen.diff +pub fn array_len_raw(arr: [u8; N]) -> usize { + let arr: &[_] = &arr; + let arr = std::ptr::addr_of!(*arr); + unsafe { &*arr }.len() +} + fn main() { let _ = array_bound(3, &[0, 1, 2, 3]); let mut tmp = [0, 1, 2, 3, 4]; let _ = array_bound_mut(3, &mut [0, 1, 2, 3]); let _ = array_len(&[0]); let _ = array_len_by_value([0, 2]); + let _ = array_len_reborrow([0, 2]); + let _ = array_len_raw([0, 2]); } diff --git a/tests/mir-opt/lower_array_len_e2e.array_bound.PreCodegen.after.mir b/tests/mir-opt/lower_array_len_e2e.array_bound.PreCodegen.after.mir deleted file mode 100644 index dee1d538395..00000000000 --- a/tests/mir-opt/lower_array_len_e2e.array_bound.PreCodegen.after.mir +++ /dev/null @@ -1,41 +0,0 @@ -// MIR for `array_bound` after PreCodegen - -fn array_bound(_1: usize, _2: &[u8; N]) -> u8 { - debug index => _1; // in scope 0 at $DIR/lower_array_len_e2e.rs:+0:36: +0:41 - debug slice => _2; // in scope 0 at $DIR/lower_array_len_e2e.rs:+0:50: +0:55 - let mut _0: u8; // return place in scope 0 at $DIR/lower_array_len_e2e.rs:+0:70: +0:72 - let mut _3: bool; // in scope 0 at $DIR/lower_array_len_e2e.rs:+1:8: +1:27 - let mut _4: usize; // in scope 0 at $DIR/lower_array_len_e2e.rs:+1:16: +1:27 - let mut _5: usize; // in scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21 - let mut _6: bool; // in scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21 - - bb0: { - StorageLive(_3); // scope 0 at $DIR/lower_array_len_e2e.rs:+1:8: +1:27 - StorageLive(_4); // scope 0 at $DIR/lower_array_len_e2e.rs:+1:16: +1:27 - _4 = const N; // scope 0 at $DIR/lower_array_len_e2e.rs:+1:16: +1:27 - _3 = Lt(_1, move _4); // scope 0 at $DIR/lower_array_len_e2e.rs:+1:8: +1:27 - StorageDead(_4); // scope 0 at $DIR/lower_array_len_e2e.rs:+1:26: +1:27 - switchInt(move _3) -> [0: bb3, otherwise: bb1]; // scope 0 at $DIR/lower_array_len_e2e.rs:+1:8: +1:27 - } - - bb1: { - _5 = const N; // scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21 - _6 = Lt(_1, _5); // scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21 - assert(move _6, "index out of bounds: the length is {} but the index is {}", move _5, _1) -> bb2; // scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21 - } - - bb2: { - _0 = (*_2)[_1]; // scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21 - goto -> bb4; // scope 0 at $DIR/lower_array_len_e2e.rs:+1:5: +5:6 - } - - bb3: { - _0 = const 42_u8; // scope 0 at $DIR/lower_array_len_e2e.rs:+4:9: +4:11 - goto -> bb4; // scope 0 at $DIR/lower_array_len_e2e.rs:+1:5: +5:6 - } - - bb4: { - StorageDead(_3); // scope 0 at $DIR/lower_array_len_e2e.rs:+5:5: +5:6 - return; // scope 0 at $DIR/lower_array_len_e2e.rs:+6:2: +6:2 - } -} diff --git a/tests/mir-opt/lower_array_len_e2e.array_bound_mut.PreCodegen.after.mir b/tests/mir-opt/lower_array_len_e2e.array_bound_mut.PreCodegen.after.mir deleted file mode 100644 index e35fe758ab1..00000000000 --- a/tests/mir-opt/lower_array_len_e2e.array_bound_mut.PreCodegen.after.mir +++ /dev/null @@ -1,54 +0,0 @@ -// MIR for `array_bound_mut` after PreCodegen - -fn array_bound_mut(_1: usize, _2: &mut [u8; N]) -> u8 { - debug index => _1; // in scope 0 at $DIR/lower_array_len_e2e.rs:+0:40: +0:45 - debug slice => _2; // in scope 0 at $DIR/lower_array_len_e2e.rs:+0:54: +0:59 - let mut _0: u8; // return place in scope 0 at $DIR/lower_array_len_e2e.rs:+0:78: +0:80 - let mut _3: bool; // in scope 0 at $DIR/lower_array_len_e2e.rs:+1:8: +1:27 - let mut _4: usize; // in scope 0 at $DIR/lower_array_len_e2e.rs:+1:16: +1:27 - let mut _5: usize; // in scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21 - let mut _6: bool; // in scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21 - let _7: usize; // in scope 0 at $DIR/lower_array_len_e2e.rs:+4:15: +4:16 - let mut _8: usize; // in scope 0 at $DIR/lower_array_len_e2e.rs:+4:9: +4:17 - let mut _9: bool; // in scope 0 at $DIR/lower_array_len_e2e.rs:+4:9: +4:17 - - bb0: { - StorageLive(_3); // scope 0 at $DIR/lower_array_len_e2e.rs:+1:8: +1:27 - StorageLive(_4); // scope 0 at $DIR/lower_array_len_e2e.rs:+1:16: +1:27 - _4 = const N; // scope 0 at $DIR/lower_array_len_e2e.rs:+1:16: +1:27 - _3 = Lt(_1, move _4); // scope 0 at $DIR/lower_array_len_e2e.rs:+1:8: +1:27 - StorageDead(_4); // scope 0 at $DIR/lower_array_len_e2e.rs:+1:26: +1:27 - switchInt(move _3) -> [0: bb3, otherwise: bb1]; // scope 0 at $DIR/lower_array_len_e2e.rs:+1:8: +1:27 - } - - bb1: { - _5 = const N; // scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21 - _6 = Lt(_1, _5); // scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21 - assert(move _6, "index out of bounds: the length is {} but the index is {}", move _5, _1) -> bb2; // scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21 - } - - bb2: { - _0 = (*_2)[_1]; // scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21 - goto -> bb5; // scope 0 at $DIR/lower_array_len_e2e.rs:+1:5: +7:6 - } - - bb3: { - StorageLive(_7); // scope 0 at $DIR/lower_array_len_e2e.rs:+4:15: +4:16 - _7 = const 0_usize; // scope 0 at $DIR/lower_array_len_e2e.rs:+4:15: +4:16 - _8 = const N; // scope 0 at $DIR/lower_array_len_e2e.rs:+4:9: +4:17 - _9 = Lt(const 0_usize, _8); // scope 0 at $DIR/lower_array_len_e2e.rs:+4:9: +4:17 - assert(move _9, "index out of bounds: the length is {} but the index is {}", move _8, const 0_usize) -> bb4; // scope 0 at $DIR/lower_array_len_e2e.rs:+4:9: +4:17 - } - - bb4: { - (*_2)[_7] = const 42_u8; // scope 0 at $DIR/lower_array_len_e2e.rs:+4:9: +4:22 - StorageDead(_7); // scope 0 at $DIR/lower_array_len_e2e.rs:+4:22: +4:23 - _0 = const 42_u8; // scope 0 at $DIR/lower_array_len_e2e.rs:+6:9: +6:11 - goto -> bb5; // scope 0 at $DIR/lower_array_len_e2e.rs:+1:5: +7:6 - } - - bb5: { - StorageDead(_3); // scope 0 at $DIR/lower_array_len_e2e.rs:+7:5: +7:6 - return; // scope 0 at $DIR/lower_array_len_e2e.rs:+8:2: +8:2 - } -} diff --git a/tests/mir-opt/lower_array_len_e2e.array_len.PreCodegen.after.mir b/tests/mir-opt/lower_array_len_e2e.array_len.PreCodegen.after.mir deleted file mode 100644 index 4b19f679558..00000000000 --- a/tests/mir-opt/lower_array_len_e2e.array_len.PreCodegen.after.mir +++ /dev/null @@ -1,11 +0,0 @@ -// MIR for `array_len` after PreCodegen - -fn array_len(_1: &[u8; N]) -> usize { - debug arr => _1; // in scope 0 at $DIR/lower_array_len_e2e.rs:+0:34: +0:37 - let mut _0: usize; // return place in scope 0 at $DIR/lower_array_len_e2e.rs:+0:52: +0:57 - - bb0: { - _0 = const N; // scope 0 at $DIR/lower_array_len_e2e.rs:+1:5: +1:14 - return; // scope 0 at $DIR/lower_array_len_e2e.rs:+2:2: +2:2 - } -} diff --git a/tests/mir-opt/lower_array_len_e2e.array_len_by_value.PreCodegen.after.mir b/tests/mir-opt/lower_array_len_e2e.array_len_by_value.PreCodegen.after.mir deleted file mode 100644 index 4dc0ba9a268..00000000000 --- a/tests/mir-opt/lower_array_len_e2e.array_len_by_value.PreCodegen.after.mir +++ /dev/null @@ -1,11 +0,0 @@ -// MIR for `array_len_by_value` after PreCodegen - -fn array_len_by_value(_1: [u8; N]) -> usize { - debug arr => _1; // in scope 0 at $DIR/lower_array_len_e2e.rs:+0:43: +0:46 - let mut _0: usize; // return place in scope 0 at $DIR/lower_array_len_e2e.rs:+0:60: +0:65 - - bb0: { - _0 = const N; // scope 0 at $DIR/lower_array_len_e2e.rs:+1:5: +1:14 - return; // scope 0 at $DIR/lower_array_len_e2e.rs:+2:2: +2:2 - } -} diff --git a/tests/mir-opt/lower_array_len_e2e.rs b/tests/mir-opt/lower_array_len_e2e.rs deleted file mode 100644 index d8e4e521ee6..00000000000 --- a/tests/mir-opt/lower_array_len_e2e.rs +++ /dev/null @@ -1,39 +0,0 @@ -// compile-flags: -Z mir-opt-level=4 -Zunsound-mir-opts - -// EMIT_MIR lower_array_len_e2e.array_bound.PreCodegen.after.mir -pub fn array_bound(index: usize, slice: &[u8; N]) -> u8 { - if index < slice.len() { - slice[index] - } else { - 42 - } -} - -// EMIT_MIR lower_array_len_e2e.array_bound_mut.PreCodegen.after.mir -pub fn array_bound_mut(index: usize, slice: &mut [u8; N]) -> u8 { - if index < slice.len() { - slice[index] - } else { - slice[0] = 42; - - 42 - } -} - -// EMIT_MIR lower_array_len_e2e.array_len.PreCodegen.after.mir -pub fn array_len(arr: &[u8; N]) -> usize { - arr.len() -} - -// EMIT_MIR lower_array_len_e2e.array_len_by_value.PreCodegen.after.mir -pub fn array_len_by_value(arr: [u8; N]) -> usize { - arr.len() -} - -fn main() { - let _ = array_bound(3, &[0, 1, 2, 3]); - let mut tmp = [0, 1, 2, 3, 4]; - let _ = array_bound_mut(3, &mut [0, 1, 2, 3]); - let _ = array_len(&[0]); - let _ = array_len_by_value([0, 2]); -}