1
Fork 0

Remove uniform_array_move_out passes

These passes were buggy, MIR building is now responsible for
canonicalizing `ConstantIndex` projections and `MoveData` is responsible
for splitting `Subslice` projections.
This commit is contained in:
Matthew Jasper 2019-11-22 22:03:25 +00:00
parent bf278ebd9d
commit 96dc03bad7
10 changed files with 168 additions and 489 deletions

View file

@ -78,10 +78,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
.collect();
if move_out_indices.is_empty() {
let root_place = self
.prefixes(used_place, PrefixSet::All)
.last()
.unwrap();
let root_place = PlaceRef { projection: &[], ..used_place };
if !self.uninitialized_error_reported.insert(root_place) {
debug!(

View file

@ -174,7 +174,7 @@ fn do_mir_borrowck<'a, 'tcx>(
let mut errors_buffer = Vec::new();
let (move_data, move_errors): (MoveData<'tcx>, Option<Vec<(Place<'tcx>, MoveError<'tcx>)>>) =
match MoveData::gather_moves(&body, tcx) {
match MoveData::gather_moves(&body, tcx, param_env) {
Ok(move_data) => (move_data, None),
Err((move_data, move_errors)) => (move_data, Some(move_errors)),
};
@ -1600,7 +1600,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
(prefix, place_span.0, place_span.1),
mpi,
);
return; // don't bother finding other problems.
}
}
Err(NoMovePathFound::ReachedStatic) => {
@ -1614,6 +1613,46 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
}
}
/// Subslices correspond to multiple move paths, so we iterate through the
/// elements of the base array. For each element we check
///
/// * Does this element overlap with our slice.
/// * Is any part of it uninitialized.
fn check_if_subslice_element_is_moved(
&mut self,
location: Location,
desired_action: InitializationRequiringAction,
place_span: (PlaceRef<'cx, 'tcx>, Span),
maybe_uninits: &FlowAtLocation<'tcx, MaybeUninitializedPlaces<'cx, 'tcx>>,
from: u32,
to: u32,
) {
if let Some(mpi) = self.move_path_for_place(place_span.0) {
let mut child = self.move_data.move_paths[mpi].first_child;
while let Some(child_mpi) = child {
let child_move_place = &self.move_data.move_paths[child_mpi];
let child_place = &child_move_place.place;
let last_proj = child_place.projection.last().unwrap();
if let ProjectionElem::ConstantIndex { offset, from_end, .. } = last_proj {
debug_assert!(!from_end, "Array constant indexing shouldn't be `from_end`.");
if (from..to).contains(offset) {
if let Some(uninit_child) = maybe_uninits.has_any_child_of(child_mpi) {
self.report_use_of_moved_or_uninitialized(
location,
desired_action,
(place_span.0, place_span.0, place_span.1),
uninit_child,
);
return; // don't bother finding other problems.
}
}
}
child = child_move_place.next_sibling;
}
}
}
fn check_if_path_or_subpath_is_moved(
&mut self,
location: Location,
@ -1640,6 +1679,30 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
self.check_if_full_path_is_moved(location, desired_action, place_span, flow_state);
if let [
base_proj @ ..,
ProjectionElem::Subslice { from, to, from_end: false },
] = place_span.0.projection {
let place_ty = Place::ty_from(
place_span.0.base,
base_proj,
self.body(),
self.infcx.tcx,
);
if let ty::Array(..) = place_ty.ty.kind {
let array_place = PlaceRef { base: place_span.0.base, projection: base_proj };
self.check_if_subslice_element_is_moved(
location,
desired_action,
(array_place, place_span.1),
maybe_uninits,
*from,
*to,
);
return;
}
}
// A move of any shallow suffix of `place` also interferes
// with an attempt to use `place`. This is scenario 3 above.
//

View file

@ -533,8 +533,8 @@ fn place_projection_conflict<'tcx>(
}
}
(ProjectionElem::ConstantIndex { offset, min_length: _, from_end: true },
ProjectionElem::Subslice { to, .. })
| (ProjectionElem::Subslice { to, .. },
ProjectionElem::Subslice { to, from_end: true, .. })
| (ProjectionElem::Subslice { to, from_end: true, .. },
ProjectionElem::ConstantIndex { offset, min_length: _, from_end: true }) => {
if offset > to {
debug!("place_element_conflict: \

View file

@ -4,7 +4,7 @@ use rustc::ty::{self, TyCtxt};
use rustc_index::vec::IndexVec;
use smallvec::{smallvec, SmallVec};
use std::collections::hash_map::Entry;
use std::convert::TryInto;
use std::mem;
use super::abs_domain::Lift;
@ -17,12 +17,13 @@ use super::{
struct MoveDataBuilder<'a, 'tcx> {
body: &'a Body<'tcx>,
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
data: MoveData<'tcx>,
errors: Vec<(Place<'tcx>, MoveError<'tcx>)>,
}
impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
fn new(body: &'a Body<'tcx>, tcx: TyCtxt<'tcx>) -> Self {
fn new(body: &'a Body<'tcx>, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Self {
let mut move_paths = IndexVec::new();
let mut path_map = IndexVec::new();
let mut init_path_map = IndexVec::new();
@ -30,6 +31,7 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
MoveDataBuilder {
body,
tcx,
param_env,
errors: Vec::new(),
data: MoveData {
moves: IndexVec::new(),
@ -148,42 +150,47 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
InteriorOfSliceOrArray { ty: place_ty, is_index: true },
));
}
_ => {
// FIXME: still badly broken
}
_ => {}
},
_ => {}
};
let proj = &place.projection[..i+1];
base = match self
.builder
.data
.rev_lookup
.projections
.entry((base, elem.lift()))
{
Entry::Occupied(ent) => *ent.get(),
Entry::Vacant(ent) => {
let path = MoveDataBuilder::new_move_path(
&mut self.builder.data.move_paths,
&mut self.builder.data.path_map,
&mut self.builder.data.init_path_map,
Some(base),
Place {
base: place.base.clone(),
projection: tcx.intern_place_elems(proj),
},
);
ent.insert(path);
path
}
};
base = self.add_move_path(base, elem, |tcx| {
Place {
base: place.base.clone(),
projection: tcx.intern_place_elems(&place.projection[..i+1]),
}
});
}
Ok(base)
}
fn add_move_path(
&mut self,
base: MovePathIndex,
elem: &PlaceElem<'tcx>,
mk_place: impl FnOnce(TyCtxt<'tcx>) -> Place<'tcx>,
) -> MovePathIndex {
let MoveDataBuilder {
data: MoveData { rev_lookup, move_paths, path_map, init_path_map, .. },
tcx,
..
} = self.builder;
*rev_lookup.projections
.entry((base, elem.lift()))
.or_insert_with(move || {
let path = MoveDataBuilder::new_move_path(
move_paths,
path_map,
init_path_map,
Some(base),
mk_place(*tcx),
);
path
})
}
fn create_move_path(&mut self, place: &Place<'tcx>) {
// This is an non-moving access (such as an overwrite or
// drop), so this not being a valid move path is OK.
@ -214,8 +221,9 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
pub(super) fn gather_moves<'tcx>(
body: &Body<'tcx>,
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> Result<MoveData<'tcx>, (MoveData<'tcx>, Vec<(Place<'tcx>, MoveError<'tcx>)>)> {
let mut builder = MoveDataBuilder::new(body, tcx);
let mut builder = MoveDataBuilder::new(body, tcx, param_env);
builder.gather_args();
@ -411,20 +419,67 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
fn gather_move(&mut self, place: &Place<'tcx>) {
debug!("gather_move({:?}, {:?})", self.loc, place);
let path = match self.move_path_for(place) {
Ok(path) | Err(MoveError::UnionMove { path }) => path,
Err(error @ MoveError::IllegalMove { .. }) => {
self.builder.errors.push((place.clone(), error));
return;
if let [
ref base @ ..,
ProjectionElem::Subslice { from, to, from_end: false },
] = **place.projection {
// Split `Subslice` patterns into the corresponding list of
// `ConstIndex` patterns. This is done to ensure that all move paths
// are disjoint, which is expected by drop elaboration.
let base_place = Place {
base: place.base.clone(),
projection: self.builder.tcx.intern_place_elems(base),
};
let base_path = match self.move_path_for(&base_place) {
Ok(path) => path,
Err(MoveError::UnionMove { path }) => {
self.record_move(place, path);
return;
}
Err(error @ MoveError::IllegalMove { .. }) => {
self.builder.errors.push((base_place, error));
return;
}
};
let base_ty = base_place.ty(self.builder.body, self.builder.tcx).ty;
let len: u32 = match base_ty.kind {
ty::Array(_, size) => {
let length = size.eval_usize(self.builder.tcx, self.builder.param_env);
length.try_into().expect(
"slice pattern of array with more than u32::MAX elements"
)
}
_ => bug!("from_end: false slice pattern of non-array type"),
};
for offset in from..to {
let elem = ProjectionElem::ConstantIndex {
offset,
min_length: len,
from_end: false,
};
let path = self.add_move_path(
base_path,
&elem,
|tcx| tcx.mk_place_elem(base_place.clone(), elem),
);
self.record_move(place, path);
}
};
let move_out = self.builder.data.moves.push(MoveOut { path: path, source: self.loc });
} else {
match self.move_path_for(place) {
Ok(path) | Err(MoveError::UnionMove { path }) => self.record_move(place, path),
Err(error @ MoveError::IllegalMove { .. }) => {
self.builder.errors.push((place.clone(), error));
}
};
}
}
fn record_move(&mut self, place: &Place<'tcx>, path: MovePathIndex) {
let move_out = self.builder.data.moves.push(MoveOut { path: path, source: self.loc });
debug!(
"gather_move({:?}, {:?}): adding move {:?} of {:?}",
self.loc, place, move_out, path
);
self.builder.data.path_map[path].push(move_out);
self.builder.data.loc_map[self.loc].push(move_out);
}

View file

@ -26,7 +26,7 @@ impl<'tcx> MirPass<'tcx> for ElaborateDrops {
let def_id = src.def_id();
let param_env = tcx.param_env(src.def_id()).with_reveal_all();
let move_data = match MoveData::gather_moves(body, tcx) {
let move_data = match MoveData::gather_moves(body, tcx, param_env) {
Ok(move_data) => move_data,
Err(_) => bug!("No `move_errors` should be allowed in MIR borrowck"),
};

View file

@ -35,7 +35,6 @@ pub mod copy_prop;
pub mod const_prop;
pub mod generator;
pub mod inline;
pub mod uniform_array_move_out;
pub mod uninhabited_enum_branching;
pub(crate) fn provide(providers: &mut Providers<'_>) {
@ -229,7 +228,6 @@ fn mir_const(tcx: TyCtxt<'_>, def_id: DefId) -> &Steal<BodyAndCache<'_>> {
// What we need to do constant evaluation.
&simplify::SimplifyCfg::new("initial"),
&rustc_peek::SanityCheck,
&uniform_array_move_out::UniformArrayMoveOut,
]);
body.ensure_predecessors();
tcx.alloc_steal_mir(body)
@ -294,7 +292,6 @@ fn run_optimization_passes<'tcx>(
// Optimizations begin.
&uninhabited_enum_branching::UninhabitedEnumBranching,
&simplify::SimplifyCfg::new("after-uninhabited-enum-branching"),
&uniform_array_move_out::RestoreSubsliceArrayMoveOut::new(tcx),
&inline::Inline,
// Lowering generator control-flow and variables

View file

@ -37,7 +37,7 @@ impl<'tcx> MirPass<'tcx> for SanityCheck {
let attributes = tcx.get_attrs(def_id);
let param_env = tcx.param_env(def_id);
let move_data = MoveData::gather_moves(body, tcx).unwrap();
let move_data = MoveData::gather_moves(body, tcx, param_env).unwrap();
let mdpe = MoveDataParamEnv { move_data: move_data, param_env: param_env };
let dead_unwinds = BitSet::new_empty(body.basic_blocks().len());
let flow_inits =

View file

@ -1,381 +0,0 @@
// This pass converts move out from array by Subslice and
// ConstIndex{.., from_end: true} to ConstIndex move out(s) from begin
// of array. It allows detect error by mir borrowck and elaborate
// drops for array without additional work.
//
// Example:
//
// let a = [ box 1,box 2, box 3];
// if b {
// let [_a.., _] = a;
// } else {
// let [.., _b] = a;
// }
//
// mir statement _10 = move _2[:-1]; replaced by:
// StorageLive(_12);
// _12 = move _2[0 of 3];
// StorageLive(_13);
// _13 = move _2[1 of 3];
// _10 = [move _12, move _13]
// StorageDead(_12);
// StorageDead(_13);
//
// and mir statement _11 = move _2[-1 of 1]; replaced by:
// _11 = move _2[2 of 3];
//
// FIXME: integrate this transformation to the mir build
use rustc::ty;
use rustc::ty::TyCtxt;
use rustc::mir::*;
use rustc::mir::visit::{Visitor, PlaceContext, NonUseContext};
use rustc_index::vec::{IndexVec};
use crate::transform::{MirPass, MirSource};
use crate::util::patch::MirPatch;
pub struct UniformArrayMoveOut;
impl<'tcx> MirPass<'tcx> for UniformArrayMoveOut {
fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut BodyAndCache<'tcx>) {
let mut patch = MirPatch::new(body);
let param_env = tcx.param_env(src.def_id());
{
let read_only_cache = read_only!(body);
let mut visitor
= UniformArrayMoveOutVisitor{ body, patch: &mut patch, tcx, param_env};
visitor.visit_body(read_only_cache);
}
patch.apply(body);
}
}
struct UniformArrayMoveOutVisitor<'a, 'tcx> {
body: &'a Body<'tcx>,
patch: &'a mut MirPatch<'tcx>,
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
}
impl<'a, 'tcx> Visitor<'tcx> for UniformArrayMoveOutVisitor<'a, 'tcx> {
fn visit_assign(&mut self,
dst_place: &Place<'tcx>,
rvalue: &Rvalue<'tcx>,
location: Location) {
if let Rvalue::Use(Operand::Move(ref src_place)) = rvalue {
if let &[ref proj_base @ .., elem] = src_place.projection.as_ref() {
if let ProjectionElem::ConstantIndex{offset: _,
min_length: _,
from_end: false} = elem {
// no need to transformation
} else {
let place_ty =
Place::ty_from(&src_place.base, proj_base, self.body, self.tcx).ty;
if let ty::Array(item_ty, const_size) = place_ty.kind {
if let Some(size) = const_size.try_eval_usize(self.tcx, self.param_env) {
assert!(size <= u32::max_value() as u64,
"uniform array move out doesn't supported
for array bigger then u32");
self.uniform(
location,
dst_place,
&src_place.base,
&src_place.projection,
item_ty,
size as u32,
);
}
}
}
}
}
self.super_assign(dst_place, rvalue, location)
}
}
impl<'a, 'tcx> UniformArrayMoveOutVisitor<'a, 'tcx> {
fn uniform(&mut self,
location: Location,
dst_place: &Place<'tcx>,
base: &PlaceBase<'tcx>,
proj: &[PlaceElem<'tcx>],
item_ty: &'tcx ty::TyS<'tcx>,
size: u32) {
if let [proj_base @ .., elem] = proj {
match elem {
// uniforms statements like_10 = move _2[:-1];
ProjectionElem::Subslice{from, to} => {
self.patch.make_nop(location);
let temps : Vec<_> = (*from..(size-*to)).map(|i| {
let temp =
self.patch.new_temp(item_ty, self.body.source_info(location).span);
self.patch.add_statement(location, StatementKind::StorageLive(temp));
let mut projection = proj_base.to_vec();
projection.push(ProjectionElem::ConstantIndex {
offset: i,
min_length: size,
from_end: false,
});
self.patch.add_assign(
location,
Place::from(temp),
Rvalue::Use(Operand::Move(Place {
base: base.clone(),
projection: self.tcx.intern_place_elems(&projection),
})),
);
temp
}).collect();
self.patch.add_assign(
location,
dst_place.clone(),
Rvalue::Aggregate(
box AggregateKind::Array(item_ty),
temps.iter().map(
|x| Operand::Move(Place::from(*x))
).collect()
)
);
for temp in temps {
self.patch.add_statement(location, StatementKind::StorageDead(temp));
}
}
// uniforms statements like _11 = move _2[-1 of 1];
ProjectionElem::ConstantIndex{offset, min_length: _, from_end: true} => {
self.patch.make_nop(location);
let mut projection = proj_base.to_vec();
projection.push(ProjectionElem::ConstantIndex {
offset: size - offset,
min_length: size,
from_end: false,
});
self.patch.add_assign(
location,
dst_place.clone(),
Rvalue::Use(Operand::Move(Place {
base: base.clone(),
projection: self.tcx.intern_place_elems(&projection),
})),
);
}
_ => {}
}
}
}
}
// Restore Subslice move out after analysis
// Example:
//
// next statements:
// StorageLive(_12);
// _12 = move _2[0 of 3];
// StorageLive(_13);
// _13 = move _2[1 of 3];
// _10 = [move _12, move _13]
// StorageDead(_12);
// StorageDead(_13);
//
// replaced by _10 = move _2[:-1];
pub struct RestoreSubsliceArrayMoveOut<'tcx> {
tcx: TyCtxt<'tcx>
}
impl<'tcx> MirPass<'tcx> for RestoreSubsliceArrayMoveOut<'tcx> {
fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut BodyAndCache<'tcx>) {
let mut patch = MirPatch::new(body);
let param_env = tcx.param_env(src.def_id());
{
let read_only_cache = read_only!(body);
let mut visitor = RestoreDataCollector {
locals_use: IndexVec::from_elem(LocalUse::new(), &body.local_decls),
candidates: vec![],
};
visitor.visit_body(read_only_cache);
for candidate in &visitor.candidates {
let statement = &body[candidate.block].statements[candidate.statement_index];
if let StatementKind::Assign(box(ref dst_place, ref rval)) = statement.kind {
if let Rvalue::Aggregate(box AggregateKind::Array(_), ref items) = *rval {
let items : Vec<_> = items.iter().map(|item| {
if let Operand::Move(place) = item {
if let Some(local) = place.as_local() {
let local_use = &visitor.locals_use[local];
let opt_index_and_place =
Self::try_get_item_source(local_use, body);
// each local should be used twice:
// in assign and in aggregate statements
if local_use.use_count == 2 && opt_index_and_place.is_some() {
let (index, src_place) = opt_index_and_place.unwrap();
return Some((local_use, index, src_place));
}
}
}
None
}).collect();
let opt_src_place = items.first().and_then(|x| *x).map(|x| x.2);
let opt_size = opt_src_place.and_then(|src_place| {
let src_ty = Place::ty_from(
src_place.base,
src_place.projection,
&**body,
tcx
).ty;
if let ty::Array(_, ref size_o) = src_ty.kind {
size_o.try_eval_usize(tcx, param_env)
} else {
None
}
});
let restore_subslice = RestoreSubsliceArrayMoveOut { tcx };
restore_subslice
.check_and_patch(*candidate, &items, opt_size, &mut patch, dst_place);
}
}
}
}
patch.apply(body);
}
}
impl RestoreSubsliceArrayMoveOut<'tcx> {
pub fn new(tcx: TyCtxt<'tcx>) -> Self {
RestoreSubsliceArrayMoveOut { tcx }
}
// Checks that source has size, all locals are inited from same source place and
// indices is an integer interval. If all checks pass do the replacent.
// items are Vec<Option<LocalUse, index in source array, source place for init local>>
fn check_and_patch(&self,
candidate: Location,
items: &[Option<(&LocalUse, u32, PlaceRef<'_, 'tcx>)>],
opt_size: Option<u64>,
patch: &mut MirPatch<'tcx>,
dst_place: &Place<'tcx>) {
let opt_src_place = items.first().and_then(|x| *x).map(|x| x.2);
if opt_size.is_some() && items.iter().all(
|l| l.is_some() && l.unwrap().2 == opt_src_place.unwrap()) {
let src_place = opt_src_place.unwrap();
let indices: Vec<_> = items.iter().map(|x| x.unwrap().1).collect();
for i in 1..indices.len() {
if indices[i - 1] + 1 != indices[i] {
return;
}
}
let min = *indices.first().unwrap();
let max = *indices.last().unwrap();
for item in items {
let locals_use = item.unwrap().0;
patch.make_nop(locals_use.alive.unwrap());
patch.make_nop(locals_use.dead.unwrap());
patch.make_nop(locals_use.first_use.unwrap());
}
patch.make_nop(candidate);
let size = opt_size.unwrap() as u32;
let mut projection = src_place.projection.to_vec();
projection.push(ProjectionElem::Subslice { from: min, to: size - max - 1 });
patch.add_assign(
candidate,
dst_place.clone(),
Rvalue::Use(Operand::Move(Place {
base: src_place.base.clone(),
projection: self.tcx.intern_place_elems(&projection),
})),
);
}
}
fn try_get_item_source<'a>(local_use: &LocalUse,
body: &'a Body<'tcx>) -> Option<(u32, PlaceRef<'a, 'tcx>)> {
if let Some(location) = local_use.first_use {
let block = &body[location.block];
if block.statements.len() > location.statement_index {
let statement = &block.statements[location.statement_index];
if let StatementKind::Assign(
box(place, Rvalue::Use(Operand::Move(src_place)))
) = &statement.kind {
if let (Some(_), PlaceRef {
base: _,
projection: &[.., ProjectionElem::ConstantIndex {
offset, min_length: _, from_end: false
}],
}) = (place.as_local(), src_place.as_ref()) {
if let StatementKind::Assign(
box(_, Rvalue::Use(Operand::Move(place)))
) = &statement.kind {
if let PlaceRef {
base,
projection: &[ref proj_base @ .., _],
} = place.as_ref() {
return Some((offset, PlaceRef {
base,
projection: proj_base,
}))
}
}
}
}
}
}
None
}
}
#[derive(Copy, Clone, Debug)]
struct LocalUse {
alive: Option<Location>,
dead: Option<Location>,
use_count: u32,
first_use: Option<Location>,
}
impl LocalUse {
pub fn new() -> Self {
LocalUse{alive: None, dead: None, use_count: 0, first_use: None}
}
}
struct RestoreDataCollector {
locals_use: IndexVec<Local, LocalUse>,
candidates: Vec<Location>,
}
impl<'tcx> Visitor<'tcx> for RestoreDataCollector {
fn visit_assign(&mut self,
place: &Place<'tcx>,
rvalue: &Rvalue<'tcx>,
location: Location) {
if let Rvalue::Aggregate(box AggregateKind::Array(_), _) = *rvalue {
self.candidates.push(location);
}
self.super_assign(place, rvalue, location)
}
fn visit_local(&mut self,
local: &Local,
context: PlaceContext,
location: Location) {
let local_use = &mut self.locals_use[*local];
match context {
PlaceContext::NonUse(NonUseContext::StorageLive) => local_use.alive = Some(location),
PlaceContext::NonUse(NonUseContext::StorageDead) => local_use.dead = Some(location),
PlaceContext::NonUse(NonUseContext::VarDebugInfo) => {}
_ => {
local_use.use_count += 1;
if local_use.first_use.is_none() {
local_use.first_use = Some(location);
}
}
}
}
}

View file

@ -21,9 +21,6 @@ fn main() {
// _0 = move (_1.0: u32);
// return;
// }
// bb2 (cleanup): {
// resume;
// }
// }
// END rustc.add.ConstProp.before.mir
// START rustc.add.ConstProp.after.mir
@ -38,9 +35,6 @@ fn main() {
// _0 = const 4u32;
// return;
// }
// bb2 (cleanup): {
// resume;
// }
// }
// END rustc.add.ConstProp.after.mir
// START rustc.add.PreCodegen.before.mir

View file

@ -18,58 +18,12 @@ fn main() {
// END RUST SOURCE
// START rustc.move_out_from_end.UniformArrayMoveOut.before.mir
// StorageLive(_6);
// _6 = move _1[-1 of 1];
// _0 = ();
// END rustc.move_out_from_end.UniformArrayMoveOut.before.mir
// START rustc.move_out_from_end.UniformArrayMoveOut.after.mir
// StorageLive(_6);
// START rustc.move_out_from_end.mir_map.0.mir
// _6 = move _1[1 of 2];
// nop;
// _0 = ();
// END rustc.move_out_from_end.UniformArrayMoveOut.after.mir
// END rustc.move_out_from_end.mir_map.0.mir
// START rustc.move_out_by_subslice.UniformArrayMoveOut.before.mir
// StorageLive(_6);
// _6 = move _1[0:];
// END rustc.move_out_by_subslice.UniformArrayMoveOut.before.mir
// START rustc.move_out_by_subslice.UniformArrayMoveOut.after.mir
// StorageLive(_6);
// StorageLive(_7);
// _7 = move _1[0 of 2];
// StorageLive(_8);
// _8 = move _1[1 of 2];
// _6 = [move _7, move _8];
// StorageDead(_7);
// StorageDead(_8);
// nop;
// START rustc.move_out_by_subslice.mir_map.0.mir
// _6 = move _1[0..2];
// _0 = ();
// END rustc.move_out_by_subslice.UniformArrayMoveOut.after.mir
// START rustc.move_out_by_subslice.RestoreSubsliceArrayMoveOut.before.mir
// StorageLive(_6);
// StorageLive(_7);
// _7 = move _1[0 of 2];
// StorageLive(_8);
// _8 = move _1[1 of 2];
// _6 = [move _7, move _8];
// StorageDead(_7);
// StorageDead(_8);
// _0 = ();
// END rustc.move_out_by_subslice.RestoreSubsliceArrayMoveOut.before.mir
// START rustc.move_out_by_subslice.RestoreSubsliceArrayMoveOut.after.mir
// StorageLive(_6);
// nop;
// nop;
// nop;
// nop;
// _6 = move _1[0:];
// nop;
// nop;
// nop;
// _0 = ();
// END rustc.move_out_by_subslice.RestoreSubsliceArrayMoveOut.after.mir
// END rustc.move_out_by_subslice.mir_map.0.mir