From 02aec40dc9c0f67c60a1ec353d06a88b63387d9b Mon Sep 17 00:00:00 2001 From: Eduard Burtescu Date: Sun, 14 Aug 2016 06:34:14 +0300 Subject: [PATCH 1/2] [MIR] Add Storage{Live,Dead} statements to emit llvm.lifetime.{start,end}. --- src/librustc/mir/repr.rs | 13 +- src/librustc/mir/visit.rs | 10 ++ .../borrowck/mir/dataflow/impls.rs | 2 + .../borrowck/mir/dataflow/sanity_check.rs | 2 + .../borrowck/mir/gather_moves.rs | 2 + src/librustc_borrowck/borrowck/mir/mod.rs | 2 + src/librustc_mir/build/block.rs | 2 + src/librustc_mir/build/expr/as_temp.rs | 9 +- src/librustc_mir/build/matches/mod.rs | 42 ++++++ src/librustc_mir/build/mod.rs | 3 +- src/librustc_mir/build/scope.rs | 138 +++++++++++++----- src/librustc_mir/transform/deaggregator.rs | 5 +- src/librustc_mir/transform/promote_consts.rs | 34 +++-- src/librustc_mir/transform/qualify_consts.rs | 12 +- src/librustc_mir/transform/type_check.rs | 9 ++ src/librustc_trans/mir/analyze.rs | 8 +- src/librustc_trans/mir/constant.rs | 2 + src/librustc_trans/mir/statement.rs | 20 +++ src/test/codegen/lifetime_start_end.rs | 54 +++++++ .../debuginfo/function-arg-initialization.rs | 4 +- src/test/mir-opt/storage_ranges.rs | 45 ++++++ 21 files changed, 359 insertions(+), 59 deletions(-) create mode 100644 src/test/codegen/lifetime_start_end.rs create mode 100644 src/test/mir-opt/storage_ranges.rs diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index 08614ca253be5..454c1ff816753 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -688,8 +688,17 @@ pub struct Statement<'tcx> { #[derive(Clone, Debug, RustcEncodable, RustcDecodable)] pub enum StatementKind<'tcx> { + /// Write the RHS Rvalue to the LHS Lvalue. Assign(Lvalue<'tcx>, Rvalue<'tcx>), - SetDiscriminant{ lvalue: Lvalue<'tcx>, variant_index: usize }, + + /// Write the discriminant for a variant to the enum Lvalue. + SetDiscriminant { lvalue: Lvalue<'tcx>, variant_index: usize }, + + /// Start a live range for the storage of the local. + StorageLive(Lvalue<'tcx>), + + /// End the current live range for the storage of the local. + StorageDead(Lvalue<'tcx>), } impl<'tcx> Debug for Statement<'tcx> { @@ -697,6 +706,8 @@ impl<'tcx> Debug for Statement<'tcx> { use self::StatementKind::*; match self.kind { Assign(ref lv, ref rv) => write!(fmt, "{:?} = {:?}", lv, rv), + StorageLive(ref lv) => write!(fmt, "StorageLive({:?})", lv), + StorageDead(ref lv) => write!(fmt, "StorageDead({:?})", lv), SetDiscriminant{lvalue: ref lv, variant_index: index} => { write!(fmt, "discriminant({:?}) = {:?}", lv, index) } diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index d44f00ed2cbe2..d3526f618a904 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -326,6 +326,12 @@ macro_rules! make_mir_visitor { StatementKind::SetDiscriminant{ ref $($mutability)* lvalue, .. } => { self.visit_lvalue(lvalue, LvalueContext::Store); } + StatementKind::StorageLive(ref $($mutability)* lvalue) => { + self.visit_lvalue(lvalue, LvalueContext::StorageLive); + } + StatementKind::StorageDead(ref $($mutability)* lvalue) => { + self.visit_lvalue(lvalue, LvalueContext::StorageDead); + } } } @@ -759,4 +765,8 @@ pub enum LvalueContext { // Consumed as part of an operand Consume, + + // Starting and ending a storage live range + StorageLive, + StorageDead, } diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/impls.rs b/src/librustc_borrowck/borrowck/mir/dataflow/impls.rs index 57b335bd5eee4..90858e4e8b8be 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow/impls.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow/impls.rs @@ -459,6 +459,8 @@ impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> { sets.kill_set.add(&moi); }); } + repr::StatementKind::StorageLive(_) | + repr::StatementKind::StorageDead(_) => {} } } diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs b/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs index ccde429a17113..9a4865755e799 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs @@ -104,6 +104,8 @@ fn each_block<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>, repr::StatementKind::Assign(ref lvalue, ref rvalue) => { (lvalue, rvalue) } + repr::StatementKind::StorageLive(_) | + repr::StatementKind::StorageDead(_) => continue, repr::StatementKind::SetDiscriminant{ .. } => span_bug!(stmt.source_info.span, "sanity_check should run before Deaggregator inserts SetDiscriminant"), diff --git a/src/librustc_borrowck/borrowck/mir/gather_moves.rs b/src/librustc_borrowck/borrowck/mir/gather_moves.rs index e965dcc169c2d..d2ef3356afb9d 100644 --- a/src/librustc_borrowck/borrowck/mir/gather_moves.rs +++ b/src/librustc_borrowck/borrowck/mir/gather_moves.rs @@ -616,6 +616,8 @@ fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MoveD Rvalue::InlineAsm { .. } => {} } } + StatementKind::StorageLive(_) | + StatementKind::StorageDead(_) => {} StatementKind::SetDiscriminant{ .. } => { span_bug!(stmt.source_info.span, "SetDiscriminant should not exist during borrowck"); diff --git a/src/librustc_borrowck/borrowck/mir/mod.rs b/src/librustc_borrowck/borrowck/mir/mod.rs index c563fdb8f44e6..dbee0ea9b00e9 100644 --- a/src/librustc_borrowck/borrowck/mir/mod.rs +++ b/src/librustc_borrowck/borrowck/mir/mod.rs @@ -378,6 +378,8 @@ fn drop_flag_effects_for_location<'a, 'tcx, F>( move_data.rev_lookup.find(lvalue), |moi| callback(moi, DropFlagState::Present)) } + repr::StatementKind::StorageLive(_) | + repr::StatementKind::StorageDead(_) => {} }, None => { debug!("drop_flag_effects: replace {:?}", block.terminator()); diff --git a/src/librustc_mir/build/block.rs b/src/librustc_mir/build/block.rs index 7e650c5bd3d06..34d7973264631 100644 --- a/src/librustc_mir/build/block.rs +++ b/src/librustc_mir/build/block.rs @@ -68,6 +68,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // FIXME #30046 ^~~~ this.expr_into_pattern(block, pattern, init) })); + } else { + this.storage_live_for_bindings(block, &pattern); } // Enter the visibility scope, after evaluating the initializer. diff --git a/src/librustc_mir/build/expr/as_temp.rs b/src/librustc_mir/build/expr/as_temp.rs index da128b8dd5626..85128cbbbafef 100644 --- a/src/librustc_mir/build/expr/as_temp.rs +++ b/src/librustc_mir/build/expr/as_temp.rs @@ -37,6 +37,14 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let temp = this.temp(expr_ty.clone()); let temp_lifetime = expr.temp_lifetime; let expr_span = expr.span; + let source_info = this.source_info(expr_span); + + if temp_lifetime.is_some() { + this.cfg.push(block, Statement { + source_info: source_info, + kind: StatementKind::StorageLive(temp.clone()) + }); + } // Careful here not to cause an infinite cycle. If we always // called `into`, then for lvalues like `x.f`, it would @@ -49,7 +57,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { Category::Lvalue => { let lvalue = unpack!(block = this.as_lvalue(block, expr)); let rvalue = Rvalue::Use(Operand::Consume(lvalue)); - let source_info = this.source_info(expr_span); this.cfg.push_assign(block, source_info, &temp, rvalue); } _ => { diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index a94adafa80213..ec390704d0789 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs @@ -122,6 +122,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { PatternKind::Binding { mode: BindingMode::ByValue, var, subpattern: None, .. } => { + self.storage_live_for_bindings(block, &irrefutable_pat); let lvalue = Lvalue::Var(self.var_indices[&var]); return self.into(&lvalue, block, initializer); } @@ -206,6 +207,43 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } var_scope } + + /// Emit `StorageLive` for every binding in the pattern. + pub fn storage_live_for_bindings(&mut self, + block: BasicBlock, + pattern: &Pattern<'tcx>) { + match *pattern.kind { + PatternKind::Binding { var, ref subpattern, .. } => { + let lvalue = Lvalue::Var(self.var_indices[&var]); + let source_info = self.source_info(pattern.span); + self.cfg.push(block, Statement { + source_info: source_info, + kind: StatementKind::StorageLive(lvalue) + }); + + if let Some(subpattern) = subpattern.as_ref() { + self.storage_live_for_bindings(block, subpattern); + } + } + PatternKind::Array { ref prefix, ref slice, ref suffix } | + PatternKind::Slice { ref prefix, ref slice, ref suffix } => { + for subpattern in prefix.iter().chain(slice).chain(suffix) { + self.storage_live_for_bindings(block, subpattern); + } + } + PatternKind::Constant { .. } | PatternKind::Range { .. } | PatternKind::Wild => { + } + PatternKind::Deref { ref subpattern } => { + self.storage_live_for_bindings(block, subpattern); + } + PatternKind::Leaf { ref subpatterns } | + PatternKind::Variant { ref subpatterns, .. } => { + for subpattern in subpatterns { + self.storage_live_for_bindings(block, &subpattern.pattern); + } + } + } + } } /// List of blocks for each arm (and potentially other metadata in the @@ -665,6 +703,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { }; let source_info = self.source_info(binding.span); + self.cfg.push(block, Statement { + source_info: source_info, + kind: StatementKind::StorageLive(Lvalue::Var(var_index)) + }); self.cfg.push_assign(block, source_info, &Lvalue::Var(var_index), rvalue); } diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index 362e1e26fdf1e..814d56f44ac30 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -238,7 +238,8 @@ pub fn construct_const<'a, 'gcx, 'tcx>(hir: Cx<'a, 'gcx, 'tcx>, let span = tcx.map.span(item_id); let mut builder = Builder::new(hir, span); - let extent = ROOT_CODE_EXTENT; + let extent = tcx.region_maps.temporary_scope(ast_expr.id) + .unwrap_or(ROOT_CODE_EXTENT); let mut block = START_BLOCK; let _ = builder.in_scope(extent, block, |builder| { let expr = builder.hir.mirror(ast_expr); diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs index 1703fee9360cc..cae9e8379897c 100644 --- a/src/librustc_mir/build/scope.rs +++ b/src/librustc_mir/build/scope.rs @@ -107,6 +107,17 @@ pub struct Scope<'tcx> { /// `ScopeAuxiliary`, but kept here for convenience extent: CodeExtent, + /// Whether there's anything to do for the cleanup path, that is, + /// when unwinding through this scope. This includes destructors, + /// but not StorageDead statements, which don't get emitted at all + /// for unwinding, for several reasons: + /// * clang doesn't emit llvm.lifetime.end for C++ unwinding + /// * LLVM's memory dependency analysis can't handle it atm + /// * pollutting the cleanup MIR with StorageDead creates + /// landing pads even though there's no actual destructors + /// * freeing up stack space has no effect during unwinding + needs_cleanup: bool, + /// set of lvalues to drop when exiting this scope. This starts /// out empty but grows as variables are declared during the /// building process. This is a stack, so we always drop from the @@ -139,11 +150,19 @@ struct DropData<'tcx> { /// lvalue to drop location: Lvalue<'tcx>, - /// The cached block for the cleanups-on-diverge path. This block - /// contains code to run the current drop and all the preceding - /// drops (i.e. those having lower index in Drop’s Scope drop - /// array) - cached_block: Option + /// Whether this is a full value Drop, or just a StorageDead. + kind: DropKind +} + +enum DropKind { + Value { + /// The cached block for the cleanups-on-diverge path. This block + /// contains code to run the current drop and all the preceding + /// drops (i.e. those having lower index in Drop’s Scope drop + /// array) + cached_block: Option + }, + Storage } struct FreeData<'tcx> { @@ -182,7 +201,9 @@ impl<'tcx> Scope<'tcx> { fn invalidate_cache(&mut self) { self.cached_exits = FnvHashMap(); for dropdata in &mut self.drops { - dropdata.cached_block = None; + if let DropKind::Value { ref mut cached_block } = dropdata.kind { + *cached_block = None; + } } if let Some(ref mut freedata) = self.free { freedata.cached_block = None; @@ -194,8 +215,14 @@ impl<'tcx> Scope<'tcx> { /// Precondition: the caches must be fully filled (i.e. diverge_cleanup is called) in order for /// this method to work correctly. fn cached_block(&self) -> Option { - if let Some(data) = self.drops.last() { - Some(data.cached_block.expect("drop cache is not filled")) + let mut drops = self.drops.iter().rev().filter_map(|data| { + match data.kind { + DropKind::Value { cached_block } => Some(cached_block), + DropKind::Storage => None + } + }); + if let Some(cached_block) = drops.next() { + Some(cached_block.expect("drop cache is not filled")) } else if let Some(ref data) = self.free { Some(data.cached_block.expect("free cache is not filled")) } else { @@ -265,6 +292,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { id: id, visibility_scope: vis_scope, extent: extent, + needs_cleanup: false, drops: vec![], free: None, cached_exits: FnvHashMap() @@ -415,23 +443,37 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { extent: CodeExtent, lvalue: &Lvalue<'tcx>, lvalue_ty: Ty<'tcx>) { - if !self.hir.needs_drop(lvalue_ty) { - return - } + let needs_drop = self.hir.needs_drop(lvalue_ty); + let drop_kind = if needs_drop { + DropKind::Value { cached_block: None } + } else { + // Only temps and vars need their storage dead. + match *lvalue { + Lvalue::Temp(_) | Lvalue::Var(_) => DropKind::Storage, + _ => return + } + }; + for scope in self.scopes.iter_mut().rev() { if scope.extent == extent { + if let DropKind::Value { .. } = drop_kind { + scope.needs_cleanup = true; + } + // No need to invalidate any caches here. The just-scheduled drop will branch into // the drop that comes before it in the vector. scope.drops.push(DropData { span: span, location: lvalue.clone(), - cached_block: None + kind: drop_kind }); return; } else { // We must invalidate all the cached_blocks leading up to the scope we’re // looking for, because all of the blocks in the chain will become incorrect. - scope.invalidate_cache() + if let DropKind::Value { .. } = drop_kind { + scope.invalidate_cache() + } } } span_bug!(span, "extent {:?} not in scope to drop {:?}", extent, lvalue); @@ -453,6 +495,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // We also must invalidate the caches in the scope for which the free is scheduled // because the drops must branch into the free we schedule here. scope.invalidate_cache(); + scope.needs_cleanup = true; scope.free = Some(FreeData { span: span, value: value.clone(), @@ -478,10 +521,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { /// See module comment for more details. None indicates there’s no /// cleanup to do at this point. pub fn diverge_cleanup(&mut self) -> Option { - if self.scopes.iter().all(|scope| scope.drops.is_empty() && scope.free.is_none()) { + if !self.scopes.iter().any(|scope| scope.needs_cleanup) { return None; } - assert!(!self.scopes.is_empty()); // or `all` above would be true + assert!(!self.scopes.is_empty()); // or `any` above would be false let unit_temp = self.get_unit_temp(); let Builder { ref mut hir, ref mut cfg, ref mut scopes, @@ -510,7 +553,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { resumeblk }; - for scope in scopes.iter_mut().filter(|s| !s.drops.is_empty() || s.free.is_some()) { + for scope in scopes.iter_mut().filter(|s| s.needs_cleanup) { target = build_diverge_scope(hir.tcx(), cfg, &unit_temp, scope, target); } Some(target) @@ -591,21 +634,44 @@ fn build_scope_drops<'tcx>(cfg: &mut CFG<'tcx>, -> BlockAnd<()> { let mut iter = scope.drops.iter().rev().peekable(); while let Some(drop_data) = iter.next() { - // Try to find the next block with its cached block for us to diverge into in case the - // drop panics. - let on_diverge = iter.peek().iter().flat_map(|dd| dd.cached_block.into_iter()).next(); - // If there’s no `cached_block`s within current scope, we must look for one in the - // enclosing scope. - let on_diverge = on_diverge.or_else(||{ - earlier_scopes.iter().rev().flat_map(|s| s.cached_block()).next() - }); - let next = cfg.start_new_block(); - cfg.terminate(block, scope.source_info(drop_data.span), TerminatorKind::Drop { - location: drop_data.location.clone(), - target: next, - unwind: on_diverge - }); - block = next; + let source_info = scope.source_info(drop_data.span); + if let DropKind::Value { .. } = drop_data.kind { + // Try to find the next block with its cached block + // for us to diverge into in case the drop panics. + let on_diverge = iter.peek().iter().filter_map(|dd| { + match dd.kind { + DropKind::Value { cached_block } => cached_block, + DropKind::Storage => None + } + }).next(); + // If there’s no `cached_block`s within current scope, + // we must look for one in the enclosing scope. + let on_diverge = on_diverge.or_else(||{ + earlier_scopes.iter().rev().flat_map(|s| s.cached_block()).next() + }); + let next = cfg.start_new_block(); + cfg.terminate(block, source_info, TerminatorKind::Drop { + location: drop_data.location.clone(), + target: next, + unwind: on_diverge + }); + block = next; + } + match drop_data.kind { + DropKind::Value { .. } | + DropKind::Storage => { + // Only temps and vars need their storage dead. + match drop_data.location { + Lvalue::Temp(_) | Lvalue::Var(_) => {} + _ => continue + } + + cfg.push(block, Statement { + source_info: source_info, + kind: StatementKind::StorageDead(drop_data.location.clone()) + }); + } + } } block.unit() } @@ -653,7 +719,13 @@ fn build_diverge_scope<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, // *forward* order, so that we generate drops[0] first (right to // left in diagram above). for drop_data in &mut scope.drops { - target = if let Some(cached_block) = drop_data.cached_block { + // Only full value drops are emitted in the diverging path, + // not StorageDead. + let cached_block = match drop_data.kind { + DropKind::Value { ref mut cached_block } => cached_block, + DropKind::Storage => continue + }; + target = if let Some(cached_block) = *cached_block { cached_block } else { let block = cfg.start_new_cleanup_block(); @@ -663,7 +735,7 @@ fn build_diverge_scope<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, target: target, unwind: None }); - drop_data.cached_block = Some(block); + *cached_block = Some(block); block }; } diff --git a/src/librustc_mir/transform/deaggregator.rs b/src/librustc_mir/transform/deaggregator.rs index cd6f0ed9cbac6..cb3010a5cf43d 100644 --- a/src/librustc_mir/transform/deaggregator.rs +++ b/src/librustc_mir/transform/deaggregator.rs @@ -50,8 +50,7 @@ impl<'tcx> MirPass<'tcx> for Deaggregator { let orig_stmt = bb.statements.pop().unwrap(); let (lhs, rhs) = match orig_stmt.kind { StatementKind::Assign(ref lhs, ref rhs) => (lhs, rhs), - StatementKind::SetDiscriminant{ .. } => - span_bug!(src_info.span, "expected aggregate, not {:?}", orig_stmt.kind), + _ => span_bug!(src_info.span, "expected assign, not {:?}", orig_stmt), }; let (agg_kind, operands) = match rhs { &Rvalue::Aggregate(ref agg_kind, ref operands) => (agg_kind, operands), @@ -114,7 +113,7 @@ fn get_aggregate_statement_index<'a, 'tcx, 'b>(start: usize, let ref statement = statements[i]; let rhs = match statement.kind { StatementKind::Assign(_, ref rhs) => rhs, - StatementKind::SetDiscriminant{ .. } => continue, + _ => continue, }; let (kind, operands) = match rhs { &Rvalue::Aggregate(ref kind, ref operands) => (kind, operands), diff --git a/src/librustc_mir/transform/promote_consts.rs b/src/librustc_mir/transform/promote_consts.rs index eb0d8697f15d4..17dd85abd64e3 100644 --- a/src/librustc_mir/transform/promote_consts.rs +++ b/src/librustc_mir/transform/promote_consts.rs @@ -87,8 +87,12 @@ impl<'tcx> Visitor<'tcx> for TempCollector { if let Lvalue::Temp(index) = *lvalue { // Ignore drops, if the temp gets promoted, // then it's constant and thus drop is noop. - if let LvalueContext::Drop = context { - return; + // Storage live ranges are also irrelevant. + match context { + LvalueContext::Drop | + LvalueContext::StorageLive | + LvalueContext::StorageDead => return, + _ => {} } let temp = &mut self.temps[index]; @@ -219,12 +223,12 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { let (mut rvalue, mut call) = (None, None); let source_info = if stmt_idx < no_stmts { let statement = &mut self.source[bb].statements[stmt_idx]; - let mut rhs = match statement.kind { + let rhs = match statement.kind { StatementKind::Assign(_, ref mut rhs) => rhs, - StatementKind::SetDiscriminant{ .. } => - span_bug!(statement.source_info.span, - "cannot promote SetDiscriminant {:?}", - statement), + _ => { + span_bug!(statement.source_info.span, "{:?} is not an assignment", + statement); + } }; if self.keep_original { rvalue = Some(rhs.clone()); @@ -311,11 +315,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { StatementKind::Assign(_, ref mut rvalue) => { mem::replace(rvalue, Rvalue::Use(new_operand)) } - StatementKind::SetDiscriminant{ .. } => { - span_bug!(statement.source_info.span, - "cannot promote SetDiscriminant {:?}", - statement); - } + _ => bug!() } } Candidate::ShuffleIndices(bb) => { @@ -354,8 +354,10 @@ pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>, let statement = &mir[bb].statements[stmt_idx]; let dest = match statement.kind { StatementKind::Assign(ref dest, _) => dest, - StatementKind::SetDiscriminant{ .. } => - panic!("cannot promote SetDiscriminant"), + _ => { + span_bug!(statement.source_info.span, + "expected assignment to promote"); + } }; if let Lvalue::Temp(index) = *dest { if temps[index] == TempState::PromotedOut { @@ -408,7 +410,9 @@ pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>, for block in mir.basic_blocks_mut() { block.statements.retain(|statement| { match statement.kind { - StatementKind::Assign(Lvalue::Temp(index), _) => { + StatementKind::Assign(Lvalue::Temp(index), _) | + StatementKind::StorageLive(Lvalue::Temp(index)) | + StatementKind::StorageDead(Lvalue::Temp(index)) => { !promoted(index) } _ => true diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 7ebc72bcc5fca..87a10f1bb5bfc 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -854,7 +854,17 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { fn visit_statement(&mut self, bb: BasicBlock, statement: &Statement<'tcx>) { assert_eq!(self.location.block, bb); - self.nest(|this| this.super_statement(bb, statement)); + self.nest(|this| { + this.visit_source_info(&statement.source_info); + match statement.kind { + StatementKind::Assign(ref lvalue, ref rvalue) => { + this.visit_assign(bb, lvalue, rvalue); + } + StatementKind::SetDiscriminant { .. } | + StatementKind::StorageLive(_) | + StatementKind::StorageDead(_) => {} + } + }); self.location.statement_index += 1; } diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs index 934357c9e1da2..79e31167f2f7f 100644 --- a/src/librustc_mir/transform/type_check.rs +++ b/src/librustc_mir/transform/type_check.rs @@ -382,6 +382,15 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { variant_index); }; } + StatementKind::StorageLive(ref lv) | + StatementKind::StorageDead(ref lv) => { + match *lv { + Lvalue::Temp(_) | Lvalue::Var(_) => {} + _ => { + span_mirbug!(self, stmt, "bad lvalue: expected temp or var"); + } + } + } } } diff --git a/src/librustc_trans/mir/analyze.rs b/src/librustc_trans/mir/analyze.rs index d6dbefec03467..de5581af2912d 100644 --- a/src/librustc_trans/mir/analyze.rs +++ b/src/librustc_trans/mir/analyze.rs @@ -161,8 +161,11 @@ impl<'mir, 'bcx, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'bcx, 'tcx> { LvalueContext::Call => { self.mark_assigned(index); } - LvalueContext::Consume => { - } + + LvalueContext::StorageLive | + LvalueContext::StorageDead | + LvalueContext::Consume => {} + LvalueContext::Store | LvalueContext::Inspect | LvalueContext::Borrow { .. } | @@ -170,6 +173,7 @@ impl<'mir, 'bcx, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'bcx, 'tcx> { LvalueContext::Projection => { self.mark_as_lvalue(index); } + LvalueContext::Drop => { let ty = lvalue.ty(self.mir, self.bcx.tcx()); let ty = self.bcx.monomorphize(&ty.to_ty(self.bcx.tcx())); diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs index 7ca94b6356e40..31fee560fe369 100644 --- a/src/librustc_trans/mir/constant.rs +++ b/src/librustc_trans/mir/constant.rs @@ -285,6 +285,8 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { Err(err) => if failure.is_ok() { failure = Err(err); } } } + mir::StatementKind::StorageLive(_) | + mir::StatementKind::StorageDead(_) => {} mir::StatementKind::SetDiscriminant{ .. } => { span_bug!(span, "SetDiscriminant should not appear in constants?"); } diff --git a/src/librustc_trans/mir/statement.rs b/src/librustc_trans/mir/statement.rs index 7e3074f4cedf0..1167208955368 100644 --- a/src/librustc_trans/mir/statement.rs +++ b/src/librustc_trans/mir/statement.rs @@ -10,6 +10,7 @@ use rustc::mir::repr as mir; +use base; use common::{self, BlockAndBuilder}; use super::MirContext; @@ -71,6 +72,25 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { ); bcx } + mir::StatementKind::StorageLive(ref lvalue) => { + self.trans_storage_liveness(bcx, lvalue, base::Lifetime::Start) + } + mir::StatementKind::StorageDead(ref lvalue) => { + self.trans_storage_liveness(bcx, lvalue, base::Lifetime::End) + } + } + } + + fn trans_storage_liveness(&self, + bcx: BlockAndBuilder<'bcx, 'tcx>, + lvalue: &mir::Lvalue<'tcx>, + intrinsic: base::Lifetime) + -> BlockAndBuilder<'bcx, 'tcx> { + if let Some(index) = self.mir.local_index(lvalue) { + if let LocalRef::Lvalue(tr_lval) = self.locals[index] { + intrinsic.call(&bcx, tr_lval.llval); + } } + bcx } } diff --git a/src/test/codegen/lifetime_start_end.rs b/src/test/codegen/lifetime_start_end.rs new file mode 100644 index 0000000000000..cf91e7a8bcb66 --- /dev/null +++ b/src/test/codegen/lifetime_start_end.rs @@ -0,0 +1,54 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -O -C no-prepopulate-passes + +#![crate_type = "lib"] +#![feature(rustc_attrs)] + +// CHECK-LABEL: @test +#[no_mangle] +#[rustc_mir] // FIXME #27840 MIR has different codegen. +pub fn test() { + let a = 0; + &a; // keep variable in an alloca + +// CHECK: [[S_a:%[0-9]+]] = bitcast i32* %a to i8* +// CHECK: call void @llvm.lifetime.start(i{{[0-9 ]+}}, i8* [[S_a]]) + + { + let b = &Some(a); + &b; // keep variable in an alloca + +// CHECK: [[S_b:%[0-9]+]] = bitcast %"2.std::option::Option"** %b to i8* +// CHECK: call void @llvm.lifetime.start(i{{[0-9 ]+}}, i8* [[S_b]]) + +// CHECK: [[S_tmp2:%[0-9]+]] = bitcast %"2.std::option::Option"* %tmp2 to i8* +// CHECK: call void @llvm.lifetime.start(i{{[0-9 ]+}}, i8* [[S_tmp2]]) + +// CHECK: [[E_tmp2:%[0-9]+]] = bitcast %"2.std::option::Option"* %tmp2 to i8* +// CHECK: call void @llvm.lifetime.end(i{{[0-9 ]+}}, i8* [[E_tmp2]]) + +// CHECK: [[E_b:%[0-9]+]] = bitcast %"2.std::option::Option"** %b to i8* +// CHECK: call void @llvm.lifetime.end(i{{[0-9 ]+}}, i8* [[E_b]]) + } + + let c = 1; + &c; // keep variable in an alloca + +// CHECK: [[S_c:%[0-9]+]] = bitcast i32* %c to i8* +// CHECK: call void @llvm.lifetime.start(i{{[0-9 ]+}}, i8* [[S_c]]) + +// CHECK: [[E_c:%[0-9]+]] = bitcast i32* %c to i8* +// CHECK: call void @llvm.lifetime.end(i{{[0-9 ]+}}, i8* [[E_c]]) + +// CHECK: [[E_a:%[0-9]+]] = bitcast i32* %a to i8* +// CHECK: call void @llvm.lifetime.end(i{{[0-9 ]+}}, i8* [[E_a]]) +} diff --git a/src/test/debuginfo/function-arg-initialization.rs b/src/test/debuginfo/function-arg-initialization.rs index add0938409405..57654acaf73d8 100644 --- a/src/test/debuginfo/function-arg-initialization.rs +++ b/src/test/debuginfo/function-arg-initialization.rs @@ -288,8 +288,8 @@ fn while_expr(mut x: u64, y: u64, z: u64) -> u64 { } fn loop_expr(mut x: u64, y: u64, z: u64) -> u64 { - loop { // #break - x += z; + loop { + x += z; // #break if x + y > 1000 { return x; diff --git a/src/test/mir-opt/storage_ranges.rs b/src/test/mir-opt/storage_ranges.rs new file mode 100644 index 0000000000000..b116d9c3c41cf --- /dev/null +++ b/src/test/mir-opt/storage_ranges.rs @@ -0,0 +1,45 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn main() { + let a = 0; + { + let b = &Some(a); + } + let c = 1; +} + +// END RUST SOURCE +// START rustc.node4.PreTrans.after.mir +// bb0: { +// StorageLive(var0); // scope 0 at storage_ranges.rs:12:9: 12:10 +// var0 = const 0i32; // scope 0 at storage_ranges.rs:12:13: 12:14 +// StorageLive(var1); // scope 1 at storage_ranges.rs:14:13: 14:14 +// StorageLive(tmp1); // scope 1 at storage_ranges.rs:14:18: 14:25 +// StorageLive(tmp2); // scope 1 at storage_ranges.rs:14:23: 14:24 +// tmp2 = var0; // scope 1 at storage_ranges.rs:14:23: 14:24 +// tmp1 = std::prelude::v1::Some(tmp2,); // scope 1 at storage_ranges.rs:14:18: 14:25 +// var1 = &tmp1; // scope 1 at storage_ranges.rs:14:17: 14:25 +// StorageDead(tmp2); // scope 1 at storage_ranges.rs:14:23: 14:24 +// tmp0 = (); // scope 2 at storage_ranges.rs:13:5: 15:6 +// StorageDead(tmp1); // scope 1 at storage_ranges.rs:14:18: 14:25 +// StorageDead(var1); // scope 1 at storage_ranges.rs:14:13: 14:14 +// StorageLive(var2); // scope 1 at storage_ranges.rs:16:9: 16:10 +// var2 = const 1i32; // scope 1 at storage_ranges.rs:16:13: 16:14 +// return = (); // scope 3 at storage_ranges.rs:11:11: 17:2 +// StorageDead(var2); // scope 1 at storage_ranges.rs:16:9: 16:10 +// StorageDead(var0); // scope 0 at storage_ranges.rs:12:9: 12:10 +// goto -> bb1; // scope 0 at storage_ranges.rs:11:1: 17:2 +// } +// +// bb1: { +// return; // scope 0 at storage_ranges.rs:11:1: 17:2 +// } +// END rustc.node4.PreTrans.after.mir From 1bb14445160329c2ca5ff9c202e791ca0098d944 Mon Sep 17 00:00:00 2001 From: Eduard Burtescu Date: Sun, 14 Aug 2016 11:16:28 +0300 Subject: [PATCH 2/2] Get rid of the .note interpretation of rustc dylib metadata. --- src/librustc_metadata/loader.rs | 41 +++++++++++++++------------------ src/librustc_trans/base.rs | 13 ++++++++--- 2 files changed, 28 insertions(+), 26 deletions(-) diff --git a/src/librustc_metadata/loader.rs b/src/librustc_metadata/loader.rs index 48c8bcff1ec56..cf1dd71a0a12a 100644 --- a/src/librustc_metadata/loader.rs +++ b/src/librustc_metadata/loader.rs @@ -867,34 +867,29 @@ fn get_metadata_section_imp(target: &Target, flavor: CrateFlavor, filename: &Pat } pub fn meta_section_name(target: &Target) -> &'static str { + // Historical note: + // + // When using link.exe it was seen that the section name `.note.rustc` + // was getting shortened to `.note.ru`, and according to the PE and COFF + // specification: + // + // > Executable images do not use a string table and do not support + // > section names longer than 8 characters + // + // https://msdn.microsoft.com/en-us/library/windows/hardware/gg463119.aspx + // + // As a result, we choose a slightly shorter name! As to why + // `.note.rustc` works on MinGW, that's another good question... + if target.options.is_like_osx { - "__DATA,__note.rustc" - } else if target.options.is_like_msvc { - // When using link.exe it was seen that the section name `.note.rustc` - // was getting shortened to `.note.ru`, and according to the PE and COFF - // specification: - // - // > Executable images do not use a string table and do not support - // > section names longer than 8 characters - // - // https://msdn.microsoft.com/en-us/library/windows/hardware/gg463119.aspx - // - // As a result, we choose a slightly shorter name! As to why - // `.note.rustc` works on MinGW, that's another good question... - ".rustc" + "__DATA,.rustc" } else { - ".note.rustc" + ".rustc" } } -pub fn read_meta_section_name(target: &Target) -> &'static str { - if target.options.is_like_osx { - "__note.rustc" - } else if target.options.is_like_msvc { - ".rustc" - } else { - ".note.rustc" - } +pub fn read_meta_section_name(_target: &Target) -> &'static str { + ".rustc" } // A diagnostic function for dumping crate metadata to an output stream diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index 1077cb296c1ac..df893842337c4 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -2250,10 +2250,17 @@ fn write_metadata(cx: &SharedCrateContext, }; unsafe { llvm::LLVMSetInitializer(llglobal, llconst); - let name = + let section_name = cx.tcx().sess.cstore.metadata_section_name(&cx.sess().target.target); - let name = CString::new(name).unwrap(); - llvm::LLVMSetSection(llglobal, name.as_ptr()) + let name = CString::new(section_name).unwrap(); + llvm::LLVMSetSection(llglobal, name.as_ptr()); + + // Also generate a .section directive to force no + // flags, at least for ELF outputs, so that the + // metadata doesn't get loaded into memory. + let directive = format!(".section {}", section_name); + let directive = CString::new(directive).unwrap(); + llvm::LLVMSetModuleInlineAsm(cx.metadata_llmod(), directive.as_ptr()) } return metadata; }