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 e798cc93cb09a..7d34e3978cd5a 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 98% rename from src/librustc_mir/dataflow/mod.rs rename to src/librustc_mir/analysis/dataflow/mod.rs index 9c7d9b398cc56..2d2ae3ecbf92b 100644 --- a/src/librustc_mir/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 @@ -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/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/analysis/eventflow.rs b/src/librustc_mir/analysis/eventflow.rs new file mode 100644 index 0000000000000..95deeed4a6323 --- /dev/null +++ b/src/librustc_mir/analysis/eventflow.rs @@ -0,0 +1,393 @@ +// 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 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) + } + + 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/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/local_paths/accesses.rs b/src/librustc_mir/analysis/local_paths/accesses.rs new file mode 100644 index 0000000000000..c26a46e97c4dd --- /dev/null +++ b/src/librustc_mir/analysis/local_paths/accesses.rs @@ -0,0 +1,184 @@ +// 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::dataflow::{do_dataflow, BitDenotation, BlockSets, DebugFormatted}; +use analysis::eventflow::{Backward, Events, EventFlowResults, Forward, PastAndFuture}; +use analysis::local_paths::{LocalPaths, PathId}; +use analysis::local_paths::borrows::MaybeBorrowed; +use analysis::locations::FlatLocations; + +pub struct Accesses<'a> { + pub results: PastAndFuture, + EventFlowResults<'a, Backward, PathId>> +} + +impl<'a> Accesses<'a> { + pub fn collect(mir: &Mir, + local_paths: &LocalPaths, + 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, local_paths), + |_, path| DebugFormatted::new(&path)) + }); + + let mut collector = AccessPathCollector { + local_paths, + location: Location { + block: START_BLOCK, + statement_index: !0 + }, + accesses: Events::new(mir, flat_locations, local_paths.total_count()), + 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); + } + } + let results = collector.accesses.flow(mir.args_iter().map(|arg| { + // All arguments have been accessed prior to the call to this function. + local_paths.locals[arg] + // FIXME(eddyb) this should work with just `arg`, not also its descendants. + })); + Accesses { results } + } +} + +struct AccessPathCollector<'a, 'b, 'tcx: 'a> { + local_paths: &'a LocalPaths<'tcx>, + accesses: Events<'a, 'b, 'tcx, PathId>, + location: Location, + maybe_borrowed: IdxSetBuf +} + +impl<'a, 'b, 'tcx> AccessPathCollector<'a, 'b, 'tcx> { + fn access_anything_borrowed(&mut 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_place(&mut self, + place: &Place<'tcx>, + context: PlaceContext<'tcx>, + location: Location) { + assert_eq!(self.location, location); + + if context.is_use() { + match self.local_paths.place_path_acessed_prefix(place) { + Ok(path) | Err(Some(path)) => { + self.accesses.insert_at(path, location); + } + Err(None) => {} + } + } + + // Traverse the projections in `place`. + let context = if context.is_mutating_use() { + PlaceContext::Projection(Mutability::Mut) + } else { + PlaceContext::Projection(Mutability::Not) + }; + let mut place = place; + while let Place::Projection(ref proj) = *place { + self.visit_projection_elem(&proj.elem, context, location); + place = &proj.base; + } + } + + // Handle the locals used in indexing projections. + fn visit_local(&mut self, + &local: &Local, + context: PlaceContext, + location: Location) { + assert_eq!(self.location, location); + + if context.is_use() { + self.accesses.insert_at(self.local_paths.locals[local], location); + } + } + + fn visit_projection_elem(&mut self, + elem: &PlaceElem<'tcx>, + context: PlaceContext<'tcx>, + location: Location) { + assert_eq!(self.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) { + assert_eq!(self.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/local_paths/borrows.rs b/src/librustc_mir/analysis/local_paths/borrows.rs new file mode 100644 index 0000000000000..56b74f5d74fea --- /dev/null +++ b/src/librustc_mir/analysis/local_paths/borrows.rs @@ -0,0 +1,133 @@ +// 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 std::iter; +use analysis::dataflow::{BitDenotation, BlockSets, InitialFlow}; +use analysis::local_paths::{LocalPaths, PathId}; + +#[derive(Copy, Clone)] +pub struct MaybeBorrowed<'a, 'tcx: 'a> { + mir: &'a Mir<'tcx>, + local_paths: &'a LocalPaths<'tcx> +} + +impl<'a, 'tcx: 'a> MaybeBorrowed<'a, 'tcx> { + pub fn new(mir: &'a Mir<'tcx>, local_paths: &'a LocalPaths<'tcx>) -> Self { + MaybeBorrowed { mir, local_paths } + } +} + +impl<'a, 'tcx> BitDenotation for MaybeBorrowed<'a, 'tcx> { + type Idx = PathId; + fn name() -> &'static str { "maybe_borrowed" } + fn bits_per_block(&self) -> usize { + self.local_paths.total_count() + } + + fn start_block_effect(&self, _sets: &mut IdxSet) { + // Nothing is borrowed on function entry + } + + fn statement_effect(&self, + sets: &mut BlockSets, + location: Location) { + { + let mut moves = MoveCollector { + local_paths: self.local_paths, + sets + }; + moves.visit_location(self.mir, 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 place = place; + while let Place::Projection(ref proj) = *place { + if let ProjectionElem::Deref = proj.elem { + return; + } + place = &proj.base; + } + } + + match self.local_paths.place_path_acessed_prefix(place) { + Ok(path) | Err(Some(path)) => { + sets.gen(&path); + } + Err(None) => {} + } + } + StatementKind::StorageDead(local) => { + // FIXME(eddyb) use ranges here for performance. + let path = self.local_paths.locals[local]; + for path in iter::once(path).chain(self.local_paths.descendants(path)) { + sets.kill(&path); + } + } + // FIXME(eddyb) cancel all borrows on `yield` (unless the generator is immovable). + _ => {} + } + } + + fn terminator_effect(&self, + sets: &mut BlockSets, + location: Location) { + let mut moves = MoveCollector { + local_paths: self.local_paths, + 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, 'tcx: 'a> { + local_paths: &'a LocalPaths<'tcx>, + sets: &'a mut BlockSets<'b, PathId> +} + +impl<'a, 'b, 'tcx> Visitor<'tcx> for MoveCollector<'a, 'b, 'tcx> { + fn visit_operand(&mut self, operand: &Operand, _: Location) { + if let Operand::Move(ref place) = *operand { + if let Ok(path) = self.local_paths.place_path_acessed_prefix(place) { + self.sets.kill(&path); + } + } + } +} diff --git a/src/librustc_mir/analysis/local_paths/collect.rs b/src/librustc_mir/analysis/local_paths/collect.rs new file mode 100644 index 0000000000000..2dba9afb462b1 --- /dev/null +++ b/src/librustc_mir/analysis/local_paths/collect.rs @@ -0,0 +1,176 @@ +// 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_data_structures::fx::FxHashMap; +use rustc::mir::*; +use rustc::mir::visit::{PlaceContext, Visitor}; +use rustc::ty::Ty; +use analysis::local_paths::{LocalPaths, PathData, PathId}; + +impl<'tcx> LocalPaths<'tcx> { + pub fn collect(mir: &Mir<'tcx>) -> Self { + let mut collector = LocalPathCollector { + locals: mir.local_decls.iter().map(|decl| { + PathTree::new(decl.ty) + }) + .collect() + }; + // All arguments have been accessed prior to the call to this function. + for arg in mir.args_iter() { + collector.locals[arg].accessed = true; + } + collector.visit_mir(mir); + let mut fields = FxHashMap::default(); + let mut variants = FxHashMap::default(); + let mut data = IndexVec::new(); + LocalPaths { + locals: collector.locals.iter().map(|tree| { + tree.flatten(&mut fields, &mut variants, &mut data) + }).collect(), + fields, + variants, + data + } + } +} + +struct PathTree<'tcx> { + ty: Ty<'tcx>, + accessed: bool, + fields: FxHashMap>, + variants: FxHashMap>, +} + +impl<'tcx> PathTree<'tcx> { + fn new(ty: Ty<'tcx>) -> Self { + PathTree { + ty, + accessed: false, + fields: FxHashMap::default(), + variants: FxHashMap::default() + } + } + + fn project(&mut self, elem: &PlaceElem<'tcx>) -> Option<&mut Self> { + if let Some(adt) = self.ty.ty_adt_def() { + // Packed types have additional restrictions + // and it's easier to just not look into them. + if adt.repr.packed() { + return None; + } + + // Enums and unions have overlapping members, so every access + // of any member must be treated as an access of any other. + if adt.is_union() || adt.is_enum() { + self.accessed = true; + } + } + + match *elem { + ProjectionElem::Field(f, ty) => { + Some(self.fields.entry(f).or_insert(PathTree::new(ty))) + } + ProjectionElem::Downcast(_, v) => { + Some(self.variants.entry(v).or_insert(PathTree::new(self.ty))) + } + // Could support indexing by constants in the future. + ProjectionElem::ConstantIndex { .. } | + ProjectionElem::Subslice { .. } => None, + // Can't support without alias analysis. + ProjectionElem::Index(_) | + ProjectionElem::Deref => None + } + } + + fn flatten(&self, + fields: &mut FxHashMap<(PathId, Field), PathId>, + variants: &mut FxHashMap<(PathId, usize), PathId>, + data: &mut IndexVec>) + -> PathId { + let root = data.push(PathData { + ty: self.ty, + last_descendant: PathId::new(0), + accessed: self.accessed + }); + for (&f, child) in &self.fields { + let child = child.flatten(fields, variants, data); + fields.insert((root, f), child); + } + for (&v, child) in &self.variants { + let child = child.flatten(fields, variants, data); + variants.insert((root, v), child); + } + data[root].last_descendant = data.last().unwrap(); + root + } +} + +struct LocalPathCollector<'tcx> { + locals: IndexVec> +} + +impl<'tcx> LocalPathCollector<'tcx> { + fn place_path(&mut self, place: &Place<'tcx>) -> Option<&mut PathTree<'tcx>> { + match *place { + Place::Local(local) => Some(&mut self.locals[local]), + Place::Static(_) => None, + Place::Projection(ref proj) => { + let base = self.place_path(&proj.base)?; + base.project(&proj.elem) + } + } + } +} + +impl<'tcx> Visitor<'tcx> for LocalPathCollector<'tcx> { + fn visit_place(&mut self, + place: &Place<'tcx>, + context: PlaceContext, + location: Location) { + if let Some(path) = self.place_path(place) { + if context.is_use() { + path.accessed = true; + } + } + + // Traverse the projections in `place`. + let context = if context.is_mutating_use() { + PlaceContext::Projection(Mutability::Mut) + } else { + PlaceContext::Projection(Mutability::Not) + }; + let mut place = place; + while let Place::Projection(ref proj) = *place { + self.visit_projection_elem(&proj.elem, context, location); + place = &proj.base; + } + } + + // Handle the locals used in indexing projections. + fn visit_local(&mut self, + &local: &Local, + context: PlaceContext, + _: Location) { + if context.is_use() { + self.locals[local].accessed = true; + } + } + + fn visit_terminator_kind(&mut self, + block: BasicBlock, + kind: &TerminatorKind<'tcx>, + location: Location) { + if let TerminatorKind::Return = *kind { + self.visit_local(&RETURN_PLACE, PlaceContext::Move, location); + } + self.super_terminator_kind(block, kind, location); + } +} diff --git a/src/librustc_mir/analysis/local_paths/mod.rs b/src/librustc_mir/analysis/local_paths/mod.rs new file mode 100644 index 0000000000000..8fb2dfaad2b48 --- /dev/null +++ b/src/librustc_mir/analysis/local_paths/mod.rs @@ -0,0 +1,170 @@ +// 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_data_structures::fx::FxHashMap; +use rustc::mir::*; +use rustc::ty::Ty; +use std::iter::Step; +use std::ops::Range; + +pub mod accesses; +pub mod borrows; +pub mod collect; + +newtype_index!(PathId { DEBUG_FORMAT = "PathId({})" }); + +impl Step for PathId { + fn steps_between(start: &Self, end: &Self) -> Option { + Step::steps_between(&start.index(), &end.index()) + } + fn replace_one(&mut self) -> Self { + *self = PathId::new(self.index().replace_one()); + *self + } + fn replace_zero(&mut self) -> Self { + *self = PathId::new(self.index().replace_zero()); + *self + } + fn add_one(&self) -> Self { + PathId::new(self.index().add_one()) + } + fn sub_one(&self) -> Self { + PathId::new(self.index().sub_one()) + } + fn add_usize(&self, n: usize) -> Option { + self.index().add_usize(n).map(PathId::new) + } +} + +pub struct PathData<'tcx> { + pub ty: Ty<'tcx>, + pub last_descendant: PathId, + + /// Whether this path is ever directly accessed, + /// instead of being just a parent of a path that is. + // FIXME(eddyb) have a separate notion of "access path", + // to keep the sets working on it small. + pub accessed: bool +} + +/// A forest of `Place` interior paths into `Local` roots, flattened in +/// pre-order, with each node immediatelly followed by its descendants. +/// +/// Paths into dereferences aren't tracked, as they count as distinct +/// "interior" roots, which aren't meaningful without alias analysis. +/// As such, users must handle indirect accesses themselves. +/// +/// Paths into array elements aren't currently supported but they could be. +pub struct LocalPaths<'tcx> { + pub data: IndexVec>, + pub locals: IndexVec, + pub fields: FxHashMap<(PathId, Field), PathId>, + pub variants: FxHashMap<(PathId, usize), PathId> +} + +impl<'tcx> LocalPaths<'tcx> { + pub fn total_count(&self) -> usize { + self.data.len() + } + + pub fn descendants(&self, path: PathId) -> Range { + path.add_one()..self.data[path].last_descendant.add_one() + } + + pub fn children<'a>(&'a self, path: PathId) -> Children<'a, 'tcx> { + Children { + local_paths: self, + descendants: self.descendants(path) + } + } + + /// Obtain the `PathId` for the `elem` component of `base`, if it is tracked. + pub fn project(&self, base: PathId, elem: &ProjectionElem) -> Option { + match *elem { + ProjectionElem::Field(f, _) => self.fields.get(&(base, f)).cloned(), + ProjectionElem::Downcast(_, v) => self.variants.get(&(base, v)).cloned(), + // Could support indexing by constants in the future. + ProjectionElem::ConstantIndex { .. } | + ProjectionElem::Subslice { .. } => None, + // Can't support without alias analysis. + ProjectionElem::Index(_) | + ProjectionElem::Deref => None + } + } + + /// If possible, obtain a `PathId` for the complete `Place` (as `Ok(_)`), + /// otherwise, give the longest `PathId` prefix (as `Err(Some(_))`). + pub fn place_path(&self, place: &Place) -> Result> { + match *place { + Place::Local(local) => Ok(self.locals[local]), + Place::Static(_) => Err(None), + Place::Projection(ref proj) => { + let base = self.place_path(&proj.base)?; + match self.project(base, &proj.elem) { + Some(child) => Ok(child), + None => Err(Some(base)) + } + } + } + } + + /// Like `place_path`, but for the shortest accessed path prefix of the `place`. + /// If the path doesn't refer to the complete `place`, it's returned in `Err`. + pub fn place_path_acessed_prefix(&self, place: &Place) -> Result> { + match *place { + Place::Local(local) => Ok(self.locals[local]), + Place::Static(_) => Err(None), + Place::Projection(ref proj) => { + let base = self.place_path_acessed_prefix(&proj.base)?; + if self.data[base].accessed { + return Err(Some(base)); + } + match self.project(base, &proj.elem) { + Some(child) => Ok(child), + None => Err(Some(base)) + } + } + } + } + + /// Add a new local to the given `mir` (which is assumed to be the + /// one `self` was created from) and record a new path for it. + pub fn create_and_record_new_local(&mut self, + mir: &mut Mir<'tcx>, + decl: LocalDecl<'tcx>) + -> Local { + let path = self.data.push(PathData { + ty: decl.ty, + last_descendant: PathId::new(0), + accessed: true + }); + self.data[path].last_descendant = path; + + let local = mir.local_decls.push(decl); + assert_eq!(self.locals.push(path), local); + local + } +} + +pub struct Children<'a, 'tcx: 'a> { + local_paths: &'a LocalPaths<'tcx>, + descendants: Range +} + +impl<'a, 'tcx> Iterator for Children<'a, 'tcx> { + type Item = PathId; + fn next(&mut self) -> Option { + self.descendants.next().map(|child| { + self.descendants.start = self.local_paths.descendants(child).end; + child + }) + } +} 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 new file mode 100644 index 0000000000000..9c7a354615847 --- /dev/null +++ b/src/librustc_mir/analysis/mod.rs @@ -0,0 +1,17 @@ +// 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 eventflow; +pub mod liveness; +pub mod local_paths; +pub mod locations; 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 c4df7349391e2..7271bab821b50 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 66ca74b0139a3..5c1f0f2ce6da6 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 7ca8d0bdd500b..63c29bb8e5756 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 1699ad0f19cf6..fd6e531283bd8 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -34,6 +34,7 @@ Rust MIR: a lowered representation of Rust. Also: an experiment! #![feature(range_contains)] #![feature(rustc_diagnostic_macros)] #![feature(placement_in_syntax)] +#![feature(step_trait)] #![feature(collection_placement)] #![feature(nonzero)] #![feature(underscore_lifetimes)] @@ -60,9 +61,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 bbc7803b84d8e..03485ef183943 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/deaggregator.rs b/src/librustc_mir/transform/deaggregator.rs index eccb0d231b89d..44c35960abaf8 100644 --- a/src/librustc_mir/transform/deaggregator.rs +++ b/src/librustc_mir/transform/deaggregator.rs @@ -21,116 +21,103 @@ impl MirPass for Deaggregator { tcx: TyCtxt<'a, 'tcx, 'tcx>, source: MirSource, mir: &mut Mir<'tcx>) { - let node_path = tcx.item_path_str(source.def_id); - debug!("running on: {:?}", node_path); - // we only run when mir_opt_level > 2 - if tcx.sess.opts.debugging_opts.mir_opt_level <= 2 { - return; - } - // 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) { - (hir::BodyOwnerKind::Fn, None) => {}, - _ => return + (_, 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 + } + } } - // In fact, we might not want to trigger in other cases. - // Ex: when we could use SROA. See issue #35259 - for bb in mir.basic_blocks_mut() { - let mut curr: usize = 0; - while let Some(idx) = get_aggregate_statement_index(curr, &bb.statements) { - // do the replacement - debug!("removing statement {:?}", idx); - let src_info = bb.statements[idx].source_info; - let suffix_stmts = bb.statements.split_off(idx+1); + let can_deaggregate = |statement: &Statement| { + if let StatementKind::Assign(_, ref rhs) = statement.kind { + if let Rvalue::Aggregate(..) = *rhs { + return true; + } + } + + false + }; + + let (basic_blocks, local_decls) = mir.basic_blocks_and_local_decls_mut(); + for bb in basic_blocks { + let mut start = 0; + while let Some(i) = bb.statements[start..].iter().position(&can_deaggregate) { + let i = start + i; + + // FIXME(eddyb) this is probably more expensive than it should be. + // Ideally we'd move the block's statements all at once. + let suffix_stmts = bb.statements.split_off(i + 1); let orig_stmt = bb.statements.pop().unwrap(); - let (lhs, rhs) = match orig_stmt.kind { - StatementKind::Assign(ref lhs, ref rhs) => (lhs, rhs), - _ => span_bug!(src_info.span, "expected assign, not {:?}", orig_stmt), - }; - let (agg_kind, operands) = match rhs { - &Rvalue::Aggregate(ref agg_kind, ref operands) => (agg_kind, operands), - _ => span_bug!(src_info.span, "expected aggregate, not {:?}", rhs), + let source_info = orig_stmt.source_info; + let (mut lhs, kind, operands) = match orig_stmt.kind { + StatementKind::Assign(lhs, Rvalue::Aggregate(kind, operands)) + => (lhs, kind, operands), + _ => bug!() }; - let (adt_def, variant, substs) = match **agg_kind { - AggregateKind::Adt(adt_def, variant, substs, None) - => (adt_def, variant, substs), - _ => span_bug!(src_info.span, "expected struct, not {:?}", rhs), + + let mut set_discriminant = None; + let active_field_index = match *kind { + AggregateKind::Adt(adt_def, variant_index, _, active_field_index) => { + if adt_def.is_enum() { + set_discriminant = Some(Statement { + kind: StatementKind::SetDiscriminant { + place: lhs.clone(), + variant_index, + }, + source_info, + }); + lhs = lhs.downcast(adt_def, variant_index); + } + active_field_index + } + _ => None }; - let n = bb.statements.len(); - bb.statements.reserve(n + operands.len() + suffix_stmts.len()); - for (i, op) in operands.iter().enumerate() { - let ref variant_def = adt_def.variants[variant]; - let ty = variant_def.fields[i].ty(tcx, substs); - let rhs = Rvalue::Use(op.clone()); - let lhs_cast = if adt_def.is_enum() { - Place::Projection(Box::new(PlaceProjection { - base: lhs.clone(), - elem: ProjectionElem::Downcast(adt_def, variant), - })) - } else { - lhs.clone() - }; + let new_total_count = bb.statements.len() + + operands.len() + + (set_discriminant.is_some() as usize) + + suffix_stmts.len(); + bb.statements.reserve(new_total_count); - let lhs_proj = Place::Projection(Box::new(PlaceProjection { - base: lhs_cast, - elem: ProjectionElem::Field(Field::new(i), ty), - })); - let new_statement = Statement { - source_info: src_info, - kind: StatementKind::Assign(lhs_proj, rhs), + for (j, op) in operands.into_iter().enumerate() { + let lhs_field = if let AggregateKind::Array(_) = *kind { + // FIXME(eddyb) `offset` should be u64. + let offset = j as u32; + assert_eq!(offset as usize, j); + lhs.clone().elem(ProjectionElem::ConstantIndex { + offset, + // FIXME(eddyb) `min_length` doesn't appear to be used. + min_length: offset + 1, + from_end: false + }) + } else { + let ty = op.ty(local_decls, tcx); + let field = Field::new(active_field_index.unwrap_or(j)); + lhs.clone().field(field, ty) }; - debug!("inserting: {:?} @ {:?}", new_statement, idx + i); - bb.statements.push(new_statement); + bb.statements.push(Statement { + source_info, + kind: StatementKind::Assign(lhs_field, Rvalue::Use(op)), + }); } - // if the aggregate was an enum, we need to set the discriminant - if adt_def.is_enum() { - let set_discriminant = Statement { - kind: StatementKind::SetDiscriminant { - place: lhs.clone(), - variant_index: variant, - }, - source_info: src_info, - }; - bb.statements.push(set_discriminant); - }; + // If the aggregate was an enum, we need to set the discriminant. + bb.statements.extend(set_discriminant); - curr = bb.statements.len(); + start = bb.statements.len(); bb.statements.extend(suffix_stmts); } } } } - -fn get_aggregate_statement_index<'a, 'tcx, 'b>(start: usize, - statements: &Vec>) - -> Option { - for i in start..statements.len() { - let ref statement = statements[i]; - let rhs = match statement.kind { - StatementKind::Assign(_, ref rhs) => rhs, - _ => continue, - }; - let (kind, operands) = match rhs { - &Rvalue::Aggregate(ref kind, ref operands) => (kind, operands), - _ => continue, - }; - let (adt_def, variant) = match **kind { - AggregateKind::Adt(adt_def, variant, _, None) => (adt_def, variant), - _ => continue, - }; - if operands.len() == 0 { - // don't deaggregate () - continue; - } - debug!("getting variant {:?}", variant); - debug!("for adt_def {:?}", adt_def); - return Some(i); - }; - None -} 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 812665f5fa498..115fd62c54da1 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::{Kind, 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/mod.rs b/src/librustc_mir/transform/mod.rs index 563405fccc970..e38af9bc92251 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -44,6 +44,7 @@ pub mod copy_prop; pub mod generator; pub mod inline; pub mod lower_128bit; +pub mod unify_places; pub(crate) fn provide(providers: &mut Providers) { self::qualify_consts::provide(providers); @@ -255,14 +256,19 @@ fn optimized_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx // Optimizations begin. inline::Inline, + + // Lowering generator control-flow and variables + // has to happen before we do anything else to them. + generator::StateTransform, + instcombine::InstCombine, deaggregator::Deaggregator, + unify_places::UnifyPlaces, copy_prop::CopyPropagation, remove_noop_landing_pads::RemoveNoopLandingPads, simplify::SimplifyCfg::new("final"), simplify::SimplifyLocals, - generator::StateTransform, add_call_guards::CriticalCallEdges, dump_mir::Marker("PreTrans"), ]; 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/transform/simplify.rs b/src/librustc_mir/transform/simplify.rs index e7675b4ceaf29..2c6ed1f19b7fd 100644 --- a/src/librustc_mir/transform/simplify.rs +++ b/src/librustc_mir/transform/simplify.rs @@ -42,6 +42,7 @@ use rustc_data_structures::indexed_vec::{Idx, IndexVec}; use rustc::ty::TyCtxt; use rustc::mir::*; use rustc::mir::visit::{MutVisitor, Visitor, PlaceContext}; +use rustc::session::config::FullDebugInfo; use std::borrow::Cow; use transform::{MirPass, MirSource}; @@ -281,16 +282,24 @@ pub struct SimplifyLocals; impl MirPass for SimplifyLocals { fn run_pass<'a, 'tcx>(&self, - _: TyCtxt<'a, 'tcx, 'tcx>, + tcx: TyCtxt<'a, 'tcx, 'tcx>, _: MirSource, mir: &mut Mir<'tcx>) { let mut marker = DeclMarker { locals: BitVector::new(mir.local_decls.len()) }; marker.visit_mir(mir); // Return pointer and arguments are always live - marker.locals.insert(0); - for idx in mir.args_iter() { - marker.locals.insert(idx.index()); + marker.locals.insert(RETURN_PLACE.index()); + for arg in mir.args_iter() { + marker.locals.insert(arg.index()); } + + // We may need to keep dead user variables live for debuginfo. + if tcx.sess.opts.debuginfo == FullDebugInfo { + for local in mir.vars_iter() { + marker.locals.insert(local.index()); + } + } + let map = make_local_map(&mut mir.local_decls, marker.locals); // Update references to all vars and tmps now LocalUpdater { map: map }.visit_mir(mir); diff --git a/src/librustc_mir/transform/unify_places.rs b/src/librustc_mir/transform/unify_places.rs new file mode 100644 index 0000000000000..ac07c31663c22 --- /dev/null +++ b/src/librustc_mir/transform/unify_places.rs @@ -0,0 +1,498 @@ +// 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::fx::FxHashMap; +use rustc_data_structures::indexed_vec::{Idx, IndexVec}; +use rustc_data_structures::indexed_set::IdxSet; +use rustc::hir; +use rustc::mir::*; +use rustc::mir::visit::{PlaceContext, MutVisitor, Visitor}; +use rustc::session::config::FullDebugInfo; +use rustc::ty::TyCtxt; +use std::iter; +use std::mem; +use analysis::eventflow::{After, Before, SparseBitSet}; +use analysis::local_paths::{LocalPaths, PathId}; +use analysis::local_paths::accesses::Accesses; +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) + } +} + +struct UnionFindWithConflictsAndPriority { + finder: Finder, + conflicts: UnionFindSymRel, + priority: P +} + +#[derive(Debug)] +struct Conflict; +impl T, T: Ord> UnionFindWithConflictsAndPriority { + fn union(&mut self, a: I, b: I) -> Result { + let a = self.finder.find(a); + let b = self.finder.find(b); + if a == b { + return Ok(a); + } + + if self.conflicts.relates(a, b) { + return Err(Conflict); + } + + let conflict_root = self.conflicts.union(a, b); + + let a_priority = ((self.priority)(a), a == conflict_root); + let b_priority = ((self.priority)(b), b == conflict_root); + let (root, child) = if a_priority > b_priority { + (a, b) + } else { + (b, a) + }; + self.finder.parent[child] = root; + + Ok(root) + } +} + +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 + } + } + } + + // FIXME(eddyb) We should allow multiple user variables + // per local for debuginfo instead of not optimizing. + // FIXME(eddyb) (easier) Mark user variables as not renameble, + // we can always optimize *at least* temporaries! + if tcx.sess.opts.debuginfo == FullDebugInfo { + return; + } + // HACK(eddyb) testing to debug misoptimization + // if tcx.sess.opts.debugging_opts.mir_opt_level <= 1 { return; } + + let local_paths = &mut LocalPaths::collect(mir); + let mut path_to_place: FxHashMap = FxHashMap::default(); + let mut dependent_suffix; + let mut replacement_finder = { + let flat_locations = &FlatLocations::collect(mir); + let accesses = &Accesses::collect(mir, local_paths, flat_locations); + let mut observers = accesses.results.observe(); + let mut conflicts = IndexVec::from_elem_n(SparseBitSet::new(), + local_paths.total_count()); + 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) { + debug!("unify_places: {:?} live", i); + // FIXME(eddyb) Reduce the cost of this already Q_Q. + for j in past.iter() { + if i != j && future.contains(&j) { + if conflicts[i].insert(j) { + debug!("unify_places: conflicts with {:?}", j); + } + } + } + } + } + }; + let mut checked_after_last_statement = false; + for (statement_index, stmt) in data.statements.iter().enumerate() { + // FIXME(eddyb) remove this debug! and most of the others. + debug!("unify_places: {:?}", stmt); + 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 Ok(dest_path) = local_paths.place_path(dest) { + if let Ok(src_path) = local_paths.place_path(src) { + candidates.push((dest, dest_path, src, src_path)); + 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; + } + debug!("unify_places: {:?}", data.terminator()); + 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: (0..local_paths.total_count()).map(PathId::new).collect(), + }, + relation: conflicts + }; + + // FIXME(eddyb) find a better name than `dependent_suffix`. + dependent_suffix = { + let mut dependency_collector = DependencyCollector { + local_paths, + conflicts: &mut conflicts, + dependent_suffix: IndexVec::from_elem_n(0, local_paths.total_count()) + }; + dependency_collector.visit_mir(mir); + + // FIXME(eddyb) just pass every local through the dependency collector. + + // Treat every path in the return place and arguments + // as dependent, to avoid any of them being renamed away. + for local in iter::once(RETURN_PLACE).chain(mir.args_iter()) { + let path = local_paths.locals[local]; + dependency_collector.dependent_suffix[path] = 1; + for descendant in local_paths.descendants(path) { + dependency_collector.dependent_suffix[descendant] = 1; + dependency_collector.conflicts.union(descendant, path); + } + } + + for local in mir.vars_and_temps_iter() { + dependency_collector.collect_path(local_paths.locals[local], &mut vec![]); + } + + dependency_collector.dependent_suffix + }; + + let mut uf = UnionFindWithConflictsAndPriority { + finder: Finder { + parent: (0..local_paths.total_count()).map(PathId::new).collect(), + }, + conflicts, + priority: |path| { + // FIXME(eddyb) prefer shorter paths if they're both + // the same category (var/arg/return). + // That would achieve a form of SROA, maybe? + + dependent_suffix[path] > 0 + } + }; + + // Union together all the candidate source and targets. + // Candidates may fail if they could cause a conflict. + for (a, a_path, b, b_path) in candidates { + // HACK(eddyb) this is a bit messy, maybe should go into `union`. + let either_independent = + dependent_suffix[uf.finder.find(a_path)] == 0 || + dependent_suffix[uf.finder.find(b_path)] == 0; + if !either_independent { + continue; + } + + let union_result = uf.union(a_path, b_path); + debug!("unify_places: candidate ({:?} => {:?}) <- ({:?} => {:?}) unified to {:?}", + a_path, a, b_path, b, union_result); + + if let Ok(root) = union_result { + // Cache the MIR `Place` representations of paths, for + // the paths that we might end up replacing to later. + let place = if root == a_path { + a + } else if root == b_path { + b + } else { + continue; + }; + path_to_place.entry(root).or_insert_with(|| place.clone()); + } + } + + uf.finder + }; + + // Rename unrenamed independent fields when they would be + // clobbered from one of their parents being renamed over. + // FIXME(eddyb) Do we need this? If `x.a` isn't accessed but `x` and `x.a.b` are? + { + let mut used_as_rename_destination = SparseBitSet::new(); + for from in PathId::new(0)..PathId::new(local_paths.total_count()) { + let to = replacement_finder.find(from); + if from != to { + used_as_rename_destination.insert(to); + } + } + + for local in mir.vars_and_temps_iter() { + // FIXME(eddyb) use a range iterator over `used_as_rename_destination`. + let local_path = local_paths.locals[local]; + let rename_destinations = iter::once(local_path) + .chain(local_paths.descendants(local_path)) + .filter(|&p| used_as_rename_destination.contains(p)); + for destination in rename_destinations { + for path in local_paths.descendants(destination) { + let independent = dependent_suffix[path] == 0; + let renamed = replacement_finder.find(path) != path; + if independent && !renamed { + // Rename `path` to a a fresh temp to avoid clobbering. + let mut decl = mir.local_decls[local].clone(); + decl.ty = local_paths.data[path].ty; + decl.name = None; + decl.is_user_variable = false; + let new = local_paths.create_and_record_new_local(mir, decl); + let new_path = local_paths.locals[new]; + assert_eq!(new_path, replacement_finder.parent.push(new_path)); + assert_eq!(new_path, dependent_suffix.push(0)); + path_to_place.insert(new_path, Place::Local(new)); + replacement_finder.parent[path] = new_path; + + debug!("unify_places: de-clobbering {:?} (in {:?}) to \ + ({:?} => {:?})", + path, local, new_path, new); + } + } + } + } + } + + // Apply the replacements we computed previously. + let mut replacer = Replacer { + local_paths, + dependent_suffix: &dependent_suffix, + path_to_place, + replacement_finder + }; + replacer.visit_mir(mir); + } +} + +struct DependencyCollector<'a, 'tcx: 'a> { + local_paths: &'a LocalPaths<'tcx>, + conflicts: &'a mut UnionFindSymRel, + dependent_suffix: IndexVec +} + +impl<'a, 'tcx> DependencyCollector<'a, 'tcx> { + // FIXME(eddyb) Keep track of left/right ranges of variants + // at every level of parnts and check conflicts between. + // fields within the current path and lateral parent variants. + // Note that it appears to be working anyway, because of + // `Discriminant` and `SetDiscriminant` touching the whole enum. + fn collect_path(&mut self, path: PathId, prefixes: &mut Vec) { + prefixes.push(path); + for child in self.local_paths.children(path) { + self.collect_path(child, prefixes); + } + assert_eq!(prefixes.pop(), Some(path)); + + // This path is dependent (if at all) on the longest accessed prefix. + for (i, &parent) in prefixes.iter().rev().enumerate() { + if self.local_paths.data[parent].accessed { + debug!("unify_places: {:?} depends on {:?} (suffix length {})", + path, parent, i + 1); + self.dependent_suffix[path] = i as u32 + 1; + + // This path is now dependent on `parent`, so we need + // all of its conflicts to be reachable from `parent`. + // FIXME(eddyb) This is symmetric transitivity - maybe + // we want it to be directional instead? This will + // happily put `a.x` and `a.y` in the same conflict + // group (`a`'s`) just because they depend on `a`. + self.conflicts.union(path, parent); + + break; + } + } + + } +} + +impl<'a, 'tcx> Visitor<'tcx> for DependencyCollector<'a, 'tcx> { + fn visit_projection_elem(&mut self, + elem: &PlaceElem<'tcx>, + context: PlaceContext<'tcx>, + location: Location) { + if let ProjectionElem::Index(i) = *elem { + let path = self.local_paths.locals[i]; + // Array indices should be integers which have no fields. + assert_eq!(self.local_paths.descendants(path).count(), 0); + self.dependent_suffix[path] = 1; + } + self.super_projection_elem(elem, context, location); + } +} + +struct Replacer<'a, 'tcx: 'a> { + local_paths: &'a LocalPaths<'tcx>, + dependent_suffix: &'a IndexVec, + path_to_place: FxHashMap>, + replacement_finder: Finder +} + +impl<'a, 'tcx> Replacer<'a, 'tcx> { + fn replace_if_needed(&mut self, place: &mut Place<'tcx>, mut skip: u32) { + // FIXME(eddyb) use a helper function to get the `PathId` + // at every level of the `Place` in `O(n)` instead of `O(n^2)`. + if skip == 0 { + if let Ok(from) = self.local_paths.place_path(place) { + let dependent_suffix = self.dependent_suffix[from]; + let to = self.replacement_finder.find(from); + + // This place is fully independent, so we either replace it, + // or leave alone (even if e.g. its parent might be replaced). + if dependent_suffix == 0 { + if to != from { + *place = self.path_to_place[&to].clone(); + + // Recurse in case the replacement is dependent + // on a parent that also needs to be replaced. + // FIXME(eddyb) precompute this into `path_to_place`. + self.replace_if_needed(place, 0); + } + return; + } + + assert_eq!(to, from); + skip = dependent_suffix - 1; + } + } else { + skip -= 1; + } + + if let Place::Projection(ref mut proj) = *place { + self.replace_if_needed(&mut proj.base, skip); + } + } +} + +impl<'a, 'tcx> MutVisitor<'tcx> for Replacer<'a, 'tcx> { + fn visit_place(&mut self, place: &mut Place<'tcx>, _: PlaceContext<'tcx>, _: Location) { + self.replace_if_needed(place, 0); + } + + 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.kind = StatementKind::Nop; + } + _ => {} + } + + self.super_statement(block, statement, location); + + // Remove self-assignments resulting from replaced move chains. + // FIXME(eddyb) do this without comparing `Place`s. + // (within `super_statement` above we know `PathId`s for both sides) + 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 + }; + if nop { + statement.kind = StatementKind::Nop; + } + } +} 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; 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 1f900a3770eb3..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__5:%[0-9]+]] = bitcast %"core::option::Option"* %_5 to i8* -// CHECK: call void @llvm.lifetime.start{{.*}}(i{{[0-9 ]+}}, i8* [[S__5]]) - -// 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__5:%[0-9]+]] = bitcast %"core::option::Option"* %_5 to i8* -// CHECK: call void @llvm.lifetime.end{{.*}}(i{{[0-9 ]+}}, i8* [[E__5]]) - } - - 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/match.rs b/src/test/codegen/match.rs index 660b6346c57f1..c9d0427dd0ad4 100644 --- a/src/test/codegen/match.rs +++ b/src/test/codegen/match.rs @@ -19,7 +19,7 @@ pub enum E { // CHECK-LABEL: @exhaustive_match #[no_mangle] -pub fn exhaustive_match(e: E) { +pub fn exhaustive_match(e: E, unit: ()) { // CHECK: switch{{.*}}, label %[[OTHERWISE:[a-zA-Z0-9_]+]] [ // CHECK-NEXT: i[[TY:[0-9]+]] [[DISCR:[0-9]+]], label %[[A:[a-zA-Z0-9_]+]] // CHECK-NEXT: i[[TY:[0-9]+]] [[DISCR:[0-9]+]], label %[[B:[a-zA-Z0-9_]+]] @@ -31,7 +31,7 @@ pub fn exhaustive_match(e: E) { // CHECK: [[OTHERWISE]]: // CHECK-NEXT: unreachable match e { - E::A => (), - E::B => (), + E::A => unit, + E::B => unit, } } 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/closure_expressions.rs b/src/test/incremental/hashes/closure_expressions.rs index d8a87da5918a9..73418f4886070 100644 --- a/src/test/incremental/hashes/closure_expressions.rs +++ b/src/test/incremental/hashes/closure_expressions.rs @@ -64,7 +64,7 @@ pub fn change_parameter_pattern() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody, MirValidated, MirOptimized, TypeckTables")] +#[rustc_clean(cfg="cfail2", except="HirBody, MirValidated, TypeckTables")] #[rustc_clean(cfg="cfail3")] pub fn change_parameter_pattern() { let _ = |&x: &u32| x; 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/incremental/issue-38222.rs b/src/test/incremental/issue-38222.rs index 7bb8af74eeb7e..f890672aa8f58 100644 --- a/src/test/incremental/issue-38222.rs +++ b/src/test/incremental/issue-38222.rs @@ -18,7 +18,7 @@ #![feature(rustc_attrs)] -#![rustc_partition_translated(module="issue_38222-mod1", cfg="rpass2")] +#![rustc_partition_reused(module="issue_38222-mod1", cfg="rpass2")] // If trans had added a dependency edge to the Krate dep-node, nothing would // be re-used, so checking that this module was re-used is sufficient. 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 35bb231df5adf..3ed4b76a5f365 100644 --- a/src/test/mir-opt/copy_propagation_arg.rs +++ b/src/test/mir-opt/copy_propagation_arg.rs @@ -71,14 +71,13 @@ 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; -// _0 = (); // return; // } // END rustc.bar.CopyPropagation.before.mir @@ -96,11 +95,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); -// _0 = (); +// ... // return; // } // END rustc.baz.CopyPropagation.before.mir @@ -116,12 +114,10 @@ fn main() { // bb0: { // ... // _3 = _1; -// _2 = move _3; // ... // _1 = const 123i32; // ... -// _4 = _2; -// _0 = move _4; +// _0 = _3; // ... // return; // } @@ -133,7 +129,7 @@ fn main() { // ... // _1 = const 123i32; // ... -// _0 = move _3; +// _0 = _3; // ... // return; // } diff --git a/src/test/mir-opt/deaggregator_test_multiple.rs b/src/test/mir-opt/deaggregator_test_multiple.rs index 3a9a458fd464d..5127ed5885371 100644 --- a/src/test/mir-opt/deaggregator_test_multiple.rs +++ b/src/test/mir-opt/deaggregator_test_multiple.rs @@ -52,7 +52,8 @@ fn main() { // ((_4 as A).0: i32) = move _5; // discriminant(_4) = 0; // ... -// _0 = [move _2, move _4]; +// _0[0 of 1] = move _2; +// _0[1 of 2] = move _4; // ... // 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; // } diff --git a/src/test/run-make/sepcomp-cci-copies/Makefile b/src/test/run-make/sepcomp-cci-copies/Makefile index ccd4e1b0e715a..36f913ff3faac 100644 --- a/src/test/run-make/sepcomp-cci-copies/Makefile +++ b/src/test/run-make/sepcomp-cci-copies/Makefile @@ -2,9 +2,11 @@ # Check that cross-crate inlined items are inlined in all compilation units # that refer to them, and not in any other compilation units. +# Note that we have to pass `-C codegen-units=6` because up to two CGUs may be +# created for each source module (see `rustc_mir::monomorphize::partitioning`). all: $(RUSTC) cci_lib.rs - $(RUSTC) foo.rs --emit=llvm-ir -C codegen-units=3 \ + $(RUSTC) foo.rs --emit=llvm-ir -C codegen-units=6 \ -Z inline-in-all-cgus [ "$$(cat "$(TMPDIR)"/foo.*.ll | grep -c define\ .*cci_fn)" -eq "2" ] diff --git a/src/test/run-pass/mir_raw_fat_ptr.rs b/src/test/run-pass/mir_raw_fat_ptr.rs index 846318ec4fd34..0daaf56be769f 100644 --- a/src/test/run-pass/mir_raw_fat_ptr.rs +++ b/src/test/run-pass/mir_raw_fat_ptr.rs @@ -156,6 +156,7 @@ fn main() { S(10, [11, 12, 13]), S(4, [5, 6]) ); + mem::drop(&ss); // HACK(eddyb) avoid optimizations splitting `ss.{0,1,2}`. assert_inorder(&[ &ss.0 as *const S<[u8]>, &ss.1 as *const S<[u8]>, diff --git a/src/test/ui/print_type_sizes/generics.rs b/src/test/ui/print_type_sizes/generics.rs index d0e5bd1d92abf..21fdbb3f5a1cc 100644 --- a/src/test/ui/print_type_sizes/generics.rs +++ b/src/test/ui/print_type_sizes/generics.rs @@ -72,7 +72,7 @@ pub fn f1(x: T) { fn start(_: isize, _: *const *const u8) -> isize { let _b: Pair = Pair::new(0, 0); let _s: Pair = Pair::new(SevenBytes::new(), SevenBytes::new()); - let _z: ZeroSized = ZeroSized; + let ref _z: ZeroSized = ZeroSized; f1::(SevenBytes::new()); 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..6aaa49a969037 100644 --- a/src/test/ui/print_type_sizes/no_duplicates.rs +++ b/src/test/ui/print_type_sizes/no_duplicates.rs @@ -20,11 +20,13 @@ pub struct SevenBytes([u8; 7]); pub fn f1() { - let _s: SevenBytes = SevenBytes([0; 7]); + let mut s: SevenBytes = SevenBytes([0; 7]); + drop(&mut s); } #[start] fn start(_: isize, _: *const *const u8) -> isize { - let _s: SevenBytes = SevenBytes([0; 7]); + let mut s: SevenBytes = SevenBytes([0; 7]); + drop(&mut s); 0 }