]> git.lizzy.rs Git - rust.git/blob - src/librustc_trans/mir/analyze.rs
Mir: Add Terminatorkind::Abort
[rust.git] / src / librustc_trans / 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::bitvec::BitVector;
15 use rustc_data_structures::indexed_vec::{Idx, IndexVec};
16 use rustc::middle::const_val::ConstVal;
17 use rustc::mir::{self, Location, TerminatorKind, Literal};
18 use rustc::mir::visit::{Visitor, PlaceContext};
19 use rustc::mir::traversal;
20 use rustc::ty;
21 use rustc::ty::layout::LayoutOf;
22 use type_of::LayoutLlvmExt;
23 use super::MirContext;
24
25 pub fn memory_locals<'a, 'tcx>(mircx: &MirContext<'a, 'tcx>) -> BitVector {
26     let mir = mircx.mir;
27     let mut analyzer = LocalAnalyzer::new(mircx);
28
29     analyzer.visit_mir(mir);
30
31     for (index, ty) in mir.local_decls.iter().map(|l| l.ty).enumerate() {
32         let ty = mircx.monomorphize(&ty);
33         debug!("local {} has type {:?}", index, ty);
34         let layout = mircx.ccx.layout_of(ty);
35         if layout.is_llvm_immediate() {
36             // These sorts of types are immediates that we can store
37             // in an ValueRef without an alloca.
38         } else if layout.is_llvm_scalar_pair() {
39             // We allow pairs and uses of any of their 2 fields.
40         } else {
41             // These sorts of types require an alloca. Note that
42             // is_llvm_immediate() may *still* be true, particularly
43             // for newtypes, but we currently force some types
44             // (e.g. structs) into an alloca unconditionally, just so
45             // that we don't have to deal with having two pathways
46             // (gep vs extractvalue etc).
47             analyzer.mark_as_memory(mir::Local::new(index));
48         }
49     }
50
51     analyzer.memory_locals
52 }
53
54 struct LocalAnalyzer<'mir, 'a: 'mir, 'tcx: 'a> {
55     cx: &'mir MirContext<'a, 'tcx>,
56     memory_locals: BitVector,
57     seen_assigned: BitVector
58 }
59
60 impl<'mir, 'a, 'tcx> LocalAnalyzer<'mir, 'a, 'tcx> {
61     fn new(mircx: &'mir MirContext<'a, 'tcx>) -> LocalAnalyzer<'mir, 'a, 'tcx> {
62         let mut analyzer = LocalAnalyzer {
63             cx: mircx,
64             memory_locals: BitVector::new(mircx.mir.local_decls.len()),
65             seen_assigned: BitVector::new(mircx.mir.local_decls.len())
66         };
67
68         // Arguments get assigned to by means of the function being called
69         for idx in 0..mircx.mir.arg_count {
70             analyzer.seen_assigned.insert(idx + 1);
71         }
72
73         analyzer
74     }
75
76     fn mark_as_memory(&mut self, local: mir::Local) {
77         debug!("marking {:?} as memory", local);
78         self.memory_locals.insert(local.index());
79     }
80
81     fn mark_assigned(&mut self, local: mir::Local) {
82         if !self.seen_assigned.insert(local.index()) {
83             self.mark_as_memory(local);
84         }
85     }
86 }
87
88 impl<'mir, 'a, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'tcx> {
89     fn visit_assign(&mut self,
90                     block: mir::BasicBlock,
91                     place: &mir::Place<'tcx>,
92                     rvalue: &mir::Rvalue<'tcx>,
93                     location: Location) {
94         debug!("visit_assign(block={:?}, place={:?}, rvalue={:?})", block, place, rvalue);
95
96         if let mir::Place::Local(index) = *place {
97             self.mark_assigned(index);
98             if !self.cx.rvalue_creates_operand(rvalue) {
99                 self.mark_as_memory(index);
100             }
101         } else {
102             self.visit_place(place, PlaceContext::Store, location);
103         }
104
105         self.visit_rvalue(rvalue, location);
106     }
107
108     fn visit_terminator_kind(&mut self,
109                              block: mir::BasicBlock,
110                              kind: &mir::TerminatorKind<'tcx>,
111                              location: Location) {
112         match *kind {
113             mir::TerminatorKind::Call {
114                 func: mir::Operand::Constant(box mir::Constant {
115                     literal: Literal::Value {
116                         value: &ty::Const { val: ConstVal::Function(def_id, _), .. }, ..
117                     }, ..
118                 }),
119                 ref args, ..
120             } if Some(def_id) == self.cx.ccx.tcx().lang_items().box_free_fn() => {
121                 // box_free(x) shares with `drop x` the property that it
122                 // is not guaranteed to be statically dominated by the
123                 // definition of x, so x must always be in an alloca.
124                 if let mir::Operand::Move(ref place) = args[0] {
125                     self.visit_place(place, PlaceContext::Drop, location);
126                 }
127             }
128             _ => {}
129         }
130
131         self.super_terminator_kind(block, kind, location);
132     }
133
134     fn visit_place(&mut self,
135                     place: &mir::Place<'tcx>,
136                     context: PlaceContext<'tcx>,
137                     location: Location) {
138         debug!("visit_place(place={:?}, context={:?})", place, context);
139         let ccx = self.cx.ccx;
140
141         if let mir::Place::Projection(ref proj) = *place {
142             // Allow uses of projections that are ZSTs or from scalar fields.
143             let is_consume = match context {
144                 PlaceContext::Copy | PlaceContext::Move => true,
145                 _ => false
146             };
147             if is_consume {
148                 let base_ty = proj.base.ty(self.cx.mir, ccx.tcx());
149                 let base_ty = self.cx.monomorphize(&base_ty);
150
151                 // ZSTs don't require any actual memory access.
152                 let elem_ty = base_ty.projection_ty(ccx.tcx(), &proj.elem).to_ty(ccx.tcx());
153                 let elem_ty = self.cx.monomorphize(&elem_ty);
154                 if ccx.layout_of(elem_ty).is_zst() {
155                     return;
156                 }
157
158                 if let mir::ProjectionElem::Field(..) = proj.elem {
159                     let layout = ccx.layout_of(base_ty.to_ty(ccx.tcx()));
160                     if layout.is_llvm_immediate() || layout.is_llvm_scalar_pair() {
161                         // Recurse with the same context, instead of `Projection`,
162                         // potentially stopping at non-operand projections,
163                         // which would trigger `mark_as_memory` on locals.
164                         self.visit_place(&proj.base, context, location);
165                         return;
166                     }
167                 }
168             }
169
170             // A deref projection only reads the pointer, never needs the place.
171             if let mir::ProjectionElem::Deref = proj.elem {
172                 return self.visit_place(&proj.base, PlaceContext::Copy, location);
173             }
174         }
175
176         self.super_place(place, context, location);
177     }
178
179     fn visit_local(&mut self,
180                    &index: &mir::Local,
181                    context: PlaceContext<'tcx>,
182                    _: Location) {
183         match context {
184             PlaceContext::Call => {
185                 self.mark_assigned(index);
186             }
187
188             PlaceContext::StorageLive |
189             PlaceContext::StorageDead |
190             PlaceContext::Validate |
191             PlaceContext::Copy |
192             PlaceContext::Move => {}
193
194             PlaceContext::Inspect |
195             PlaceContext::Store |
196             PlaceContext::AsmOutput |
197             PlaceContext::Borrow { .. } |
198             PlaceContext::Projection(..) => {
199                 self.mark_as_memory(index);
200             }
201
202             PlaceContext::Drop => {
203                 let ty = mir::Place::Local(index).ty(self.cx.mir, self.cx.ccx.tcx());
204                 let ty = self.cx.monomorphize(&ty.to_ty(self.cx.ccx.tcx()));
205
206                 // Only need the place if we're actually dropping it.
207                 if self.cx.ccx.shared().type_needs_drop(ty) {
208                     self.mark_as_memory(index);
209                 }
210             }
211         }
212     }
213 }
214
215 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
216 pub enum CleanupKind {
217     NotCleanup,
218     Funclet,
219     Internal { funclet: mir::BasicBlock }
220 }
221
222 impl CleanupKind {
223     pub fn funclet_bb(self, for_bb: mir::BasicBlock) -> Option<mir::BasicBlock> {
224         match self {
225             CleanupKind::NotCleanup => None,
226             CleanupKind::Funclet => Some(for_bb),
227             CleanupKind::Internal { funclet } => Some(funclet),
228         }
229     }
230 }
231
232 pub fn cleanup_kinds<'a, 'tcx>(mir: &mir::Mir<'tcx>) -> IndexVec<mir::BasicBlock, CleanupKind> {
233     fn discover_masters<'tcx>(result: &mut IndexVec<mir::BasicBlock, CleanupKind>,
234                               mir: &mir::Mir<'tcx>) {
235         for (bb, data) in mir.basic_blocks().iter_enumerated() {
236             match data.terminator().kind {
237                 TerminatorKind::Goto { .. } |
238                 TerminatorKind::Resume |
239                 TerminatorKind::Abort |
240                 TerminatorKind::Return |
241                 TerminatorKind::GeneratorDrop |
242                 TerminatorKind::Unreachable |
243                 TerminatorKind::SwitchInt { .. } |
244                 TerminatorKind::Yield { .. } |
245                 TerminatorKind::FalseEdges { .. } => {
246                     /* nothing to do */
247                 }
248                 TerminatorKind::Call { cleanup: unwind, .. } |
249                 TerminatorKind::Assert { cleanup: unwind, .. } |
250                 TerminatorKind::DropAndReplace { unwind, .. } |
251                 TerminatorKind::Drop { unwind, .. } => {
252                     if let Some(unwind) = unwind {
253                         debug!("cleanup_kinds: {:?}/{:?} registering {:?} as funclet",
254                                bb, data, unwind);
255                         result[unwind] = CleanupKind::Funclet;
256                     }
257                 }
258             }
259         }
260     }
261
262     fn propagate<'tcx>(result: &mut IndexVec<mir::BasicBlock, CleanupKind>,
263                        mir: &mir::Mir<'tcx>) {
264         let mut funclet_succs = IndexVec::from_elem(None, mir.basic_blocks());
265
266         let mut set_successor = |funclet: mir::BasicBlock, succ| {
267             match funclet_succs[funclet] {
268                 ref mut s @ None => {
269                     debug!("set_successor: updating successor of {:?} to {:?}",
270                            funclet, succ);
271                     *s = Some(succ);
272                 },
273                 Some(s) => if s != succ {
274                     span_bug!(mir.span, "funclet {:?} has 2 parents - {:?} and {:?}",
275                               funclet, s, succ);
276                 }
277             }
278         };
279
280         for (bb, data) in traversal::reverse_postorder(mir) {
281             let funclet = match result[bb] {
282                 CleanupKind::NotCleanup => continue,
283                 CleanupKind::Funclet => bb,
284                 CleanupKind::Internal { funclet } => funclet,
285             };
286
287             debug!("cleanup_kinds: {:?}/{:?}/{:?} propagating funclet {:?}",
288                    bb, data, result[bb], funclet);
289
290             for &succ in data.terminator().successors().iter() {
291                 let kind = result[succ];
292                 debug!("cleanup_kinds: propagating {:?} to {:?}/{:?}",
293                        funclet, succ, kind);
294                 match kind {
295                     CleanupKind::NotCleanup => {
296                         result[succ] = CleanupKind::Internal { funclet: funclet };
297                     }
298                     CleanupKind::Funclet => {
299                         if funclet != succ {
300                             set_successor(funclet, succ);
301                         }
302                     }
303                     CleanupKind::Internal { funclet: succ_funclet } => {
304                         if funclet != succ_funclet {
305                             // `succ` has 2 different funclet going into it, so it must
306                             // be a funclet by itself.
307
308                             debug!("promoting {:?} to a funclet and updating {:?}", succ,
309                                    succ_funclet);
310                             result[succ] = CleanupKind::Funclet;
311                             set_successor(succ_funclet, succ);
312                             set_successor(funclet, succ);
313                         }
314                     }
315                 }
316             }
317         }
318     }
319
320     let mut result = IndexVec::from_elem(CleanupKind::NotCleanup, mir.basic_blocks());
321
322     discover_masters(&mut result, mir);
323     propagate(&mut result, mir);
324     debug!("cleanup_kinds: result={:?}", result);
325     result
326 }