use rustc_ast as ast;
use rustc_hir::lang_items::LangItem;
use rustc_index::vec::Idx;
-use rustc_middle::mir;
use rustc_middle::mir::interpret::ConstValue;
use rustc_middle::mir::AssertKind;
+use rustc_middle::mir::{self, SwitchTargets};
use rustc_middle::ty::layout::{FnAbiExt, HasTyCtxt};
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::{self, Instance, Ty, TypeFoldable};
use rustc_target::abi::{self, LayoutOf};
use rustc_target::spec::abi::Abi;
-use std::borrow::Cow;
-
/// Used by `FunctionCx::codegen_terminator` for emitting common patterns
/// e.g., creating a basic block, calling a function, etc.
struct TerminatorCodegenHelper<'tcx> {
mut bx: Bx,
discr: &mir::Operand<'tcx>,
switch_ty: Ty<'tcx>,
- values: &Cow<'tcx, [u128]>,
- targets: &Vec<mir::BasicBlock>,
+ targets: &SwitchTargets<'tcx>,
) {
let discr = self.codegen_operand(&mut bx, &discr);
// `switch_ty` is redundant, sanity-check that.
assert_eq!(discr.layout.ty, switch_ty);
- if targets.len() == 2 {
- // If there are two targets, emit br instead of switch
- let lltrue = helper.llblock(self, targets[0]);
- let llfalse = helper.llblock(self, targets[1]);
+ helper.maybe_sideeffect(self.mir, &mut bx, targets.all_targets());
+
+ let mut target_iter = targets.iter();
+ if target_iter.len() == 1 {
+ // If there are two targets (one conditional, one fallback), emit br instead of switch
+ let (test_value, target) = target_iter.next().unwrap();
+ let lltrue = helper.llblock(self, target);
+ let llfalse = helper.llblock(self, targets.otherwise());
if switch_ty == bx.tcx().types.bool {
- helper.maybe_sideeffect(self.mir, &mut bx, targets.as_slice());
// Don't generate trivial icmps when switching on bool
- if let [0] = values[..] {
- bx.cond_br(discr.immediate(), llfalse, lltrue);
- } else {
- assert_eq!(&values[..], &[1]);
- bx.cond_br(discr.immediate(), lltrue, llfalse);
+ match test_value {
+ 0 => bx.cond_br(discr.immediate(), llfalse, lltrue),
+ 1 => bx.cond_br(discr.immediate(), lltrue, llfalse),
+ _ => bug!(),
}
} else {
let switch_llty = bx.immediate_backend_type(bx.layout_of(switch_ty));
- let llval = bx.const_uint_big(switch_llty, values[0]);
+ let llval = bx.const_uint_big(switch_llty, test_value);
let cmp = bx.icmp(IntPredicate::IntEQ, discr.immediate(), llval);
- helper.maybe_sideeffect(self.mir, &mut bx, targets.as_slice());
bx.cond_br(cmp, lltrue, llfalse);
}
} else {
- helper.maybe_sideeffect(self.mir, &mut bx, targets.as_slice());
- let (otherwise, targets) = targets.split_last().unwrap();
bx.switch(
discr.immediate(),
- helper.llblock(self, *otherwise),
- values
- .iter()
- .zip(targets)
- .map(|(&value, target)| (value, helper.llblock(self, *target))),
+ helper.llblock(self, targets.otherwise()),
+ target_iter.map(|(value, target)| (value, helper.llblock(self, target))),
);
}
}
helper.funclet_br(self, &mut bx, target);
}
- mir::TerminatorKind::SwitchInt { ref discr, switch_ty, ref values, ref targets } => {
- self.codegen_switchint_terminator(helper, bx, discr, switch_ty, values, targets);
+ mir::TerminatorKind::SwitchInt { ref discr, switch_ty, ref targets } => {
+ self.codegen_switchint_terminator(helper, bx, discr, switch_ty, targets);
}
mir::TerminatorKind::Return => {
pub use super::query::*;
+#[derive(Debug, Clone, TyEncodable, TyDecodable, HashStable, PartialEq)]
+pub struct SwitchTargets<'tcx> {
+ /// Possible values. The locations to branch to in each case
+ /// are found in the corresponding indices from the `targets` vector.
+ values: Cow<'tcx, [u128]>,
+
+ /// Possible branch sites. The last element of this vector is used
+ /// for the otherwise branch, so targets.len() == values.len() + 1
+ /// should hold.
+ //
+ // This invariant is quite non-obvious and also could be improved.
+ // One way to make this invariant is to have something like this instead:
+ //
+ // branches: Vec<(ConstInt, BasicBlock)>,
+ // otherwise: Option<BasicBlock> // exhaustive if None
+ //
+ // However we’ve decided to keep this as-is until we figure a case
+ // where some other approach seems to be strictly better than other.
+ targets: Vec<BasicBlock>,
+}
+
+impl<'tcx> SwitchTargets<'tcx> {
+ /// Creates switch targets from an iterator of values and target blocks.
+ ///
+ /// The iterator may be empty, in which case the `SwitchInt` instruction is equivalent to
+ /// `goto otherwise;`.
+ pub fn new(targets: impl Iterator<Item = (u128, BasicBlock)>, otherwise: BasicBlock) -> Self {
+ let (values, mut targets): (Vec<_>, Vec<_>) = targets.unzip();
+ targets.push(otherwise);
+ Self { values: values.into(), targets }
+ }
+
+ /// Builds a switch targets definition that jumps to `then` if the tested value equals `value`,
+ /// and to `else_` if not.
+ pub fn static_if(value: &'static [u128; 1], then: BasicBlock, else_: BasicBlock) -> Self {
+ Self { values: Cow::Borrowed(value), targets: vec![then, else_] }
+ }
+
+ /// Returns the fallback target that is jumped to when none of the values match the operand.
+ pub fn otherwise(&self) -> BasicBlock {
+ *self.targets.last().unwrap()
+ }
+
+ /// Returns an iterator over the switch targets.
+ ///
+ /// The iterator will yield tuples containing the value and corresponding target to jump to, not
+ /// including the `otherwise` fallback target.
+ ///
+ /// Note that this may yield 0 elements. Only the `otherwise` branch is mandatory.
+ pub fn iter(&self) -> SwitchTargetsIter<'_> {
+ SwitchTargetsIter { inner: self.values.iter().zip(self.targets.iter()) }
+ }
+
+ /// Returns a slice with all possible jump targets (including the fallback target).
+ pub fn all_targets(&self) -> &[BasicBlock] {
+ &self.targets
+ }
+
+ pub fn all_targets_mut(&mut self) -> &mut [BasicBlock] {
+ &mut self.targets
+ }
+}
+
+pub struct SwitchTargetsIter<'a> {
+ inner: iter::Zip<slice::Iter<'a, u128>, slice::Iter<'a, BasicBlock>>,
+}
+
+impl<'a> Iterator for SwitchTargetsIter<'a> {
+ type Item = (u128, BasicBlock);
+
+ fn next(&mut self) -> Option<Self::Item> {
+ self.inner.next().map(|(val, bb)| (*val, *bb))
+ }
+
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ self.inner.size_hint()
+ }
+}
+
+impl<'a> ExactSizeIterator for SwitchTargetsIter<'a> {}
+
#[derive(Clone, TyEncodable, TyDecodable, HashStable, PartialEq)]
pub enum TerminatorKind<'tcx> {
/// Block should have one successor in the graph; we jump there.
/// FIXME: remove this redundant information. Currently, it is relied on by pretty-printing.
switch_ty: Ty<'tcx>,
- /// Possible values. The locations to branch to in each case
- /// are found in the corresponding indices from the `targets` vector.
- values: Cow<'tcx, [u128]>,
-
- /// Possible branch sites. The last element of this vector is used
- /// for the otherwise branch, so targets.len() == values.len() + 1
- /// should hold.
- //
- // This invariant is quite non-obvious and also could be improved.
- // One way to make this invariant is to have something like this instead:
- //
- // branches: Vec<(ConstInt, BasicBlock)>,
- // otherwise: Option<BasicBlock> // exhaustive if None
- //
- // However we’ve decided to keep this as-is until we figure a case
- // where some other approach seems to be strictly better than other.
- targets: Vec<BasicBlock>,
+ targets: SwitchTargets<'tcx>,
},
/// Indicates that the landing pad is finished and unwinding should
t: BasicBlock,
f: BasicBlock,
) -> TerminatorKind<'tcx> {
- static BOOL_SWITCH_FALSE: &[u128] = &[0];
TerminatorKind::SwitchInt {
discr: cond,
switch_ty: tcx.types.bool,
- values: From::from(BOOL_SWITCH_FALSE),
- targets: vec![f, t],
+ targets: SwitchTargets::static_if(&[0], f, t),
}
}
| FalseUnwind { real_target: ref t, unwind: Some(ref u) } => {
Some(t).into_iter().chain(slice::from_ref(u))
}
- SwitchInt { ref targets, .. } => None.into_iter().chain(&targets[..]),
+ SwitchInt { ref targets, .. } => None.into_iter().chain(&targets.targets[..]),
FalseEdge { ref real_target, ref imaginary_target } => {
Some(real_target).into_iter().chain(slice::from_ref(imaginary_target))
}
| FalseUnwind { real_target: ref mut t, unwind: Some(ref mut u) } => {
Some(t).into_iter().chain(slice::from_mut(u))
}
- SwitchInt { ref mut targets, .. } => None.into_iter().chain(&mut targets[..]),
+ SwitchInt { ref mut targets, .. } => None.into_iter().chain(&mut targets.targets[..]),
FalseEdge { ref mut real_target, ref mut imaginary_target } => {
Some(real_target).into_iter().chain(slice::from_mut(imaginary_target))
}
match *self {
Return | Resume | Abort | Unreachable | GeneratorDrop => vec![],
Goto { .. } => vec!["".into()],
- SwitchInt { ref values, switch_ty, .. } => ty::tls::with(|tcx| {
+ SwitchInt { ref targets, switch_ty, .. } => ty::tls::with(|tcx| {
let param_env = ty::ParamEnv::empty();
let switch_ty = tcx.lift(&switch_ty).unwrap();
let size = tcx.layout_of(param_env.and(switch_ty)).unwrap().size;
- values
+ targets
+ .values
.iter()
.map(|&u| {
ty::Const::from_scalar(tcx, Scalar::from_uint(u, size), switch_ty)
let kind = match self.kind {
Goto { target } => Goto { target },
- SwitchInt { ref discr, switch_ty, ref values, ref targets } => SwitchInt {
+ SwitchInt { ref discr, switch_ty, ref targets } => SwitchInt {
discr: discr.fold_with(folder),
switch_ty: switch_ty.fold_with(folder),
- values: values.clone(),
targets: targets.clone(),
},
Drop { ref place, target, unwind } => {
TerminatorKind::SwitchInt {
discr,
switch_ty,
- values: _,
targets: _
} => {
self.visit_operand(discr, location);
self.check_activations(location);
match &terminator.kind {
- TerminatorKind::SwitchInt { ref discr, switch_ty: _, values: _, targets: _ } => {
+ TerminatorKind::SwitchInt { ref discr, switch_ty: _, targets: _ } => {
self.consume_operand(location, discr);
}
TerminatorKind::Drop { place: drop_place, target: _, unwind: _ } => {
self.check_activations(loc, span, flow_state);
match term.kind {
- TerminatorKind::SwitchInt { ref discr, switch_ty: _, values: _, targets: _ } => {
+ TerminatorKind::SwitchInt { ref discr, switch_ty: _, targets: _ } => {
self.consume_operand(loc, (discr, span), flow_state);
}
TerminatorKind::Drop { place: ref drop_place, target: _, unwind: _ } => {
self.assert_iscleanup(body, block_data, target, is_cleanup)
}
TerminatorKind::SwitchInt { ref targets, .. } => {
- for target in targets {
+ for target in targets.all_targets() {
self.assert_iscleanup(body, block_data, *target, is_cleanup);
}
}
use rustc_index::bit_set::BitSet;
-use rustc_middle::mir::{self, BasicBlock, Location};
+use rustc_middle::mir::{self, BasicBlock, Location, SwitchTargets};
use rustc_middle::ty::TyCtxt;
use std::ops::RangeInclusive;
}
}
- SwitchInt { ref targets, ref values, ref discr, switch_ty: _ } => {
+ SwitchInt { ref targets, ref discr, switch_ty: _ } => {
let mut applier = SwitchIntEdgeEffectApplier {
exit_state,
- targets: targets.as_ref(),
- values: values.as_ref(),
+ targets,
propagate,
effects_applied: false,
};
} = applier;
if !effects_applied {
- for &target in targets.iter() {
- propagate(target, exit_state);
+ for target in targets.all_targets() {
+ propagate(*target, exit_state);
}
}
}
struct SwitchIntEdgeEffectApplier<'a, D, F> {
exit_state: &'a mut D,
- values: &'a [u128],
- targets: &'a [BasicBlock],
+ targets: &'a SwitchTargets<'a>,
propagate: F,
effects_applied: bool,
assert!(!self.effects_applied);
let mut tmp = None;
- for (&value, &target) in self.values.iter().zip(self.targets.iter()) {
+ for (value, target) in self.targets.iter() {
let tmp = opt_clone_from_or_clone(&mut tmp, self.exit_state);
apply_edge_effect(tmp, SwitchIntTarget { value: Some(value), target });
(self.propagate)(target, tmp);
// Once we get to the final, "otherwise" branch, there is no need to preserve `exit_state`,
// so pass it directly to `apply_edge_effect` to save a clone of the dataflow state.
- let otherwise = self.targets.last().copied().unwrap();
+ let otherwise = self.targets.otherwise();
apply_edge_effect(self.exit_state, SwitchIntTarget { value: None, target: otherwise });
(self.propagate)(otherwise, self.exit_state);
Goto { target } => self.go_to_block(target),
- SwitchInt { ref discr, ref values, ref targets, switch_ty } => {
+ SwitchInt { ref discr, ref targets, switch_ty } => {
let discr = self.read_immediate(self.eval_operand(discr, None)?)?;
trace!("SwitchInt({:?})", *discr);
assert_eq!(discr.layout.ty, switch_ty);
// Branch to the `otherwise` case by default, if no match is found.
- assert!(!targets.is_empty());
- let mut target_block = targets[targets.len() - 1];
+ assert!(!targets.iter().is_empty());
+ let mut target_block = targets.otherwise();
- for (index, &const_int) in values.iter().enumerate() {
+ for (const_int, target) in targets.iter() {
// Compare using binary_op, to also support pointer values
let res = self
.overflowing_binary_op(
)?
.0;
if res.to_bool()? {
- target_block = targets[index];
+ target_block = target;
break;
}
}
use crate::{transform::MirPass, util::patch::MirPatch};
use rustc_middle::mir::*;
use rustc_middle::ty::{Ty, TyCtxt};
-use std::{borrow::Cow, fmt::Debug};
+use std::fmt::Debug;
use super::simplify::simplify_cfg;
StatementKind::Assign(box (Place::from(not_equal_temp), not_equal_rvalue)),
);
- let (mut targets_to_jump_to, values_to_jump_to): (Vec<_>, Vec<_>) = opt_to_apply
+ let new_targets = opt_to_apply
.infos
.iter()
.flat_map(|x| x.second_switch_info.targets_with_values.iter())
- .cloned()
- .unzip();
+ .cloned();
+
+ let targets = SwitchTargets::new(
+ new_targets,
+ opt_to_apply.infos[0].first_switch_info.otherwise_bb,
+ );
- // add otherwise case in the end
- targets_to_jump_to.push(opt_to_apply.infos[0].first_switch_info.otherwise_bb);
// new block that jumps to the correct discriminant case. This block is switched to if the discriminants are equal
let new_switch_data = BasicBlockData::new(Some(Terminator {
source_info: opt_to_apply.infos[0].second_switch_info.discr_source_info,
// the first and second discriminants are equal, so just pick one
discr: Operand::Copy(first_descriminant_place),
switch_ty: discr_type,
- values: Cow::from(values_to_jump_to),
- targets: targets_to_jump_to,
+ targets,
},
}));
/// The basic block that the otherwise branch points to
otherwise_bb: BasicBlock,
/// Target along with the value being branched from. Otherwise is not included
- targets_with_values: Vec<(BasicBlock, u128)>,
+ targets_with_values: Vec<(u128, BasicBlock)>,
discr_source_info: SourceInfo,
/// The place of the discriminant used in the switch
discr_used_in_switch: Place<'tcx>,
let discr = self.find_switch_discriminant_info(bb, switch)?;
// go through each target, finding a discriminant read, and a switch
- let results = discr.targets_with_values.iter().map(|(target, value)| {
+ let results = discr.targets_with_values.iter().map(|(value, target)| {
self.find_discriminant_switch_pairing(&discr, target.clone(), value.clone())
});
}
// check that the value being matched on is the same. The
- if this_bb_discr_info.targets_with_values.iter().find(|x| x.1 == value).is_none() {
+ if this_bb_discr_info.targets_with_values.iter().find(|x| x.0 == value).is_none() {
trace!("NO: values being matched on are not the same");
return None;
}
// ```
// We check this by seeing that the value of the first discriminant is the only other discriminant value being used as a target in the second switch
if !(this_bb_discr_info.targets_with_values.len() == 1
- && this_bb_discr_info.targets_with_values[0].1 == value)
+ && this_bb_discr_info.targets_with_values[0].0 == value)
{
trace!(
"NO: The second switch did not have only 1 target (besides otherwise) that had the same value as the value from the first switch that got us here"
switch: &Terminator<'tcx>,
) -> Option<SwitchDiscriminantInfo<'tcx>> {
match &switch.kind {
- TerminatorKind::SwitchInt { discr, targets, values, .. } => {
+ TerminatorKind::SwitchInt { discr, targets, .. } => {
let discr_local = discr.place()?.as_local()?;
// the declaration of the discriminant read. Place of this read is being used in the switch
let discr_decl = &self.body.local_decls()[discr_local];
let discr_ty = discr_decl.ty;
// the otherwise target lies as the last element
- let otherwise_bb = targets.get(values.len())?.clone();
- let targets_with_values = targets
- .iter()
- .zip(values.iter())
- .map(|(t, v)| (t.clone(), v.clone()))
- .collect();
+ let otherwise_bb = targets.otherwise();
+ let targets_with_values = targets.iter().collect();
// find the place of the adt where the discriminant is being read from
// assume this is the last statement of the block
use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt};
use rustc_target::abi::VariantIdx;
use rustc_target::spec::PanicStrategy;
-use std::borrow::Cow;
use std::{iter, ops};
pub struct StateTransform;
) {
let default_block = insert_term_block(body, default);
let (assign, discr) = transform.get_discr(body);
+ let switch_targets =
+ SwitchTargets::new(cases.iter().map(|(i, bb)| ((*i) as u128, *bb)), default_block);
let switch = TerminatorKind::SwitchInt {
discr: Operand::Move(discr),
switch_ty: transform.discr_ty,
- values: Cow::from(cases.iter().map(|&(i, _)| i as u128).collect::<Vec<_>>()),
- targets: cases.iter().map(|&(_, d)| d).chain(iter::once(default_block)).collect(),
+ targets: switch_targets,
};
let source_info = SourceInfo::outermost(body.span);
*target = self.update_target(*target);
}
TerminatorKind::SwitchInt { ref mut targets, .. } => {
- for tgt in targets {
+ for tgt in targets.all_targets_mut() {
*tgt = self.update_target(*tgt);
}
}
discr: Operand::Copy(ref place) | Operand::Move(ref place),
switch_ty,
ref targets,
- ref values,
..
- } if targets.len() == 2 && values.len() == 1 && targets[0] != targets[1] => {
- (place, values[0], switch_ty, targets[0], targets[1])
+ } if targets.iter().len() == 1 => {
+ let (value, target) = targets.iter().next().unwrap();
+ if target == targets.otherwise() {
+ continue;
+ }
+ (place, value, switch_ty, target, targets.otherwise())
}
// Only optimize switch int statements
_ => continue,
TerminatorKind::SwitchInt {
discr: Operand::Constant(ref c),
switch_ty,
- ref values,
ref targets,
..
} => {
let constant = c.literal.try_eval_bits(tcx, param_env, switch_ty);
if let Some(constant) = constant {
- let (otherwise, targets) = targets.split_last().unwrap();
- let mut ret = TerminatorKind::Goto { target: *otherwise };
- for (&v, t) in values.iter().zip(targets.iter()) {
+ let otherwise = targets.otherwise();
+ let mut ret = TerminatorKind::Goto { target: otherwise };
+ for (v, t) in targets.iter() {
if v == constant {
- ret = TerminatorKind::Goto { target: *t };
+ ret = TerminatorKind::Goto { target: t };
break;
}
}
+use std::iter;
+
use super::MirPass;
use rustc_middle::{
mir::{
interpret::Scalar, BasicBlock, BinOp, Body, Operand, Place, Rvalue, Statement,
- StatementKind, TerminatorKind,
+ StatementKind, SwitchTargets, TerminatorKind,
},
ty::{Ty, TyCtxt},
};
Scalar::Ptr(_) => continue,
};
const FALSE: u128 = 0;
- let mut new_targets = opt.targets.clone();
- let first_is_false_target = opt.values[0] == FALSE;
+
+ let mut new_targets = opt.targets;
+ let first_value = new_targets.iter().next().unwrap().0;
+ let first_is_false_target = first_value == FALSE;
match opt.op {
BinOp::Eq => {
// if the assignment was Eq we want the true case to be first
if first_is_false_target {
- new_targets.swap(0, 1);
+ new_targets.all_targets_mut().swap(0, 1);
}
}
BinOp::Ne => {
// if the assignment was Ne we want the false case to be first
if !first_is_false_target {
- new_targets.swap(0, 1);
+ new_targets.all_targets_mut().swap(0, 1);
}
}
_ => unreachable!(),
}
storage_deads_to_remove.push((stmt_idx, opt.bb_idx));
// if we have StorageDeads to remove then make sure to insert them at the top of each target
- for bb_idx in new_targets.iter() {
+ for bb_idx in new_targets.all_targets() {
storage_deads_to_insert.push((
*bb_idx,
Statement {
}
}
- let terminator = bb.terminator_mut();
+ let [bb_cond, bb_otherwise] = match new_targets.all_targets() {
+ [a, b] => [*a, *b],
+ e => bug!("expected 2 switch targets, got: {:?}", e),
+ };
+
+ let targets = SwitchTargets::new(iter::once((new_value, bb_cond)), bb_otherwise);
+ let terminator = bb.terminator_mut();
terminator.kind = TerminatorKind::SwitchInt {
discr: Operand::Move(opt.to_switch_on),
switch_ty: opt.branch_value_ty,
- values: vec![new_value].into(),
- targets: new_targets,
+ targets,
};
}
.iter_enumerated()
.filter_map(|(bb_idx, bb)| {
// find switch
- let (place_switched_on, values, targets, place_switched_on_moved) = match &bb
- .terminator()
- .kind
- {
- rustc_middle::mir::TerminatorKind::SwitchInt {
- discr, values, targets, ..
- } => Some((discr.place()?, values, targets, discr.is_move())),
- _ => None,
- }?;
+ let (place_switched_on, targets, place_switched_on_moved) =
+ match &bb.terminator().kind {
+ rustc_middle::mir::TerminatorKind::SwitchInt { discr, targets, .. } => {
+ Some((discr.place()?, targets, discr.is_move()))
+ }
+ _ => None,
+ }?;
// find the statement that assigns the place being switched on
bb.statements.iter().enumerate().rev().find_map(|(stmt_idx, stmt)| {
branch_value_scalar,
branch_value_ty,
op: *op,
- values: values.clone().into_owned(),
targets: targets.clone(),
})
}
branch_value_ty: Ty<'tcx>,
/// Either Eq or Ne
op: BinOp,
- /// Current values used in the switch target. This needs to be replaced with the branch_value
- values: Vec<u128>,
/// Current targets used in the switch
- targets: Vec<BasicBlock>,
+ targets: SwitchTargets<'tcx>,
}
.iter_enumerated()
.filter_map(|(bb_idx, bb)| {
let (discr_switched_on, targets_and_values) = match &bb.terminator().kind {
- TerminatorKind::SwitchInt { targets, discr, values, .. } => {
- // if values.len() == targets.len() - 1, we need to include None where no value is present
- // such that the zip does not throw away targets. If no `otherwise` case is in targets, the zip will simply throw away the added None
- let values_extended = values.iter().map(|x|Some(*x)).chain(once(None));
- let targets_and_values:Vec<_> = targets.iter().zip(values_extended)
- .map(|(target, value)| SwitchTargetAndValue{target:*target, value})
+ TerminatorKind::SwitchInt { targets, discr, .. } => {
+ let targets_and_values: Vec<_> = targets.iter()
+ .map(|(val, target)| SwitchTargetAndValue { target, value: Some(val) })
+ .chain(once(SwitchTargetAndValue { target: targets.otherwise(), value: None }))
.collect();
- assert_eq!(targets.len(), targets_and_values.len());
- (discr, targets_and_values)},
+ (discr, targets_and_values)
+ },
_ => return None,
};
use crate::transform::MirPass;
use rustc_data_structures::stable_set::FxHashSet;
use rustc_middle::mir::{
- BasicBlock, BasicBlockData, Body, Local, Operand, Rvalue, StatementKind, TerminatorKind,
+ BasicBlock, BasicBlockData, Body, Local, Operand, Rvalue, StatementKind, SwitchTargets,
+ TerminatorKind,
};
use rustc_middle::ty::layout::TyAndLayout;
use rustc_middle::ty::{Ty, TyCtxt};
trace!("allowed_variants = {:?}", allowed_variants);
- if let TerminatorKind::SwitchInt { values, targets, .. } =
+ if let TerminatorKind::SwitchInt { targets, .. } =
&mut body.basic_blocks_mut()[bb].terminator_mut().kind
{
- // take otherwise out early
- let otherwise = targets.pop().unwrap();
- assert_eq!(targets.len(), values.len());
- let mut i = 0;
- targets.retain(|_| {
- let keep = allowed_variants.contains(&values[i]);
- i += 1;
- keep
- });
- targets.push(otherwise);
-
- values.to_mut().retain(|var| allowed_variants.contains(var));
+ let new_targets = SwitchTargets::new(
+ targets.iter().filter(|(val, _)| allowed_variants.contains(val)),
+ targets.otherwise(),
+ );
+
+ *targets = new_targets;
} else {
unreachable!()
}
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_middle::mir::*;
use rustc_middle::ty::TyCtxt;
-use std::borrow::Cow;
pub struct UnreachablePropagation;
{
let terminator = match *terminator_kind {
TerminatorKind::Goto { target } if predicate(target) => TerminatorKind::Unreachable,
- TerminatorKind::SwitchInt { ref discr, switch_ty, ref values, ref targets } => {
- let original_targets_len = targets.len();
- let (otherwise, targets) = targets.split_last().unwrap();
+ TerminatorKind::SwitchInt { ref discr, switch_ty, ref targets } => {
+ let otherwise = targets.otherwise();
+
+ let original_targets_len = targets.iter().len() + 1;
let (mut values, mut targets): (Vec<_>, Vec<_>) =
- values.iter().zip(targets.iter()).filter(|(_, &t)| !predicate(t)).unzip();
+ targets.iter().filter(|(_, bb)| !predicate(*bb)).unzip();
- if !predicate(*otherwise) {
- targets.push(*otherwise);
+ if !predicate(otherwise) {
+ targets.push(otherwise);
} else {
values.pop();
}
TerminatorKind::SwitchInt {
discr: discr.clone(),
switch_ty,
- values: Cow::from(values),
- targets,
+ targets: SwitchTargets::new(
+ values.iter().copied().zip(targets.iter().copied()),
+ *targets.last().unwrap(),
+ ),
}
} else {
return None;
TerminatorKind::Goto { target } => {
self.check_edge(location, *target, EdgeKind::Normal);
}
- TerminatorKind::SwitchInt { targets, values, switch_ty, discr } => {
+ TerminatorKind::SwitchInt { targets, switch_ty, discr } => {
let ty = discr.ty(&self.body.local_decls, self.tcx);
if ty != *switch_ty {
self.fail(
),
);
}
- if targets.len() != values.len() + 1 {
- self.fail(
- location,
- format!(
- "encountered `SwitchInt` terminator with {} values, but {} targets (should be values+1)",
- values.len(),
- targets.len(),
- ),
- );
- }
- for target in targets {
- self.check_edge(location, *target, EdgeKind::Normal);
+ for (_, target) in targets.iter() {
+ self.check_edge(location, target, EdgeKind::Normal);
}
+ self.check_edge(location, targets.otherwise(), EdgeKind::Normal);
}
TerminatorKind::Drop { target, unwind, .. } => {
self.check_edge(location, *target, EdgeKind::Normal);
kind: TerminatorKind::SwitchInt {
discr: Operand::Move(discr),
switch_ty: discr_ty,
- values: From::from(values.to_owned()),
- targets: blocks,
+ targets: SwitchTargets::new(
+ values.iter().copied().zip(blocks.iter().copied()),
+ *blocks.last().unwrap(),
+ ),
},
}),
is_cleanup: unwind.is_cleanup(),
let elem_size = Place::from(self.new_temp(tcx.types.usize));
let len = Place::from(self.new_temp(tcx.types.usize));
- static USIZE_SWITCH_ZERO: &[u128] = &[0];
+ static USIZE_SWITCH_ZERO: &[u128; 1] = &[0];
let base_block = BasicBlockData {
statements: vec![
kind: TerminatorKind::SwitchInt {
discr: move_(elem_size),
switch_ty: tcx.types.usize,
- values: From::from(USIZE_SWITCH_ZERO),
- targets: vec![
+ targets: SwitchTargets::static_if(
+ USIZE_SWITCH_ZERO,
self.drop_loop_pair(ety, false, len),
self.drop_loop_pair(ety, true, len),
- ],
+ ),
},
}),
};
let target_blocks = make_target_blocks(self);
// Variants is a BitVec of indexes into adt_def.variants.
let num_enum_variants = adt_def.variants.len();
- let used_variants = variants.count();
debug_assert_eq!(target_blocks.len(), num_enum_variants + 1);
let otherwise_block = *target_blocks.last().unwrap();
- let mut targets = Vec::with_capacity(used_variants + 1);
- let mut values = Vec::with_capacity(used_variants);
let tcx = self.hir.tcx();
- for (idx, discr) in adt_def.discriminants(tcx) {
- if variants.contains(idx) {
- debug_assert_ne!(
- target_blocks[idx.index()],
- otherwise_block,
- "no canididates for tested discriminant: {:?}",
- discr,
- );
- values.push(discr.val);
- targets.push(target_blocks[idx.index()]);
- } else {
- debug_assert_eq!(
- target_blocks[idx.index()],
- otherwise_block,
- "found canididates for untested discriminant: {:?}",
- discr,
- );
- }
- }
- targets.push(otherwise_block);
- debug!(
- "num_enum_variants: {}, tested variants: {:?}, variants: {:?}",
- num_enum_variants, values, variants
+ let switch_targets = SwitchTargets::new(
+ adt_def.discriminants(tcx).filter_map(|(idx, discr)| {
+ if variants.contains(idx) {
+ debug_assert_ne!(
+ target_blocks[idx.index()],
+ otherwise_block,
+ "no canididates for tested discriminant: {:?}",
+ discr,
+ );
+ Some((discr.val, target_blocks[idx.index()]))
+ } else {
+ debug_assert_eq!(
+ target_blocks[idx.index()],
+ otherwise_block,
+ "found canididates for untested discriminant: {:?}",
+ discr,
+ );
+ None
+ }
+ }),
+ otherwise_block,
);
+ debug!("num_enum_variants: {}, variants: {:?}", num_enum_variants, variants);
let discr_ty = adt_def.repr.discr_type().to_ty(tcx);
let discr = self.temp(discr_ty, test.span);
self.cfg.push_assign(block, source_info, discr, Rvalue::Discriminant(place));
- assert_eq!(values.len() + 1, targets.len());
self.cfg.terminate(
block,
source_info,
TerminatorKind::SwitchInt {
discr: Operand::Move(discr),
switch_ty: discr_ty,
- values: From::from(values),
- targets,
+ targets: switch_targets,
},
);
}
} else {
// The switch may be inexhaustive so we have a catch all block
debug_assert_eq!(options.len() + 1, target_blocks.len());
+ let otherwise_block = *target_blocks.last().unwrap();
+ let switch_targets = SwitchTargets::new(
+ options.values().copied().zip(target_blocks),
+ otherwise_block,
+ );
TerminatorKind::SwitchInt {
discr: Operand::Copy(place),
switch_ty,
- values: options.values().copied().collect(),
- targets: target_blocks,
+ targets: switch_targets,
}
};
self.cfg.terminate(block, source_info, terminator);
TerminatorKind::SwitchInt {
discr,
switch_ty: _,
- values: _,
targets: _,
} => check_operand(tcx, discr, span, body),