Change SwitchTarget representation

The new structure encodes its invariant, which reduces the likelihood
of having an inconsistent representation. It is also more intuitive and
user friendly.

I encapsulated the structure for now in case we decide to change it back.
This commit is contained in:
Celina G. Val 2023-11-29 12:49:32 -08:00
parent 07921b50ba
commit 3e0b2fac5d
4 changed files with 72 additions and 44 deletions

View File

@ -579,13 +579,12 @@ fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
mir::TerminatorKind::SwitchInt { discr, targets } => TerminatorKind::SwitchInt { mir::TerminatorKind::SwitchInt { discr, targets } => TerminatorKind::SwitchInt {
discr: discr.stable(tables), discr: discr.stable(tables),
targets: { targets: {
let (value_vec, mut target_vec): (Vec<_>, Vec<_>) = let branches = targets.iter().map(|(val, target)| (val, target.as_usize()));
targets.iter().map(|(value, target)| (value, target.as_usize())).unzip(); stable_mir::mir::SwitchTargets::new(
// We need to push otherwise as last element to ensure it's same as in MIR. branches.collect(),
target_vec.push(targets.otherwise().as_usize()); targets.otherwise().as_usize(),
stable_mir::mir::SwitchTargets { value: value_vec, targets: target_vec } )
}, },
otherwise: targets.otherwise().as_usize(),
}, },
mir::TerminatorKind::UnwindResume => TerminatorKind::Resume, mir::TerminatorKind::UnwindResume => TerminatorKind::Resume,
mir::TerminatorKind::UnwindTerminate(_) => TerminatorKind::Abort, mir::TerminatorKind::UnwindTerminate(_) => TerminatorKind::Abort,

View File

@ -3,7 +3,7 @@
AdtDef, ClosureDef, Const, CoroutineDef, GenericArgs, Movability, Region, RigidTy, Ty, TyKind, AdtDef, ClosureDef, Const, CoroutineDef, GenericArgs, Movability, Region, RigidTy, Ty, TyKind,
}; };
use crate::{Error, Opaque, Span, Symbol}; use crate::{Error, Opaque, Span, Symbol};
use std::{io, slice}; use std::io;
/// The SMIR representation of a single function. /// The SMIR representation of a single function.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Body { pub struct Body {
@ -23,6 +23,8 @@ pub struct Body {
pub(super) var_debug_info: Vec<VarDebugInfo>, pub(super) var_debug_info: Vec<VarDebugInfo>,
} }
pub type BasicBlockIdx = usize;
impl Body { impl Body {
/// Constructs a `Body`. /// Constructs a `Body`.
/// ///
@ -114,22 +116,21 @@ pub struct Terminator {
} }
impl Terminator { impl Terminator {
pub fn successors(&self) -> Successors<'_> { pub fn successors(&self) -> Successors {
self.kind.successors() self.kind.successors()
} }
} }
pub type Successors<'a> = impl Iterator<Item = usize> + 'a; pub type Successors = Vec<BasicBlockIdx>;
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
pub enum TerminatorKind { pub enum TerminatorKind {
Goto { Goto {
target: usize, target: BasicBlockIdx,
}, },
SwitchInt { SwitchInt {
discr: Operand, discr: Operand,
targets: SwitchTargets, targets: SwitchTargets,
otherwise: usize,
}, },
Resume, Resume,
Abort, Abort,
@ -137,43 +138,42 @@ pub enum TerminatorKind {
Unreachable, Unreachable,
Drop { Drop {
place: Place, place: Place,
target: usize, target: BasicBlockIdx,
unwind: UnwindAction, unwind: UnwindAction,
}, },
Call { Call {
func: Operand, func: Operand,
args: Vec<Operand>, args: Vec<Operand>,
destination: Place, destination: Place,
target: Option<usize>, target: Option<BasicBlockIdx>,
unwind: UnwindAction, unwind: UnwindAction,
}, },
Assert { Assert {
cond: Operand, cond: Operand,
expected: bool, expected: bool,
msg: AssertMessage, msg: AssertMessage,
target: usize, target: BasicBlockIdx,
unwind: UnwindAction, unwind: UnwindAction,
}, },
CoroutineDrop,
InlineAsm { InlineAsm {
template: String, template: String,
operands: Vec<InlineAsmOperand>, operands: Vec<InlineAsmOperand>,
options: String, options: String,
line_spans: String, line_spans: String,
destination: Option<usize>, destination: Option<BasicBlockIdx>,
unwind: UnwindAction, unwind: UnwindAction,
}, },
} }
impl TerminatorKind { impl TerminatorKind {
pub fn successors(&self) -> Successors<'_> { pub fn successors(&self) -> Successors {
use self::TerminatorKind::*; use self::TerminatorKind::*;
match *self { match *self {
Call { target: Some(t), unwind: UnwindAction::Cleanup(ref u), .. } Call { target: Some(t), unwind: UnwindAction::Cleanup(u), .. }
| Drop { target: t, unwind: UnwindAction::Cleanup(ref u), .. } | Drop { target: t, unwind: UnwindAction::Cleanup(u), .. }
| Assert { target: t, unwind: UnwindAction::Cleanup(ref u), .. } | Assert { target: t, unwind: UnwindAction::Cleanup(u), .. }
| InlineAsm { destination: Some(t), unwind: UnwindAction::Cleanup(ref u), .. } => { | InlineAsm { destination: Some(t), unwind: UnwindAction::Cleanup(u), .. } => {
Some(t).into_iter().chain(slice::from_ref(u).into_iter().copied()) vec![t, u]
} }
Goto { target: t } Goto { target: t }
| Call { target: None, unwind: UnwindAction::Cleanup(t), .. } | Call { target: None, unwind: UnwindAction::Cleanup(t), .. }
@ -182,21 +182,18 @@ pub fn successors(&self) -> Successors<'_> {
| Assert { target: t, unwind: _, .. } | Assert { target: t, unwind: _, .. }
| InlineAsm { destination: None, unwind: UnwindAction::Cleanup(t), .. } | InlineAsm { destination: None, unwind: UnwindAction::Cleanup(t), .. }
| InlineAsm { destination: Some(t), unwind: _, .. } => { | InlineAsm { destination: Some(t), unwind: _, .. } => {
Some(t).into_iter().chain((&[]).into_iter().copied()) vec![t]
} }
CoroutineDrop Return
| Return
| Resume | Resume
| Abort | Abort
| Unreachable | Unreachable
| Call { target: None, unwind: _, .. } | Call { target: None, unwind: _, .. }
| InlineAsm { destination: None, unwind: _, .. } => { | InlineAsm { destination: None, unwind: _, .. } => {
None.into_iter().chain((&[]).into_iter().copied()) vec![]
}
SwitchInt { ref targets, .. } => {
None.into_iter().chain(targets.targets.iter().copied())
} }
SwitchInt { ref targets, .. } => targets.all_targets(),
} }
} }
@ -205,7 +202,6 @@ pub fn unwind(&self) -> Option<&UnwindAction> {
TerminatorKind::Goto { .. } TerminatorKind::Goto { .. }
| TerminatorKind::Return | TerminatorKind::Return
| TerminatorKind::Unreachable | TerminatorKind::Unreachable
| TerminatorKind::CoroutineDrop
| TerminatorKind::Resume | TerminatorKind::Resume
| TerminatorKind::Abort | TerminatorKind::Abort
| TerminatorKind::SwitchInt { .. } => None, | TerminatorKind::SwitchInt { .. } => None,
@ -231,7 +227,7 @@ pub enum UnwindAction {
Continue, Continue,
Unreachable, Unreachable,
Terminate, Terminate,
Cleanup(usize), Cleanup(BasicBlockIdx),
} }
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
@ -662,10 +658,45 @@ pub struct Constant {
pub literal: Const, pub literal: Const,
} }
/// The possible branch sites of a [TerminatorKind::SwitchInt].
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
pub struct SwitchTargets { pub struct SwitchTargets {
pub value: Vec<u128>, /// The conditional branches where the first element represents the value that guards this
pub targets: Vec<usize>, /// branch, and the second element is the branch target.
branches: Vec<(u128, BasicBlockIdx)>,
/// The `otherwise` branch which will be taken in case none of the conditional branches are
/// satisfied.
otherwise: BasicBlockIdx,
}
impl SwitchTargets {
/// All possible targets including the `otherwise` target.
pub fn all_targets(&self) -> Successors {
Some(self.otherwise)
.into_iter()
.chain(self.branches.iter().map(|(_, target)| *target))
.collect()
}
/// The `otherwise` branch target.
pub fn otherwise(&self) -> BasicBlockIdx {
self.otherwise
}
/// The conditional targets which are only taken if the pattern matches the given value.
pub fn branches(&self) -> impl Iterator<Item = (u128, BasicBlockIdx)> + '_ {
self.branches.iter().copied()
}
/// The number of targets including `otherwise`.
pub fn len(&self) -> usize {
self.branches.len() + 1
}
/// Create a new SwitchTargets from the given branches and `otherwise` target.
pub fn new(branches: Vec<(u128, BasicBlockIdx)>, otherwise: BasicBlockIdx) -> SwitchTargets {
SwitchTargets { branches, otherwise }
}
} }
#[derive(Copy, Clone, Debug, Eq, PartialEq)] #[derive(Copy, Clone, Debug, Eq, PartialEq)]

View File

@ -76,7 +76,8 @@ pub fn pretty_statement(statement: &StatementKind) -> String {
pub fn pretty_terminator<W: io::Write>(terminator: &TerminatorKind, w: &mut W) -> io::Result<()> { pub fn pretty_terminator<W: io::Write>(terminator: &TerminatorKind, w: &mut W) -> io::Result<()> {
write!(w, "{}", pretty_terminator_head(terminator))?; write!(w, "{}", pretty_terminator_head(terminator))?;
let successor_count = terminator.successors().count(); let successors = terminator.successors();
let successor_count = successors.len();
let labels = pretty_successor_labels(terminator); let labels = pretty_successor_labels(terminator);
let show_unwind = !matches!(terminator.unwind(), None | Some(UnwindAction::Cleanup(_))); let show_unwind = !matches!(terminator.unwind(), None | Some(UnwindAction::Cleanup(_)));
@ -98,12 +99,12 @@ pub fn pretty_terminator<W: io::Write>(terminator: &TerminatorKind, w: &mut W) -
Ok(()) Ok(())
} }
(1, false) => { (1, false) => {
write!(w, " -> {:?}", terminator.successors().next().unwrap())?; write!(w, " -> {:?}", successors[0])?;
Ok(()) Ok(())
} }
_ => { _ => {
write!(w, " -> [")?; write!(w, " -> [")?;
for (i, target) in terminator.successors().enumerate() { for (i, target) in successors.iter().enumerate() {
if i > 0 { if i > 0 {
write!(w, ", ")?; write!(w, ", ")?;
} }
@ -157,7 +158,6 @@ pub fn pretty_terminator_head(terminator: &TerminatorKind) -> String {
pretty.push_str(")"); pretty.push_str(")");
pretty pretty
} }
CoroutineDrop => format!(" coroutine_drop"),
InlineAsm { .. } => todo!(), InlineAsm { .. } => todo!(),
} }
} }
@ -165,12 +165,11 @@ pub fn pretty_terminator_head(terminator: &TerminatorKind) -> String {
pub fn pretty_successor_labels(terminator: &TerminatorKind) -> Vec<String> { pub fn pretty_successor_labels(terminator: &TerminatorKind) -> Vec<String> {
use self::TerminatorKind::*; use self::TerminatorKind::*;
match terminator { match terminator {
Resume | Abort | Return | Unreachable | CoroutineDrop => vec![], Resume | Abort | Return | Unreachable => vec![],
Goto { .. } => vec!["".to_string()], Goto { .. } => vec!["".to_string()],
SwitchInt { targets, .. } => targets SwitchInt { targets, .. } => targets
.value .branches()
.iter() .map(|(val, _target)| format!("{val}"))
.map(|target| format!("{}", target))
.chain(iter::once("otherwise".into())) .chain(iter::once("otherwise".into()))
.collect(), .collect(),
Drop { unwind: UnwindAction::Cleanup(_), .. } => vec!["return".into(), "unwind".into()], Drop { unwind: UnwindAction::Cleanup(_), .. } => vec!["return".into(), "unwind".into()],

View File

@ -237,8 +237,7 @@ fn super_terminator(&mut self, term: &Terminator, location: Location) {
TerminatorKind::Goto { .. } TerminatorKind::Goto { .. }
| TerminatorKind::Resume | TerminatorKind::Resume
| TerminatorKind::Abort | TerminatorKind::Abort
| TerminatorKind::Unreachable | TerminatorKind::Unreachable => {}
| TerminatorKind::CoroutineDrop => {}
TerminatorKind::Assert { cond, expected: _, msg, target: _, unwind: _ } => { TerminatorKind::Assert { cond, expected: _, msg, target: _, unwind: _ } => {
self.visit_operand(cond, location); self.visit_operand(cond, location);
self.visit_assert_msg(msg, location); self.visit_assert_msg(msg, location);
@ -268,7 +267,7 @@ fn super_terminator(&mut self, term: &Terminator, location: Location) {
let local = RETURN_LOCAL; let local = RETURN_LOCAL;
self.visit_local(&local, PlaceContext::NON_MUTATING, location); self.visit_local(&local, PlaceContext::NON_MUTATING, location);
} }
TerminatorKind::SwitchInt { discr, targets: _, otherwise: _ } => { TerminatorKind::SwitchInt { discr, targets: _ } => {
self.visit_operand(discr, location); self.visit_operand(discr, location);
} }
} }