]> git.lizzy.rs Git - rust.git/blob - src/librustc_codegen_llvm/mir/analyze.rs
14f48a4ab63dcd31c23dbe493371e1888c4ef5cb
[rust.git] / src / librustc_codegen_llvm / mir / analyze.rs
1 // Copyright 2012-2014 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.
4 //
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.
10
11 //! An analysis to determine which locals require allocas and
12 //! which do not.
13
14 use rustc_data_structures::bit_set::BitSet;
15 use rustc_data_structures::graph::dominators::Dominators;
16 use rustc_data_structures::indexed_vec::{Idx, IndexVec};
17 use rustc::mir::{self, Location, TerminatorKind};
18 use rustc::mir::visit::{Visitor, PlaceContext, MutatingUseContext, NonMutatingUseContext};
19 use rustc::mir::traversal;
20 use rustc::ty;
21 use rustc::ty::layout::LayoutOf;
22 use type_of::LayoutLlvmExt;
23 use super::FunctionCx;
24 use value::Value;
25
26 pub fn non_ssa_locals(fx: &FunctionCx<'a, 'll, 'tcx, &'ll Value>) -> BitSet<mir::Local> {
27     let mir = fx.mir;
28     let mut analyzer = LocalAnalyzer::new(fx);
29
30     analyzer.visit_mir(mir);
31
32     for (index, ty) in mir.local_decls.iter().map(|l| l.ty).enumerate() {
33         let ty = fx.monomorphize(&ty);
34         debug!("local {} has type {:?}", index, ty);
35         let layout = fx.cx.layout_of(ty);
36         if layout.is_llvm_immediate() {
37             // These sorts of types are immediates that we can store
38             // in an Value without an alloca.
39         } else if layout.is_llvm_scalar_pair() {
40             // We allow pairs and uses of any of their 2 fields.
41         } else {
42             // These sorts of types require an alloca. Note that
43             // is_llvm_immediate() may *still* be true, particularly
44             // for newtypes, but we currently force some types
45             // (e.g. structs) into an alloca unconditionally, just so
46             // that we don't have to deal with having two pathways
47             // (gep vs extractvalue etc).
48             analyzer.not_ssa(mir::Local::new(index));
49         }
50     }
51
52     analyzer.non_ssa_locals
53 }
54
55 struct LocalAnalyzer<'mir, 'a: 'mir, 'll: 'a, 'tcx: 'll, V: 'll> {
56     fx: &'mir FunctionCx<'a, 'll, 'tcx, V>,
57     dominators: Dominators<mir::BasicBlock>,
58     non_ssa_locals: BitSet<mir::Local>,
59     // The location of the first visited direct assignment to each
60     // local, or an invalid location (out of bounds `block` index).
61     first_assignment: IndexVec<mir::Local, Location>
62 }
63
64 impl LocalAnalyzer<'mir, 'a, 'll, 'tcx, &'ll Value> {
65     fn new(fx: &'mir FunctionCx<'a, 'll, 'tcx, &'ll Value>) -> Self {
66         let invalid_location =
67             mir::BasicBlock::new(fx.mir.basic_blocks().len()).start_location();
68         let mut analyzer = LocalAnalyzer {
69             fx,
70             dominators: fx.mir.dominators(),
71             non_ssa_locals: BitSet::new_empty(fx.mir.local_decls.len()),
72             first_assignment: IndexVec::from_elem(invalid_location, &fx.mir.local_decls)
73         };
74
75         // Arguments get assigned to by means of the function being called
76         for arg in fx.mir.args_iter() {
77             analyzer.first_assignment[arg] = mir::START_BLOCK.start_location();
78         }
79
80         analyzer
81     }
82
83     fn first_assignment(&self, local: mir::Local) -> Option<Location> {
84         let location = self.first_assignment[local];
85         if location.block.index() < self.fx.mir.basic_blocks().len() {
86             Some(location)
87         } else {
88             None
89         }
90     }
91
92     fn not_ssa(&mut self, local: mir::Local) {
93         debug!("marking {:?} as non-SSA", local);
94         self.non_ssa_locals.insert(local);
95     }
96
97     fn assign(&mut self, local: mir::Local, location: Location) {
98         if self.first_assignment(local).is_some() {
99             self.not_ssa(local);
100         } else {
101             self.first_assignment[local] = location;
102         }
103     }
104 }
105
106 impl Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'll, 'tcx, &'ll Value> {
107     fn visit_assign(&mut self,
108                     block: mir::BasicBlock,
109                     place: &mir::Place<'tcx>,
110                     rvalue: &mir::Rvalue<'tcx>,
111                     location: Location) {
112         debug!("visit_assign(block={:?}, place={:?}, rvalue={:?})", block, place, rvalue);
113
114         if let mir::Place::Local(index) = *place {
115             self.assign(index, location);
116             if !self.fx.rvalue_creates_operand(rvalue) {
117                 self.not_ssa(index);
118             }
119         } else {
120             self.visit_place(
121                 place,
122                 PlaceContext::MutatingUse(MutatingUseContext::Store),
123                 location
124             );
125         }
126
127         self.visit_rvalue(rvalue, location);
128     }
129
130     fn visit_terminator_kind(&mut self,
131                              block: mir::BasicBlock,
132                              kind: &mir::TerminatorKind<'tcx>,
133                              location: Location) {
134         let check = match *kind {
135             mir::TerminatorKind::Call {
136                 func: mir::Operand::Constant(ref c),
137                 ref args, ..
138             } => match c.ty.sty {
139                 ty::FnDef(did, _) => Some((did, args)),
140                 _ => None,
141             },
142             _ => None,
143         };
144         if let Some((def_id, args)) = check {
145             if Some(def_id) == self.fx.cx.tcx.lang_items().box_free_fn() {
146                 // box_free(x) shares with `drop x` the property that it
147                 // is not guaranteed to be statically dominated by the
148                 // definition of x, so x must always be in an alloca.
149                 if let mir::Operand::Move(ref place) = args[0] {
150                     self.visit_place(
151                         place,
152                         PlaceContext::MutatingUse(MutatingUseContext::Drop),
153                         location
154                     );
155                 }
156             }
157         }
158
159         self.super_terminator_kind(block, kind, location);
160     }
161
162     fn visit_place(&mut self,
163                    place: &mir::Place<'tcx>,
164                    context: PlaceContext<'tcx>,
165                    location: Location) {
166         debug!("visit_place(place={:?}, context={:?})", place, context);
167         let cx = self.fx.cx;
168
169         if let mir::Place::Projection(ref proj) = *place {
170             // Allow uses of projections that are ZSTs or from scalar fields.
171             let is_consume = match context {
172                 PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) |
173                 PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) => true,
174                 _ => false
175             };
176             if is_consume {
177                 let base_ty = proj.base.ty(self.fx.mir, cx.tcx);
178                 let base_ty = self.fx.monomorphize(&base_ty);
179
180                 // ZSTs don't require any actual memory access.
181                 let elem_ty = base_ty
182                     .projection_ty(cx.tcx, &proj.elem)
183                     .to_ty(cx.tcx);
184                 let elem_ty = self.fx.monomorphize(&elem_ty);
185                 if cx.layout_of(elem_ty).is_zst() {
186                     return;
187                 }
188
189                 if let mir::ProjectionElem::Field(..) = proj.elem {
190                     let layout = cx.layout_of(base_ty.to_ty(cx.tcx));
191                     if layout.is_llvm_immediate() || layout.is_llvm_scalar_pair() {
192                         // Recurse with the same context, instead of `Projection`,
193                         // potentially stopping at non-operand projections,
194                         // which would trigger `not_ssa` on locals.
195                         self.visit_place(&proj.base, context, location);
196                         return;
197                     }
198                 }
199             }
200
201             // A deref projection only reads the pointer, never needs the place.
202             if let mir::ProjectionElem::Deref = proj.elem {
203                 return self.visit_place(
204                     &proj.base,
205                     PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy),
206                     location
207                 );
208             }
209         }
210
211         self.super_place(place, context, location);
212     }
213
214     fn visit_local(&mut self,
215                    &local: &mir::Local,
216                    context: PlaceContext<'tcx>,
217                    location: Location) {
218         match context {
219             PlaceContext::MutatingUse(MutatingUseContext::Call) => {
220                 self.assign(local, location);
221             }
222
223             PlaceContext::NonUse(_) |
224             PlaceContext::MutatingUse(MutatingUseContext::Retag) => {}
225
226             PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) |
227             PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) => {
228                 // Reads from uninitialized variables (e.g. in dead code, after
229                 // optimizations) require locals to be in (uninitialized) memory.
230                 // NB: there can be uninitialized reads of a local visited after
231                 // an assignment to that local, if they happen on disjoint paths.
232                 let ssa_read = match self.first_assignment(local) {
233                     Some(assignment_location) => {
234                         assignment_location.dominates(location, &self.dominators)
235                     }
236                     None => false
237                 };
238                 if !ssa_read {
239                     self.not_ssa(local);
240                 }
241             }
242
243             PlaceContext::NonMutatingUse(NonMutatingUseContext::Inspect) |
244             PlaceContext::MutatingUse(MutatingUseContext::Store) |
245             PlaceContext::MutatingUse(MutatingUseContext::AsmOutput) |
246             PlaceContext::MutatingUse(MutatingUseContext::Borrow(..)) |
247             PlaceContext::MutatingUse(MutatingUseContext::Projection) |
248             PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow(..)) |
249             PlaceContext::NonMutatingUse(NonMutatingUseContext::UniqueBorrow(..)) |
250             PlaceContext::NonMutatingUse(NonMutatingUseContext::ShallowBorrow(..)) |
251             PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection) => {
252                 self.not_ssa(local);
253             }
254
255             PlaceContext::MutatingUse(MutatingUseContext::Drop) => {
256                 let ty = mir::Place::Local(local).ty(self.fx.mir, self.fx.cx.tcx);
257                 let ty = self.fx.monomorphize(&ty.to_ty(self.fx.cx.tcx));
258
259                 // Only need the place if we're actually dropping it.
260                 if self.fx.cx.type_needs_drop(ty) {
261                     self.not_ssa(local);
262                 }
263             }
264         }
265     }
266 }
267
268 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
269 pub enum CleanupKind {
270     NotCleanup,
271     Funclet,
272     Internal { funclet: mir::BasicBlock }
273 }
274
275 impl CleanupKind {
276     pub fn funclet_bb(self, for_bb: mir::BasicBlock) -> Option<mir::BasicBlock> {
277         match self {
278             CleanupKind::NotCleanup => None,
279             CleanupKind::Funclet => Some(for_bb),
280             CleanupKind::Internal { funclet } => Some(funclet),
281         }
282     }
283 }
284
285 pub fn cleanup_kinds<'a, 'tcx>(mir: &mir::Mir<'tcx>) -> IndexVec<mir::BasicBlock, CleanupKind> {
286     fn discover_masters<'tcx>(result: &mut IndexVec<mir::BasicBlock, CleanupKind>,
287                               mir: &mir::Mir<'tcx>) {
288         for (bb, data) in mir.basic_blocks().iter_enumerated() {
289             match data.terminator().kind {
290                 TerminatorKind::Goto { .. } |
291                 TerminatorKind::Resume |
292                 TerminatorKind::Abort |
293                 TerminatorKind::Return |
294                 TerminatorKind::GeneratorDrop |
295                 TerminatorKind::Unreachable |
296                 TerminatorKind::SwitchInt { .. } |
297                 TerminatorKind::Yield { .. } |
298                 TerminatorKind::FalseEdges { .. } |
299                 TerminatorKind::FalseUnwind { .. } => {
300                     /* nothing to do */
301                 }
302                 TerminatorKind::Call { cleanup: unwind, .. } |
303                 TerminatorKind::Assert { cleanup: unwind, .. } |
304                 TerminatorKind::DropAndReplace { unwind, .. } |
305                 TerminatorKind::Drop { unwind, .. } => {
306                     if let Some(unwind) = unwind {
307                         debug!("cleanup_kinds: {:?}/{:?} registering {:?} as funclet",
308                                bb, data, unwind);
309                         result[unwind] = CleanupKind::Funclet;
310                     }
311                 }
312             }
313         }
314     }
315
316     fn propagate<'tcx>(result: &mut IndexVec<mir::BasicBlock, CleanupKind>,
317                        mir: &mir::Mir<'tcx>) {
318         let mut funclet_succs = IndexVec::from_elem(None, mir.basic_blocks());
319
320         let mut set_successor = |funclet: mir::BasicBlock, succ| {
321             match funclet_succs[funclet] {
322                 ref mut s @ None => {
323                     debug!("set_successor: updating successor of {:?} to {:?}",
324                            funclet, succ);
325                     *s = Some(succ);
326                 },
327                 Some(s) => if s != succ {
328                     span_bug!(mir.span, "funclet {:?} has 2 parents - {:?} and {:?}",
329                               funclet, s, succ);
330                 }
331             }
332         };
333
334         for (bb, data) in traversal::reverse_postorder(mir) {
335             let funclet = match result[bb] {
336                 CleanupKind::NotCleanup => continue,
337                 CleanupKind::Funclet => bb,
338                 CleanupKind::Internal { funclet } => funclet,
339             };
340
341             debug!("cleanup_kinds: {:?}/{:?}/{:?} propagating funclet {:?}",
342                    bb, data, result[bb], funclet);
343
344             for &succ in data.terminator().successors() {
345                 let kind = result[succ];
346                 debug!("cleanup_kinds: propagating {:?} to {:?}/{:?}",
347                        funclet, succ, kind);
348                 match kind {
349                     CleanupKind::NotCleanup => {
350                         result[succ] = CleanupKind::Internal { funclet };
351                     }
352                     CleanupKind::Funclet => {
353                         if funclet != succ {
354                             set_successor(funclet, succ);
355                         }
356                     }
357                     CleanupKind::Internal { funclet: succ_funclet } => {
358                         if funclet != succ_funclet {
359                             // `succ` has 2 different funclet going into it, so it must
360                             // be a funclet by itself.
361
362                             debug!("promoting {:?} to a funclet and updating {:?}", succ,
363                                    succ_funclet);
364                             result[succ] = CleanupKind::Funclet;
365                             set_successor(succ_funclet, succ);
366                             set_successor(funclet, succ);
367                         }
368                     }
369                 }
370             }
371         }
372     }
373
374     let mut result = IndexVec::from_elem(CleanupKind::NotCleanup, mir.basic_blocks());
375
376     discover_masters(&mut result, mir);
377     propagate(&mut result, mir);
378     debug!("cleanup_kinds: result={:?}", result);
379     result
380 }