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