From 229f45b51164d5979ca48aab1686a062034b3668 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Sat, 17 Feb 2018 17:46:15 +0200 Subject: [PATCH 1/9] rustc_mir: add a pass for splitting locals into their fields (aka SROA). --- src/librustc_mir/transform/mod.rs | 2 + .../transform/split_local_fields.rs | 429 ++++++++++++++++++ src/librustc_trans/mir/analyze.rs | 85 ++-- src/librustc_trans/mir/mod.rs | 2 +- src/test/compile-fail/huge-enum.rs | 4 +- src/test/compile-fail/huge-struct.rs | 2 +- src/test/run-pass/mir_raw_fat_ptr.rs | 6 +- src/test/run-pass/raw-fat-ptr.rs | 6 +- src/test/ui/print_type_sizes/niche-filling.rs | 6 +- src/test/ui/print_type_sizes/no_duplicates.rs | 4 +- src/test/ui/print_type_sizes/uninhabited.rs | 4 +- 11 files changed, 507 insertions(+), 43 deletions(-) create mode 100644 src/librustc_mir/transform/split_local_fields.rs diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index b16c1436a1cbe..182b317075d72 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -45,6 +45,7 @@ pub mod generator; pub mod inline; pub mod lower_128bit; pub mod uniform_array_move_out; +pub mod split_local_fields; pub(crate) fn provide(providers: &mut Providers) { self::qualify_consts::provide(providers); @@ -265,6 +266,7 @@ fn optimized_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx instcombine::InstCombine, deaggregator::Deaggregator, + split_local_fields::SplitLocalFields, copy_prop::CopyPropagation, remove_noop_landing_pads::RemoveNoopLandingPads, simplify::SimplifyCfg::new("final"), diff --git a/src/librustc_mir/transform/split_local_fields.rs b/src/librustc_mir/transform/split_local_fields.rs new file mode 100644 index 0000000000000..b0d596f81bdc4 --- /dev/null +++ b/src/librustc_mir/transform/split_local_fields.rs @@ -0,0 +1,429 @@ +// Copyright 2018 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. + +use rustc::hir; +use rustc::ty::{self, TyCtxt, Ty}; +use rustc::ty::util::IntTypeExt; +use rustc::middle::const_val::ConstVal; +use rustc::mir::*; +use rustc::mir::visit::{PlaceContext, MutVisitor, Visitor}; +use rustc::session::config::FullDebugInfo; +use rustc_const_math::ConstInt; +use rustc_data_structures::indexed_vec::{Idx, IndexVec}; +use std::collections::BTreeMap; +use syntax_pos::Span; +use transform::{MirPass, MirSource}; + +pub struct SplitLocalFields; + +impl MirPass for SplitLocalFields { + fn run_pass<'a, 'tcx>(&self, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + source: MirSource, + mir: &mut Mir<'tcx>) { + // Don't run on constant MIR, because trans might not be able to + // evaluate the modified MIR. + // FIXME(eddyb) Remove check after miri is merged. + let id = tcx.hir.as_local_node_id(source.def_id).unwrap(); + match (tcx.hir.body_owner_kind(id), source.promoted) { + (_, Some(_)) | + (hir::BodyOwnerKind::Const, _) | + (hir::BodyOwnerKind::Static(_), _) => return, + + (hir::BodyOwnerKind::Fn, _) => { + if tcx.is_const_fn(source.def_id) { + // Don't run on const functions, as, again, trans might not be able to evaluate + // the optimized IR. + return + } + } + } + + let mut collector = LocalPathCollector { + locals: mir.local_decls.iter().map(|decl| { + LocalPath::new(decl.ty) + }).collect() + }; + + // Can't split return and arguments. + collector.locals[RETURN_PLACE].make_opaque(); + for arg in mir.args_iter() { + collector.locals[arg].make_opaque(); + } + + // We need to keep user variables intact for debuginfo. + if tcx.sess.opts.debuginfo == FullDebugInfo { + for local in mir.vars_iter() { + collector.locals[local].make_opaque(); + } + } + + collector.visit_mir(mir); + + let replacements = collector.locals.iter_enumerated_mut().map(|(local, root)| { + // Don't rename locals that are entirely opaque. + match root.interior { + LocalPathInterior::Opaque { .. } => local.index()..local.index()+1, + LocalPathInterior::Split { .. } => { + let orig_decl = mir.local_decls[local].clone(); + let first = mir.local_decls.len(); + root.split_into_locals(tcx, &mut mir.local_decls, &orig_decl); + first..mir.local_decls.len() + } + } + }).collect::>(); + + // Expand `Storage{Live,Dead}` statements to refer to the replacement locals. + for bb in mir.basic_blocks_mut() { + bb.expand_statements(|stmt| { + let (local, is_live) = match stmt.kind { + StatementKind::StorageLive(local) => (local, true), + StatementKind::StorageDead(local) => (local, false), + _ => return None + }; + let range = replacements[local].clone(); + let source_info = stmt.source_info; + Some(range.map(move |i| { + let new_local = Local::new(i); + Statement { + source_info, + kind: if is_live { + StatementKind::StorageLive(new_local) + } else { + StatementKind::StorageDead(new_local) + } + } + })) + }); + } + drop(replacements); + + // Lastly, replace all the opaque paths with their new locals. + let mut replacer = LocalPathReplacer { + tcx, + span: mir.span, + locals: collector.locals + }; + replacer.visit_mir(mir); + } +} + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +struct VariantField { + variant_index: usize, + field: u64 +} + +struct LocalPath<'tcx> { + ty: Ty<'tcx>, + interior: LocalPathInterior<'tcx> +} + +enum LocalPathInterior<'tcx> { + /// This path needs to remain self-contained, e.g. due to accesses / borrows. + Opaque { + replacement_local: Option + }, + + /// This path' can be split into separate locals for its fields. + Split { + discr_local: Option, + fields: BTreeMap> + } +} + +impl<'a, 'tcx> LocalPath<'tcx> { + fn new(ty: Ty<'tcx>) -> Self { + let mut path = LocalPath { + ty, + interior: LocalPathInterior::Split { + discr_local: None, + fields: BTreeMap::new() + } + }; + + if let ty::TyAdt(adt_def, _) = ty.sty { + // Unions have (observably) overlapping members, so don't split them. + if adt_def.is_union() { + path.make_opaque(); + } + } + + path + } + + fn make_opaque(&mut self) { + if let LocalPathInterior::Split { .. } = self.interior { + self.interior = LocalPathInterior::Opaque { + replacement_local: None + }; + } + } + + fn project(&mut self, elem: &PlaceElem<'tcx>, variant_index: usize) -> Option<&mut Self> { + match *elem { + ProjectionElem::Field(f, ty) => { + if let LocalPathInterior::Split { ref mut fields, .. } = self.interior { + let field = VariantField { + variant_index, + field: f.index() as u64 + }; + return Some(fields.entry(field).or_insert(LocalPath::new(ty))); + } + } + ProjectionElem::Downcast(..) => { + bug!("should be handled by the caller of `LocalPath::project`"); + } + // FIXME(eddyb) support indexing by constants. + ProjectionElem::ConstantIndex { .. } | + ProjectionElem::Subslice { .. } => {} + // Can't support without alias analysis. + ProjectionElem::Index(_) | + ProjectionElem::Deref => {} + } + + // If we can't project, we must be opaque. + self.make_opaque(); + None + } + + fn split_into_locals(&mut self, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + local_decls: &mut IndexVec>, + base_decl: &LocalDecl<'tcx>) { + match self.interior { + LocalPathInterior::Opaque { ref mut replacement_local } => { + let mut decl = base_decl.clone(); + decl.ty = self.ty; + decl.name = None; + decl.is_user_variable = false; + *replacement_local = Some(local_decls.push(decl)); + } + LocalPathInterior::Split { + ref mut discr_local, + ref mut fields + } => { + if let ty::TyAdt(adt_def, _) = self.ty.sty { + if adt_def.is_enum() { + let discr_ty = adt_def.repr.discr_type().to_ty(tcx); + let mut decl = base_decl.clone(); + decl.ty = discr_ty; + decl.name = None; + decl.is_user_variable = false; + *discr_local = Some(local_decls.push(decl)); + } + } + for field in fields.values_mut() { + field.split_into_locals(tcx, local_decls, base_decl); + } + } + } + } +} + +struct LocalPathCollector<'tcx> { + locals: IndexVec> +} + +impl<'tcx> LocalPathCollector<'tcx> { + fn place_path(&mut self, place: &Place<'tcx>) -> Option<&mut LocalPath<'tcx>> { + match *place { + Place::Local(local) => Some(&mut self.locals[local]), + Place::Static(_) => None, + Place::Projection(box ref proj) => { + let (base, variant_index) = match proj.base { + Place::Projection(box Projection { + ref base, + elem: ProjectionElem::Downcast(_, variant_index) + }) => (base, variant_index), + ref base => (base, 0), + }; + + // Locals used as indices shouldn't be optimized. + if let ProjectionElem::Index(i) = proj.elem { + self.locals[i].make_opaque(); + } + + self.place_path(base)?.project(&proj.elem, variant_index) + } + } + } +} + +impl<'tcx> Visitor<'tcx> for LocalPathCollector<'tcx> { + fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, _: Location) { + if context.is_use() { + if let Some(path) = self.place_path(place) { + path.make_opaque(); + } + } + } + + // Special-case `(Set)Discriminant(place)` to not mark `place` as opaque. + fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { + if let Rvalue::Discriminant(ref place) = *rvalue { + self.place_path(place); + return; + } + self.super_rvalue(rvalue, location); + } + + fn visit_statement(&mut self, + block: BasicBlock, + statement: &Statement<'tcx>, + location: Location) { + if let StatementKind::SetDiscriminant { ref place, .. } = statement.kind { + self.place_path(place); + return; + } + self.super_statement(block, statement, location); + } +} + +struct LocalPathReplacer<'a, 'tcx: 'a> { + tcx: TyCtxt<'a, 'tcx, 'tcx>, + span: Span, + locals: IndexVec> +} + +impl<'a, 'tcx> LocalPathReplacer<'a, 'tcx> { + fn replace(&mut self, place: &mut Place<'tcx>) -> Option<&mut LocalPath<'tcx>> { + let path = match *place { + Place::Local(ref mut local) => { + let path = &mut self.locals[*local]; + match path.interior { + LocalPathInterior::Opaque { replacement_local } => { + *local = replacement_local.unwrap_or(*local); + } + _ => {} + } + return Some(path); + } + Place::Static(_) => return None, + Place::Projection(box ref mut proj) => { + let (base, variant_index) = match proj.base { + Place::Projection(box Projection { + ref mut base, + elem: ProjectionElem::Downcast(_, variant_index) + }) => (base, variant_index), + ref mut base => (base, 0) + }; + self.replace(base)?.project(&proj.elem, variant_index)? + } + }; + match path.interior { + LocalPathInterior::Opaque { replacement_local } => { + *place = Place::Local(replacement_local.expect("missing replacement")); + } + _ => {} + } + Some(path) + } +} + +impl<'a, 'tcx> MutVisitor<'tcx> for LocalPathReplacer<'a, 'tcx> { + fn visit_place(&mut self, place: &mut Place<'tcx>, _: PlaceContext, _: Location) { + self.replace(place); + } + + // Special-case `(Set)Discriminant(place)` to use `discr_local` for `place`. + fn visit_rvalue(&mut self, rvalue: &mut Rvalue<'tcx>, location: Location) { + let tcx = self.tcx; + let span = self.span; + + let mut replacement = None; + if let Rvalue::Discriminant(ref mut place) = rvalue { + if let Some(path) = self.replace(place) { + if let LocalPathInterior::Split { discr_local, .. } = path.interior { + if let ty::TyAdt(adt_def, _) = path.ty.sty { + if adt_def.is_enum() { + let discr_place = + Place::Local(discr_local.expect("missing discriminant")); + replacement = Some(Rvalue::Use(Operand::Copy(discr_place))); + } + } + + // Non-enums don't have discriminants other than `0u8`. + if replacement.is_none() { + let discr = tcx.mk_const(ty::Const { + val: ConstVal::Integral(ConstInt::U8(0)), + ty: tcx.types.u8 + }); + replacement = Some(Rvalue::Use(Operand::Constant(box Constant { + span, + ty: discr.ty, + literal: Literal::Value { + value: discr + }, + }))); + } + } + } + } + // HACK(eddyb) clean this double matching post-NLL. + if let Rvalue::Discriminant(_) = rvalue { + if let Some(replacement) = replacement { + *rvalue = replacement; + } + return; + } + self.super_rvalue(rvalue, location); + } + + fn visit_statement(&mut self, + block: BasicBlock, + statement: &mut Statement<'tcx>, + location: Location) { + self.span = statement.source_info.span; + + let tcx = self.tcx; + let span = self.span; + + let mut replacement = None; + if let StatementKind::SetDiscriminant { ref mut place, variant_index } = statement.kind { + if let Some(path) = self.replace(place) { + if let LocalPathInterior::Split { discr_local, .. } = path.interior { + if let ty::TyAdt(adt_def, _) = path.ty.sty { + if adt_def.is_enum() { + let discr_place = + Place::Local(discr_local.expect("missing discriminant")); + let discr = adt_def.discriminant_for_variant(tcx, variant_index); + let discr = tcx.mk_const(ty::Const { + val: ConstVal::Integral(discr), + ty: adt_def.repr.discr_type().to_ty(tcx) + }); + let discr = Rvalue::Use(Operand::Constant(box Constant { + span, + ty: discr.ty, + literal: Literal::Value { + value: discr + }, + })); + replacement = Some(StatementKind::Assign(discr_place, discr)); + } + } + + // Non-enums don't have discriminants to set. + if replacement.is_none() { + replacement = Some(StatementKind::Nop); + } + } + } + } + // HACK(eddyb) clean this double matching post-NLL. + if let StatementKind::SetDiscriminant { .. } = statement.kind { + if let Some(replacement) = replacement { + statement.kind = replacement; + } + return; + } + self.super_statement(block, statement, location); + } +} diff --git a/src/librustc_trans/mir/analyze.rs b/src/librustc_trans/mir/analyze.rs index f683703ce6d53..abeb1238e2573 100644 --- a/src/librustc_trans/mir/analyze.rs +++ b/src/librustc_trans/mir/analyze.rs @@ -12,6 +12,7 @@ //! which do not. use rustc_data_structures::bitvec::BitVector; +use rustc_data_structures::control_flow_graph::dominators::Dominators; use rustc_data_structures::indexed_vec::{Idx, IndexVec}; use rustc::middle::const_val::ConstVal; use rustc::mir::{self, Location, TerminatorKind, Literal}; @@ -22,7 +23,7 @@ use rustc::ty::layout::LayoutOf; use type_of::LayoutLlvmExt; use super::FunctionCx; -pub fn memory_locals<'a, 'tcx>(fx: &FunctionCx<'a, 'tcx>) -> BitVector { +pub fn non_ssa_locals<'a, 'tcx>(fx: &FunctionCx<'a, 'tcx>) -> BitVector { let mir = fx.mir; let mut analyzer = LocalAnalyzer::new(fx); @@ -44,43 +45,60 @@ pub fn memory_locals<'a, 'tcx>(fx: &FunctionCx<'a, 'tcx>) -> BitVector { // (e.g. structs) into an alloca unconditionally, just so // that we don't have to deal with having two pathways // (gep vs extractvalue etc). - analyzer.mark_as_memory(mir::Local::new(index)); + analyzer.not_ssa(mir::Local::new(index)); } } - analyzer.memory_locals + analyzer.non_ssa_locals } struct LocalAnalyzer<'mir, 'a: 'mir, 'tcx: 'a> { fx: &'mir FunctionCx<'a, 'tcx>, - memory_locals: BitVector, - seen_assigned: BitVector + dominators: Dominators, + non_ssa_locals: BitVector, + // The location of the first visited direct assignment to each + // local, or an invalid location (out of bounds `block` index). + first_assignment: IndexVec } impl<'mir, 'a, 'tcx> LocalAnalyzer<'mir, 'a, 'tcx> { fn new(fx: &'mir FunctionCx<'a, 'tcx>) -> LocalAnalyzer<'mir, 'a, 'tcx> { + let invalid_location = + mir::BasicBlock::new(fx.mir.basic_blocks().len()).start_location(); let mut analyzer = LocalAnalyzer { fx, - memory_locals: BitVector::new(fx.mir.local_decls.len()), - seen_assigned: BitVector::new(fx.mir.local_decls.len()) + dominators: fx.mir.dominators(), + non_ssa_locals: BitVector::new(fx.mir.local_decls.len()), + first_assignment: IndexVec::from_elem(invalid_location, &fx.mir.local_decls) }; // Arguments get assigned to by means of the function being called - for idx in 0..fx.mir.arg_count { - analyzer.seen_assigned.insert(idx + 1); + for arg in fx.mir.args_iter() { + analyzer.first_assignment[arg] = mir::START_BLOCK.start_location(); } analyzer } - fn mark_as_memory(&mut self, local: mir::Local) { - debug!("marking {:?} as memory", local); - self.memory_locals.insert(local.index()); + fn first_assignment(&self, local: mir::Local) -> Option { + let location = self.first_assignment[local]; + if location.block.index() < self.fx.mir.basic_blocks().len() { + Some(location) + } else { + None + } } - fn mark_assigned(&mut self, local: mir::Local) { - if !self.seen_assigned.insert(local.index()) { - self.mark_as_memory(local); + fn not_ssa(&mut self, local: mir::Local) { + debug!("marking {:?} as non-SSA", local); + self.non_ssa_locals.insert(local.index()); + } + + fn assign(&mut self, local: mir::Local, location: Location) { + if self.first_assignment(local).is_some() { + self.not_ssa(local); + } else { + self.first_assignment[local] = location; } } } @@ -94,9 +112,9 @@ impl<'mir, 'a, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'tcx> { debug!("visit_assign(block={:?}, place={:?}, rvalue={:?})", block, place, rvalue); if let mir::Place::Local(index) = *place { - self.mark_assigned(index); + self.assign(index, location); if !self.fx.rvalue_creates_operand(rvalue) { - self.mark_as_memory(index); + self.not_ssa(index); } } else { self.visit_place(place, PlaceContext::Store, location); @@ -160,7 +178,7 @@ impl<'mir, 'a, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'tcx> { if layout.is_llvm_immediate() || layout.is_llvm_scalar_pair() { // Recurse with the same context, instead of `Projection`, // potentially stopping at non-operand projections, - // which would trigger `mark_as_memory` on locals. + // which would trigger `not_ssa` on locals. self.visit_place(&proj.base, context, location); return; } @@ -177,35 +195,50 @@ impl<'mir, 'a, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'tcx> { } fn visit_local(&mut self, - &index: &mir::Local, + &local: &mir::Local, context: PlaceContext<'tcx>, - _: Location) { + location: Location) { match context { PlaceContext::Call => { - self.mark_assigned(index); + self.assign(local, location); } PlaceContext::StorageLive | PlaceContext::StorageDead | - PlaceContext::Validate | + PlaceContext::Validate => {} + PlaceContext::Copy | - PlaceContext::Move => {} + PlaceContext::Move => { + // Reads from uninitialized variables (e.g. in dead code, after + // optimizations) require locals to be in (uninitialized) memory. + // NB: there can be uninitialized reads of a local visited after + // an assignment to that local, if they happen on disjoint paths. + let ssa_read = match self.first_assignment(local) { + Some(assignment_location) => { + assignment_location.dominates(&location, &self.dominators) + } + None => false + }; + if !ssa_read { + self.not_ssa(local); + } + } PlaceContext::Inspect | PlaceContext::Store | PlaceContext::AsmOutput | PlaceContext::Borrow { .. } | PlaceContext::Projection(..) => { - self.mark_as_memory(index); + self.not_ssa(local); } PlaceContext::Drop => { - let ty = mir::Place::Local(index).ty(self.fx.mir, self.fx.cx.tcx); + let ty = mir::Place::Local(local).ty(self.fx.mir, self.fx.cx.tcx); let ty = self.fx.monomorphize(&ty.to_ty(self.fx.cx.tcx)); // Only need the place if we're actually dropping it. if self.fx.cx.type_needs_drop(ty) { - self.mark_as_memory(index); + self.not_ssa(local); } } } diff --git a/src/librustc_trans/mir/mod.rs b/src/librustc_trans/mir/mod.rs index da01592d9118a..9de70dfc0d002 100644 --- a/src/librustc_trans/mir/mod.rs +++ b/src/librustc_trans/mir/mod.rs @@ -244,7 +244,7 @@ pub fn trans_mir<'a, 'tcx: 'a>( }, }; - let memory_locals = analyze::memory_locals(&fx); + let memory_locals = analyze::non_ssa_locals(&fx); // Allocate variable and temp allocas fx.locals = { diff --git a/src/test/compile-fail/huge-enum.rs b/src/test/compile-fail/huge-enum.rs index 6e7c05370b99d..63478ec3136f9 100644 --- a/src/test/compile-fail/huge-enum.rs +++ b/src/test/compile-fail/huge-enum.rs @@ -14,10 +14,10 @@ #[cfg(target_pointer_width = "32")] fn main() { - let big: Option<[u32; (1<<29)-1]> = None; + let ref big: Option<[u32; (1<<29)-1]> = None; } #[cfg(target_pointer_width = "64")] fn main() { - let big: Option<[u32; (1<<45)-1]> = None; + let ref big: Option<[u32; (1<<45)-1]> = None; } diff --git a/src/test/compile-fail/huge-struct.rs b/src/test/compile-fail/huge-struct.rs index a10c61d6606d0..c9c077dfbbce9 100644 --- a/src/test/compile-fail/huge-struct.rs +++ b/src/test/compile-fail/huge-struct.rs @@ -50,5 +50,5 @@ struct S1k { val: S32> } struct S1M { val: S1k> } fn main() { - let fat: Option>>> = None; + let ref fat: Option>>> = None; } diff --git a/src/test/run-pass/mir_raw_fat_ptr.rs b/src/test/run-pass/mir_raw_fat_ptr.rs index 846318ec4fd34..fa8cd9e1c0c77 100644 --- a/src/test/run-pass/mir_raw_fat_ptr.rs +++ b/src/test/run-pass/mir_raw_fat_ptr.rs @@ -149,9 +149,9 @@ fn main() { assert_inorder(buf, compare_foo); // check ordering for structs containing arrays - let ss: (S<[u8; 2]>, - S<[u8; 3]>, - S<[u8; 2]>) = ( + let ref ss: (S<[u8; 2]>, + S<[u8; 3]>, + S<[u8; 2]>) = ( S(7, [8, 9]), S(10, [11, 12, 13]), S(4, [5, 6]) diff --git a/src/test/run-pass/raw-fat-ptr.rs b/src/test/run-pass/raw-fat-ptr.rs index b4572f4577133..b98fa41309fc9 100644 --- a/src/test/run-pass/raw-fat-ptr.rs +++ b/src/test/run-pass/raw-fat-ptr.rs @@ -112,9 +112,9 @@ fn main() { assert_inorder(buf); // check ordering for structs containing arrays - let ss: (S<[u8; 2]>, - S<[u8; 3]>, - S<[u8; 2]>) = ( + let ref ss: (S<[u8; 2]>, + S<[u8; 3]>, + S<[u8; 2]>) = ( S(7, [8, 9]), S(10, [11, 12, 13]), S(4, [5, 6]) diff --git a/src/test/ui/print_type_sizes/niche-filling.rs b/src/test/ui/print_type_sizes/niche-filling.rs index 7f234e243e926..5810f27944dc9 100644 --- a/src/test/ui/print_type_sizes/niche-filling.rs +++ b/src/test/ui/print_type_sizes/niche-filling.rs @@ -86,8 +86,8 @@ fn start(_: isize, _: *const *const u8) -> isize { let _b: MyOption = Default::default(); let _c: MyOption = Default::default(); let _b: MyOption> = Default::default(); - let _e: Enum4<(), char, (), ()> = Enum4::One(()); - let _f: Enum4<(), (), bool, ()> = Enum4::One(()); - let _g: Enum4<(), (), (), MyOption> = Enum4::One(()); + let ref _e: Enum4<(), char, (), ()> = Enum4::One(()); + let ref _f: Enum4<(), (), bool, ()> = Enum4::One(()); + let ref _g: Enum4<(), (), (), MyOption> = Enum4::One(()); 0 } diff --git a/src/test/ui/print_type_sizes/no_duplicates.rs b/src/test/ui/print_type_sizes/no_duplicates.rs index d9b90260364b2..2fb1d39589b64 100644 --- a/src/test/ui/print_type_sizes/no_duplicates.rs +++ b/src/test/ui/print_type_sizes/no_duplicates.rs @@ -20,11 +20,11 @@ pub struct SevenBytes([u8; 7]); pub fn f1() { - let _s: SevenBytes = SevenBytes([0; 7]); + let ref _s: SevenBytes = SevenBytes([0; 7]); } #[start] fn start(_: isize, _: *const *const u8) -> isize { - let _s: SevenBytes = SevenBytes([0; 7]); + let ref _s: SevenBytes = SevenBytes([0; 7]); 0 } diff --git a/src/test/ui/print_type_sizes/uninhabited.rs b/src/test/ui/print_type_sizes/uninhabited.rs index 4d0396903e555..9e53ae56d6800 100644 --- a/src/test/ui/print_type_sizes/uninhabited.rs +++ b/src/test/ui/print_type_sizes/uninhabited.rs @@ -16,7 +16,7 @@ #[start] fn start(_: isize, _: *const *const u8) -> isize { - let _x: Option = None; - let _y: Result = Ok(42); + let ref _x: Option = None; + let ref _y: Result = Ok(42); 0 } From 5c02a36ce986ad59368e99a5b1973000a692477e Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Thu, 28 Dec 2017 06:03:30 +0200 Subject: [PATCH 2/9] rustc_mir: move all analyses to an analysis module. --- .../{util => analysis}/alignment.rs | 0 .../{ => analysis}/dataflow/at_location.rs | 4 ++-- .../dataflow/drop_flag_effects.rs | 0 .../{ => analysis}/dataflow/graphviz.rs | 0 .../dataflow/impls/borrowed_locals.rs | 2 +- .../{ => analysis}/dataflow/impls/borrows.rs | 4 ++-- .../{ => analysis}/dataflow/impls/mod.rs | 0 .../dataflow/impls/storage_liveness.rs | 2 +- .../{ => analysis}/dataflow/mod.rs | 0 .../dataflow/move_paths/abs_domain.rs | 0 .../dataflow/move_paths/builder.rs | 0 .../{ => analysis}/dataflow/move_paths/mod.rs | 0 .../{util => analysis}/def_use.rs | 0 .../{util => analysis}/liveness.rs | 0 src/librustc_mir/analysis/mod.rs | 14 ++++++++++++ .../borrow_check/error_reporting.rs | 4 ++-- src/librustc_mir/borrow_check/flows.rs | 8 +++---- src/librustc_mir/borrow_check/mod.rs | 22 +++++++++---------- .../borrow_check/nll/explain_borrow/mod.rs | 4 ++-- src/librustc_mir/borrow_check/nll/mod.rs | 8 +++---- .../borrow_check/nll/type_check/liveness.rs | 8 +++---- .../borrow_check/nll/type_check/mod.rs | 8 +++---- src/librustc_mir/lib.rs | 2 +- .../transform/add_moves_for_packed_drops.rs | 4 ++-- src/librustc_mir/transform/check_unsafety.rs | 4 ++-- src/librustc_mir/transform/copy_prop.rs | 2 +- src/librustc_mir/transform/elaborate_drops.rs | 14 ++++++------ src/librustc_mir/transform/generator.rs | 6 ++--- src/librustc_mir/transform/rustc_peek.rs | 21 +++++++++--------- src/librustc_mir/util/mod.rs | 4 ---- 30 files changed, 78 insertions(+), 67 deletions(-) rename src/librustc_mir/{util => analysis}/alignment.rs (100%) rename src/librustc_mir/{ => analysis}/dataflow/at_location.rs (98%) rename src/librustc_mir/{ => analysis}/dataflow/drop_flag_effects.rs (100%) rename src/librustc_mir/{ => analysis}/dataflow/graphviz.rs (100%) rename src/librustc_mir/{ => analysis}/dataflow/impls/borrowed_locals.rs (98%) rename src/librustc_mir/{ => analysis}/dataflow/impls/borrows.rs (99%) rename src/librustc_mir/{ => analysis}/dataflow/impls/mod.rs (100%) rename src/librustc_mir/{ => analysis}/dataflow/impls/storage_liveness.rs (98%) rename src/librustc_mir/{ => analysis}/dataflow/mod.rs (100%) rename src/librustc_mir/{ => analysis}/dataflow/move_paths/abs_domain.rs (100%) rename src/librustc_mir/{ => analysis}/dataflow/move_paths/builder.rs (100%) rename src/librustc_mir/{ => analysis}/dataflow/move_paths/mod.rs (100%) rename src/librustc_mir/{util => analysis}/def_use.rs (100%) rename src/librustc_mir/{util => analysis}/liveness.rs (100%) create mode 100644 src/librustc_mir/analysis/mod.rs diff --git a/src/librustc_mir/util/alignment.rs b/src/librustc_mir/analysis/alignment.rs similarity index 100% rename from src/librustc_mir/util/alignment.rs rename to src/librustc_mir/analysis/alignment.rs diff --git a/src/librustc_mir/dataflow/at_location.rs b/src/librustc_mir/analysis/dataflow/at_location.rs similarity index 98% rename from src/librustc_mir/dataflow/at_location.rs rename to src/librustc_mir/analysis/dataflow/at_location.rs index b1f73bfbe2284..987c026ee8280 100644 --- a/src/librustc_mir/dataflow/at_location.rs +++ b/src/librustc_mir/analysis/dataflow/at_location.rs @@ -15,8 +15,8 @@ use rustc::mir::{BasicBlock, Location}; use rustc_data_structures::indexed_set::{self, IdxSetBuf}; use rustc_data_structures::indexed_vec::Idx; -use dataflow::{BitDenotation, BlockSets, DataflowResults}; -use dataflow::move_paths::{HasMoveData, MovePathIndex}; +use analysis::dataflow::{BitDenotation, BlockSets, DataflowResults}; +use analysis::dataflow::move_paths::{HasMoveData, MovePathIndex}; use std::iter; diff --git a/src/librustc_mir/dataflow/drop_flag_effects.rs b/src/librustc_mir/analysis/dataflow/drop_flag_effects.rs similarity index 100% rename from src/librustc_mir/dataflow/drop_flag_effects.rs rename to src/librustc_mir/analysis/dataflow/drop_flag_effects.rs diff --git a/src/librustc_mir/dataflow/graphviz.rs b/src/librustc_mir/analysis/dataflow/graphviz.rs similarity index 100% rename from src/librustc_mir/dataflow/graphviz.rs rename to src/librustc_mir/analysis/dataflow/graphviz.rs diff --git a/src/librustc_mir/dataflow/impls/borrowed_locals.rs b/src/librustc_mir/analysis/dataflow/impls/borrowed_locals.rs similarity index 98% rename from src/librustc_mir/dataflow/impls/borrowed_locals.rs rename to src/librustc_mir/analysis/dataflow/impls/borrowed_locals.rs index 244e8b5ccd7e4..32b761e1932b4 100644 --- a/src/librustc_mir/dataflow/impls/borrowed_locals.rs +++ b/src/librustc_mir/analysis/dataflow/impls/borrowed_locals.rs @@ -12,7 +12,7 @@ pub use super::*; use rustc::mir::*; use rustc::mir::visit::Visitor; -use dataflow::BitDenotation; +use analysis::dataflow::BitDenotation; /// This calculates if any part of a MIR local could have previously been borrowed. /// This means that once a local has been borrowed, its bit will always be set diff --git a/src/librustc_mir/dataflow/impls/borrows.rs b/src/librustc_mir/analysis/dataflow/impls/borrows.rs similarity index 99% rename from src/librustc_mir/dataflow/impls/borrows.rs rename to src/librustc_mir/analysis/dataflow/impls/borrows.rs index 8ab4035cf4aec..462737092f761 100644 --- a/src/librustc_mir/dataflow/impls/borrows.rs +++ b/src/librustc_mir/analysis/dataflow/impls/borrows.rs @@ -23,8 +23,8 @@ use rustc_data_structures::bitslice::{BitwiseOperator}; use rustc_data_structures::indexed_set::{IdxSet}; use rustc_data_structures::indexed_vec::{Idx, IndexVec}; -use dataflow::{BitDenotation, BlockSets, InitialFlow}; -pub use dataflow::indexes::{BorrowIndex, ReserveOrActivateIndex}; +use analysis::dataflow::{BitDenotation, BlockSets, InitialFlow}; +pub use analysis::dataflow::indexes::{BorrowIndex, ReserveOrActivateIndex}; use borrow_check::nll::region_infer::RegionInferenceContext; use borrow_check::nll::ToRegionVid; diff --git a/src/librustc_mir/dataflow/impls/mod.rs b/src/librustc_mir/analysis/dataflow/impls/mod.rs similarity index 100% rename from src/librustc_mir/dataflow/impls/mod.rs rename to src/librustc_mir/analysis/dataflow/impls/mod.rs diff --git a/src/librustc_mir/dataflow/impls/storage_liveness.rs b/src/librustc_mir/analysis/dataflow/impls/storage_liveness.rs similarity index 98% rename from src/librustc_mir/dataflow/impls/storage_liveness.rs rename to src/librustc_mir/analysis/dataflow/impls/storage_liveness.rs index dea61542ac4e2..4856be70d555b 100644 --- a/src/librustc_mir/dataflow/impls/storage_liveness.rs +++ b/src/librustc_mir/analysis/dataflow/impls/storage_liveness.rs @@ -11,7 +11,7 @@ pub use super::*; use rustc::mir::*; -use dataflow::BitDenotation; +use analysis::dataflow::BitDenotation; #[derive(Copy, Clone)] pub struct MaybeStorageLive<'a, 'tcx: 'a> { diff --git a/src/librustc_mir/dataflow/mod.rs b/src/librustc_mir/analysis/dataflow/mod.rs similarity index 100% rename from src/librustc_mir/dataflow/mod.rs rename to src/librustc_mir/analysis/dataflow/mod.rs diff --git a/src/librustc_mir/dataflow/move_paths/abs_domain.rs b/src/librustc_mir/analysis/dataflow/move_paths/abs_domain.rs similarity index 100% rename from src/librustc_mir/dataflow/move_paths/abs_domain.rs rename to src/librustc_mir/analysis/dataflow/move_paths/abs_domain.rs diff --git a/src/librustc_mir/dataflow/move_paths/builder.rs b/src/librustc_mir/analysis/dataflow/move_paths/builder.rs similarity index 100% rename from src/librustc_mir/dataflow/move_paths/builder.rs rename to src/librustc_mir/analysis/dataflow/move_paths/builder.rs diff --git a/src/librustc_mir/dataflow/move_paths/mod.rs b/src/librustc_mir/analysis/dataflow/move_paths/mod.rs similarity index 100% rename from src/librustc_mir/dataflow/move_paths/mod.rs rename to src/librustc_mir/analysis/dataflow/move_paths/mod.rs diff --git a/src/librustc_mir/util/def_use.rs b/src/librustc_mir/analysis/def_use.rs similarity index 100% rename from src/librustc_mir/util/def_use.rs rename to src/librustc_mir/analysis/def_use.rs diff --git a/src/librustc_mir/util/liveness.rs b/src/librustc_mir/analysis/liveness.rs similarity index 100% rename from src/librustc_mir/util/liveness.rs rename to src/librustc_mir/analysis/liveness.rs diff --git a/src/librustc_mir/analysis/mod.rs b/src/librustc_mir/analysis/mod.rs new file mode 100644 index 0000000000000..14b564b444faf --- /dev/null +++ b/src/librustc_mir/analysis/mod.rs @@ -0,0 +1,14 @@ +// Copyright 2017 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. + +pub mod alignment; +pub mod dataflow; +pub mod def_use; +pub mod liveness; diff --git a/src/librustc_mir/borrow_check/error_reporting.rs b/src/librustc_mir/borrow_check/error_reporting.rs index 34551e8e76f59..a84e31df27398 100644 --- a/src/librustc_mir/borrow_check/error_reporting.rs +++ b/src/librustc_mir/borrow_check/error_reporting.rs @@ -19,8 +19,8 @@ use std::rc::Rc; use super::{MirBorrowckCtxt, Context}; use super::{InitializationRequiringAction, PrefixSet}; -use dataflow::{ActiveBorrows, BorrowData, FlowAtLocation, MovingOutStatements}; -use dataflow::move_paths::MovePathIndex; +use analysis::dataflow::{ActiveBorrows, BorrowData, FlowAtLocation, MovingOutStatements}; +use analysis::dataflow::move_paths::MovePathIndex; use util::borrowck_errors::{BorrowckErrors, Origin}; impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { diff --git a/src/librustc_mir/borrow_check/flows.rs b/src/librustc_mir/borrow_check/flows.rs index ba966c9d4e316..46cd6603b2bd5 100644 --- a/src/librustc_mir/borrow_check/flows.rs +++ b/src/librustc_mir/borrow_check/flows.rs @@ -15,10 +15,10 @@ use rustc::mir::{BasicBlock, Location}; -use dataflow::{MaybeInitializedPlaces, MaybeUninitializedPlaces}; -use dataflow::{EverInitializedPlaces, MovingOutStatements}; -use dataflow::{ActiveBorrows, FlowAtLocation, FlowsAtLocation}; -use dataflow::move_paths::HasMoveData; +use analysis::dataflow::{MaybeInitializedPlaces, MaybeUninitializedPlaces}; +use analysis::dataflow::{EverInitializedPlaces, MovingOutStatements}; +use analysis::dataflow::{ActiveBorrows, FlowAtLocation, FlowsAtLocation}; +use analysis::dataflow::move_paths::HasMoveData; use std::fmt; // (forced to be `pub` due to its use as an associated type below.) diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index c6ed971f767cf..c3104e8d1905f 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -31,17 +31,17 @@ use std::rc::Rc; use syntax::ast; use syntax_pos::Span; -use dataflow::{do_dataflow, DebugFormatted}; -use dataflow::FlowAtLocation; -use dataflow::MoveDataParamEnv; -use dataflow::{DataflowAnalysis, DataflowResultsConsumer}; -use dataflow::{MaybeInitializedPlaces, MaybeUninitializedPlaces}; -use dataflow::{EverInitializedPlaces, MovingOutStatements}; -use dataflow::{BorrowData, Borrows, ReserveOrActivateIndex}; -use dataflow::{ActiveBorrows, Reservations}; -use dataflow::indexes::BorrowIndex; -use dataflow::move_paths::{IllegalMoveOriginKind, MoveError}; -use dataflow::move_paths::{HasMoveData, LookupResult, MoveData, MovePathIndex}; +use analysis::dataflow::{do_dataflow, DebugFormatted}; +use analysis::dataflow::FlowAtLocation; +use analysis::dataflow::MoveDataParamEnv; +use analysis::dataflow::{DataflowAnalysis, DataflowResultsConsumer}; +use analysis::dataflow::{MaybeInitializedPlaces, MaybeUninitializedPlaces}; +use analysis::dataflow::{EverInitializedPlaces, MovingOutStatements}; +use analysis::dataflow::{BorrowData, Borrows, ReserveOrActivateIndex}; +use analysis::dataflow::{ActiveBorrows, Reservations}; +use analysis::dataflow::indexes::BorrowIndex; +use analysis::dataflow::move_paths::{IllegalMoveOriginKind, MoveError}; +use analysis::dataflow::move_paths::{HasMoveData, LookupResult, MoveData, MovePathIndex}; use util::borrowck_errors::{BorrowckErrors, Origin}; use std::iter; diff --git a/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs b/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs index 948c1ac0b1362..7ee87b4270a4e 100644 --- a/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs +++ b/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs @@ -10,12 +10,12 @@ use borrow_check::{Context, MirBorrowckCtxt}; use borrow_check::nll::region_infer::{Cause, RegionInferenceContext}; -use dataflow::BorrowData; +use analysis::dataflow::BorrowData; use rustc::mir::{Local, Location, Mir}; use rustc::mir::visit::{MirVisitable, PlaceContext, Visitor}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::DiagnosticBuilder; -use util::liveness::{self, DefUse, LivenessMode}; +use analysis::liveness::{self, DefUse, LivenessMode}; impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { pub(in borrow_check) fn explain_why_borrow_contains_point( diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index 07e5091da9c1e..524c21d206692 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -16,11 +16,11 @@ use rustc::util::nodemap::FxHashMap; use std::collections::BTreeSet; use std::fmt::Debug; use std::io; +use analysis::liveness::{LivenessResults, LocalSet}; +use analysis::dataflow::FlowAtLocation; +use analysis::dataflow::MaybeInitializedPlaces; +use analysis::dataflow::move_paths::MoveData; use transform::MirSource; -use util::liveness::{LivenessResults, LocalSet}; -use dataflow::FlowAtLocation; -use dataflow::MaybeInitializedPlaces; -use dataflow::move_paths::MoveData; use util as mir_util; use util::pretty::{self, ALIGN}; diff --git a/src/librustc_mir/borrow_check/nll/type_check/liveness.rs b/src/librustc_mir/borrow_check/nll/type_check/liveness.rs index a50b99937475e..b75441aa75bb7 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/liveness.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/liveness.rs @@ -8,10 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use dataflow::{FlowAtLocation, FlowsAtLocation}; +use analysis::dataflow::{FlowAtLocation, FlowsAtLocation}; use borrow_check::nll::region_infer::Cause; -use dataflow::MaybeInitializedPlaces; -use dataflow::move_paths::{HasMoveData, MoveData}; +use analysis::dataflow::MaybeInitializedPlaces; +use analysis::dataflow::move_paths::{HasMoveData, MoveData}; use rustc::mir::{BasicBlock, Location, Mir}; use rustc::mir::Local; use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; @@ -21,7 +21,7 @@ use rustc::util::common::ErrorReported; use borrow_check::nll::type_check::AtLocation; use rustc_data_structures::fx::FxHashSet; use syntax::codemap::DUMMY_SP; -use util::liveness::LivenessResults; +use analysis::liveness::LivenessResults; use super::TypeChecker; diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index d57b8e78f18a9..1aeb96971104f 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -14,9 +14,9 @@ use borrow_check::nll::region_infer::Cause; use borrow_check::nll::region_infer::ClosureRegionRequirementsExt; use borrow_check::nll::universal_regions::UniversalRegions; -use dataflow::FlowAtLocation; -use dataflow::MaybeInitializedPlaces; -use dataflow::move_paths::MoveData; +use analysis::dataflow::FlowAtLocation; +use analysis::dataflow::MaybeInitializedPlaces; +use analysis::dataflow::move_paths::MoveData; use rustc::hir::def_id::DefId; use rustc::infer::{InferCtxt, InferOk, InferResult, LateBoundRegionConversionTime, UnitResult}; use rustc::infer::region_constraints::{GenericKind, RegionConstraintData}; @@ -32,7 +32,7 @@ use std::fmt; use syntax::ast; use syntax_pos::{Span, DUMMY_SP}; use transform::{MirPass, MirSource}; -use util::liveness::LivenessResults; +use analysis::liveness::LivenessResults; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::indexed_vec::Idx; diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index 8c15d1cf8b03a..40fa09ad2df14 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -60,9 +60,9 @@ extern crate byteorder; mod diagnostics; +pub mod analysis; mod borrow_check; mod build; -mod dataflow; mod hair; mod shim; pub mod transform; diff --git a/src/librustc_mir/transform/add_moves_for_packed_drops.rs b/src/librustc_mir/transform/add_moves_for_packed_drops.rs index 203669c61badd..afde03c7ba63f 100644 --- a/src/librustc_mir/transform/add_moves_for_packed_drops.rs +++ b/src/librustc_mir/transform/add_moves_for_packed_drops.rs @@ -14,7 +14,7 @@ use rustc::ty::TyCtxt; use transform::{MirPass, MirSource}; use util::patch::MirPatch; -use util; +use analysis::alignment::is_disaligned; // This pass moves values being dropped that are within a packed // struct to a separate local before dropping them, to ensure that @@ -84,7 +84,7 @@ fn add_moves_for_packed_drops_patch<'a, 'tcx>( match terminator.kind { TerminatorKind::Drop { ref location, .. } - if util::is_disaligned(tcx, mir, param_env, location) => + if is_disaligned(tcx, mir, param_env, location) => { add_move_for_packed_drop(tcx, mir, &mut patch, terminator, loc, data.is_cleanup); diff --git a/src/librustc_mir/transform/check_unsafety.rs b/src/librustc_mir/transform/check_unsafety.rs index 3524255e03772..6d381bcc47b3a 100644 --- a/src/librustc_mir/transform/check_unsafety.rs +++ b/src/librustc_mir/transform/check_unsafety.rs @@ -23,7 +23,7 @@ use syntax::ast; use syntax::symbol::Symbol; use std::rc::Rc; -use util; +use analysis::alignment::is_disaligned; pub struct UnsafetyChecker<'a, 'tcx: 'a> { mir: &'a Mir<'tcx>, @@ -150,7 +150,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { context: PlaceContext<'tcx>, location: Location) { if let PlaceContext::Borrow { .. } = context { - if util::is_disaligned(self.tcx, self.mir, self.param_env, place) { + if is_disaligned(self.tcx, self.mir, self.param_env, place) { let source_info = self.source_info; let lint_root = self.visibility_scope_info[source_info.scope].lint_root; diff --git a/src/librustc_mir/transform/copy_prop.rs b/src/librustc_mir/transform/copy_prop.rs index 95fe99a1bec9f..e8f23f53201f4 100644 --- a/src/librustc_mir/transform/copy_prop.rs +++ b/src/librustc_mir/transform/copy_prop.rs @@ -34,7 +34,7 @@ use rustc::mir::{Constant, Local, LocalKind, Location, Place, Mir, Operand, Rval use rustc::mir::visit::MutVisitor; use rustc::ty::TyCtxt; use transform::{MirPass, MirSource}; -use util::def_use::DefUseAnalysis; +use analysis::def_use::DefUseAnalysis; pub struct CopyPropagation; diff --git a/src/librustc_mir/transform/elaborate_drops.rs b/src/librustc_mir/transform/elaborate_drops.rs index 9eca343cb5edc..1edbc6a143e5a 100644 --- a/src/librustc_mir/transform/elaborate_drops.rs +++ b/src/librustc_mir/transform/elaborate_drops.rs @@ -8,13 +8,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use dataflow::move_paths::{HasMoveData, MoveData, MovePathIndex, LookupResult}; -use dataflow::{MaybeInitializedPlaces, MaybeUninitializedPlaces}; -use dataflow::{DataflowResults}; -use dataflow::{on_all_children_bits, on_all_drop_children_bits}; -use dataflow::{drop_flag_effects_for_location, on_lookup_result_bits}; -use dataflow::MoveDataParamEnv; -use dataflow::{self, do_dataflow, DebugFormatted}; +use analysis::dataflow::move_paths::{HasMoveData, MoveData, MovePathIndex, LookupResult}; +use analysis::dataflow::{MaybeInitializedPlaces, MaybeUninitializedPlaces}; +use analysis::dataflow::{DataflowResults}; +use analysis::dataflow::{on_all_children_bits, on_all_drop_children_bits}; +use analysis::dataflow::{drop_flag_effects_for_location, on_lookup_result_bits}; +use analysis::dataflow::MoveDataParamEnv; +use analysis::dataflow::{self, do_dataflow, DebugFormatted}; use rustc::hir; use rustc::ty::{self, TyCtxt}; use rustc::mir::*; diff --git a/src/librustc_mir/transform/generator.rs b/src/librustc_mir/transform/generator.rs index 04ebaa031fe5a..34dfa056bf4fc 100644 --- a/src/librustc_mir/transform/generator.rs +++ b/src/librustc_mir/transform/generator.rs @@ -67,7 +67,7 @@ use rustc::mir::visit::{PlaceContext, Visitor, MutVisitor}; use rustc::ty::{self, TyCtxt, AdtDef, Ty, GeneratorInterior}; use rustc::ty::subst::Substs; use util::dump_mir; -use util::liveness::{self, LivenessMode}; +use analysis::liveness::{self, LivenessMode}; use rustc_const_math::ConstInt; use rustc_data_structures::indexed_vec::Idx; use rustc_data_structures::indexed_set::IdxSetBuf; @@ -78,8 +78,8 @@ use std::mem; use transform::{MirPass, MirSource}; use transform::simplify; use transform::no_landing_pads::no_landing_pads; -use dataflow::{do_dataflow, DebugFormatted, state_for_location}; -use dataflow::{MaybeStorageLive, HaveBeenBorrowedLocals}; +use analysis::dataflow::{do_dataflow, DebugFormatted, state_for_location}; +use analysis::dataflow::{MaybeStorageLive, HaveBeenBorrowedLocals}; pub struct StateTransform; diff --git a/src/librustc_mir/transform/rustc_peek.rs b/src/librustc_mir/transform/rustc_peek.rs index 76283edac7284..4b366fecaaa58 100644 --- a/src/librustc_mir/transform/rustc_peek.rs +++ b/src/librustc_mir/transform/rustc_peek.rs @@ -18,16 +18,17 @@ use rustc_data_structures::indexed_set::IdxSetBuf; use rustc_data_structures::indexed_vec::Idx; use transform::{MirPass, MirSource}; -use dataflow::{do_dataflow, DebugFormatted}; -use dataflow::MoveDataParamEnv; -use dataflow::BitDenotation; -use dataflow::DataflowResults; -use dataflow::{DefinitelyInitializedPlaces, MaybeInitializedPlaces, MaybeUninitializedPlaces}; -use dataflow::move_paths::{MovePathIndex, LookupResult}; -use dataflow::move_paths::{HasMoveData, MoveData}; -use dataflow; - -use dataflow::has_rustc_mir_with; +use analysis::dataflow::{do_dataflow, DebugFormatted}; +use analysis::dataflow::MoveDataParamEnv; +use analysis::dataflow::BitDenotation; +use analysis::dataflow::DataflowResults; +use analysis::dataflow::{DefinitelyInitializedPlaces, MaybeInitializedPlaces}; +use analysis::dataflow::MaybeUninitializedPlaces; +use analysis::dataflow::move_paths::{MovePathIndex, LookupResult}; +use analysis::dataflow::move_paths::{HasMoveData, MoveData}; +use analysis::dataflow; + +use analysis::dataflow::has_rustc_mir_with; pub struct SanityCheck; diff --git a/src/librustc_mir/util/mod.rs b/src/librustc_mir/util/mod.rs index eebe5a86018ea..2f3bcca157372 100644 --- a/src/librustc_mir/util/mod.rs +++ b/src/librustc_mir/util/mod.rs @@ -10,15 +10,11 @@ pub mod borrowck_errors; pub mod elaborate_drops; -pub mod def_use; pub mod patch; -mod alignment; mod graphviz; pub(crate) mod pretty; -pub mod liveness; -pub use self::alignment::is_disaligned; pub use self::pretty::{dump_enabled, dump_mir, write_mir_pretty, PassWhere}; pub use self::graphviz::{write_mir_graphviz}; pub use self::graphviz::write_node_label as write_graphviz_node_label; From 9fb4ae575e3af5093a649233eacce12ed5e840b9 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Thu, 18 Jan 2018 16:42:02 +0200 Subject: [PATCH 3/9] rustc_mir: add a Location flattening analysis tool. --- src/librustc_mir/analysis/locations.rs | 50 ++++++++++++++++++++++++++ src/librustc_mir/analysis/mod.rs | 1 + 2 files changed, 51 insertions(+) create mode 100644 src/librustc_mir/analysis/locations.rs diff --git a/src/librustc_mir/analysis/locations.rs b/src/librustc_mir/analysis/locations.rs new file mode 100644 index 0000000000000..d823e3f91f361 --- /dev/null +++ b/src/librustc_mir/analysis/locations.rs @@ -0,0 +1,50 @@ +// Copyright 2018 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. + +use rustc_data_structures::indexed_vec::{IndexVec, Idx}; +use rustc::mir::*; +use std::ops::Range; + +newtype_index!(FlatLocation { DEBUG_FORMAT = "FlatLocation({})" }); + +/// Maps `Location`s containing a block index and a statement/terminator +/// index within the block, to a single linearized `FlatLocation` index. +pub struct FlatLocations { + pub block_start: IndexVec, + pub total_count: usize +} + +impl FlatLocations { + pub fn collect(mir: &Mir) -> Self { + let mut next_start = FlatLocation::new(0); + FlatLocations { + block_start: mir.basic_blocks().iter().map(|block| { + let start = next_start; + next_start = FlatLocation::new(start.index() + block.statements.len() + 1); + start + }).collect(), + total_count: next_start.index() + } + } + + pub fn get(&self, location: Location) -> FlatLocation { + let block_range = self.block_range(location.block); + let id = FlatLocation::new(block_range.start.index() + location.statement_index); + assert!(id < block_range.end); + id + } + + pub fn block_range(&self, block: BasicBlock) -> Range { + let next_block = BasicBlock::new(block.index() + 1); + let next_start = self.block_start.get(next_block).cloned() + .unwrap_or(FlatLocation::new(self.total_count)); + self.block_start[block]..next_start + } +} diff --git a/src/librustc_mir/analysis/mod.rs b/src/librustc_mir/analysis/mod.rs index 14b564b444faf..905e6467538cb 100644 --- a/src/librustc_mir/analysis/mod.rs +++ b/src/librustc_mir/analysis/mod.rs @@ -12,3 +12,4 @@ pub mod alignment; pub mod dataflow; pub mod def_use; pub mod liveness; +pub mod locations; From ed395ca5dfffff13d4f52f8b2c831e0d8fa294a3 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Fri, 19 Jan 2018 18:31:05 +0200 Subject: [PATCH 4/9] rustc_mir: add an "event saturating dataflow" framework. --- src/librustc_mir/analysis/eventflow.rs | 389 +++++++++++++++++++++++++ src/librustc_mir/analysis/mod.rs | 1 + 2 files changed, 390 insertions(+) create mode 100644 src/librustc_mir/analysis/eventflow.rs diff --git a/src/librustc_mir/analysis/eventflow.rs b/src/librustc_mir/analysis/eventflow.rs new file mode 100644 index 0000000000000..ac4adf7c5754a --- /dev/null +++ b/src/librustc_mir/analysis/eventflow.rs @@ -0,0 +1,389 @@ +// Copyright 2018 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. + +pub use self::SeekLocation::*; + +use rustc_data_structures::indexed_set::{IdxSet, IdxSetBuf}; +use rustc_data_structures::indexed_vec::{Idx, IndexVec}; +use rustc_data_structures::bitvec::BitMatrix; +use rustc::mir::*; +use std::collections::{BTreeMap, VecDeque}; +use std::iter; +use std::marker::PhantomData; +use analysis::locations::{FlatLocation, FlatLocations}; + +// FIXME(eddyb) move to rustc_data_structures. +#[derive(Clone)] +pub struct SparseBitSet { + map: BTreeMap, + _marker: PhantomData, +} + +fn key_and_mask(index: I) -> (u32, u128) { + let index = index.index(); + let key = index / 128; + let key_u32 = key as u32; + assert_eq!(key_u32 as usize, key); + (key_u32, 1 << (index % 128)) +} + +impl SparseBitSet { + pub fn new() -> Self { + SparseBitSet { + map: BTreeMap::new(), + _marker: PhantomData + } + } + + pub fn contains(&self, index: I) -> bool { + let (key, mask) = key_and_mask(index); + self.map.get(&key).map_or(false, |bits| (bits & mask) != 0) + } + + pub fn insert(&mut self, index: I) -> bool { + let (key, mask) = key_and_mask(index); + let bits = self.map.entry(key).or_insert(0); + let old_bits = *bits; + let new_bits = old_bits | mask; + *bits = new_bits; + new_bits != old_bits + } + + pub fn remove(&mut self, index: I) -> bool { + let (key, mask) = key_and_mask(index); + if let Some(bits) = self.map.get_mut(&key) { + let old_bits = *bits; + let new_bits = old_bits & !mask; + *bits = new_bits; + // FIXME(eddyb) maybe remove entry if now `0`. + new_bits != old_bits + } else { + false + } + } + + pub fn iter<'a>(&'a self) -> impl Iterator + 'a { + self.map.iter().flat_map(|(&key, &bits)| { + let base = key as usize * 128; + (0..128).filter_map(move |i| { + if (bits & (1 << i)) != 0 { + Some(I::new(base + i)) + } else { + None + } + }) + }) + } +} + +/// A pair with named fields (`past` and `future`). +/// Used solely to avoid mixing the two up. +#[derive(Copy, Clone, Default)] +pub struct PastAndFuture { + pub past: P, + pub future: F +} + +/// Event (dataflow) propagation direction. +/// Past events are propagated forward, while +/// future events are propagated backward. +pub trait Direction: 'static { + const FORWARD: bool; + fn each_propagation_edge(mir: &Mir, from: BasicBlock, f: F) + where F: FnMut(BasicBlock); + fn each_block_location(flat_locations: &FlatLocations, block: BasicBlock, f: F) + where F: FnMut(FlatLocation); +} + +pub enum Forward {} +impl Direction for Forward { + const FORWARD: bool = true; + fn each_propagation_edge(mir: &Mir, from: BasicBlock, mut f: F) + where F: FnMut(BasicBlock) + { + for &to in mir.basic_blocks()[from].terminator().successors().iter() { + f(to); + } + } + fn each_block_location(flat_locations: &FlatLocations, block: BasicBlock, mut f: F) + where F: FnMut(FlatLocation) + { + let range = flat_locations.block_range(block); + // FIXME(eddyb) implement `Step` on `FlatLocation`. + for i in range.start.index()..range.end.index() { + f(FlatLocation::new(i)) + } + } +} + +pub enum Backward {} +impl Direction for Backward { + const FORWARD: bool = false; + fn each_propagation_edge(mir: &Mir, from: BasicBlock, mut f: F) + where F: FnMut(BasicBlock) + { + for &to in mir.predecessors_for(from).iter() { + f(to); + } + } + fn each_block_location(flat_locations: &FlatLocations, block: BasicBlock, mut f: F) + where F: FnMut(FlatLocation) + { + let range = flat_locations.block_range(block); + // FIXME(eddyb) implement `Step` on `FlatLocation`. + for i in (range.start.index()..range.end.index()).rev() { + f(FlatLocation::new(i)) + } + } +} + +pub struct Events<'a, 'b, 'tcx: 'a, I: Idx> { + mir: &'a Mir<'tcx>, + flat_locations: &'b FlatLocations, + count: usize, + at_location: IndexVec>, + in_block: BitMatrix +} + +impl<'a, 'b, 'tcx, I: Idx> Events<'a, 'b, 'tcx, I> { + pub fn new(mir: &'a Mir<'tcx>, + flat_locations: &'b FlatLocations, + count: usize) + -> Self { + Events { + mir, + flat_locations, + count, + at_location: IndexVec::from_elem_n(SparseBitSet::new(), + flat_locations.total_count), + in_block: BitMatrix::new(mir.basic_blocks().len(), count) + } + } + + pub fn insert_at(&mut self, index: I, location: Location) { + let flat_location = self.flat_locations.get(location); + self.at_location[flat_location].insert(index); + self.in_block.add(location.block.index(), index.index()); + } + + pub fn flow

(self, entry_past: P) + -> PastAndFuture, + EventFlowResults<'b, Backward, I>> + where P: Iterator + { + + PastAndFuture { + past: self.flow_in_direction(entry_past.map(|i| (START_BLOCK, i))), + future: self.flow_in_direction(iter::empty()) + } + } + + fn flow_in_direction(&self, external: E) + -> EventFlowResults<'b, D, I> + where E: Iterator + { + let mut queue = VecDeque::with_capacity(self.mir.basic_blocks().len()); + let mut enqueued = IdxSetBuf::new_filled(self.mir.basic_blocks().len()); + + // 0. Add some external events in the past/future of certain blocks. + let mut into_block = BitMatrix::new(self.mir.basic_blocks().len(), self.count); + for (block, i) in external { + into_block.add(block.index(), i.index()); + } + + // 1. Propagate `in_block` events to immediate successors/predecessors. + for from in self.mir.basic_blocks().indices() { + D::each_propagation_edge(&self.mir, from, |to| { + // FIXME(eddyb) This could use a version of `BitMatrix::merge` + // between two rows that are in diferent `BitMatrix`es. + for i in self.in_block.iter(from.index()) { + into_block.add(to.index(), i); + } + }); + queue.push_back(from); + } + + // 2. Propagate `into_block` events until saturation is achieved. + while let Some(from) = queue.pop_front() { + D::each_propagation_edge(&self.mir, from, |to| { + if into_block.merge(from.index(), to.index()) { + if enqueued.add(&to) { + queue.push_back(to); + } + } + }); + enqueued.remove(&from); + } + + // 3. Cache the difference between consecutive locations within each block. + let mut out_of_block = into_block.clone(); + let mut diff_at_location = IndexVec::from_elem_n(SparseBitSet::new(), + self.flat_locations.total_count); + for block in self.mir.basic_blocks().indices() { + D::each_block_location(&self.flat_locations, block, |flat_location| { + let at_location = &self.at_location[flat_location]; + let diff = &mut diff_at_location[flat_location]; + // FIXME(eddyb) This could use per-"word" bitwise operations. + for i in at_location.iter() { + if out_of_block.add(block.index(), i.index()) { + diff.insert(i); + } + } + }); + } + + let (block_entry, block_exit) = if D::FORWARD { + (into_block, out_of_block) + } else { + (out_of_block, into_block) + }; + + EventFlowResults { + flat_locations: self.flat_locations, + count: self.count, + block_entry, + block_exit, + diff_at_location, + _marker: PhantomData + } + } +} + +#[derive(Clone)] +pub struct EventFlowResults<'a, D: Direction, I: Idx> { + flat_locations: &'a FlatLocations, + count: usize, + + /// Bits propagated into the start of the block, from predecessors. + block_entry: BitMatrix, + + /// Bits propagated out of the end of the block, into successors. + block_exit: BitMatrix, + + /// Bits that change at each statement/terminator, because they're + /// either the first occurence (in the past only after the location), + /// or the last occurence (in the future only before the location). + diff_at_location: IndexVec>, + + _marker: PhantomData +} + +impl<'a, D: Direction, I: Idx> EventFlowResults<'a, D, I> { + pub fn observe(&'a self) -> Observer<'a, D, I> { + Observer { + results: self, + location: Location { + block: START_BLOCK, + statement_index: !0 + }, + state_before: IdxSetBuf::new_empty(self.count), + } + } +} + +impl<'a, I: Idx> PastAndFuture, + EventFlowResults<'a, Backward, I>> { + pub fn observe(&'a self) -> PastAndFuture, + Observer<'a, Backward, I>> { + PastAndFuture { + past: self.past.observe(), + future: self.future.observe() + } + } +} + +pub struct Observer<'a, D: Direction, I: Idx> { + results: &'a EventFlowResults<'a, D, I>, + location: Location, + state_before: IdxSetBuf, +} + +#[derive(Copy, Clone)] +pub enum SeekLocation { + Before(Location), + After(Location) +} + +impl<'a, D: Direction, I: Idx> Observer<'a, D, I> { + pub fn seek(&mut self, to: SeekLocation) -> &IdxSet { + // Ensure the location is valid for a statement/terminator. + match to { + Before(location) | After(location) => { + self.results.flat_locations.get(location); + } + } + + let to = match to { + Before(location) => location, + After(location) => location.successor_within_block() + }; + + // Seek to the start or end of the block if we were in a different one. + if self.location.block != to.block || self.location.statement_index == !0 { + self.state_before.clear(); + + let block_range = self.results.flat_locations.block_range(to.block); + let locations_in_block = block_range.end.index() - block_range.start.index(); + + // FIXME(eddyb) These could use copies of whole rows. + if to.statement_index < locations_in_block / 2 { + for i in self.results.block_entry.iter(to.block.index()) { + self.state_before.add(&I::new(i)); + } + self.location.statement_index = 0; + } else { + for i in self.results.block_exit.iter(to.block.index()) { + self.state_before.add(&I::new(i)); + } + self.location.statement_index = locations_in_block; + } + self.location.block = to.block; + } + + while self.location.statement_index < to.statement_index { + let flat_location = self.results.flat_locations.get(self.location); + // FIXME(eddyb) These could use per-"word" bitwise operations. + for i in self.results.diff_at_location[flat_location].iter() { + if D::FORWARD { + self.state_before.add(&i); + } else { + self.state_before.remove(&i); + } + } + self.location.statement_index += 1; + } + + while self.location.statement_index > to.statement_index { + self.location.statement_index -= 1; + let flat_location = self.results.flat_locations.get(self.location); + // FIXME(eddyb) These could use per-"word" bitwise operations. + for i in self.results.diff_at_location[flat_location].iter() { + if D::FORWARD { + self.state_before.remove(&i); + } else { + self.state_before.add(&i); + } + } + } + + &self.state_before + } +} + +impl<'a, I: Idx> PastAndFuture, + Observer<'a, Backward, I>> { + pub fn seek(&mut self, to: SeekLocation) + -> PastAndFuture<&IdxSet, &IdxSet> { + PastAndFuture { + past: self.past.seek(to), + future: self.future.seek(to) + } + } +} diff --git a/src/librustc_mir/analysis/mod.rs b/src/librustc_mir/analysis/mod.rs index 905e6467538cb..24abdde8e64b1 100644 --- a/src/librustc_mir/analysis/mod.rs +++ b/src/librustc_mir/analysis/mod.rs @@ -11,5 +11,6 @@ pub mod alignment; pub mod dataflow; pub mod def_use; +pub mod eventflow; pub mod liveness; pub mod locations; From 1e761bb1b81d34ecece1fe8d6ed9cea507f4a91c Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Wed, 17 Jan 2018 16:33:54 +0200 Subject: [PATCH 5/9] rustc_mir: add a borrow dataflow analysis for locals. --- src/librustc_mir/analysis/borrows.rs | 110 ++++++++++++++++++++++ src/librustc_mir/analysis/dataflow/mod.rs | 4 +- src/librustc_mir/analysis/mod.rs | 1 + 3 files changed, 113 insertions(+), 2 deletions(-) create mode 100644 src/librustc_mir/analysis/borrows.rs diff --git a/src/librustc_mir/analysis/borrows.rs b/src/librustc_mir/analysis/borrows.rs new file mode 100644 index 0000000000000..7ac889080b7cf --- /dev/null +++ b/src/librustc_mir/analysis/borrows.rs @@ -0,0 +1,110 @@ +// Copyright 2018 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. + +use rustc_data_structures::indexed_set::IdxSet; +use rustc_data_structures::bitslice::BitwiseOperator; +use rustc::mir::*; +use rustc::mir::visit::Visitor; +use analysis::dataflow::{BitDenotation, BlockSets, InitialFlow}; + +#[derive(Copy, Clone)] +pub struct MaybeBorrowed<'a, 'tcx: 'a> { + mir: &'a Mir<'tcx> +} + +impl<'a, 'tcx: 'a> MaybeBorrowed<'a, 'tcx> { + pub fn new(mir: &'a Mir<'tcx>) -> Self { + MaybeBorrowed { mir } + } +} + +impl<'a, 'tcx> BitDenotation for MaybeBorrowed<'a, 'tcx> { + type Idx = Local; + fn name() -> &'static str { "maybe_borrowed" } + fn bits_per_block(&self) -> usize { + self.mir.local_decls.len() + } + + fn start_block_effect(&self, _sets: &mut IdxSet) { + // Nothing is borrowed on function entry + } + + fn statement_effect(&self, + sets: &mut BlockSets, + location: Location) { + match self.mir[location.block].statements[location.statement_index].kind { + StatementKind::Assign(_, Rvalue::Ref(.., ref place)) => { + // Ignore `place`s based on a dereference, when `gen`-ing borrows, + // as the resulting reference can't actually point to a local path + // that isn't already borrowed, and definitely not the base reference. + let mut base = place; + while let Place::Projection(ref proj) = *base { + if let ProjectionElem::Deref = proj.elem { + break; + } + base = &proj.base; + } + + if let Place::Local(local) = *base { + sets.gen(&local); + } + } + StatementKind::StorageDead(local) => { + sets.kill(&local); + } + // FIXME(eddyb) cancel all borrows on `yield` (unless the generator is immovable). + _ => {} + } + + let mut moves = MoveCollector { sets }; + moves.visit_location(self.mir, location); + } + + fn terminator_effect(&self, + sets: &mut BlockSets, + location: Location) { + let mut moves = MoveCollector { sets }; + moves.visit_location(self.mir, location); + } + + fn propagate_call_return(&self, + _in_out: &mut IdxSet, + _call_bb: BasicBlock, + _dest_bb: BasicBlock, + _dest_place: &Place) { + // Nothing to do when a call returns successfully + } +} + +impl<'a, 'tcx> BitwiseOperator for MaybeBorrowed<'a, 'tcx> { + #[inline] + fn join(&self, pred1: usize, pred2: usize) -> usize { + pred1 | pred2 // "maybe" means we union effects of both preds + } +} + +impl<'a, 'tcx> InitialFlow for MaybeBorrowed<'a, 'tcx> { + #[inline] + fn bottom_value() -> bool { + false // bottom = unborrowed + } +} + +struct MoveCollector<'a, 'b: 'a> { + sets: &'a mut BlockSets<'b, Local> +} + +impl<'a, 'b, 'tcx> Visitor<'tcx> for MoveCollector<'a, 'b> { + fn visit_operand(&mut self, operand: &Operand, _: Location) { + if let Operand::Move(Place::Local(local)) = *operand { + self.sets.kill(&local); + } + } +} diff --git a/src/librustc_mir/analysis/dataflow/mod.rs b/src/librustc_mir/analysis/dataflow/mod.rs index 9c7d9b398cc56..bf1446acc7b7c 100644 --- a/src/librustc_mir/analysis/dataflow/mod.rs +++ b/src/librustc_mir/analysis/dataflow/mod.rs @@ -513,7 +513,7 @@ pub struct BlockSets<'a, E: Idx> { } impl<'a, E:Idx> BlockSets<'a, E> { - fn gen(&mut self, e: &E) { + pub(crate) fn gen(&mut self, e: &E) { self.gen_set.add(e); self.kill_set.remove(e); } @@ -538,7 +538,7 @@ impl<'a, E:Idx> BlockSets<'a, E> { } } - fn kill(&mut self, e: &E) { + pub(crate) fn kill(&mut self, e: &E) { self.gen_set.remove(e); self.kill_set.add(e); } diff --git a/src/librustc_mir/analysis/mod.rs b/src/librustc_mir/analysis/mod.rs index 24abdde8e64b1..e76547c41525f 100644 --- a/src/librustc_mir/analysis/mod.rs +++ b/src/librustc_mir/analysis/mod.rs @@ -9,6 +9,7 @@ // except according to those terms. pub mod alignment; +pub mod borrows; pub mod dataflow; pub mod def_use; pub mod eventflow; From ba535cd96233d8bf51ce61bf2b6520091d994159 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Fri, 19 Jan 2018 14:26:07 +0200 Subject: [PATCH 6/9] rustc_mir: add a local access analysis. --- src/librustc_mir/analysis/accesses.rs | 142 ++++++++++++++++++++++ src/librustc_mir/analysis/dataflow/mod.rs | 18 +-- src/librustc_mir/analysis/mod.rs | 1 + 3 files changed, 152 insertions(+), 9 deletions(-) create mode 100644 src/librustc_mir/analysis/accesses.rs diff --git a/src/librustc_mir/analysis/accesses.rs b/src/librustc_mir/analysis/accesses.rs new file mode 100644 index 0000000000000..7fcca54c7849d --- /dev/null +++ b/src/librustc_mir/analysis/accesses.rs @@ -0,0 +1,142 @@ +// Copyright 2018 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. + +use rustc_data_structures::indexed_set::IdxSetBuf; +use rustc_data_structures::indexed_vec::Idx; +use rustc::mir::*; +use rustc::mir::visit::{PlaceContext, Visitor}; +use rustc::ty; +use syntax::ast; +use analysis::borrows::MaybeBorrowed; +use analysis::dataflow::{do_dataflow, BitDenotation, BlockSets, DebugFormatted}; +use analysis::eventflow::{Backward, Events, EventFlowResults, Forward, PastAndFuture}; +use analysis::locations::FlatLocations; + +pub struct Accesses<'a> { + pub results: PastAndFuture, + EventFlowResults<'a, Backward, Local>> +} + +impl<'a> Accesses<'a> { + pub fn collect(mir: &Mir, flat_locations: &'a FlatLocations) -> Self { + let borrows = ty::tls::with(|tcx| { + do_dataflow(tcx, mir, ast::DUMMY_NODE_ID, &[], + &IdxSetBuf::new_empty(mir.basic_blocks().len()), + MaybeBorrowed::new(mir), + |_, path| DebugFormatted::new(&path)) + }); + + let mut collector = AccessPathCollector { + location: Location { + block: START_BLOCK, + statement_index: !0 + }, + accesses: Events::new(mir, flat_locations, mir.local_decls.len()), + maybe_borrowed: IdxSetBuf::new_empty(0) + }; + + // FIXME(eddyb) introduce a seeker for this (like in eventflow), + // maybe reusing `dataflow::at_location(::FlowAtLocation)`. + // That would remove the need for asserting the location. + + for (block, data) in mir.basic_blocks().iter_enumerated() { + collector.location.block = block; + collector.maybe_borrowed = borrows.sets().on_entry_set_for(block.index()).to_owned(); + + let on_entry = &mut collector.maybe_borrowed.clone(); + let kill_set = &mut collector.maybe_borrowed.clone(); + for (i, statement) in data.statements.iter().enumerate() { + collector.location.statement_index = i; + borrows.operator().before_statement_effect(&mut BlockSets { + on_entry, + kill_set, + gen_set: &mut collector.maybe_borrowed, + }, collector.location); + // FIXME(eddyb) get rid of temporary with NLL/2phi. + let location = collector.location; + collector.visit_statement(block, statement, location); + borrows.operator().statement_effect(&mut BlockSets { + on_entry, + kill_set, + gen_set: &mut collector.maybe_borrowed, + }, collector.location); + } + + if let Some(ref terminator) = data.terminator { + collector.location.statement_index = data.statements.len(); + borrows.operator().before_terminator_effect(&mut BlockSets { + on_entry, + kill_set, + gen_set: &mut collector.maybe_borrowed, + }, collector.location); + // FIXME(eddyb) get rid of temporary with NLL/2phi. + let location = collector.location; + collector.visit_terminator(block, terminator, location); + } + } + // All arguments have been accessed prior to the call to this function. + let results = collector.accesses.flow(mir.args_iter()); + Accesses { results } + } +} + +struct AccessPathCollector<'a, 'b, 'tcx: 'a> { + accesses: Events<'a, 'b, 'tcx, Local>, + location: Location, + maybe_borrowed: IdxSetBuf +} + +impl<'a, 'b, 'tcx> AccessPathCollector<'a, 'b, 'tcx> { + fn access_anything_borrowed(&mut self, location: Location) { + assert_eq!(self.location, location); + + // FIXME(eddyb) OR `maybe_borrowed` into the accesses for performance. + for path in self.maybe_borrowed.iter() { + self.accesses.insert_at(path, location); + } + } +} + +impl<'a, 'b, 'tcx> Visitor<'tcx> for AccessPathCollector<'a, 'b, 'tcx> { + fn visit_local(&mut self, + &local: &Local, + context: PlaceContext, + location: Location) { + if context.is_use() { + self.accesses.insert_at(local, location); + } + } + + fn visit_projection_elem(&mut self, + elem: &PlaceElem<'tcx>, + context: PlaceContext<'tcx>, + location: Location) { + if let ProjectionElem::Deref = *elem { + self.access_anything_borrowed(location); + } + self.super_projection_elem(elem, context, location); + } + + fn visit_terminator_kind(&mut self, + block: BasicBlock, + kind: &TerminatorKind<'tcx>, + location: Location) { + match *kind { + TerminatorKind::Call { .. } => { + self.access_anything_borrowed(location); + } + TerminatorKind::Return => { + self.visit_local(&RETURN_PLACE, PlaceContext::Move, location); + } + _ => {} + } + self.super_terminator_kind(block, kind, location); + } +} diff --git a/src/librustc_mir/analysis/dataflow/mod.rs b/src/librustc_mir/analysis/dataflow/mod.rs index bf1446acc7b7c..2d2ae3ecbf92b 100644 --- a/src/librustc_mir/analysis/dataflow/mod.rs +++ b/src/librustc_mir/analysis/dataflow/mod.rs @@ -121,14 +121,14 @@ pub struct MoveDataParamEnv<'gcx, 'tcx> { pub(crate) param_env: ty::ParamEnv<'gcx>, } -pub(crate) fn do_dataflow<'a, 'gcx, 'tcx, BD, P>(tcx: TyCtxt<'a, 'gcx, 'tcx>, - mir: &'a Mir<'tcx>, - node_id: ast::NodeId, - attributes: &[ast::Attribute], - dead_unwinds: &IdxSet, - bd: BD, - p: P) - -> DataflowResults +pub(crate) fn do_dataflow(tcx: TyCtxt, + mir: &Mir, + node_id: ast::NodeId, + attributes: &[ast::Attribute], + dead_unwinds: &IdxSet, + bd: BD, + p: P) + -> DataflowResults where BD: BitDenotation + InitialFlow, P: Fn(&BD, BD::Idx) -> DebugFormatted { @@ -139,7 +139,7 @@ pub(crate) fn do_dataflow<'a, 'gcx, 'tcx, BD, P>(tcx: TyCtxt<'a, 'gcx, 'tcx>, impl<'a, 'gcx: 'tcx, 'tcx: 'a, BD> DataflowAnalysis<'a, 'tcx, BD> where BD: BitDenotation { pub(crate) fn run

(self, - tcx: TyCtxt<'a, 'gcx, 'tcx>, + tcx: TyCtxt, node_id: ast::NodeId, attributes: &[ast::Attribute], p: P) -> DataflowResults diff --git a/src/librustc_mir/analysis/mod.rs b/src/librustc_mir/analysis/mod.rs index e76547c41525f..75ef8e359bc4e 100644 --- a/src/librustc_mir/analysis/mod.rs +++ b/src/librustc_mir/analysis/mod.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +pub mod accesses; pub mod alignment; pub mod borrows; pub mod dataflow; From b6877eab489bddc102a299553d212e3799df958a Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Tue, 28 Nov 2017 08:24:38 +0200 Subject: [PATCH 7/9] rustc_mir: implement a "place unification" optimization (aka source/destination propagation). --- src/librustc_mir/analysis/eventflow.rs | 4 + src/librustc_mir/transform/mod.rs | 2 + src/librustc_mir/transform/unify_places.rs | 404 ++++++++++++++++++ src/librustc_trans/mir/place.rs | 8 +- src/test/codegen/align-struct.rs | 5 +- src/test/codegen/lifetime_start_end.rs | 52 --- src/test/codegen/zip.rs | 2 +- src/test/incremental/hashes/for_loops.rs | 2 +- src/test/mir-opt/copy_propagation.rs | 7 +- src/test/mir-opt/copy_propagation_arg.rs | 14 +- .../mir-opt/inline-closure-borrows-arg.rs | 8 +- src/test/mir-opt/inline-closure.rs | 2 +- 12 files changed, 435 insertions(+), 75 deletions(-) create mode 100644 src/librustc_mir/transform/unify_places.rs delete mode 100644 src/test/codegen/lifetime_start_end.rs diff --git a/src/librustc_mir/analysis/eventflow.rs b/src/librustc_mir/analysis/eventflow.rs index ac4adf7c5754a..95deeed4a6323 100644 --- a/src/librustc_mir/analysis/eventflow.rs +++ b/src/librustc_mir/analysis/eventflow.rs @@ -42,6 +42,10 @@ impl SparseBitSet { } } + pub fn capacity(&self) -> usize { + self.map.len() * 128 + } + pub fn contains(&self, index: I) -> bool { let (key, mask) = key_and_mask(index); self.map.get(&key).map_or(false, |bits| (bits & mask) != 0) diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index 182b317075d72..db476b048080c 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -46,6 +46,7 @@ pub mod inline; pub mod lower_128bit; pub mod uniform_array_move_out; pub mod split_local_fields; +pub mod unify_places; pub(crate) fn provide(providers: &mut Providers) { self::qualify_consts::provide(providers); @@ -267,6 +268,7 @@ fn optimized_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx instcombine::InstCombine, deaggregator::Deaggregator, split_local_fields::SplitLocalFields, + unify_places::UnifyPlaces, copy_prop::CopyPropagation, remove_noop_landing_pads::RemoveNoopLandingPads, simplify::SimplifyCfg::new("final"), diff --git a/src/librustc_mir/transform/unify_places.rs b/src/librustc_mir/transform/unify_places.rs new file mode 100644 index 0000000000000..513e9bf52fa39 --- /dev/null +++ b/src/librustc_mir/transform/unify_places.rs @@ -0,0 +1,404 @@ +// Copyright 2018 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. + +use rustc_data_structures::indexed_vec::{Idx, IndexVec}; +use rustc_data_structures::indexed_set::{IdxSet, IdxSetBuf}; +use rustc::hir; +use rustc::mir::*; +use rustc::mir::tcx::PlaceTy; +use rustc::mir::visit::{PlaceContext, MutVisitor, Visitor}; +use rustc::session::config::FullDebugInfo; +use rustc::ty::TyCtxt; +use std::mem; +use analysis::accesses::Accesses; +use analysis::eventflow::{After, Before, SparseBitSet}; +use analysis::locations::FlatLocations; +use transform::{MirPass, MirSource}; + +struct Finder { + parent: IndexVec, +} + +impl Finder { + fn find(&mut self, i: I) -> I { + let parent = self.parent[i]; + if i == parent { + return i; + } + let root = self.find(parent); + if root != parent { + self.parent[i] = root; + } + root + } +} + +/// Union-find for the points of a binary symmetric relation. +/// Note that the relation is not transitive, only `union` is. +struct UnionFindSymRel { + finder: Finder, + relation: IndexVec>, +} + +impl UnionFindSymRel { + fn union(&mut self, a: I, b: I) -> I { + let a = self.finder.find(a); + let b = self.finder.find(b); + if a == b { + return a; + } + + let (root, child) = if self.relation[a].capacity() > self.relation[b].capacity() { + (a, b) + } else { + (b, a) + }; + self.finder.parent[child] = root; + + // Have to juggle the `self.relation` elements as we have + // no way to borrow two disjoint elements at the same time. + let child_relation = mem::replace(&mut self.relation[child], SparseBitSet::new()); + // FIXME(eddyb) This could use per-"word" bitwise operations. + for i in child_relation.iter() { + // HACK(eddyb) this is really expensive, but used to propagate the relation. + let i = self.finder.find(i); + self.relation[root].insert(i); + self.relation[i].insert(root); + } + self.relation[child] = child_relation; + + root + } + + fn relates(&mut self, a: I, b: I) -> bool { + let a = self.finder.find(a); + let b = self.finder.find(b); + self.relation[a].contains(b) || self.relation[b].contains(a) + } +} + +#[derive(Copy, Clone, Debug)] +struct LocalInterior<'a, 'tcx: 'a> { + base: Local, + subplace: Option<&'a Place<'tcx>> +} + +impl<'a, 'tcx> LocalInterior<'a, 'tcx> { + fn from_place(tcx: TyCtxt<'a, 'tcx, 'tcx>, + mir: &Mir<'tcx>, + place: &'a Place<'tcx>) + -> Option<(Self, PlaceTy<'tcx>)> { + match *place { + Place::Local(base) => { + Some((LocalInterior { base, subplace: None }, + PlaceTy::from_ty(mir.local_decls[base].ty))) + } + Place::Static(_) => None, + Place::Projection(ref proj) => { + let (base, base_ty) = Self::from_place(tcx, mir, &proj.base)?; + + // Packed types can have under-aligned fields, which + // can't be freely used wherever places are required + // and/or assumed to be aligned, e.g. safe borrows. + let adt_def = match base_ty { + PlaceTy::Ty { ty } => ty.ty_adt_def(), + PlaceTy::Downcast { adt_def, .. } => Some(adt_def) + }; + if adt_def.map_or(false, |adt| adt.repr.packed()) { + return None; + } + + let ty = match proj.elem { + ProjectionElem::Field(..) | + ProjectionElem::Downcast(..) | + ProjectionElem::ConstantIndex { .. } | + ProjectionElem::Subslice { .. } => { + base_ty.projection_ty(tcx, &proj.elem) + } + + ProjectionElem::Index(_) | + ProjectionElem::Deref => return None + }; + Some((LocalInterior { + base: base.base, + subplace: Some(place) + }, ty)) + } + } + } +} + +#[derive(Copy, Clone, Debug)] +struct RenameCandidate<'a, 'tcx: 'a> { + from: Local, + to: LocalInterior<'a, 'tcx> +} + +impl<'a, 'tcx> RenameCandidate<'a, 'tcx> { + fn new(a: LocalInterior<'a, 'tcx>, b: LocalInterior<'a, 'tcx>) -> Option { + // Only locals may be renamed. + let (from, to) = match (a.subplace, b.subplace) { + (None, _) => (a.base, b), + (_, None) => (b.base, a), + _ => return None + }; + Some(RenameCandidate { from, to }) + } +} + +impl<'a, 'tcx> RenameCandidate<'a, 'tcx> { + fn filter(self, can_rename: &IdxSet) -> Option { + if self.from == self.to.base { + return None; + } + + if can_rename.contains(&self.from) { + Some(self) + } else if can_rename.contains(&self.to.base) && self.to.subplace.is_none() { + Some(RenameCandidate { + from: self.to.base, + to: LocalInterior { + base: self.from, + subplace: None + } + }) + } else { + None + } + } +} + +pub struct UnifyPlaces; + +impl MirPass for UnifyPlaces { + fn run_pass<'a, 'tcx>(&self, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + source: MirSource, + mir: &mut Mir<'tcx>) { + // Don't run on constant MIR, because trans might not be able to + // evaluate the modified MIR. + // FIXME(eddyb) Remove check after miri is merged. + let id = tcx.hir.as_local_node_id(source.def_id).unwrap(); + match (tcx.hir.body_owner_kind(id), source.promoted) { + (_, Some(_)) | + (hir::BodyOwnerKind::Const, _) | + (hir::BodyOwnerKind::Static(_), _) => return, + + (hir::BodyOwnerKind::Fn, _) => { + if tcx.is_const_fn(source.def_id) { + // Don't run on const functions, as, again, trans might not be able to evaluate + // the optimized IR. + return + } + } + } + + let mut replacement_place = IndexVec::from_elem(None, &mir.local_decls); + let mut replacement_finder = Finder { + parent: mir.local_decls.indices().collect(), + }; + { + let can_rename = &mut IdxSetBuf::new_empty(mir.local_decls.len()); + + // We need to keep user variables intact for debuginfo. + // FIXME(eddyb) We should allow multiple user variables + // per local for debuginfo instead of not optimizing them. + if tcx.sess.opts.debuginfo == FullDebugInfo { + for local in mir.temps_iter() { + can_rename.add(&local); + } + } else { + // FIXME(eddyb) use ranges for performance. + for local in mir.vars_and_temps_iter() { + can_rename.add(&local); + } + } + + { + let mut can_rename_collector = CanRenameCollector { + can_rename + }; + can_rename_collector.visit_mir(mir); + } + + let flat_locations = &FlatLocations::collect(mir); + let accesses = &Accesses::collect(mir, flat_locations); + let mut observers = accesses.results.observe(); + let mut conflicts = IndexVec::from_elem(SparseBitSet::new(), &mir.local_decls); + let mut candidates = vec![]; + for (block, data) in mir.basic_blocks().iter_enumerated() { + let mut add_conflicts_at = |past: &IdxSet<_>, future: &IdxSet<_>| { + // FIXME(eddyb) use `diff_at_location` (how?) as an optimization. + for i in past.iter() { + if future.contains(&i) { + // FIXME(eddyb) Reduce the cost of this already Q_Q. + for j in past.iter() { + if i != j && future.contains(&j) { + conflicts[i].insert(j); + } + } + } + } + }; + let mut checked_after_last_statement = false; + for (statement_index, stmt) in data.statements.iter().enumerate() { + let location = Location { block, statement_index }; + match stmt.kind { + // FIXME(eddyb) figure out how to allow copies. + // Maybe if there is any candidate that's a copy, + // mark the unification as needing copies? Unclear. + // StatementKind::Assign(ref dest, Rvalue::Use(Operand::Copy(ref src))) | + StatementKind::Assign(ref dest, Rvalue::Use(Operand::Move(ref src))) => { + if let Some((dest, _)) = LocalInterior::from_place(tcx, mir, dest) { + if let Some((src, _)) = LocalInterior::from_place(tcx, mir, src) { + candidates.extend(RenameCandidate::new(dest, src) + .and_then(|c| c.filter(can_rename))); + if !checked_after_last_statement { + add_conflicts_at( + observers.past.seek(Before(location)), + observers.future.seek(Before(location))); + } + checked_after_last_statement = false; + continue; + } + } + } + _ => {} + } + add_conflicts_at( + observers.past.seek(After(location)), + observers.future.seek(Before(location))); + checked_after_last_statement = true; + } + let location = Location { + block, + statement_index: data.statements.len() + }; + add_conflicts_at( + observers.past.seek(After(location)), + observers.future.seek(Before(location))); + } + + let mut conflicts = UnionFindSymRel { + finder: Finder { + parent: mir.local_decls.indices().collect(), + }, + relation: conflicts + }; + + // Union together all the candidate source and targets. + // Candidates may fail if they could cause a conflict. + for mut candidate in candidates { + debug!("unify_places: original: {:?}", candidate); + candidate.from = replacement_finder.find(candidate.from); + candidate.to.base = replacement_finder.find(candidate.to.base); + let candidate = candidate.filter(can_rename); + debug!("unify_places: filtered: {:?}", candidate); + if let Some(RenameCandidate { from, to }) = candidate { + if conflicts.relates(from, to.base) { + continue; + } + conflicts.union(from, to.base); + + if let Some(to_place) = to.subplace { + debug!("unify_places: {:?} -> {:?}", from, to_place); + replacement_place[from] = Some(to_place.clone()); + can_rename.remove(&from); + } else { + debug!("unify_places: {:?} -> {:?}", from, to.base); + replacement_finder.parent[from] = to.base; + } + } + } + } + + // Apply the replacements we computed previously. + let mut replacer = Replacer { + replacement_place, + replacement_finder + }; + replacer.visit_mir(mir); + } +} + +struct CanRenameCollector<'a> { + can_rename: &'a mut IdxSet +} + +impl<'a, 'tcx> Visitor<'tcx> for CanRenameCollector<'a> { + fn visit_projection_elem(&mut self, + elem: &PlaceElem<'tcx>, + context: PlaceContext<'tcx>, + location: Location) { + if let ProjectionElem::Index(i) = *elem { + // FIXME(eddyb) We could rename locals used as indices, + // but only to other whole locals, not their fields. + self.can_rename.remove(&i); + } + self.super_projection_elem(elem, context, location); + } +} + +struct Replacer<'tcx> { + replacement_place: IndexVec>>, + replacement_finder: Finder +} + +impl<'tcx> MutVisitor<'tcx> for Replacer<'tcx> { + fn visit_place(&mut self, + place: &mut Place<'tcx>, + context: PlaceContext<'tcx>, + location: Location) { + if let Place::Local(from) = *place { + let to = self.replacement_finder.find(from); + let to_place = self.replacement_place[to].as_ref().cloned(); + if let Some(to_place) = to_place { + *place = to_place; + } else if to != from { + *place = Place::Local(to); + } + + // Recurse in case the replacement also needs to be replaced. + // FIXME(eddyb) precompute it into `replacement_place`. + } + + self.super_place(place, context, location); + } + + fn visit_statement(&mut self, + block: BasicBlock, + statement: &mut Statement<'tcx>, + location: Location) { + // FIXME(eddyb) fuse storage liveness ranges instead of removing them. + match statement.kind { + StatementKind::StorageLive(_local) | + StatementKind::StorageDead(_local) => { + // FIXME(eddyb) figure out how to even detect relevancy. + statement.make_nop(); + } + _ => {} + } + + self.super_statement(block, statement, location); + + // Remove self-assignments resulting from replaced move chains. + let nop = match statement.kind { + StatementKind::Assign(ref dest, Rvalue::Use(Operand::Copy(ref src))) | + StatementKind::Assign(ref dest, Rvalue::Use(Operand::Move(ref src))) => { + dest == src + } + _ => false + }; + // HACK(eddyb) clean this up post-NLL. + if nop { + statement.make_nop(); + } + } +} diff --git a/src/librustc_trans/mir/place.rs b/src/librustc_trans/mir/place.rs index 99770476e12f9..fe07a01d287c0 100644 --- a/src/librustc_trans/mir/place.rs +++ b/src/librustc_trans/mir/place.rs @@ -162,7 +162,13 @@ impl<'a, 'tcx> PlaceRef<'tcx> { let cx = bx.cx; let field = self.layout.field(cx, ix); let offset = self.layout.fields.offset(ix); - let align = self.align.min(self.layout.align).min(field.align); + let align = if self.layout.abi == layout::Abi::Uninhabited { + // Uninhabited places can only exist in dead code, so we can pretend + // alignment is ideal, to avoid tripping off special cases elsewhere. + field.align + } else { + self.align.min(self.layout.align).min(field.align) + }; let simple = || { // Unions and newtypes only use an offset of 0. diff --git a/src/test/codegen/align-struct.rs b/src/test/codegen/align-struct.rs index 155319cb1541f..6d0de4472fe92 100644 --- a/src/test/codegen/align-struct.rs +++ b/src/test/codegen/align-struct.rs @@ -14,9 +14,11 @@ #![crate_type = "lib"] #[repr(align(64))] +#[derive(Copy, Clone)] pub struct Align64(i32); // CHECK: %Align64 = type { [0 x i32], i32, [15 x i32] } +#[derive(Copy, Clone)] pub struct Nested64 { a: Align64, b: i32, @@ -32,6 +34,7 @@ pub enum Enum4 { // CHECK: %Enum4 = type { [0 x i32], i32, [1 x i32] } // CHECK: %"Enum4::A" = type { [1 x i32], i32, [0 x i32] } +#[derive(Copy, Clone)] pub enum Enum64 { A(Align64), B(i32), @@ -59,7 +62,7 @@ pub fn nested64(a: Align64, b: i32, c: i32, d: i8) -> Nested64 { // CHECK-LABEL: @enum4 #[no_mangle] pub fn enum4(a: i32) -> Enum4 { -// CHECK: %e4 = alloca %Enum4, align 4 +// CHECK: alloca %Enum4, align 4 let e4 = Enum4::A(a); e4 } diff --git a/src/test/codegen/lifetime_start_end.rs b/src/test/codegen/lifetime_start_end.rs deleted file mode 100644 index 62aa93398ac42..0000000000000 --- a/src/test/codegen/lifetime_start_end.rs +++ /dev/null @@ -1,52 +0,0 @@ -// 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"] - -// CHECK-LABEL: @test -#[no_mangle] -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 %"core::option::Option"** %b to i8* -// CHECK: call void @llvm.lifetime.start{{.*}}(i{{[0-9 ]+}}, i8* [[S_b]]) - -// CHECK: [[S__4:%[0-9]+]] = bitcast %"core::option::Option"* %_4 to i8* -// CHECK: call void @llvm.lifetime.start{{.*}}(i{{[0-9 ]+}}, i8* [[S__4]]) - -// CHECK: [[E_b:%[0-9]+]] = bitcast %"core::option::Option"** %b to i8* -// CHECK: call void @llvm.lifetime.end{{.*}}(i{{[0-9 ]+}}, i8* [[E_b]]) - -// CHECK: [[E__4:%[0-9]+]] = bitcast %"core::option::Option"* %_4 to i8* -// CHECK: call void @llvm.lifetime.end{{.*}}(i{{[0-9 ]+}}, i8* [[E__4]]) - } - - 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/codegen/zip.rs b/src/test/codegen/zip.rs index d0051c5165fe1..e9088cb0d53cf 100644 --- a/src/test/codegen/zip.rs +++ b/src/test/codegen/zip.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// compile-flags: -C no-prepopulate-passes -O +// compile-flags: -O #![crate_type = "lib"] diff --git a/src/test/incremental/hashes/for_loops.rs b/src/test/incremental/hashes/for_loops.rs index 105afd30d28ab..8f8a707b5ac30 100644 --- a/src/test/incremental/hashes/for_loops.rs +++ b/src/test/incremental/hashes/for_loops.rs @@ -59,7 +59,7 @@ pub fn change_iteration_variable_name() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody, MirValidated, MirOptimized")] +#[rustc_clean(cfg="cfail2", except="HirBody, MirValidated")] #[rustc_clean(cfg="cfail3")] pub fn change_iteration_variable_name() { let mut _x = 0; diff --git a/src/test/mir-opt/copy_propagation.rs b/src/test/mir-opt/copy_propagation.rs index 50d8a5154c449..ce435bf94b5a3 100644 --- a/src/test/mir-opt/copy_propagation.rs +++ b/src/test/mir-opt/copy_propagation.rs @@ -24,10 +24,7 @@ fn main() { // ... // _3 = _1; // ... -// _2 = move _3; -// ... -// _4 = _2; -// _0 = move _4; +// _0 = _3; // ... // return; // } @@ -35,7 +32,7 @@ fn main() { // START rustc.test.CopyPropagation.after.mir // bb0: { // ... -// _0 = move _1; +// _0 = _1; // ... // return; // } diff --git a/src/test/mir-opt/copy_propagation_arg.rs b/src/test/mir-opt/copy_propagation_arg.rs index feadec6bbf76e..c5dd80cc80268 100644 --- a/src/test/mir-opt/copy_propagation_arg.rs +++ b/src/test/mir-opt/copy_propagation_arg.rs @@ -71,12 +71,12 @@ fn main() { // END rustc.foo.CopyPropagation.after.mir // START rustc.bar.CopyPropagation.before.mir // bb0: { -// StorageLive(_3); +// ... // _3 = _1; // _2 = const dummy(move _3) -> bb1; // } // bb1: { -// StorageDead(_3); +// ... // _1 = const 5u8; // ... // return; @@ -97,10 +97,10 @@ fn main() { // END rustc.bar.CopyPropagation.after.mir // START rustc.baz.CopyPropagation.before.mir // bb0: { -// StorageLive(_2); +// ... // _2 = _1; // _1 = move _2; -// StorageDead(_2); +// ... // ... // return; // } @@ -118,12 +118,10 @@ fn main() { // bb0: { // ... // _3 = _1; -// _2 = move _3; // ... // _1 = const 123i32; // ... -// _4 = _2; -// _0 = move _4; +// _0 = _3; // ... // return; // } @@ -135,7 +133,7 @@ fn main() { // ... // _1 = const 123i32; // ... -// _0 = move _3; +// _0 = _3; // ... // return; // } diff --git a/src/test/mir-opt/inline-closure-borrows-arg.rs b/src/test/mir-opt/inline-closure-borrows-arg.rs index 3fb54f90984dd..6ce51be3ec5db 100644 --- a/src/test/mir-opt/inline-closure-borrows-arg.rs +++ b/src/test/mir-opt/inline-closure-borrows-arg.rs @@ -38,11 +38,9 @@ fn foo(_t: T, q: &i32) -> i32 { // ... // _7 = &(*_2); // _5 = (move _6, move _7); -// _9 = move (_5.0: &i32); -// _10 = move (_5.1: &i32); -// StorageLive(_8); -// _8 = (*_9); -// _0 = move _8; +// _8 = move (_5.0: &i32); +// _9 = move (_5.1: &i32); +// _0 = (*_8); // ... // return; // } diff --git a/src/test/mir-opt/inline-closure.rs b/src/test/mir-opt/inline-closure.rs index dc8ff13c03a88..22e7de31e90cf 100644 --- a/src/test/mir-opt/inline-closure.rs +++ b/src/test/mir-opt/inline-closure.rs @@ -36,7 +36,7 @@ fn foo(_t: T, q: i32) -> i32 { // _5 = (move _6, move _7); // _8 = move (_5.0: i32); // _9 = move (_5.1: i32); -// _0 = move _8; +// _0 = _8; // ... // return; // } From 5b6de2cb85e1f02b4a69442447ffbeb1257e5cff Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Thu, 22 Feb 2018 00:49:10 +0200 Subject: [PATCH 8/9] (HACK): chunked SparseBitSet API. --- src/librustc_mir/analysis/eventflow.rs | 179 +++++++++++++++------ src/librustc_mir/transform/unify_places.rs | 103 +++++++----- 2 files changed, 194 insertions(+), 88 deletions(-) diff --git a/src/librustc_mir/analysis/eventflow.rs b/src/librustc_mir/analysis/eventflow.rs index 95deeed4a6323..e569f7ee65151 100644 --- a/src/librustc_mir/analysis/eventflow.rs +++ b/src/librustc_mir/analysis/eventflow.rs @@ -10,11 +10,12 @@ pub use self::SeekLocation::*; -use rustc_data_structures::indexed_set::{IdxSet, IdxSetBuf}; +use rustc_data_structures::indexed_set::IdxSetBuf; use rustc_data_structures::indexed_vec::{Idx, IndexVec}; use rustc_data_structures::bitvec::BitMatrix; use rustc::mir::*; use std::collections::{BTreeMap, VecDeque}; +use std::collections::btree_map::Entry; use std::iter; use std::marker::PhantomData; use analysis::locations::{FlatLocation, FlatLocations}; @@ -22,69 +23,147 @@ use analysis::locations::{FlatLocation, FlatLocations}; // FIXME(eddyb) move to rustc_data_structures. #[derive(Clone)] pub struct SparseBitSet { - map: BTreeMap, + chunk_bits: BTreeMap, _marker: PhantomData, } -fn key_and_mask(index: I) -> (u32, u128) { - let index = index.index(); - let key = index / 128; - let key_u32 = key as u32; - assert_eq!(key_u32 as usize, key); - (key_u32, 1 << (index % 128)) +#[derive(Copy, Clone)] +pub struct SparseChunk { + key: u32, + bits: u128, + _marker: PhantomData, +} + +impl SparseChunk { + pub fn one(index: I) -> Self { + let index = index.index(); + let key_usize = index / 128; + let key = key_usize as u32; + assert_eq!(key as usize, key_usize); + SparseChunk { + key, + bits: 1 << (index % 128), + _marker: PhantomData + } + } + + pub fn any(&self) -> bool { + self.bits != 0 + } + + pub fn iter(&self) -> impl Iterator { + let base = self.key as usize * 128; + let mut bits = self.bits; + (0..128).map(move |i| { + let current_bits = bits; + bits >>= 1; + (i, current_bits) + }).take_while(|&(_, bits)| bits != 0) + .filter_map(move |(i, bits)| { + if (bits & 1) != 0 { + Some(I::new(base + i)) + } else { + None + } + }) + } } impl SparseBitSet { pub fn new() -> Self { SparseBitSet { - map: BTreeMap::new(), + chunk_bits: BTreeMap::new(), _marker: PhantomData } } pub fn capacity(&self) -> usize { - self.map.len() * 128 + self.chunk_bits.len() * 128 } - pub fn contains(&self, index: I) -> bool { - let (key, mask) = key_and_mask(index); - self.map.get(&key).map_or(false, |bits| (bits & mask) != 0) + pub fn contains_chunk(&self, chunk: SparseChunk) -> SparseChunk { + SparseChunk { + bits: self.chunk_bits.get(&chunk.key).map_or(0, |bits| bits & chunk.bits), + ..chunk + } } - pub fn insert(&mut self, index: I) -> bool { - let (key, mask) = key_and_mask(index); - let bits = self.map.entry(key).or_insert(0); + pub fn insert_chunk(&mut self, chunk: SparseChunk) -> SparseChunk { + if chunk.bits == 0 { + return chunk; + } + let bits = self.chunk_bits.entry(chunk.key).or_insert(0); let old_bits = *bits; - let new_bits = old_bits | mask; + let new_bits = old_bits | chunk.bits; *bits = new_bits; - new_bits != old_bits - } - - pub fn remove(&mut self, index: I) -> bool { - let (key, mask) = key_and_mask(index); - if let Some(bits) = self.map.get_mut(&key) { - let old_bits = *bits; - let new_bits = old_bits & !mask; - *bits = new_bits; - // FIXME(eddyb) maybe remove entry if now `0`. - new_bits != old_bits - } else { - false + let changed = new_bits ^ old_bits; + SparseChunk { + bits: changed, + ..chunk } } - pub fn iter<'a>(&'a self) -> impl Iterator + 'a { - self.map.iter().flat_map(|(&key, &bits)| { - let base = key as usize * 128; - (0..128).filter_map(move |i| { - if (bits & (1 << i)) != 0 { - Some(I::new(base + i)) + pub fn remove_chunk(&mut self, chunk: SparseChunk) -> SparseChunk { + if chunk.bits == 0 { + return chunk; + } + let changed = match self.chunk_bits.entry(chunk.key) { + Entry::Occupied(mut bits) => { + let old_bits = *bits.get(); + let new_bits = old_bits & !chunk.bits; + if new_bits == 0 { + bits.remove(); } else { - None + bits.insert(new_bits); } - }) + new_bits ^ old_bits + } + Entry::Vacant(_) => 0 + }; + SparseChunk { + bits: changed, + ..chunk + } + } + + pub fn clear(&mut self) { + self.chunk_bits.clear(); + } + + pub fn chunks<'a>(&'a self) -> impl Iterator> + 'a { + self.chunk_bits.iter().map(|(&key, &bits)| { + SparseChunk { + key, + bits, + _marker: PhantomData + } }) } + + pub fn contains(&self, index: I) -> bool { + self.contains_chunk(SparseChunk::one(index)).any() + } + + pub fn insert(&mut self, index: I) -> bool { + self.insert_chunk(SparseChunk::one(index)).any() + } + + pub fn remove(&mut self, index: I) -> bool { + self.remove_chunk(SparseChunk::one(index)).any() + } + + pub fn iter<'a>(&'a self) -> impl Iterator + 'a { + self.chunks().flat_map(|chunk| chunk.iter()) + } +} + +impl Extend> for SparseBitSet { + fn extend>>(&mut self, chunks: T) { + // FIXME(eddyb) Maybe this could be implemented more efficiently? + for chunk in chunks { + self.insert_chunk(chunk); + } + } } /// A pair with named fields (`past` and `future`). @@ -287,7 +366,7 @@ impl<'a, D: Direction, I: Idx> EventFlowResults<'a, D, I> { block: START_BLOCK, statement_index: !0 }, - state_before: IdxSetBuf::new_empty(self.count), + state_before: SparseBitSet::new(), } } } @@ -306,7 +385,7 @@ impl<'a, I: Idx> PastAndFuture, pub struct Observer<'a, D: Direction, I: Idx> { results: &'a EventFlowResults<'a, D, I>, location: Location, - state_before: IdxSetBuf, + state_before: SparseBitSet, } #[derive(Copy, Clone)] @@ -316,7 +395,7 @@ pub enum SeekLocation { } impl<'a, D: Direction, I: Idx> Observer<'a, D, I> { - pub fn seek(&mut self, to: SeekLocation) -> &IdxSet { + pub fn seek(&mut self, to: SeekLocation) -> &SparseBitSet { // Ensure the location is valid for a statement/terminator. match to { Before(location) | After(location) => { @@ -339,12 +418,12 @@ impl<'a, D: Direction, I: Idx> Observer<'a, D, I> { // FIXME(eddyb) These could use copies of whole rows. if to.statement_index < locations_in_block / 2 { for i in self.results.block_entry.iter(to.block.index()) { - self.state_before.add(&I::new(i)); + self.state_before.insert(I::new(i)); } self.location.statement_index = 0; } else { for i in self.results.block_exit.iter(to.block.index()) { - self.state_before.add(&I::new(i)); + self.state_before.insert(I::new(i)); } self.location.statement_index = locations_in_block; } @@ -354,11 +433,11 @@ impl<'a, D: Direction, I: Idx> Observer<'a, D, I> { while self.location.statement_index < to.statement_index { let flat_location = self.results.flat_locations.get(self.location); // FIXME(eddyb) These could use per-"word" bitwise operations. - for i in self.results.diff_at_location[flat_location].iter() { + for i in self.results.diff_at_location[flat_location].chunks() { if D::FORWARD { - self.state_before.add(&i); + self.state_before.insert_chunk(i); } else { - self.state_before.remove(&i); + self.state_before.remove_chunk(i); } } self.location.statement_index += 1; @@ -368,11 +447,11 @@ impl<'a, D: Direction, I: Idx> Observer<'a, D, I> { self.location.statement_index -= 1; let flat_location = self.results.flat_locations.get(self.location); // FIXME(eddyb) These could use per-"word" bitwise operations. - for i in self.results.diff_at_location[flat_location].iter() { + for i in self.results.diff_at_location[flat_location].chunks() { if D::FORWARD { - self.state_before.remove(&i); + self.state_before.remove_chunk(i); } else { - self.state_before.add(&i); + self.state_before.insert_chunk(i); } } } @@ -384,7 +463,7 @@ impl<'a, D: Direction, I: Idx> Observer<'a, D, I> { impl<'a, I: Idx> PastAndFuture, Observer<'a, Backward, I>> { pub fn seek(&mut self, to: SeekLocation) - -> PastAndFuture<&IdxSet, &IdxSet> { + -> PastAndFuture<&SparseBitSet, &SparseBitSet> { PastAndFuture { past: self.past.seek(to), future: self.future.seek(to) diff --git a/src/librustc_mir/transform/unify_places.rs b/src/librustc_mir/transform/unify_places.rs index 513e9bf52fa39..3a3c614f75883 100644 --- a/src/librustc_mir/transform/unify_places.rs +++ b/src/librustc_mir/transform/unify_places.rs @@ -24,9 +24,17 @@ use transform::{MirPass, MirSource}; struct Finder { parent: IndexVec, + children: IndexVec> } impl Finder { + fn new(universe: &IndexVec) -> Self { + Finder { + parent: universe.indices().collect(), + children: IndexVec::from_elem(SparseBitSet::new(), universe) + } + } + fn find(&mut self, i: I) -> I { let parent = self.parent[i]; if i == parent { @@ -38,6 +46,16 @@ impl Finder { } root } + + fn reparent(&mut self, child: I, root: I) { + assert_eq!(self.parent[child], child); + assert_eq!(self.parent[root], root); + self.parent[child] = root; + + let child_children = mem::replace(&mut self.children[child], SparseBitSet::new()); + self.children[root].extend(child_children.chunks()); + self.children[root].insert(child); + } } /// Union-find for the points of a binary symmetric relation. @@ -60,19 +78,12 @@ impl UnionFindSymRel { } else { (b, a) }; - self.finder.parent[child] = root; + self.finder.reparent(child, root); - // Have to juggle the `self.relation` elements as we have - // no way to borrow two disjoint elements at the same time. + // Union the child's `relation` edges into those of its new parent. + // See `relates` for how the resulting `relation` sets are used let child_relation = mem::replace(&mut self.relation[child], SparseBitSet::new()); - // FIXME(eddyb) This could use per-"word" bitwise operations. - for i in child_relation.iter() { - // HACK(eddyb) this is really expensive, but used to propagate the relation. - let i = self.finder.find(i); - self.relation[root].insert(i); - self.relation[i].insert(root); - } - self.relation[child] = child_relation; + self.relation[root].extend(child_relation.chunks()); root } @@ -80,7 +91,15 @@ impl UnionFindSymRel { fn relates(&mut self, a: I, b: I) -> bool { let a = self.finder.find(a); let b = self.finder.find(b); - self.relation[a].contains(b) || self.relation[b].contains(a) + + // `self.relation[a]` is the union of all `self.relation[x]` where + // `find(x) == a`, however, the elements themselves have not been + // passed through `find` (for performance reasons), so we need to + // check not just `b`, but also all `y` where `find(y) == b`. + let relation_a = &self.relation[a]; + relation_a.contains(b) || self.finder.children[b].chunks().any(|b| { + relation_a.contains_chunk(b).any() + }) } } @@ -201,10 +220,8 @@ impl MirPass for UnifyPlaces { } let mut replacement_place = IndexVec::from_elem(None, &mir.local_decls); - let mut replacement_finder = Finder { - parent: mir.local_decls.indices().collect(), - }; - { + let mut replacement_finder = Finder::new(&mir.local_decls); + let conflicts = { let can_rename = &mut IdxSetBuf::new_empty(mir.local_decls.len()); // We need to keep user variables intact for debuginfo. @@ -234,17 +251,11 @@ impl MirPass for UnifyPlaces { let mut conflicts = IndexVec::from_elem(SparseBitSet::new(), &mir.local_decls); let mut candidates = vec![]; for (block, data) in mir.basic_blocks().iter_enumerated() { - let mut add_conflicts_at = |past: &IdxSet<_>, future: &IdxSet<_>| { + let mut add_conflicts_at = |past: &SparseBitSet<_>, future: &SparseBitSet<_>| { // FIXME(eddyb) use `diff_at_location` (how?) as an optimization. - for i in past.iter() { - if future.contains(&i) { - // FIXME(eddyb) Reduce the cost of this already Q_Q. - for j in past.iter() { - if i != j && future.contains(&j) { - conflicts[i].insert(j); - } - } - } + let live = || past.chunks().map(|chunk| future.contains_chunk(chunk)); + for i in live().flat_map(|chunk| chunk.iter()) { + conflicts[i].extend(live()); } }; let mut checked_after_last_statement = false; @@ -286,10 +297,13 @@ impl MirPass for UnifyPlaces { observers.future.seek(Before(location))); } + // HACK(eddyb) remove problematic "self-conflicts". + for (i, conflicts) in conflicts.iter_enumerated_mut() { + conflicts.remove(i); + } + let mut conflicts = UnionFindSymRel { - finder: Finder { - parent: mir.local_decls.indices().collect(), - }, + finder: Finder::new(&mir.local_decls), relation: conflicts }; @@ -305,6 +319,11 @@ impl MirPass for UnifyPlaces { if conflicts.relates(from, to.base) { continue; } + + // FIXME(eddyb) We have both `conflicts` and `replacement_finder` - + // do we need to, though? We could always union the base variables + // and keep a side-table (like `replacement_place`) for interior + // projections needed for a given local on top of what it unified to. conflicts.union(from, to.base); if let Some(to_place) = to.subplace { @@ -313,16 +332,19 @@ impl MirPass for UnifyPlaces { can_rename.remove(&from); } else { debug!("unify_places: {:?} -> {:?}", from, to.base); - replacement_finder.parent[from] = to.base; + replacement_finder.reparent(from, to.base); } } } - } + + conflicts + }; // Apply the replacements we computed previously. let mut replacer = Replacer { replacement_place, - replacement_finder + replacement_finder, + conflicts }; replacer.visit_mir(mir); } @@ -348,7 +370,9 @@ impl<'a, 'tcx> Visitor<'tcx> for CanRenameCollector<'a> { struct Replacer<'tcx> { replacement_place: IndexVec>>, - replacement_finder: Finder + replacement_finder: Finder, + // FIXME(eddyb) this needs a better name than `conflicts` (everywhere). + conflicts: UnionFindSymRel } impl<'tcx> MutVisitor<'tcx> for Replacer<'tcx> { @@ -376,12 +400,15 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'tcx> { block: BasicBlock, statement: &mut Statement<'tcx>, location: Location) { - // FIXME(eddyb) fuse storage liveness ranges instead of removing them. match statement.kind { - StatementKind::StorageLive(_local) | - StatementKind::StorageDead(_local) => { - // FIXME(eddyb) figure out how to even detect relevancy. - statement.make_nop(); + StatementKind::StorageLive(local) | + StatementKind::StorageDead(local) => { + let intact = self.conflicts.finder.find(local) == local && + self.conflicts.finder.children[local].iter().next().is_none(); + // FIXME(eddyb) fuse storage liveness ranges instead of removing them. + if !intact { + statement.make_nop(); + } } _ => {} } From c858ee538cc1224b13984efbdeb021b2ae92c965 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Thu, 1 Mar 2018 05:41:21 +0200 Subject: [PATCH 9/9] (HACK): use eventflow diffs for computing live sets faster. --- src/librustc_mir/analysis/eventflow.rs | 78 ++++++++++++++++++++-- src/librustc_mir/transform/unify_places.rs | 47 ++++++++++--- 2 files changed, 109 insertions(+), 16 deletions(-) diff --git a/src/librustc_mir/analysis/eventflow.rs b/src/librustc_mir/analysis/eventflow.rs index e569f7ee65151..50e468efd3faf 100644 --- a/src/librustc_mir/analysis/eventflow.rs +++ b/src/librustc_mir/analysis/eventflow.rs @@ -82,6 +82,9 @@ impl SparseBitSet { } pub fn contains_chunk(&self, chunk: SparseChunk) -> SparseChunk { + if chunk.bits == 0 { + return chunk; + } SparseChunk { bits: self.chunk_bits.get(&chunk.key).map_or(0, |bits| bits & chunk.bits), ..chunk @@ -155,6 +158,10 @@ impl SparseBitSet { pub fn iter<'a>(&'a self) -> impl Iterator + 'a { self.chunks().flat_map(|chunk| chunk.iter()) } + + pub fn is_empty(&self) -> bool { + self.iter().next().is_none() + } } impl Extend> for SparseBitSet { @@ -394,8 +401,45 @@ pub enum SeekLocation { After(Location) } +pub enum Diff { + Nothing, + Added(T), + Removed(T), + Unknown +} + +impl Diff { + pub fn add(&mut self, x: T) { + *self = match *self { + Diff::Nothing => Diff::Added(x), + _ => Diff::Unknown + }; + } + + pub fn remove(&mut self, x: T) { + *self = match *self { + Diff::Nothing => Diff::Removed(x), + _ => Diff::Unknown + }; + } + + pub fn only_added(self) -> Self { + match self { + Diff::Removed(_) => Diff::Nothing, + _ => self + } + } +} + impl<'a, D: Direction, I: Idx> Observer<'a, D, I> { pub fn seek(&mut self, to: SeekLocation) -> &SparseBitSet { + self.seek_diff(to).0 + } + + pub fn seek_diff(&mut self, to: SeekLocation) + -> (&SparseBitSet, Diff<&'a SparseBitSet>) { + let mut total_diff = Diff::Nothing; + // Ensure the location is valid for a statement/terminator. match to { Before(location) | After(location) => { @@ -428,12 +472,20 @@ impl<'a, D: Direction, I: Idx> Observer<'a, D, I> { self.location.statement_index = locations_in_block; } self.location.block = to.block; + total_diff = Diff::Unknown; } while self.location.statement_index < to.statement_index { let flat_location = self.results.flat_locations.get(self.location); - // FIXME(eddyb) These could use per-"word" bitwise operations. - for i in self.results.diff_at_location[flat_location].chunks() { + let diff = &self.results.diff_at_location[flat_location]; + if !diff.is_empty() { + if D::FORWARD { + total_diff.add(diff); + } else { + total_diff.remove(diff); + } + } + for i in diff.chunks() { if D::FORWARD { self.state_before.insert_chunk(i); } else { @@ -446,8 +498,15 @@ impl<'a, D: Direction, I: Idx> Observer<'a, D, I> { while self.location.statement_index > to.statement_index { self.location.statement_index -= 1; let flat_location = self.results.flat_locations.get(self.location); - // FIXME(eddyb) These could use per-"word" bitwise operations. - for i in self.results.diff_at_location[flat_location].chunks() { + let diff = &self.results.diff_at_location[flat_location]; + if !diff.is_empty() { + if D::FORWARD { + total_diff.remove(diff); + } else { + total_diff.add(diff); + } + } + for i in diff.chunks() { if D::FORWARD { self.state_before.remove_chunk(i); } else { @@ -456,7 +515,7 @@ impl<'a, D: Direction, I: Idx> Observer<'a, D, I> { } } - &self.state_before + (&self.state_before, total_diff) } } @@ -469,4 +528,13 @@ impl<'a, I: Idx> PastAndFuture, future: self.future.seek(to) } } + + pub fn seek_diff(&mut self, to: SeekLocation) + -> PastAndFuture<(&SparseBitSet, Diff<&'a SparseBitSet>), + (&SparseBitSet, Diff<&'a SparseBitSet>)> { + PastAndFuture { + past: self.past.seek_diff(to), + future: self.future.seek_diff(to) + } + } } diff --git a/src/librustc_mir/transform/unify_places.rs b/src/librustc_mir/transform/unify_places.rs index 3a3c614f75883..d2d0f836d4353 100644 --- a/src/librustc_mir/transform/unify_places.rs +++ b/src/librustc_mir/transform/unify_places.rs @@ -18,7 +18,7 @@ use rustc::session::config::FullDebugInfo; use rustc::ty::TyCtxt; use std::mem; use analysis::accesses::Accesses; -use analysis::eventflow::{After, Before, SparseBitSet}; +use analysis::eventflow::{After, Before, Diff, SparseBitSet}; use analysis::locations::FlatLocations; use transform::{MirPass, MirSource}; @@ -251,12 +251,37 @@ impl MirPass for UnifyPlaces { let mut conflicts = IndexVec::from_elem(SparseBitSet::new(), &mir.local_decls); let mut candidates = vec![]; for (block, data) in mir.basic_blocks().iter_enumerated() { - let mut add_conflicts_at = |past: &SparseBitSet<_>, future: &SparseBitSet<_>| { - // FIXME(eddyb) use `diff_at_location` (how?) as an optimization. + let mut add_conflicts_at = |(past, past_diff): (&SparseBitSet<_>, + Diff<&SparseBitSet<_>>), + (future, future_diff): (&SparseBitSet<_>, + Diff<&SparseBitSet<_>>)| { let live = || past.chunks().map(|chunk| future.contains_chunk(chunk)); - for i in live().flat_map(|chunk| chunk.iter()) { + let (unchanged, new) = match (past_diff.only_added(), + future_diff.only_added()) { + (Diff::Removed(_), _) | (_, Diff::Removed(_)) => bug!(), + + (Diff::Nothing, Diff::Nothing) => return, + + (Diff::Unknown, _) | (_, Diff::Unknown) | + (Diff::Added(_), Diff::Added(_)) => { + for i in live().flat_map(|chunk| chunk.iter()) { + conflicts[i].extend(live()); + } + return; + } + + (Diff::Added(past_new), Diff::Nothing) => (future, past_new), + (Diff::Nothing, Diff::Added(future_new)) => (past, future_new), + }; + let live_new = || new.chunks().map(|chunk| unchanged.contains_chunk(chunk)); + for i in live_new().flat_map(|chunk| chunk.iter()) { conflicts[i].extend(live()); } + // FIXME(eddyb) avoid doing overlapping work here. + // Could maybe make the conflicts matrix symmetric after the fact? + for i in live().flat_map(|chunk| chunk.iter()) { + conflicts[i].extend(live_new()); + } }; let mut checked_after_last_statement = false; for (statement_index, stmt) in data.statements.iter().enumerate() { @@ -273,8 +298,8 @@ impl MirPass for UnifyPlaces { .and_then(|c| c.filter(can_rename))); if !checked_after_last_statement { add_conflicts_at( - observers.past.seek(Before(location)), - observers.future.seek(Before(location))); + observers.past.seek_diff(Before(location)), + observers.future.seek_diff(Before(location))); } checked_after_last_statement = false; continue; @@ -284,8 +309,8 @@ impl MirPass for UnifyPlaces { _ => {} } add_conflicts_at( - observers.past.seek(After(location)), - observers.future.seek(Before(location))); + observers.past.seek_diff(After(location)), + observers.future.seek_diff(Before(location))); checked_after_last_statement = true; } let location = Location { @@ -293,8 +318,8 @@ impl MirPass for UnifyPlaces { statement_index: data.statements.len() }; add_conflicts_at( - observers.past.seek(After(location)), - observers.future.seek(Before(location))); + observers.past.seek_diff(After(location)), + observers.future.seek_diff(Before(location))); } // HACK(eddyb) remove problematic "self-conflicts". @@ -404,7 +429,7 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'tcx> { StatementKind::StorageLive(local) | StatementKind::StorageDead(local) => { let intact = self.conflicts.finder.find(local) == local && - self.conflicts.finder.children[local].iter().next().is_none(); + self.conflicts.finder.children[local].is_empty(); // FIXME(eddyb) fuse storage liveness ranges instead of removing them. if !intact { statement.make_nop();