Refactor how SwitchInt stores jump targets
This commit is contained in:
parent
cae8bc1f23
commit
432535da2b
22 changed files with 247 additions and 206 deletions
|
@ -16,6 +16,87 @@ use std::slice;
|
|||
|
||||
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.
|
||||
|
@ -32,23 +113,7 @@ pub enum TerminatorKind<'tcx> {
|
|||
/// 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
|
||||
|
@ -227,12 +292,10 @@ impl<'tcx> TerminatorKind<'tcx> {
|
|||
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),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -263,7 +326,7 @@ impl<'tcx> TerminatorKind<'tcx> {
|
|||
| 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))
|
||||
}
|
||||
|
@ -297,7 +360,7 @@ impl<'tcx> TerminatorKind<'tcx> {
|
|||
| 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))
|
||||
}
|
||||
|
@ -469,11 +532,12 @@ impl<'tcx> TerminatorKind<'tcx> {
|
|||
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)
|
||||
|
|
|
@ -21,10 +21,9 @@ impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> {
|
|||
|
||||
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 } => {
|
||||
|
|
|
@ -453,7 +453,6 @@ macro_rules! make_mir_visitor {
|
|||
TerminatorKind::SwitchInt {
|
||||
discr,
|
||||
switch_ty,
|
||||
values: _,
|
||||
targets: _
|
||||
} => {
|
||||
self.visit_operand(discr, location);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue