1 // Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 //! Identify those variables whose entire value will eventually be
12 //! returned from the fn via the RETURN_PLACE. As an optimization, we
13 //! can skip computing liveness results for those variables. The idea
14 //! is that the return type of the fn only ever contains free
15 //! regions. Therefore, the types of those variables are going to
16 //! ultimately be contrained to outlive those free regions -- since
17 //! free regions are always live for the entire body, this implies
18 //! that the liveness results are not important for those regions.
19 //! This is most important in the "fns" that we create to represent static
20 //! values, since those are often really quite large, and all regions in them
21 //! will ultimately be constrained to be `'static`. Two examples:
24 //! fn foo() -> &'static [u32] { &[] }
25 //! static FOO: &[u32] = &[];
28 //! In both these cases, the return value will only have static lifetime.
30 //! NB: The simple logic here relies on the fact that outlives
31 //! relations in our analysis don't have locations. Otherwise, we
32 //! would have to restrict ourselves to values that are
33 //! *unconditionally* returned (which would still cover the "big
34 //! static value" case).
36 //! The way that this code works is to use union-find -- we iterate
37 //! over the MIR and union together two variables X and Y if all
38 //! regions in the value of Y are going to be stored into X -- that
39 //! is, if `typeof(X): 'a` requires that `typeof(Y): 'a`. This means
40 //! that e.g. we can union together `x` and `y` if we have something
41 //! like `x = (y, 22)`, but not something like `x = y.f` (since there
42 //! may be regions in the type of `y` that do not appear in the field
45 use rustc::mir::visit::Visitor;
48 use rustc_data_structures::indexed_vec::Idx;
49 use rustc_data_structures::unify as ut;
51 crate struct EscapingLocals {
52 unification_table: ut::UnificationTable<ut::InPlace<AssignedLocal>>,
56 crate fn compute(mir: &Mir<'tcx>) -> Self {
57 let mut visitor = GatherAssignedLocalsVisitor::new();
58 visitor.visit_mir(mir);
61 unification_table: visitor.unification_table,
65 /// True if `local` is known to escape into static
67 crate fn escapes_into_return(&mut self, local: Local) -> bool {
68 let return_place = AssignedLocal::from(RETURN_PLACE);
69 let other_place = AssignedLocal::from(local);
70 self.unification_table.unioned(return_place, other_place)
74 /// The MIR visitor gathering the union-find of the locals used in
76 struct GatherAssignedLocalsVisitor {
77 unification_table: ut::UnificationTable<ut::InPlace<AssignedLocal>>,
80 #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
81 struct AssignedLocal(u32);
83 impl ut::UnifyKey for AssignedLocal {
86 fn index(&self) -> u32 {
90 fn from_index(i: u32) -> AssignedLocal {
94 fn tag() -> &'static str {
99 impl From<Local> for AssignedLocal {
100 fn from(item: Local) -> Self {
101 // newtype_indexes use usize but are u32s.
102 assert!(item.index() < ::std::u32::MAX as usize);
103 AssignedLocal(item.index() as u32)
107 impl GatherAssignedLocalsVisitor {
110 unification_table: ut::UnificationTable::new(),
114 fn union_locals_if_needed(&mut self, lvalue: Option<Local>, rvalue: Option<Local>) {
115 if let Some(lvalue) = lvalue {
116 if let Some(rvalue) = rvalue {
117 if lvalue != rvalue {
118 debug!("EscapingLocals: union {:?} and {:?}", lvalue, rvalue);
119 self.unification_table
120 .union(AssignedLocal::from(lvalue), AssignedLocal::from(rvalue));
127 // Returns the potential `Local` associated to this `Place` or `PlaceProjection`
128 fn find_local_in_place(place: &Place) -> Option<Local> {
130 Place::Local(local) => Some(*local),
132 // If you do e.g. `x = a.f` then only *part* of the type of
133 // `a` escapes into `x` (the part contained in `f`); if `a`'s
134 // type has regions that don't appear in `f`, those might not
136 Place::Projection(..) => None,
138 Place::Static { .. } | Place::Promoted { .. } => None,
142 // Returns the potential `Local` in this `Operand`.
143 fn find_local_in_operand(op: &Operand) -> Option<Local> {
144 // Conservatively check a subset of `Operand`s we know our
145 // benchmarks track, for example `html5ever`.
147 Operand::Copy(place) | Operand::Move(place) => find_local_in_place(place),
148 Operand::Constant(_) => None,
152 impl Visitor<'tcx> for GatherAssignedLocalsVisitor {
153 fn visit_mir(&mut self, mir: &Mir<'tcx>) {
154 // We need as many union-find keys as there are locals
155 for _ in 0..mir.local_decls.len() {
156 self.unification_table.new_key(());
166 rvalue: &Rvalue<'tcx>,
169 let local = find_local_in_place(place);
171 // Find those cases where there is a `Place` consumed by
172 // `rvalue` and we know that all regions in its type will be
173 // incorporated into `place`, the `Place` we are assigning to.
175 // `x = y` is the simplest possible case.
176 Rvalue::Use(op) => self.union_locals_if_needed(local, find_local_in_operand(op)),
178 // `X = &'r P` -- the type of `X` will be `&'r T_P`, where
179 // `T_P` is the type of `P`.
180 Rvalue::Ref(_, _, place) => {
181 // Special case: if you have `X = &*Y` (or `X = &**Y`
182 // etc), then the outlives relationships will ensure
183 // that all regions in `Y` are constrained by regions
184 // in `X` -- this is because the lifetimes of the
185 // references we deref through are required to outlive
186 // the borrow lifetime `'r` (which appears in `X`).
188 // (We don't actually need to check the type of `Y`:
189 // since `ProjectionElem::Deref` represents a built-in
190 // deref and not an overloaded deref, if the thing we
191 // deref through is not a reference, then it must be a
192 // `Box` or `*const`, in which case it contains no
194 let mut place_ref = place;
195 while let Place::Projection(proj) = place_ref {
196 if let ProjectionElem::Deref = proj.elem {
197 place_ref = &proj.base;
203 self.union_locals_if_needed(local, find_local_in_place(place_ref))
206 Rvalue::Cast(kind, op, _) => match kind {
207 CastKind::Unsize => {
208 // Casting a `&[T; N]` to `&[T]` or `&Foo` to `&Trait` --
209 // in both cases, no regions are "lost".
210 self.union_locals_if_needed(local, find_local_in_operand(op))
215 // Constructing an aggregate like `(x,)` or `Foo { x }`
216 // includes the full type of `x`.
217 Rvalue::Aggregate(_, ops) => {
218 for rvalue in ops.iter().map(find_local_in_operand) {
219 self.union_locals_if_needed(local, rvalue);
223 // For other things, be conservative and do not union.
227 self.super_assign(block, place, rvalue, location);