2016-06-20 23:55:14 +03:00
|
|
|
//! An analysis to determine which locals require allocas and
|
2015-11-03 06:35:09 -05:00
|
|
|
//! which do not.
|
|
|
|
|
Merge indexed_set.rs into bitvec.rs, and rename it bit_set.rs.
Currently we have two files implementing bitsets (and 2D bit matrices).
This commit combines them into one, taking the best features from each.
This involves renaming a lot of things. The high level changes are as
follows.
- bitvec.rs --> bit_set.rs
- indexed_set.rs --> (removed)
- BitArray + IdxSet --> BitSet (merged, see below)
- BitVector --> GrowableBitSet
- {,Sparse,Hybrid}IdxSet --> {,Sparse,Hybrid}BitSet
- BitMatrix --> BitMatrix
- SparseBitMatrix --> SparseBitMatrix
The changes within the bitset types themselves are as follows.
```
OLD OLD NEW
BitArray<C> IdxSet<T> BitSet<T>
-------- ------ ------
grow - grow
new - (remove)
new_empty new_empty new_empty
new_filled new_filled new_filled
- to_hybrid to_hybrid
clear clear clear
set_up_to set_up_to set_up_to
clear_above - clear_above
count - count
contains(T) contains(&T) contains(T)
contains_all - superset
is_empty - is_empty
insert(T) add(&T) insert(T)
insert_all - insert_all()
remove(T) remove(&T) remove(T)
words words words
words_mut words_mut words_mut
- overwrite overwrite
merge union union
- subtract subtract
- intersect intersect
iter iter iter
```
In general, when choosing names I went with:
- names that are more obvious (e.g. `BitSet` over `IdxSet`).
- names that are more like the Rust libraries (e.g. `T` over `C`,
`insert` over `add`);
- names that are more set-like (e.g. `union` over `merge`, `superset`
over `contains_all`, `domain_size` over `num_bits`).
Also, using `T` for index arguments seems more sensible than `&T` --
even though the latter is standard in Rust collection types -- because
indices are always copyable. It also results in fewer `&` and `*`
sigils in practice.
2018-09-14 15:07:25 +10:00
|
|
|
use rustc_data_structures::bit_set::BitSet;
|
2018-07-02 06:14:49 -04:00
|
|
|
use rustc_data_structures::graph::dominators::Dominators;
|
2016-06-07 17:28:36 +03:00
|
|
|
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
|
2018-01-26 16:44:10 +01:00
|
|
|
use rustc::mir::{self, Location, TerminatorKind};
|
2018-10-26 13:22:45 +02:00
|
|
|
use rustc::mir::visit::{Visitor, PlaceContext, MutatingUseContext, NonMutatingUseContext};
|
2016-06-05 19:38:22 +03:00
|
|
|
use rustc::mir::traversal;
|
2017-08-04 11:25:13 +03:00
|
|
|
use rustc::ty;
|
2018-09-20 15:47:22 +02:00
|
|
|
use rustc::ty::layout::{LayoutOf, HasTyCtxt};
|
2018-01-05 07:34:28 +02:00
|
|
|
use super::FunctionCx;
|
2018-11-16 13:45:28 +02:00
|
|
|
use traits::*;
|
2015-11-03 06:35:09 -05:00
|
|
|
|
2018-09-20 15:47:22 +02:00
|
|
|
pub fn non_ssa_locals<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>(
|
|
|
|
fx: &FunctionCx<'a, 'tcx, Bx>
|
|
|
|
) -> BitSet<mir::Local> {
|
2018-01-05 07:34:28 +02:00
|
|
|
let mir = fx.mir;
|
|
|
|
let mut analyzer = LocalAnalyzer::new(fx);
|
2015-11-03 06:35:09 -05:00
|
|
|
|
|
|
|
analyzer.visit_mir(mir);
|
|
|
|
|
2016-09-25 01:38:27 +02:00
|
|
|
for (index, ty) in mir.local_decls.iter().map(|l| l.ty).enumerate() {
|
2018-01-05 07:34:28 +02:00
|
|
|
let ty = fx.monomorphize(&ty);
|
2016-06-20 23:55:14 +03:00
|
|
|
debug!("local {} has type {:?}", index, ty);
|
2018-01-05 07:34:28 +02:00
|
|
|
let layout = fx.cx.layout_of(ty);
|
2018-10-03 13:49:57 +02:00
|
|
|
if fx.cx.is_backend_immediate(layout) {
|
2015-11-03 06:35:09 -05:00
|
|
|
// These sorts of types are immediates that we can store
|
2018-07-10 13:28:39 +03:00
|
|
|
// in an Value without an alloca.
|
2018-10-03 13:49:57 +02:00
|
|
|
} else if fx.cx.is_backend_scalar_pair(layout) {
|
2016-05-25 11:58:08 +03:00
|
|
|
// We allow pairs and uses of any of their 2 fields.
|
2015-11-03 06:35:09 -05:00
|
|
|
} else {
|
|
|
|
// These sorts of types require an alloca. Note that
|
2017-09-21 20:40:50 +03:00
|
|
|
// is_llvm_immediate() may *still* be true, particularly
|
2015-11-03 06:35:09 -05:00
|
|
|
// for newtypes, but we currently force some types
|
2018-11-27 02:59:49 +00:00
|
|
|
// (e.g., structs) into an alloca unconditionally, just so
|
2015-11-03 06:35:09 -05:00
|
|
|
// that we don't have to deal with having two pathways
|
2015-11-03 15:50:04 -05:00
|
|
|
// (gep vs extractvalue etc).
|
2018-02-17 17:46:15 +02:00
|
|
|
analyzer.not_ssa(mir::Local::new(index));
|
2015-11-03 06:35:09 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-17 17:46:15 +02:00
|
|
|
analyzer.non_ssa_locals
|
2015-11-03 06:35:09 -05:00
|
|
|
}
|
|
|
|
|
2018-09-20 15:47:22 +02:00
|
|
|
struct LocalAnalyzer<'mir, 'a: 'mir, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> {
|
|
|
|
fx: &'mir FunctionCx<'a, 'tcx, Bx>,
|
2018-02-17 17:46:15 +02:00
|
|
|
dominators: Dominators<mir::BasicBlock>,
|
Merge indexed_set.rs into bitvec.rs, and rename it bit_set.rs.
Currently we have two files implementing bitsets (and 2D bit matrices).
This commit combines them into one, taking the best features from each.
This involves renaming a lot of things. The high level changes are as
follows.
- bitvec.rs --> bit_set.rs
- indexed_set.rs --> (removed)
- BitArray + IdxSet --> BitSet (merged, see below)
- BitVector --> GrowableBitSet
- {,Sparse,Hybrid}IdxSet --> {,Sparse,Hybrid}BitSet
- BitMatrix --> BitMatrix
- SparseBitMatrix --> SparseBitMatrix
The changes within the bitset types themselves are as follows.
```
OLD OLD NEW
BitArray<C> IdxSet<T> BitSet<T>
-------- ------ ------
grow - grow
new - (remove)
new_empty new_empty new_empty
new_filled new_filled new_filled
- to_hybrid to_hybrid
clear clear clear
set_up_to set_up_to set_up_to
clear_above - clear_above
count - count
contains(T) contains(&T) contains(T)
contains_all - superset
is_empty - is_empty
insert(T) add(&T) insert(T)
insert_all - insert_all()
remove(T) remove(&T) remove(T)
words words words
words_mut words_mut words_mut
- overwrite overwrite
merge union union
- subtract subtract
- intersect intersect
iter iter iter
```
In general, when choosing names I went with:
- names that are more obvious (e.g. `BitSet` over `IdxSet`).
- names that are more like the Rust libraries (e.g. `T` over `C`,
`insert` over `add`);
- names that are more set-like (e.g. `union` over `merge`, `superset`
over `contains_all`, `domain_size` over `num_bits`).
Also, using `T` for index arguments seems more sensible than `&T` --
even though the latter is standard in Rust collection types -- because
indices are always copyable. It also results in fewer `&` and `*`
sigils in practice.
2018-09-14 15:07:25 +10:00
|
|
|
non_ssa_locals: BitSet<mir::Local>,
|
2018-02-17 17:46:15 +02:00
|
|
|
// The location of the first visited direct assignment to each
|
|
|
|
// local, or an invalid location (out of bounds `block` index).
|
|
|
|
first_assignment: IndexVec<mir::Local, Location>
|
2015-11-03 06:35:09 -05:00
|
|
|
}
|
|
|
|
|
2018-09-20 15:47:22 +02:00
|
|
|
impl<Bx: BuilderMethods<'a, 'tcx>> LocalAnalyzer<'mir, 'a, 'tcx, Bx> {
|
|
|
|
fn new(fx: &'mir FunctionCx<'a, 'tcx, Bx>) -> Self {
|
2018-02-17 17:46:15 +02:00
|
|
|
let invalid_location =
|
|
|
|
mir::BasicBlock::new(fx.mir.basic_blocks().len()).start_location();
|
Avoid unnecessary copies of arguments that are simple bindings
Initially MIR differentiated between arguments and locals, which
introduced a need to add extra copies assigning the argument to a
local, even for simple bindings. This differentiation no longer exists,
but we're still creating those copies, bloating the MIR and LLVM IR we
emit.
Additionally, the current approach means that we create debug info for
both the incoming argument (marking it as an argument), and then
immediately shadow it a local that goes by the same name. This can be
confusing when using e.g. "info args" in gdb, or when e.g. a debugger
with a GUI displays the function arguments separately from the local
variables, especially when the binding is mutable, because the argument
doesn't change, while the local variable does.
2017-10-11 20:49:36 +02:00
|
|
|
let mut analyzer = LocalAnalyzer {
|
2018-01-05 07:34:28 +02:00
|
|
|
fx,
|
2018-02-17 17:46:15 +02:00
|
|
|
dominators: fx.mir.dominators(),
|
Merge indexed_set.rs into bitvec.rs, and rename it bit_set.rs.
Currently we have two files implementing bitsets (and 2D bit matrices).
This commit combines them into one, taking the best features from each.
This involves renaming a lot of things. The high level changes are as
follows.
- bitvec.rs --> bit_set.rs
- indexed_set.rs --> (removed)
- BitArray + IdxSet --> BitSet (merged, see below)
- BitVector --> GrowableBitSet
- {,Sparse,Hybrid}IdxSet --> {,Sparse,Hybrid}BitSet
- BitMatrix --> BitMatrix
- SparseBitMatrix --> SparseBitMatrix
The changes within the bitset types themselves are as follows.
```
OLD OLD NEW
BitArray<C> IdxSet<T> BitSet<T>
-------- ------ ------
grow - grow
new - (remove)
new_empty new_empty new_empty
new_filled new_filled new_filled
- to_hybrid to_hybrid
clear clear clear
set_up_to set_up_to set_up_to
clear_above - clear_above
count - count
contains(T) contains(&T) contains(T)
contains_all - superset
is_empty - is_empty
insert(T) add(&T) insert(T)
insert_all - insert_all()
remove(T) remove(&T) remove(T)
words words words
words_mut words_mut words_mut
- overwrite overwrite
merge union union
- subtract subtract
- intersect intersect
iter iter iter
```
In general, when choosing names I went with:
- names that are more obvious (e.g. `BitSet` over `IdxSet`).
- names that are more like the Rust libraries (e.g. `T` over `C`,
`insert` over `add`);
- names that are more set-like (e.g. `union` over `merge`, `superset`
over `contains_all`, `domain_size` over `num_bits`).
Also, using `T` for index arguments seems more sensible than `&T` --
even though the latter is standard in Rust collection types -- because
indices are always copyable. It also results in fewer `&` and `*`
sigils in practice.
2018-09-14 15:07:25 +10:00
|
|
|
non_ssa_locals: BitSet::new_empty(fx.mir.local_decls.len()),
|
2018-02-17 17:46:15 +02:00
|
|
|
first_assignment: IndexVec::from_elem(invalid_location, &fx.mir.local_decls)
|
Avoid unnecessary copies of arguments that are simple bindings
Initially MIR differentiated between arguments and locals, which
introduced a need to add extra copies assigning the argument to a
local, even for simple bindings. This differentiation no longer exists,
but we're still creating those copies, bloating the MIR and LLVM IR we
emit.
Additionally, the current approach means that we create debug info for
both the incoming argument (marking it as an argument), and then
immediately shadow it a local that goes by the same name. This can be
confusing when using e.g. "info args" in gdb, or when e.g. a debugger
with a GUI displays the function arguments separately from the local
variables, especially when the binding is mutable, because the argument
doesn't change, while the local variable does.
2017-10-11 20:49:36 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
// Arguments get assigned to by means of the function being called
|
2018-02-17 17:46:15 +02:00
|
|
|
for arg in fx.mir.args_iter() {
|
|
|
|
analyzer.first_assignment[arg] = mir::START_BLOCK.start_location();
|
2016-01-21 18:57:43 +02:00
|
|
|
}
|
Avoid unnecessary copies of arguments that are simple bindings
Initially MIR differentiated between arguments and locals, which
introduced a need to add extra copies assigning the argument to a
local, even for simple bindings. This differentiation no longer exists,
but we're still creating those copies, bloating the MIR and LLVM IR we
emit.
Additionally, the current approach means that we create debug info for
both the incoming argument (marking it as an argument), and then
immediately shadow it a local that goes by the same name. This can be
confusing when using e.g. "info args" in gdb, or when e.g. a debugger
with a GUI displays the function arguments separately from the local
variables, especially when the binding is mutable, because the argument
doesn't change, while the local variable does.
2017-10-11 20:49:36 +02:00
|
|
|
|
|
|
|
analyzer
|
2015-11-03 06:35:09 -05:00
|
|
|
}
|
|
|
|
|
2018-02-17 17:46:15 +02:00
|
|
|
fn first_assignment(&self, local: mir::Local) -> Option<Location> {
|
|
|
|
let location = self.first_assignment[local];
|
|
|
|
if location.block.index() < self.fx.mir.basic_blocks().len() {
|
|
|
|
Some(location)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
2015-11-03 06:35:09 -05:00
|
|
|
}
|
2016-01-21 18:57:43 +02:00
|
|
|
|
2018-02-17 17:46:15 +02:00
|
|
|
fn not_ssa(&mut self, local: mir::Local) {
|
|
|
|
debug!("marking {:?} as non-SSA", local);
|
2018-07-22 19:23:39 +03:00
|
|
|
self.non_ssa_locals.insert(local);
|
2018-02-17 17:46:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2016-01-21 18:57:43 +02:00
|
|
|
}
|
|
|
|
}
|
2015-11-03 06:35:09 -05:00
|
|
|
}
|
|
|
|
|
2018-09-20 15:47:22 +02:00
|
|
|
impl<'mir, 'a: 'mir, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx>
|
|
|
|
for LocalAnalyzer<'mir, 'a, 'tcx, Bx> {
|
2015-11-03 06:35:09 -05:00
|
|
|
fn visit_assign(&mut self,
|
|
|
|
block: mir::BasicBlock,
|
2017-12-01 14:39:51 +02:00
|
|
|
place: &mir::Place<'tcx>,
|
2016-08-08 18:46:06 -07:00
|
|
|
rvalue: &mir::Rvalue<'tcx>,
|
|
|
|
location: Location) {
|
2017-12-01 14:39:51 +02:00
|
|
|
debug!("visit_assign(block={:?}, place={:?}, rvalue={:?})", block, place, rvalue);
|
2015-11-03 06:35:09 -05:00
|
|
|
|
2017-12-01 14:39:51 +02:00
|
|
|
if let mir::Place::Local(index) = *place {
|
2018-02-17 17:46:15 +02:00
|
|
|
self.assign(index, location);
|
2018-01-05 07:34:28 +02:00
|
|
|
if !self.fx.rvalue_creates_operand(rvalue) {
|
2018-02-17 17:46:15 +02:00
|
|
|
self.not_ssa(index);
|
2015-11-03 06:35:09 -05:00
|
|
|
}
|
2016-06-20 23:55:14 +03:00
|
|
|
} else {
|
2018-10-26 13:22:45 +02:00
|
|
|
self.visit_place(
|
|
|
|
place,
|
|
|
|
PlaceContext::MutatingUse(MutatingUseContext::Store),
|
|
|
|
location
|
|
|
|
);
|
2015-11-03 06:35:09 -05:00
|
|
|
}
|
|
|
|
|
2016-08-08 18:46:06 -07:00
|
|
|
self.visit_rvalue(rvalue, location);
|
2015-11-03 06:35:09 -05:00
|
|
|
}
|
|
|
|
|
2016-06-20 23:55:14 +03:00
|
|
|
fn visit_terminator_kind(&mut self,
|
|
|
|
block: mir::BasicBlock,
|
2016-08-08 18:46:06 -07:00
|
|
|
kind: &mir::TerminatorKind<'tcx>,
|
|
|
|
location: Location) {
|
2018-01-16 09:24:38 +01:00
|
|
|
let check = match *kind {
|
2016-06-20 23:55:14 +03:00
|
|
|
mir::TerminatorKind::Call {
|
2018-01-26 16:44:10 +01:00
|
|
|
func: mir::Operand::Constant(ref c),
|
2016-06-20 23:55:14 +03:00
|
|
|
ref args, ..
|
2018-01-26 16:44:10 +01:00
|
|
|
} => match c.ty.sty {
|
2018-08-22 01:35:02 +01:00
|
|
|
ty::FnDef(did, _) => Some((did, args)),
|
2018-01-16 09:24:38 +01:00
|
|
|
_ => None,
|
2018-01-26 16:44:10 +01:00
|
|
|
},
|
2018-01-16 09:24:38 +01:00
|
|
|
_ => None,
|
|
|
|
};
|
|
|
|
if let Some((def_id, args)) = check {
|
2018-09-20 15:47:22 +02:00
|
|
|
if Some(def_id) == self.fx.cx.tcx().lang_items().box_free_fn() {
|
2016-06-20 23:55:14 +03:00
|
|
|
// box_free(x) shares with `drop x` the property that it
|
|
|
|
// is not guaranteed to be statically dominated by the
|
|
|
|
// definition of x, so x must always be in an alloca.
|
2017-12-01 14:39:51 +02:00
|
|
|
if let mir::Operand::Move(ref place) = args[0] {
|
2018-10-26 13:22:45 +02:00
|
|
|
self.visit_place(
|
|
|
|
place,
|
|
|
|
PlaceContext::MutatingUse(MutatingUseContext::Drop),
|
|
|
|
location
|
|
|
|
);
|
2016-06-20 23:55:14 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-08 18:46:06 -07:00
|
|
|
self.super_terminator_kind(block, kind, location);
|
2016-06-20 23:55:14 +03:00
|
|
|
}
|
|
|
|
|
2017-12-01 14:39:51 +02:00
|
|
|
fn visit_place(&mut self,
|
2018-10-08 16:52:34 +02:00
|
|
|
place: &mir::Place<'tcx>,
|
|
|
|
context: PlaceContext<'tcx>,
|
|
|
|
location: Location) {
|
2017-12-01 14:39:51 +02:00
|
|
|
debug!("visit_place(place={:?}, context={:?})", place, context);
|
2018-01-05 07:34:28 +02:00
|
|
|
let cx = self.fx.cx;
|
2015-11-03 06:35:09 -05:00
|
|
|
|
2017-12-01 14:39:51 +02:00
|
|
|
if let mir::Place::Projection(ref proj) = *place {
|
2017-10-09 02:31:06 +03:00
|
|
|
// Allow uses of projections that are ZSTs or from scalar fields.
|
2017-11-17 17:19:57 +02:00
|
|
|
let is_consume = match context {
|
2018-10-26 13:22:45 +02:00
|
|
|
PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) |
|
|
|
|
PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) => true,
|
2017-11-17 17:19:57 +02:00
|
|
|
_ => false
|
|
|
|
};
|
|
|
|
if is_consume {
|
2018-09-20 15:47:22 +02:00
|
|
|
let base_ty = proj.base.ty(self.fx.mir, cx.tcx());
|
2018-01-05 07:34:28 +02:00
|
|
|
let base_ty = self.fx.monomorphize(&base_ty);
|
2017-10-09 00:38:10 +03:00
|
|
|
|
|
|
|
// ZSTs don't require any actual memory access.
|
2018-10-22 22:50:10 +02:00
|
|
|
let elem_ty = base_ty
|
2018-09-20 15:47:22 +02:00
|
|
|
.projection_ty(cx.tcx(), &proj.elem)
|
|
|
|
.to_ty(cx.tcx());
|
2018-01-05 07:34:28 +02:00
|
|
|
let elem_ty = self.fx.monomorphize(&elem_ty);
|
2018-01-05 07:04:08 +02:00
|
|
|
if cx.layout_of(elem_ty).is_zst() {
|
2017-10-09 00:38:10 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if let mir::ProjectionElem::Field(..) = proj.elem {
|
2018-09-20 15:47:22 +02:00
|
|
|
let layout = cx.layout_of(base_ty.to_ty(cx.tcx()));
|
2018-10-03 13:49:57 +02:00
|
|
|
if cx.is_backend_immediate(layout) || cx.is_backend_scalar_pair(layout) {
|
2017-11-17 17:19:57 +02:00
|
|
|
// Recurse with the same context, instead of `Projection`,
|
2017-10-09 00:38:10 +03:00
|
|
|
// potentially stopping at non-operand projections,
|
2018-02-17 17:46:15 +02:00
|
|
|
// which would trigger `not_ssa` on locals.
|
2017-12-01 14:39:51 +02:00
|
|
|
self.visit_place(&proj.base, context, location);
|
2017-10-09 00:38:10 +03:00
|
|
|
return;
|
2016-05-25 11:58:08 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-01 14:39:51 +02:00
|
|
|
// A deref projection only reads the pointer, never needs the place.
|
2017-09-03 19:14:31 +03:00
|
|
|
if let mir::ProjectionElem::Deref = proj.elem {
|
2018-10-26 13:22:45 +02:00
|
|
|
return self.visit_place(
|
|
|
|
&proj.base,
|
|
|
|
PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy),
|
|
|
|
location
|
|
|
|
);
|
2017-09-03 19:14:31 +03:00
|
|
|
}
|
|
|
|
}
|
2016-08-14 06:34:14 +03:00
|
|
|
|
2017-12-01 14:39:51 +02:00
|
|
|
self.super_place(place, context, location);
|
2017-09-03 19:14:31 +03:00
|
|
|
}
|
2016-08-14 06:34:14 +03:00
|
|
|
|
2017-09-03 19:14:31 +03:00
|
|
|
fn visit_local(&mut self,
|
2018-02-17 17:46:15 +02:00
|
|
|
&local: &mir::Local,
|
2017-12-01 14:31:47 +02:00
|
|
|
context: PlaceContext<'tcx>,
|
2018-02-17 17:46:15 +02:00
|
|
|
location: Location) {
|
2017-09-03 19:14:31 +03:00
|
|
|
match context {
|
2018-10-26 13:22:45 +02:00
|
|
|
PlaceContext::MutatingUse(MutatingUseContext::Call) => {
|
2018-02-17 17:46:15 +02:00
|
|
|
self.assign(local, location);
|
2017-09-03 19:14:31 +03:00
|
|
|
}
|
2016-08-14 06:34:14 +03:00
|
|
|
|
2018-10-24 11:47:17 +02:00
|
|
|
PlaceContext::NonUse(_) |
|
|
|
|
PlaceContext::MutatingUse(MutatingUseContext::Retag) => {}
|
2018-02-17 17:46:15 +02:00
|
|
|
|
2018-10-26 13:22:45 +02:00
|
|
|
PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) |
|
|
|
|
PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) => {
|
2018-11-27 02:59:49 +00:00
|
|
|
// Reads from uninitialized variables (e.g., in dead code, after
|
2018-02-17 17:46:15 +02:00
|
|
|
// optimizations) require locals to be in (uninitialized) memory.
|
2018-11-27 02:59:49 +00:00
|
|
|
// N.B., there can be uninitialized reads of a local visited after
|
2018-02-17 17:46:15 +02:00
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
}
|
2016-06-09 18:15:15 +03:00
|
|
|
|
2018-10-26 13:22:45 +02:00
|
|
|
PlaceContext::NonMutatingUse(NonMutatingUseContext::Inspect) |
|
|
|
|
PlaceContext::MutatingUse(MutatingUseContext::Store) |
|
|
|
|
PlaceContext::MutatingUse(MutatingUseContext::AsmOutput) |
|
|
|
|
PlaceContext::MutatingUse(MutatingUseContext::Borrow(..)) |
|
|
|
|
PlaceContext::MutatingUse(MutatingUseContext::Projection) |
|
|
|
|
PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow(..)) |
|
|
|
|
PlaceContext::NonMutatingUse(NonMutatingUseContext::UniqueBorrow(..)) |
|
|
|
|
PlaceContext::NonMutatingUse(NonMutatingUseContext::ShallowBorrow(..)) |
|
|
|
|
PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection) => {
|
2018-02-17 17:46:15 +02:00
|
|
|
self.not_ssa(local);
|
2015-11-03 06:35:09 -05:00
|
|
|
}
|
|
|
|
|
2018-10-26 13:22:45 +02:00
|
|
|
PlaceContext::MutatingUse(MutatingUseContext::Drop) => {
|
2018-09-20 15:47:22 +02:00
|
|
|
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()));
|
2017-09-03 19:14:31 +03:00
|
|
|
|
2017-12-01 14:39:51 +02:00
|
|
|
// Only need the place if we're actually dropping it.
|
2018-01-05 07:34:28 +02:00
|
|
|
if self.fx.cx.type_needs_drop(ty) {
|
2018-02-17 17:46:15 +02:00
|
|
|
self.not_ssa(local);
|
2017-09-03 19:14:31 +03:00
|
|
|
}
|
2016-06-09 18:14:47 +03:00
|
|
|
}
|
|
|
|
}
|
2015-11-03 06:35:09 -05:00
|
|
|
}
|
|
|
|
}
|
2016-05-29 22:01:06 +03:00
|
|
|
|
|
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
|
|
|
pub enum CleanupKind {
|
|
|
|
NotCleanup,
|
|
|
|
Funclet,
|
|
|
|
Internal { funclet: mir::BasicBlock }
|
|
|
|
}
|
|
|
|
|
2017-05-23 23:47:15 +03:00
|
|
|
impl CleanupKind {
|
|
|
|
pub fn funclet_bb(self, for_bb: mir::BasicBlock) -> Option<mir::BasicBlock> {
|
|
|
|
match self {
|
|
|
|
CleanupKind::NotCleanup => None,
|
|
|
|
CleanupKind::Funclet => Some(for_bb),
|
|
|
|
CleanupKind::Internal { funclet } => Some(funclet),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-17 19:54:32 -07:00
|
|
|
pub fn cleanup_kinds<'a, 'tcx>(mir: &mir::Mir<'tcx>) -> IndexVec<mir::BasicBlock, CleanupKind> {
|
2016-06-07 17:28:36 +03:00
|
|
|
fn discover_masters<'tcx>(result: &mut IndexVec<mir::BasicBlock, CleanupKind>,
|
|
|
|
mir: &mir::Mir<'tcx>) {
|
2016-06-07 21:20:50 +03:00
|
|
|
for (bb, data) in mir.basic_blocks().iter_enumerated() {
|
2016-05-29 22:01:06 +03:00
|
|
|
match data.terminator().kind {
|
|
|
|
TerminatorKind::Goto { .. } |
|
|
|
|
TerminatorKind::Resume |
|
2017-12-19 01:17:16 +01:00
|
|
|
TerminatorKind::Abort |
|
2016-05-29 22:01:06 +03:00
|
|
|
TerminatorKind::Return |
|
2016-12-26 14:34:03 +01:00
|
|
|
TerminatorKind::GeneratorDrop |
|
2016-06-08 19:26:19 +03:00
|
|
|
TerminatorKind::Unreachable |
|
2016-12-26 14:34:03 +01:00
|
|
|
TerminatorKind::SwitchInt { .. } |
|
2017-10-13 16:36:15 +03:00
|
|
|
TerminatorKind::Yield { .. } |
|
2018-01-25 01:45:45 -05:00
|
|
|
TerminatorKind::FalseEdges { .. } |
|
|
|
|
TerminatorKind::FalseUnwind { .. } => {
|
2016-05-29 22:01:06 +03:00
|
|
|
/* nothing to do */
|
|
|
|
}
|
|
|
|
TerminatorKind::Call { cleanup: unwind, .. } |
|
2016-05-25 08:39:32 +03:00
|
|
|
TerminatorKind::Assert { cleanup: unwind, .. } |
|
2016-05-29 22:01:06 +03:00
|
|
|
TerminatorKind::DropAndReplace { unwind, .. } |
|
|
|
|
TerminatorKind::Drop { unwind, .. } => {
|
|
|
|
if let Some(unwind) = unwind {
|
|
|
|
debug!("cleanup_kinds: {:?}/{:?} registering {:?} as funclet",
|
|
|
|
bb, data, unwind);
|
2016-06-07 17:28:36 +03:00
|
|
|
result[unwind] = CleanupKind::Funclet;
|
2016-05-29 22:01:06 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-07 17:28:36 +03:00
|
|
|
fn propagate<'tcx>(result: &mut IndexVec<mir::BasicBlock, CleanupKind>,
|
|
|
|
mir: &mir::Mir<'tcx>) {
|
2016-06-07 21:20:50 +03:00
|
|
|
let mut funclet_succs = IndexVec::from_elem(None, mir.basic_blocks());
|
2016-05-29 22:01:06 +03:00
|
|
|
|
|
|
|
let mut set_successor = |funclet: mir::BasicBlock, succ| {
|
2016-06-07 17:28:36 +03:00
|
|
|
match funclet_succs[funclet] {
|
2016-05-29 22:01:06 +03:00
|
|
|
ref mut s @ None => {
|
|
|
|
debug!("set_successor: updating successor of {:?} to {:?}",
|
|
|
|
funclet, succ);
|
|
|
|
*s = Some(succ);
|
|
|
|
},
|
|
|
|
Some(s) => if s != succ {
|
|
|
|
span_bug!(mir.span, "funclet {:?} has 2 parents - {:?} and {:?}",
|
|
|
|
funclet, s, succ);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
for (bb, data) in traversal::reverse_postorder(mir) {
|
2016-06-07 17:28:36 +03:00
|
|
|
let funclet = match result[bb] {
|
2016-05-29 22:01:06 +03:00
|
|
|
CleanupKind::NotCleanup => continue,
|
|
|
|
CleanupKind::Funclet => bb,
|
|
|
|
CleanupKind::Internal { funclet } => funclet,
|
|
|
|
};
|
|
|
|
|
|
|
|
debug!("cleanup_kinds: {:?}/{:?}/{:?} propagating funclet {:?}",
|
2016-06-07 17:28:36 +03:00
|
|
|
bb, data, result[bb], funclet);
|
2016-05-29 22:01:06 +03:00
|
|
|
|
2018-04-27 14:02:09 +03:00
|
|
|
for &succ in data.terminator().successors() {
|
2016-06-07 17:28:36 +03:00
|
|
|
let kind = result[succ];
|
2016-05-29 22:01:06 +03:00
|
|
|
debug!("cleanup_kinds: propagating {:?} to {:?}/{:?}",
|
|
|
|
funclet, succ, kind);
|
|
|
|
match kind {
|
|
|
|
CleanupKind::NotCleanup => {
|
2018-11-06 15:05:44 -05:00
|
|
|
result[succ] = CleanupKind::Internal { funclet };
|
2016-05-29 22:01:06 +03:00
|
|
|
}
|
|
|
|
CleanupKind::Funclet => {
|
2017-05-18 23:58:39 +03:00
|
|
|
if funclet != succ {
|
|
|
|
set_successor(funclet, succ);
|
|
|
|
}
|
2016-05-29 22:01:06 +03:00
|
|
|
}
|
|
|
|
CleanupKind::Internal { funclet: succ_funclet } => {
|
|
|
|
if funclet != succ_funclet {
|
|
|
|
// `succ` has 2 different funclet going into it, so it must
|
|
|
|
// be a funclet by itself.
|
|
|
|
|
|
|
|
debug!("promoting {:?} to a funclet and updating {:?}", succ,
|
|
|
|
succ_funclet);
|
2016-06-07 17:28:36 +03:00
|
|
|
result[succ] = CleanupKind::Funclet;
|
2016-05-29 22:01:06 +03:00
|
|
|
set_successor(succ_funclet, succ);
|
|
|
|
set_successor(funclet, succ);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-07 21:20:50 +03:00
|
|
|
let mut result = IndexVec::from_elem(CleanupKind::NotCleanup, mir.basic_blocks());
|
2016-05-29 22:01:06 +03:00
|
|
|
|
|
|
|
discover_masters(&mut result, mir);
|
|
|
|
propagate(&mut result, mir);
|
|
|
|
debug!("cleanup_kinds: result={:?}", result);
|
|
|
|
result
|
|
|
|
}
|