From b9af400a4659f722843ac96c251ec52f87998dd2 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Sat, 5 May 2018 13:27:51 +0300 Subject: [PATCH] rustc_mir: generate an extra temporary during borrowed rvalue promotion. --- src/librustc_mir/transform/promote_consts.rs | 158 +++++++++++------- .../end_region_destruction_extents_1.rs | 8 +- 2 files changed, 102 insertions(+), 64 deletions(-) diff --git a/src/librustc_mir/transform/promote_consts.rs b/src/librustc_mir/transform/promote_consts.rs index b732eeb624c6..fb7eb60545c9 100644 --- a/src/librustc_mir/transform/promote_consts.rs +++ b/src/librustc_mir/transform/promote_consts.rs @@ -25,14 +25,12 @@ use rustc::mir::*; use rustc::mir::visit::{PlaceContext, MutVisitor, Visitor}; use rustc::mir::traversal::ReversePostorder; -use rustc::ty::TyCtxt; +use rustc::ty::{self, TyCtxt}; use syntax_pos::Span; use rustc_data_structures::indexed_vec::{IndexVec, Idx}; -use std::iter; -use std::mem; -use std::usize; +use std::{cmp, iter, mem, usize}; /// State of a temporary during collection and promotion. #[derive(Copy, Clone, PartialEq, Eq, Debug)] @@ -150,9 +148,11 @@ pub fn collect_temps(mir: &Mir, rpo: &mut ReversePostorder) -> IndexVec { + tcx: TyCtxt<'a, 'tcx, 'tcx>, source: &'a mut Mir<'tcx>, promoted: Mir<'tcx>, temps: &'a mut IndexVec, + extra_statements: &'a mut Vec<(Location, Statement<'tcx>)>, /// If true, all nested temps are also kept in the /// source MIR, not moved to the promoted MIR. @@ -288,38 +288,78 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { } fn promote_candidate(mut self, candidate: Candidate) { - let span = self.promoted.span; - let new_operand = Operand::Constant(box Constant { - span, - ty: self.promoted.return_ty(), - literal: Literal::Promoted { + let mut rvalue = { + let promoted = &mut self.promoted; + let literal = Literal::Promoted { index: Promoted::new(self.source.promoted.len()) - } - }); - let mut rvalue = match candidate { - Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => { - let ref mut statement = self.source[bb].statements[stmt_idx]; - match statement.kind { - StatementKind::Assign(_, ref mut rvalue) => { - mem::replace(rvalue, Rvalue::Use(new_operand)) + }; + let operand = |ty, span| { + promoted.span = span; + promoted.local_decls[RETURN_PLACE] = + LocalDecl::new_return_place(ty, span); + Operand::Constant(box Constant { + span, + ty, + literal + }) + }; + let (blocks, local_decls) = self.source.basic_blocks_and_local_decls_mut(); + match candidate { + Candidate::Ref(loc) => { + let ref mut statement = blocks[loc.block].statements[loc.statement_index]; + match statement.kind { + StatementKind::Assign(_, Rvalue::Ref(r, bk, ref mut place)) => { + let ty = place.ty(local_decls, self.tcx).to_ty(self.tcx); + let ref_ty = self.tcx.mk_ref(r, + ty::TypeAndMut { + ty, + mutbl: bk.to_mutbl_lossy() + } + ); + let span = statement.source_info.span; + + // Create a temp to hold the promoted reference. + // This is because `*r` requires `r` to be a local, + // otherwise we would use the `promoted` directly. + let mut promoted_ref = LocalDecl::new_temp(ref_ty, span); + promoted_ref.source_info = statement.source_info; + let promoted_ref = local_decls.push(promoted_ref); + assert_eq!(self.temps.push(TempState::Unpromotable), promoted_ref); + self.extra_statements.push((loc, Statement { + source_info: statement.source_info, + kind: StatementKind::Assign( + Place::Local(promoted_ref), + Rvalue::Use(operand(ref_ty, span)), + ) + })); + let promoted_place = Place::Local(promoted_ref).deref(); + + Rvalue::Ref(r, bk, mem::replace(place, promoted_place)) + } + _ => bug!() } - _ => bug!() } - } - Candidate::Argument { bb, index } => { - match self.source[bb].terminator_mut().kind { - TerminatorKind::Call { ref mut args, .. } => { - Rvalue::Use(mem::replace(&mut args[index], new_operand)) + Candidate::Argument { bb, index } => { + let terminator = blocks[bb].terminator_mut(); + match terminator.kind { + TerminatorKind::Call { ref mut args, .. } => { + let ty = args[index].ty(local_decls, self.tcx); + let span = terminator.source_info.span; + Rvalue::Use(mem::replace(&mut args[index], operand(ty, span))) + } + _ => bug!() } - _ => bug!() } } }; + + assert_eq!(self.new_block(), START_BLOCK); self.visit_rvalue(&mut rvalue, Location { block: BasicBlock::new(0), statement_index: usize::MAX }); + let span = self.promoted.span; self.assign(RETURN_PLACE, rvalue, span); self.source.promoted.push(self.promoted); } @@ -343,43 +383,29 @@ pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>, candidates: Vec) { // Visit candidates in reverse, in case they're nested. debug!("promote_candidates({:?})", candidates); - for candidate in candidates.into_iter().rev() { - let (span, ty) = match candidate { - Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => { - let statement = &mir[bb].statements[stmt_idx]; - let dest = match statement.kind { - StatementKind::Assign(ref dest, _) => dest, - _ => { - span_bug!(statement.source_info.span, - "expected assignment to promote"); - } - }; - if let Place::Local(index) = *dest { - if temps[index] == TempState::PromotedOut { - // Already promoted. - continue; - } - } - (statement.source_info.span, dest.ty(mir, tcx).to_ty(tcx)) - } - Candidate::Argument { bb, index } => { - let terminator = mir[bb].terminator(); - let ty = match terminator.kind { - TerminatorKind::Call { ref args, .. } => { - args[index].ty(mir, tcx) - } - _ => { - span_bug!(terminator.source_info.span, - "expected call argument to promote"); - } - }; - (terminator.source_info.span, ty) - } - }; - // Declare return place local - let initial_locals = iter::once(LocalDecl::new_return_place(ty, span)) - .collect(); + let mut extra_statements = vec![]; + for candidate in candidates.into_iter().rev() { + match candidate { + Candidate::Ref(Location { block, statement_index }) => { + match mir[block].statements[statement_index].kind { + StatementKind::Assign(Place::Local(local), _) => { + if temps[local] == TempState::PromotedOut { + // Already promoted. + continue; + } + } + _ => {} + } + } + Candidate::Argument { .. } => {} + } + + + // Declare return place local so that `Mir::new` doesn't complain. + let initial_locals = iter::once( + LocalDecl::new_return_place(tcx.types.never, mir.span) + ).collect(); let mut promoter = Promoter { promoted: Mir::new( @@ -393,16 +419,24 @@ pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>, initial_locals, 0, vec![], - span + mir.span ), + tcx, source: mir, temps: &mut temps, + extra_statements: &mut extra_statements, keep_original: false }; - assert_eq!(promoter.new_block(), START_BLOCK); promoter.promote_candidate(candidate); } + // Insert each of `extra_statements` before its indicated location, which + // has to be done in reverse location order, to not invalidate the rest. + extra_statements.sort_by_key(|&(loc, _)| cmp::Reverse(loc)); + for (loc, statement) in extra_statements { + mir[loc.block].statements.insert(loc.statement_index, statement); + } + // Eliminate assignments to, and drops of promoted temps. let promoted = |index: Local| temps[index] == TempState::PromotedOut; for block in mir.basic_blocks_mut() { diff --git a/src/test/mir-opt/end_region_destruction_extents_1.rs b/src/test/mir-opt/end_region_destruction_extents_1.rs index e189f2e3b34a..15b104f6c2ff 100644 --- a/src/test/mir-opt/end_region_destruction_extents_1.rs +++ b/src/test/mir-opt/end_region_destruction_extents_1.rs @@ -130,17 +130,21 @@ unsafe impl<'a, #[may_dangle] 'b> Drop for D1<'a, 'b> { // let mut _7: &'10s S1; // let mut _8: &'10s S1; // let mut _9: S1; +// let mut _10: &'10s S1; +// let mut _11: &'12ds S1; // // bb0: { // StorageLive(_2); // StorageLive(_3); // StorageLive(_4); // StorageLive(_5); -// _5 = promoted[1]; +// _11 = promoted[1]; +// _5 = &'12ds (*_11); // _4 = &'12ds (*_5); // StorageLive(_7); // StorageLive(_8); -// _8 = promoted[0]; +// _10 = promoted[0]; +// _8 = &'10s (*_10); // _7 = &'10s (*_8); // _3 = D1<'12ds, '10s>::{{constructor}}(move _4, move _7); // EndRegion('10s);