]> git.proxmox.com Git - rustc.git/blame - src/librustc_borrowck/borrowck/mir/dataflow/mod.rs
New upstream version 1.19.0+dfsg3
[rustc.git] / src / librustc_borrowck / borrowck / mir / dataflow / mod.rs
CommitLineData
3157f602
XL
1// Copyright 2012-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.
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
c30ab7b3 11use rustc_data_structures::indexed_set::{IdxSet, IdxSetBuf};
3157f602 12use rustc_data_structures::indexed_vec::Idx;
c30ab7b3 13use rustc_data_structures::bitslice::{bitwise, BitwiseOperator};
3157f602
XL
14
15use rustc::ty::TyCtxt;
c30ab7b3 16use rustc::mir::{self, Mir};
3157f602
XL
17
18use std::fmt::Debug;
19use std::io;
20use std::mem;
21use std::path::PathBuf;
22use std::usize;
23
24use super::MirBorrowckCtxtPreDataflow;
3157f602 25
3157f602
XL
26pub use self::sanity_check::sanity_check_via_rustc_peek;
27pub use self::impls::{MaybeInitializedLvals, MaybeUninitializedLvals};
28pub use self::impls::{DefinitelyInitializedLvals, MovingOutStatements};
29
30mod graphviz;
31mod sanity_check;
32mod impls;
33
34pub trait Dataflow<BD: BitDenotation> {
32a655c1 35 fn dataflow<P>(&mut self, p: P) where P: Fn(&BD, BD::Idx) -> &Debug;
3157f602
XL
36}
37
38impl<'a, 'tcx: 'a, BD> Dataflow<BD> for MirBorrowckCtxtPreDataflow<'a, 'tcx, BD>
32a655c1 39 where BD: BitDenotation + DataflowOperator
3157f602 40{
32a655c1 41 fn dataflow<P>(&mut self, p: P) where P: Fn(&BD, BD::Idx) -> &Debug {
3157f602
XL
42 self.flow_state.build_sets();
43 self.pre_dataflow_instrumentation(|c,i| p(c,i)).unwrap();
44 self.flow_state.propagate();
45 self.post_dataflow_instrumentation(|c,i| p(c,i)).unwrap();
46 }
47}
48
49struct PropagationContext<'b, 'a: 'b, 'tcx: 'a, O>
32a655c1 50 where O: 'b + BitDenotation
3157f602
XL
51{
52 builder: &'b mut DataflowAnalysis<'a, 'tcx, O>,
53 changed: bool,
54}
55
56impl<'a, 'tcx: 'a, BD> DataflowAnalysis<'a, 'tcx, BD>
57 where BD: BitDenotation + DataflowOperator
58{
59 fn propagate(&mut self) {
60 let mut temp = IdxSetBuf::new_empty(self.flow_state.sets.bits_per_block);
61 let mut propcx = PropagationContext {
62 builder: self,
63 changed: true,
64 };
65 while propcx.changed {
66 propcx.changed = false;
67 propcx.reset(&mut temp);
68 propcx.walk_cfg(&mut temp);
69 }
70 }
71
72 fn build_sets(&mut self) {
73 // First we need to build the entry-, gen- and kill-sets. The
74 // gather_moves information provides a high-level mapping from
75 // mir-locations to the MoveOuts (and those correspond
76 // directly to gen-sets here). But we still need to figure out
77 // the kill-sets.
78
79 {
c30ab7b3 80 let sets = &mut self.flow_state.sets.for_block(mir::START_BLOCK.index());
32a655c1 81 self.flow_state.operator.start_block_effect(sets);
3157f602
XL
82 }
83
84 for (bb, data) in self.mir.basic_blocks().iter_enumerated() {
c30ab7b3 85 let &mir::BasicBlockData { ref statements, ref terminator, is_cleanup: _ } = data;
3157f602
XL
86
87 let sets = &mut self.flow_state.sets.for_block(bb.index());
88 for j_stmt in 0..statements.len() {
32a655c1 89 self.flow_state.operator.statement_effect(sets, bb, j_stmt);
3157f602
XL
90 }
91
92 if terminator.is_some() {
93 let stmts_len = statements.len();
32a655c1 94 self.flow_state.operator.terminator_effect(sets, bb, stmts_len);
3157f602
XL
95 }
96 }
97 }
98}
99
100impl<'b, 'a: 'b, 'tcx: 'a, BD> PropagationContext<'b, 'a, 'tcx, BD>
101 where BD: BitDenotation + DataflowOperator
102{
103 fn reset(&mut self, bits: &mut IdxSet<BD::Idx>) {
104 let e = if BD::bottom_value() {!0} else {0};
105 for b in bits.words_mut() {
106 *b = e;
107 }
108 }
109
110 fn walk_cfg(&mut self, in_out: &mut IdxSet<BD::Idx>) {
111 let mir = self.builder.mir;
112 for (bb_idx, bb_data) in mir.basic_blocks().iter().enumerate() {
113 let builder = &mut self.builder;
114 {
115 let sets = builder.flow_state.sets.for_block(bb_idx);
116 debug_assert!(in_out.words().len() == sets.on_entry.words().len());
117 in_out.clone_from(sets.on_entry);
118 in_out.union(sets.gen_set);
119 in_out.subtract(sets.kill_set);
120 }
121 builder.propagate_bits_into_graph_successors_of(
c30ab7b3 122 in_out, &mut self.changed, (mir::BasicBlock::new(bb_idx), bb_data));
3157f602
XL
123 }
124 }
125}
126
127fn dataflow_path(context: &str, prepost: &str, path: &str) -> PathBuf {
128 format!("{}_{}", context, prepost);
129 let mut path = PathBuf::from(path);
130 let new_file_name = {
131 let orig_file_name = path.file_name().unwrap().to_str().unwrap();
132 format!("{}_{}", context, orig_file_name)
133 };
134 path.set_file_name(new_file_name);
135 path
136}
137
138impl<'a, 'tcx: 'a, BD> MirBorrowckCtxtPreDataflow<'a, 'tcx, BD>
32a655c1 139 where BD: BitDenotation
3157f602
XL
140{
141 fn pre_dataflow_instrumentation<P>(&self, p: P) -> io::Result<()>
32a655c1 142 where P: Fn(&BD, BD::Idx) -> &Debug
3157f602
XL
143 {
144 if let Some(ref path_str) = self.print_preflow_to {
145 let path = dataflow_path(BD::name(), "preflow", path_str);
146 graphviz::print_borrowck_graph_to(self, &path, p)
147 } else {
148 Ok(())
149 }
150 }
151
152 fn post_dataflow_instrumentation<P>(&self, p: P) -> io::Result<()>
32a655c1 153 where P: Fn(&BD, BD::Idx) -> &Debug
3157f602
XL
154 {
155 if let Some(ref path_str) = self.print_postflow_to {
156 let path = dataflow_path(BD::name(), "postflow", path_str);
157 graphviz::print_borrowck_graph_to(self, &path, p)
158 } else{
159 Ok(())
160 }
161 }
162}
163
164/// Maps each block to a set of bits
165#[derive(Debug)]
166struct Bits<E:Idx> {
167 bits: IdxSetBuf<E>,
168}
169
170impl<E:Idx> Clone for Bits<E> {
171 fn clone(&self) -> Self { Bits { bits: self.bits.clone() } }
172}
173
174impl<E:Idx> Bits<E> {
175 fn new(bits: IdxSetBuf<E>) -> Self {
176 Bits { bits: bits }
177 }
178}
179
180pub struct DataflowAnalysis<'a, 'tcx: 'a, O>
32a655c1 181 where O: BitDenotation
3157f602
XL
182{
183 flow_state: DataflowState<O>,
cc61c64b 184 dead_unwinds: &'a IdxSet<mir::BasicBlock>,
3157f602 185 mir: &'a Mir<'tcx>,
3157f602
XL
186}
187
188impl<'a, 'tcx: 'a, O> DataflowAnalysis<'a, 'tcx, O>
189 where O: BitDenotation
190{
191 pub fn results(self) -> DataflowResults<O> {
192 DataflowResults(self.flow_state)
193 }
194
195 pub fn mir(&self) -> &'a Mir<'tcx> { self.mir }
196}
197
198pub struct DataflowResults<O>(DataflowState<O>) where O: BitDenotation;
199
200impl<O: BitDenotation> DataflowResults<O> {
201 pub fn sets(&self) -> &AllSets<O::Idx> {
202 &self.0.sets
203 }
204}
205
206// FIXME: This type shouldn't be public, but the graphviz::MirWithFlowState trait
207// references it in a method signature. Look into using `pub(crate)` to address this.
208pub struct DataflowState<O: BitDenotation>
209{
210 /// All the sets for the analysis. (Factored into its
211 /// own structure so that we can borrow it mutably
212 /// on its own separate from other fields.)
213 pub sets: AllSets<O::Idx>,
214
215 /// operator used to initialize, combine, and interpret bits.
216 operator: O,
217}
218
219#[derive(Debug)]
220pub struct AllSets<E: Idx> {
221 /// Analysis bitwidth for each block.
222 bits_per_block: usize,
223
224 /// Number of words associated with each block entry
225 /// equal to bits_per_block / usize::BITS, rounded up.
226 words_per_block: usize,
227
228 /// For each block, bits generated by executing the statements in
229 /// the block. (For comparison, the Terminator for each block is
230 /// handled in a flow-specific manner during propagation.)
231 gen_sets: Bits<E>,
232
233 /// For each block, bits killed by executing the statements in the
234 /// block. (For comparison, the Terminator for each block is
235 /// handled in a flow-specific manner during propagation.)
236 kill_sets: Bits<E>,
237
238 /// For each block, bits valid on entry to the block.
239 on_entry_sets: Bits<E>,
240}
241
242pub struct BlockSets<'a, E: Idx> {
243 on_entry: &'a mut IdxSet<E>,
244 gen_set: &'a mut IdxSet<E>,
245 kill_set: &'a mut IdxSet<E>,
246}
247
248impl<'a, E:Idx> BlockSets<'a, E> {
249 fn gen(&mut self, e: &E) {
250 self.gen_set.add(e);
251 self.kill_set.remove(e);
252 }
253 fn kill(&mut self, e: &E) {
254 self.gen_set.remove(e);
255 self.kill_set.add(e);
256 }
257}
258
259impl<E:Idx> AllSets<E> {
260 pub fn bits_per_block(&self) -> usize { self.bits_per_block }
261 pub fn for_block(&mut self, block_idx: usize) -> BlockSets<E> {
262 let offset = self.words_per_block * block_idx;
263 let range = E::new(offset)..E::new(offset + self.words_per_block);
264 BlockSets {
265 on_entry: self.on_entry_sets.bits.range_mut(&range),
266 gen_set: self.gen_sets.bits.range_mut(&range),
267 kill_set: self.kill_sets.bits.range_mut(&range),
268 }
269 }
270
271 fn lookup_set_for<'a>(&self, sets: &'a Bits<E>, block_idx: usize) -> &'a IdxSet<E> {
272 let offset = self.words_per_block * block_idx;
273 let range = E::new(offset)..E::new(offset + self.words_per_block);
274 sets.bits.range(&range)
275 }
276 pub fn gen_set_for(&self, block_idx: usize) -> &IdxSet<E> {
277 self.lookup_set_for(&self.gen_sets, block_idx)
278 }
279 pub fn kill_set_for(&self, block_idx: usize) -> &IdxSet<E> {
280 self.lookup_set_for(&self.kill_sets, block_idx)
281 }
282 pub fn on_entry_set_for(&self, block_idx: usize) -> &IdxSet<E> {
283 self.lookup_set_for(&self.on_entry_sets, block_idx)
284 }
285}
286
287/// Parameterization for the precise form of data flow that is used.
288pub trait DataflowOperator: BitwiseOperator {
289 /// Specifies the initial value for each bit in the `on_entry` set
290 fn bottom_value() -> bool;
291}
292
293pub trait BitDenotation {
294 /// Specifies what index type is used to access the bitvector.
295 type Idx: Idx;
296
3157f602
XL
297 /// A name describing the dataflow analysis that this
298 /// BitDenotation is supporting. The name should be something
299 /// suitable for plugging in as part of a filename e.g. avoid
300 /// space-characters or other things that tend to look bad on a
301 /// file system, like slashes or periods. It is also better for
302 /// the name to be reasonably short, again because it will be
303 /// plugged into a filename.
304 fn name() -> &'static str;
305
306 /// Size of each bitvector allocated for each block in the analysis.
32a655c1 307 fn bits_per_block(&self) -> usize;
3157f602
XL
308
309 /// Mutates the block-sets (the flow sets for the given
310 /// basic block) according to the effects that have been
311 /// established *prior* to entering the start block.
312 ///
313 /// (For example, establishing the call arguments.)
314 ///
315 /// (Typically this should only modify `sets.on_entry`, since the
316 /// gen and kill sets should reflect the effects of *executing*
317 /// the start block itself.)
32a655c1 318 fn start_block_effect(&self, sets: &mut BlockSets<Self::Idx>);
3157f602
XL
319
320 /// Mutates the block-sets (the flow sets for the given
321 /// basic block) according to the effects of evaluating statement.
322 ///
323 /// This is used, in particular, for building up the
324 /// "transfer-function" represnting the overall-effect of the
325 /// block, represented via GEN and KILL sets.
326 ///
327 /// The statement is identified as `bb_data[idx_stmt]`, where
328 /// `bb_data` is the sequence of statements identifed by `bb` in
329 /// the MIR.
330 fn statement_effect(&self,
3157f602 331 sets: &mut BlockSets<Self::Idx>,
c30ab7b3 332 bb: mir::BasicBlock,
3157f602
XL
333 idx_stmt: usize);
334
335 /// Mutates the block-sets (the flow sets for the given
336 /// basic block) according to the effects of evaluating
337 /// the terminator.
338 ///
339 /// This is used, in particular, for building up the
340 /// "transfer-function" represnting the overall-effect of the
341 /// block, represented via GEN and KILL sets.
342 ///
343 /// The effects applied here cannot depend on which branch the
344 /// terminator took.
345 fn terminator_effect(&self,
3157f602 346 sets: &mut BlockSets<Self::Idx>,
c30ab7b3 347 bb: mir::BasicBlock,
3157f602
XL
348 idx_term: usize);
349
350 /// Mutates the block-sets according to the (flow-dependent)
351 /// effect of a successful return from a Call terminator.
352 ///
353 /// If basic-block BB_x ends with a call-instruction that, upon
354 /// successful return, flows to BB_y, then this method will be
355 /// called on the exit flow-state of BB_x in order to set up the
356 /// entry flow-state of BB_y.
357 ///
358 /// This is used, in particular, as a special case during the
359 /// "propagate" loop where all of the basic blocks are repeatedly
360 /// visited. Since the effects of a Call terminator are
361 /// flow-dependent, the current MIR cannot encode them via just
362 /// GEN and KILL sets attached to the block, and so instead we add
363 /// this extra machinery to represent the flow-dependent effect.
364 ///
365 /// FIXME: Right now this is a bit of a wart in the API. It might
366 /// be better to represent this as an additional gen- and
367 /// kill-sets associated with each edge coming out of the basic
368 /// block.
369 fn propagate_call_return(&self,
3157f602 370 in_out: &mut IdxSet<Self::Idx>,
c30ab7b3
SL
371 call_bb: mir::BasicBlock,
372 dest_bb: mir::BasicBlock,
373 dest_lval: &mir::Lvalue);
3157f602
XL
374}
375
376impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D>
377 where D: BitDenotation + DataflowOperator
378{
379 pub fn new(_tcx: TyCtxt<'a, 'tcx, 'tcx>,
380 mir: &'a Mir<'tcx>,
cc61c64b 381 dead_unwinds: &'a IdxSet<mir::BasicBlock>,
3157f602 382 denotation: D) -> Self {
32a655c1 383 let bits_per_block = denotation.bits_per_block();
3157f602
XL
384 let usize_bits = mem::size_of::<usize>() * 8;
385 let words_per_block = (bits_per_block + usize_bits - 1) / usize_bits;
386
387 // (now rounded up to multiple of word size)
388 let bits_per_block = words_per_block * usize_bits;
389
390 let num_blocks = mir.basic_blocks().len();
391 let num_overall = num_blocks * bits_per_block;
392
393 let zeroes = Bits::new(IdxSetBuf::new_empty(num_overall));
394 let on_entry = Bits::new(if D::bottom_value() {
395 IdxSetBuf::new_filled(num_overall)
396 } else {
397 IdxSetBuf::new_empty(num_overall)
398 });
399
400 DataflowAnalysis {
3157f602 401 mir: mir,
cc61c64b 402 dead_unwinds: dead_unwinds,
3157f602
XL
403 flow_state: DataflowState {
404 sets: AllSets {
405 bits_per_block: bits_per_block,
406 words_per_block: words_per_block,
407 gen_sets: zeroes.clone(),
408 kill_sets: zeroes,
409 on_entry_sets: on_entry,
410 },
411 operator: denotation,
412 },
413 }
414
415 }
416}
417
418impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D>
419 where D: BitDenotation + DataflowOperator
420{
421 /// Propagates the bits of `in_out` into all the successors of `bb`,
422 /// using bitwise operator denoted by `self.operator`.
423 ///
424 /// For most blocks, this is entirely uniform. However, for blocks
425 /// that end with a call terminator, the effect of the call on the
426 /// dataflow state may depend on whether the call returned
427 /// successfully or unwound.
428 ///
429 /// To reflect this, the `propagate_call_return` method of the
430 /// `BitDenotation` mutates `in_out` when propagating `in_out` via
431 /// a call terminator; such mutation is performed *last*, to
432 /// ensure its side-effects do not leak elsewhere (e.g. into
433 /// unwind target).
434 fn propagate_bits_into_graph_successors_of(
435 &mut self,
436 in_out: &mut IdxSet<D::Idx>,
437 changed: &mut bool,
c30ab7b3 438 (bb, bb_data): (mir::BasicBlock, &mir::BasicBlockData))
3157f602
XL
439 {
440 match bb_data.terminator().kind {
c30ab7b3
SL
441 mir::TerminatorKind::Return |
442 mir::TerminatorKind::Resume |
443 mir::TerminatorKind::Unreachable => {}
444 mir::TerminatorKind::Goto { ref target } |
445 mir::TerminatorKind::Assert { ref target, cleanup: None, .. } |
446 mir::TerminatorKind::Drop { ref target, location: _, unwind: None } |
447 mir::TerminatorKind::DropAndReplace {
3157f602
XL
448 ref target, value: _, location: _, unwind: None
449 } => {
450 self.propagate_bits_into_entry_set_for(in_out, changed, target);
451 }
c30ab7b3
SL
452 mir::TerminatorKind::Assert { ref target, cleanup: Some(ref unwind), .. } |
453 mir::TerminatorKind::Drop { ref target, location: _, unwind: Some(ref unwind) } |
454 mir::TerminatorKind::DropAndReplace {
3157f602
XL
455 ref target, value: _, location: _, unwind: Some(ref unwind)
456 } => {
457 self.propagate_bits_into_entry_set_for(in_out, changed, target);
cc61c64b
XL
458 if !self.dead_unwinds.contains(&bb) {
459 self.propagate_bits_into_entry_set_for(in_out, changed, unwind);
460 }
3157f602 461 }
c30ab7b3 462 mir::TerminatorKind::SwitchInt { ref targets, .. } => {
3157f602
XL
463 for target in targets {
464 self.propagate_bits_into_entry_set_for(in_out, changed, target);
465 }
466 }
c30ab7b3 467 mir::TerminatorKind::Call { ref cleanup, ref destination, func: _, args: _ } => {
3157f602 468 if let Some(ref unwind) = *cleanup {
cc61c64b
XL
469 if !self.dead_unwinds.contains(&bb) {
470 self.propagate_bits_into_entry_set_for(in_out, changed, unwind);
471 }
3157f602
XL
472 }
473 if let Some((ref dest_lval, ref dest_bb)) = *destination {
474 // N.B.: This must be done *last*, after all other
475 // propagation, as documented in comment above.
476 self.flow_state.operator.propagate_call_return(
32a655c1 477 in_out, bb, *dest_bb, dest_lval);
3157f602
XL
478 self.propagate_bits_into_entry_set_for(in_out, changed, dest_bb);
479 }
480 }
481 }
482 }
483
484 fn propagate_bits_into_entry_set_for(&mut self,
485 in_out: &IdxSet<D::Idx>,
486 changed: &mut bool,
c30ab7b3 487 bb: &mir::BasicBlock) {
3157f602
XL
488 let entry_set = self.flow_state.sets.for_block(bb.index()).on_entry;
489 let set_changed = bitwise(entry_set.words_mut(),
490 in_out.words(),
491 &self.flow_state.operator);
492 if set_changed {
493 *changed = true;
494 }
495 }
496}