Skip to content

Commit 99b9619

Browse files
Support effects for particular edges of SwitchInt
1 parent ce56f62 commit 99b9619

File tree

2 files changed

+110
-7
lines changed

2 files changed

+110
-7
lines changed

src/librustc_mir/dataflow/generic/engine.rs

+70-5
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::fs;
55
use std::path::PathBuf;
66

77
use rustc::mir::{self, traversal, BasicBlock, Location};
8-
use rustc::ty::TyCtxt;
8+
use rustc::ty::{self, TyCtxt};
99
use rustc_data_structures::work_queue::WorkQueue;
1010
use rustc_hir::def_id::DefId;
1111
use rustc_index::bit_set::BitSet;
@@ -238,10 +238,15 @@ where
238238
}
239239
}
240240

241-
SwitchInt { ref targets, .. } => {
242-
for target in targets {
243-
self.propagate_bits_into_entry_set_for(in_out, *target, dirty_list);
244-
}
241+
SwitchInt { ref targets, ref values, ref discr, .. } => {
242+
self.propagate_bits_into_switch_int_successors(
243+
in_out,
244+
(bb, bb_data),
245+
dirty_list,
246+
discr,
247+
&*values,
248+
&*targets,
249+
);
245250
}
246251

247252
Call { cleanup, ref destination, ref func, ref args, .. } => {
@@ -287,6 +292,66 @@ where
287292
dirty_queue.insert(bb);
288293
}
289294
}
295+
296+
fn propagate_bits_into_switch_int_successors(
297+
&mut self,
298+
in_out: &mut BitSet<A::Idx>,
299+
(bb, bb_data): (BasicBlock, &mir::BasicBlockData<'tcx>),
300+
dirty_list: &mut WorkQueue<BasicBlock>,
301+
switch_on: &mir::Operand<'tcx>,
302+
values: &[u128],
303+
targets: &[BasicBlock],
304+
) {
305+
match bb_data.statements.last().map(|stmt| &stmt.kind) {
306+
// Look at the last statement to see if it is an assignment of an enum discriminant to
307+
// the local that determines the target of a `SwitchInt` like so:
308+
// _42 = discriminant(..)
309+
// SwitchInt(_42, ..)
310+
Some(mir::StatementKind::Assign(box (lhs, mir::Rvalue::Discriminant(enum_))))
311+
if Some(lhs) == switch_on.place() =>
312+
{
313+
let adt = match enum_.ty(self.body, self.tcx).ty.kind {
314+
ty::Adt(def, _) => def,
315+
_ => bug!("Switch on discriminant of non-ADT"),
316+
};
317+
318+
// MIR building adds discriminants to the `values` array in the same order as they
319+
// are yielded by `AdtDef::discriminants`. We rely on this to match each
320+
// discriminant in `values` to its corresponding variant in linear time.
321+
let mut tmp = BitSet::new_empty(in_out.domain_size());
322+
let mut discriminants = adt.discriminants(self.tcx);
323+
for (value, target) in values.iter().zip(targets.iter().copied()) {
324+
let (variant_idx, _) =
325+
discriminants.find(|&(_, discr)| discr.val == *value).expect(
326+
"Order of `AdtDef::discriminants` differed \
327+
from that of `SwitchInt::values`",
328+
);
329+
330+
tmp.overwrite(in_out);
331+
self.analysis.apply_discriminant_switch_effect(
332+
&mut tmp,
333+
bb,
334+
enum_,
335+
adt,
336+
variant_idx,
337+
);
338+
self.propagate_bits_into_entry_set_for(&tmp, target, dirty_list);
339+
}
340+
341+
std::mem::drop(tmp);
342+
343+
// Propagate dataflow state along the "otherwise" edge.
344+
let otherwise = targets.last().copied().unwrap();
345+
self.propagate_bits_into_entry_set_for(&in_out, otherwise, dirty_list);
346+
}
347+
348+
_ => {
349+
for target in targets.iter().copied() {
350+
self.propagate_bits_into_entry_set_for(&in_out, target, dirty_list);
351+
}
352+
}
353+
}
354+
}
290355
}
291356

292357
// Graphviz

src/librustc_mir/dataflow/generic/mod.rs

+40-2
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@
3535
use std::io;
3636

3737
use rustc::mir::{self, BasicBlock, Location};
38-
use rustc::ty::TyCtxt;
38+
use rustc::ty::layout::VariantIdx;
39+
use rustc::ty::{self, TyCtxt};
3940
use rustc_hir::def_id::DefId;
4041
use rustc_index::bit_set::{BitSet, HybridBitSet};
4142
use rustc_index::vec::{Idx, IndexVec};
@@ -172,7 +173,22 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
172173
return_place: &mir::Place<'tcx>,
173174
);
174175

175-
/// Calls the appropriate `Engine` constructor to find the fixpoint for this dataflow problem.
176+
/// Updates the current dataflow state with the effect of taking a particular branch in a
177+
/// `SwitchInt` terminator.
178+
///
179+
/// Much like `apply_call_return_effect`, this effect is only propagated along a single
180+
/// outgoing edge from this basic block.
181+
fn apply_discriminant_switch_effect(
182+
&self,
183+
_state: &mut BitSet<Self::Idx>,
184+
_block: BasicBlock,
185+
_enum_place: &mir::Place<'tcx>,
186+
_adt: &ty::AdtDef,
187+
_variant: VariantIdx,
188+
) {
189+
}
190+
191+
/// Creates an `Engine` to find the fixpoint for this dataflow problem.
176192
///
177193
/// You shouldn't need to override this outside this module, since the combination of the
178194
/// default impl and the one for all `A: GenKillAnalysis` will do the right thing.
@@ -249,6 +265,17 @@ pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> {
249265
args: &[mir::Operand<'tcx>],
250266
return_place: &mir::Place<'tcx>,
251267
);
268+
269+
/// See `Analysis::apply_discriminant_switch_effect`.
270+
fn discriminant_switch_effect(
271+
&self,
272+
_state: &mut impl GenKill<Self::Idx>,
273+
_block: BasicBlock,
274+
_enum_place: &mir::Place<'tcx>,
275+
_adt: &ty::AdtDef,
276+
_variant: VariantIdx,
277+
) {
278+
}
252279
}
253280

254281
impl<A> Analysis<'tcx> for A
@@ -302,6 +329,17 @@ where
302329
self.call_return_effect(state, block, func, args, return_place);
303330
}
304331

332+
fn apply_discriminant_switch_effect(
333+
&self,
334+
state: &mut BitSet<Self::Idx>,
335+
block: BasicBlock,
336+
enum_place: &mir::Place<'tcx>,
337+
adt: &ty::AdtDef,
338+
variant: VariantIdx,
339+
) {
340+
self.discriminant_switch_effect(state, block, enum_place, adt, variant);
341+
}
342+
305343
fn into_engine(
306344
self,
307345
tcx: TyCtxt<'tcx>,

0 commit comments

Comments
 (0)