implemented separate_const_switch MIR opt

un-update itertools

improve predecessor amount short-circuiting

cleanup and comments

somewhat improved drawing
This commit is contained in:
Théo Degioanni 2021-06-10 00:34:58 +02:00
parent b8be3162d7
commit a77e2ad533
9 changed files with 1055 additions and 0 deletions

View File

@ -48,6 +48,7 @@ pub mod remove_unneeded_drops;
pub mod remove_zsts;
pub mod required_consts;
pub mod rustc_peek;
pub mod separate_const_switch;
pub mod simplify;
pub mod simplify_branches;
pub mod simplify_comparison_integral;
@ -501,6 +502,7 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
// inst combine is after MatchBranchSimplification to clean up Ne(_1, false)
&multiple_return_terminators::MultipleReturnTerminators,
&instcombine::InstCombine,
&separate_const_switch::SeparateConstSwitch,
&const_prop::ConstProp,
&simplify_branches::SimplifyBranches::new("after-const-prop"),
&early_otherwise_branch::EarlyOtherwiseBranch,

View File

@ -0,0 +1,343 @@
//! A pass that duplicates switch-terminated blocks
//! into a new copy for each predecessor, provided
//! the predecessor sets the value being switched
//! over to a constant.
//!
//! The purpose of this pass is to help constant
//! propagation passes to simplify the switch terminator
//! of the copied blocks into gotos when some predecessors
//! statically determine the output of switches.
//!
//! ```text
//! x = 12 --- ---> something
//! \ / 12
//! --> switch x
//! / \ otherwise
//! x = y --- ---> something else
//! ```
//! becomes
//! ```text
//! x = 12 ---> switch x ------> something
//! \ / 12
//! X
//! / \ otherwise
//! x = y ---> switch x ------> something else
//! ```
//! so it can hopefully later be turned by another pass into
//! ```text
//! x = 12 --------------------> something
//! / 12
//! /
//! / otherwise
//! x = y ---- switch x ------> something else
//! ```
//!
//! This optimization is meant to cover simple cases
//! like `?` desugaring. For now, it thus focuses on
//! simplicity rather than completeness (it notably
//! sometimes duplicates abusively).
use crate::transform::MirPass;
use rustc_middle::mir::*;
use rustc_middle::ty::TyCtxt;
use smallvec::SmallVec;
pub struct SeparateConstSwitch;
impl<'tcx> MirPass<'tcx> for SeparateConstSwitch {
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
if tcx.sess.mir_opt_level() < 4 {
return;
}
// If execution did something, applying a simplification layer
// helps later passes optimize the copy away.
if separate_const_switch(body) > 0 {
super::simplify::simplify_cfg(tcx, body);
}
}
}
/// Returns the amount of blocks that were duplicated
pub fn separate_const_switch<'tcx>(body: &mut Body<'tcx>) -> usize {
let mut new_blocks: SmallVec<[(BasicBlock, BasicBlock); 6]> = SmallVec::new();
let predecessors = body.predecessors();
'block_iter: for (block_id, block) in body.basic_blocks().iter_enumerated() {
if let TerminatorKind::SwitchInt {
discr: Operand::Copy(switch_place) | Operand::Move(switch_place),
..
} = block.terminator().kind
{
// If the block is on an unwind path, do not
// apply the optimization as unwind paths
// rely on a unique parent invariant
if block.is_cleanup {
continue 'block_iter;
}
// If the block has fewer than 2 predecessors, ignore it
// we could maybe chain blocks that have exactly one
// predecessor, but for now we ignore that
if predecessors[block_id].len() < 2 {
continue 'block_iter;
}
// First, let's find a non-const place
// that determines the result of the switch
if let Some(switch_place) = find_determining_place(switch_place, block) {
// We now have an input place for which it would
// be interesting if predecessors assigned it from a const
let mut predecessors_left = predecessors[block_id].len();
'predec_iter: for predecessor_id in predecessors[block_id].iter().copied() {
let predecessor = &body.basic_blocks()[predecessor_id];
// First we make sure the predecessor jumps
// in a reasonable way
match &predecessor.terminator().kind {
// The following terminators are
// unconditionally valid
TerminatorKind::Goto { .. } | TerminatorKind::SwitchInt { .. } => {}
TerminatorKind::FalseEdge { real_target, .. } => {
if *real_target != block_id {
continue 'predec_iter;
}
}
// The following terminators are not allowed
TerminatorKind::Resume
| TerminatorKind::Drop { .. }
| TerminatorKind::DropAndReplace { .. }
| TerminatorKind::Call { .. }
| TerminatorKind::Assert { .. }
| TerminatorKind::FalseUnwind { .. }
| TerminatorKind::Yield { .. }
| TerminatorKind::Abort
| TerminatorKind::Return
| TerminatorKind::Unreachable
| TerminatorKind::InlineAsm { .. }
| TerminatorKind::GeneratorDrop => {
continue 'predec_iter;
}
}
if is_likely_const(switch_place, predecessor) {
new_blocks.push((predecessor_id, block_id));
predecessors_left -= 1;
if predecessors_left < 2 {
// If the original block only has one predecessor left,
// we have nothing left to do
break 'predec_iter;
}
}
}
}
}
}
// Once the analysis is done, perform the duplication
let body_span = body.span;
let copied_blocks = new_blocks.len();
let blocks = body.basic_blocks_mut();
for (pred_id, target_id) in new_blocks {
let new_block = blocks[target_id].clone();
let new_block_id = blocks.push(new_block);
let terminator = blocks[pred_id].terminator_mut();
match terminator.kind {
TerminatorKind::Goto { ref mut target } => {
*target = new_block_id;
}
TerminatorKind::FalseEdge { ref mut real_target, .. } => {
if *real_target == target_id {
*real_target = new_block_id;
}
}
TerminatorKind::SwitchInt { ref mut targets, .. } => {
targets.all_targets_mut().iter_mut().for_each(|x| {
if *x == target_id {
*x = new_block_id;
}
});
}
TerminatorKind::Resume
| TerminatorKind::Abort
| TerminatorKind::Return
| TerminatorKind::Unreachable
| TerminatorKind::GeneratorDrop
| TerminatorKind::Assert { .. }
| TerminatorKind::DropAndReplace { .. }
| TerminatorKind::FalseUnwind { .. }
| TerminatorKind::Drop { .. }
| TerminatorKind::Call { .. }
| TerminatorKind::InlineAsm { .. }
| TerminatorKind::Yield { .. } => {
span_bug!(
body_span,
"basic block terminator had unexpected kind {:?}",
&terminator.kind
)
}
}
}
copied_blocks
}
/// This function describes a rough heuristic guessing
/// whether a place is last set with a const within the block.
/// Notably, it will be overly pessimistic in cases that are already
/// not handled by `separate_const_switch`.
fn is_likely_const<'tcx>(mut tracked_place: Place<'tcx>, block: &BasicBlockData<'tcx>) -> bool {
for statement in block.statements.iter().rev() {
match &statement.kind {
StatementKind::Assign(assign) => {
if assign.0 == tracked_place {
match assign.1 {
// These rvalues are definitely constant
Rvalue::Use(Operand::Constant(_))
| Rvalue::Ref(_, _, _)
| Rvalue::AddressOf(_, _)
| Rvalue::Cast(_, Operand::Constant(_), _)
| Rvalue::NullaryOp(_, _)
| Rvalue::UnaryOp(_, Operand::Constant(_)) => return true,
// These rvalues make things ambiguous
Rvalue::Repeat(_, _)
| Rvalue::ThreadLocalRef(_)
| Rvalue::Len(_)
| Rvalue::BinaryOp(_, _)
| Rvalue::CheckedBinaryOp(_, _)
| Rvalue::Aggregate(_, _) => return false,
// These rvalues move the place to track
Rvalue::Cast(_, Operand::Copy(place) | Operand::Move(place), _)
| Rvalue::Use(Operand::Copy(place) | Operand::Move(place))
| Rvalue::UnaryOp(_, Operand::Copy(place) | Operand::Move(place))
| Rvalue::Discriminant(place) => tracked_place = place,
}
}
}
// If the discriminant is set, it is always set
// as a constant, so the job is done.
// As we are **ignoring projections**, if the place
// we are tracking sees its discriminant be set,
// that means we had to be tracking the discriminant
// specifically (as it is impossible to switch over
// an enum directly, and if we were switching over
// its content, we would have had to at least cast it to
// some variant first)
StatementKind::SetDiscriminant { place, .. } => {
if **place == tracked_place {
return true;
}
}
// If inline assembly is found, we probably should
// not try to analyze the code
StatementKind::LlvmInlineAsm(_) => return false,
// These statements have no influence on the place
// we are interested in
StatementKind::FakeRead(_)
| StatementKind::StorageLive(_)
| StatementKind::Retag(_, _)
| StatementKind::AscribeUserType(_, _)
| StatementKind::Coverage(_)
| StatementKind::StorageDead(_)
| StatementKind::CopyNonOverlapping(_)
| StatementKind::Nop => {}
}
}
// If no good reason for the place to be const is found,
// give up. We could maybe go up predecessors, but in
// most cases giving up now should be sufficient.
false
}
/// Finds a unique place that entirely determines the value
/// of `switch_place`, if it exists. This is only a heuristic.
/// Ideally we would like to track multiple determining places
/// for some edge cases, but one is enough for a lot of situations.
fn find_determining_place<'tcx>(
mut switch_place: Place<'tcx>,
block: &BasicBlockData<'tcx>,
) -> Option<Place<'tcx>> {
for statement in block.statements.iter().rev() {
match &statement.kind {
StatementKind::Assign(op) => {
if op.0 != switch_place {
continue;
}
match op.1 {
// The following rvalues move the place
// that may be const in the predecessor
Rvalue::Use(Operand::Move(new) | Operand::Copy(new))
| Rvalue::UnaryOp(_, Operand::Copy(new) | Operand::Move(new))
| Rvalue::Cast(_, Operand::Move(new) | Operand::Copy(new), _)
| Rvalue::Repeat(Operand::Move(new) | Operand::Copy(new), _)
| Rvalue::Discriminant(new)
=> switch_place = new,
// The following rvalues might still make the block
// be valid but for now we reject them
Rvalue::Len(_)
| Rvalue::Ref(_, _, _)
| Rvalue::BinaryOp(_, _)
| Rvalue::CheckedBinaryOp(_, _)
| Rvalue::Aggregate(_, _)
// The following rvalues definitely mean we cannot
// or should not apply this optimization
| Rvalue::Use(Operand::Constant(_))
| Rvalue::Repeat(Operand::Constant(_), _)
| Rvalue::ThreadLocalRef(_)
| Rvalue::AddressOf(_, _)
| Rvalue::NullaryOp(_, _)
| Rvalue::UnaryOp(_, Operand::Constant(_))
| Rvalue::Cast(_, Operand::Constant(_), _)
=> return None,
}
}
// These statements have no influence on the place
// we are interested in
StatementKind::FakeRead(_)
| StatementKind::StorageLive(_)
| StatementKind::StorageDead(_)
| StatementKind::Retag(_, _)
| StatementKind::AscribeUserType(_, _)
| StatementKind::Coverage(_)
| StatementKind::CopyNonOverlapping(_)
| StatementKind::Nop => {}
// If inline assembly is found, we probably should
// not try to analyze the code
StatementKind::LlvmInlineAsm(_) => return None,
// If the discriminant is set, it is always set
// as a constant, so the job is already done.
// As we are **ignoring projections**, if the place
// we are tracking sees its discriminant be set,
// that means we had to be tracking the discriminant
// specifically (as it is impossible to switch over
// an enum directly, and if we were switching over
// its content, we would have had to at least cast it to
// some variant first)
StatementKind::SetDiscriminant { place, .. } => {
if **place == switch_place {
return None;
}
}
}
}
Some(switch_place)
}

View File

@ -0,0 +1,140 @@
- // MIR for `identity` before ConstProp
+ // MIR for `identity` after ConstProp
fn identity(_1: Result<i32, i32>) -> Result<i32, i32> {
debug x => _1; // in scope 0 at $DIR/separate_const_switch.rs:28:13: 28:14
let mut _0: std::result::Result<i32, i32>; // return place in scope 0 at $DIR/separate_const_switch.rs:28:37: 28:53
let mut _2: i32; // in scope 0 at $DIR/separate_const_switch.rs:29:8: 29:10
let mut _3: std::ops::ControlFlow<std::result::Result<std::convert::Infallible, i32>, i32>; // in scope 0 at $DIR/separate_const_switch.rs:29:8: 29:10
let mut _4: std::result::Result<i32, i32>; // in scope 0 at $DIR/separate_const_switch.rs:29:8: 29:9
let mut _5: isize; // in scope 0 at $DIR/separate_const_switch.rs:29:9: 29:10
let _6: std::result::Result<std::convert::Infallible, i32>; // in scope 0 at $DIR/separate_const_switch.rs:29:9: 29:10
let mut _7: !; // in scope 0 at $DIR/separate_const_switch.rs:29:9: 29:10
let mut _8: std::result::Result<std::convert::Infallible, i32>; // in scope 0 at $DIR/separate_const_switch.rs:29:9: 29:10
let _9: i32; // in scope 0 at $DIR/separate_const_switch.rs:29:8: 29:10
scope 1 {
debug residual => _6; // in scope 1 at $DIR/separate_const_switch.rs:29:9: 29:10
scope 2 {
scope 8 (inlined <Result<i32, i32> as FromResidual<Result<Infallible, i32>>>::from_residual) { // at $DIR/separate_const_switch.rs:29:8: 29:10
debug residual => _8; // in scope 8 at $DIR/separate_const_switch.rs:29:8: 29:10
let _16: i32; // in scope 8 at $DIR/separate_const_switch.rs:29:8: 29:10
let mut _17: i32; // in scope 8 at $DIR/separate_const_switch.rs:29:8: 29:10
let mut _18: i32; // in scope 8 at $DIR/separate_const_switch.rs:29:8: 29:10
scope 9 {
debug e => _16; // in scope 9 at $DIR/separate_const_switch.rs:29:8: 29:10
scope 10 (inlined <i32 as From<i32>>::from) { // at $DIR/separate_const_switch.rs:29:8: 29:10
debug t => _18; // in scope 10 at $DIR/separate_const_switch.rs:29:8: 29:10
}
}
}
}
}
scope 3 {
debug val => _9; // in scope 3 at $DIR/separate_const_switch.rs:29:8: 29:10
scope 4 {
}
}
scope 5 (inlined <Result<i32, i32> as Try>::branch) { // at $DIR/separate_const_switch.rs:29:8: 29:10
debug self => _4; // in scope 5 at $DIR/separate_const_switch.rs:29:8: 29:10
let mut _10: isize; // in scope 5 at $DIR/separate_const_switch.rs:29:8: 29:10
let _11: i32; // in scope 5 at $DIR/separate_const_switch.rs:29:8: 29:10
let mut _12: i32; // in scope 5 at $DIR/separate_const_switch.rs:29:8: 29:10
let _13: i32; // in scope 5 at $DIR/separate_const_switch.rs:29:8: 29:10
let mut _14: std::result::Result<std::convert::Infallible, i32>; // in scope 5 at $DIR/separate_const_switch.rs:29:8: 29:10
let mut _15: i32; // in scope 5 at $DIR/separate_const_switch.rs:29:8: 29:10
scope 6 {
debug v => _11; // in scope 6 at $DIR/separate_const_switch.rs:29:8: 29:10
}
scope 7 {
debug e => _13; // in scope 7 at $DIR/separate_const_switch.rs:29:8: 29:10
}
}
bb0: {
StorageLive(_2); // scope 0 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageLive(_3); // scope 0 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageLive(_4); // scope 0 at $DIR/separate_const_switch.rs:29:8: 29:9
_4 = _1; // scope 0 at $DIR/separate_const_switch.rs:29:8: 29:9
StorageLive(_10); // scope 0 at $DIR/separate_const_switch.rs:29:8: 29:10
_10 = discriminant(_4); // scope 5 at $DIR/separate_const_switch.rs:29:8: 29:10
switchInt(move _10) -> [0_isize: bb5, 1_isize: bb3, otherwise: bb4]; // scope 5 at $DIR/separate_const_switch.rs:29:8: 29:10
}
bb1: {
StorageLive(_9); // scope 0 at $DIR/separate_const_switch.rs:29:8: 29:10
_9 = ((_3 as Continue).0: i32); // scope 0 at $DIR/separate_const_switch.rs:29:8: 29:10
_2 = _9; // scope 4 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageDead(_9); // scope 0 at $DIR/separate_const_switch.rs:29:9: 29:10
((_0 as Ok).0: i32) = move _2; // scope 0 at $DIR/separate_const_switch.rs:29:5: 29:11
discriminant(_0) = 0; // scope 0 at $DIR/separate_const_switch.rs:29:5: 29:11
StorageDead(_2); // scope 0 at $DIR/separate_const_switch.rs:29:10: 29:11
StorageDead(_3); // scope 0 at $DIR/separate_const_switch.rs:30:1: 30:2
return; // scope 0 at $DIR/separate_const_switch.rs:30:2: 30:2
}
bb2: {
StorageLive(_6); // scope 0 at $DIR/separate_const_switch.rs:29:9: 29:10
_6 = ((_3 as Break).0: std::result::Result<std::convert::Infallible, i32>); // scope 0 at $DIR/separate_const_switch.rs:29:9: 29:10
StorageLive(_8); // scope 2 at $DIR/separate_const_switch.rs:29:9: 29:10
_8 = _6; // scope 2 at $DIR/separate_const_switch.rs:29:9: 29:10
StorageLive(_16); // scope 8 at $DIR/separate_const_switch.rs:29:8: 29:10
_16 = move ((_8 as Err).0: i32); // scope 8 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageLive(_17); // scope 9 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageLive(_18); // scope 9 at $DIR/separate_const_switch.rs:29:8: 29:10
_18 = move _16; // scope 9 at $DIR/separate_const_switch.rs:29:8: 29:10
_17 = move _18; // scope 10 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageDead(_18); // scope 9 at $DIR/separate_const_switch.rs:29:8: 29:10
((_0 as Err).0: i32) = move _17; // scope 9 at $DIR/separate_const_switch.rs:29:8: 29:10
discriminant(_0) = 1; // scope 9 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageDead(_17); // scope 9 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageDead(_16); // scope 8 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageDead(_8); // scope 2 at $DIR/separate_const_switch.rs:29:9: 29:10
StorageDead(_6); // scope 0 at $DIR/separate_const_switch.rs:29:9: 29:10
StorageDead(_2); // scope 0 at $DIR/separate_const_switch.rs:29:10: 29:11
StorageDead(_3); // scope 0 at $DIR/separate_const_switch.rs:30:1: 30:2
return; // scope 0 at $DIR/separate_const_switch.rs:30:2: 30:2
}
bb3: {
StorageLive(_13); // scope 5 at $DIR/separate_const_switch.rs:29:8: 29:10
_13 = move ((_4 as Err).0: i32); // scope 5 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageLive(_14); // scope 7 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageLive(_15); // scope 7 at $DIR/separate_const_switch.rs:29:8: 29:10
_15 = move _13; // scope 7 at $DIR/separate_const_switch.rs:29:8: 29:10
((_14 as Err).0: i32) = move _15; // scope 7 at $DIR/separate_const_switch.rs:29:8: 29:10
discriminant(_14) = 1; // scope 7 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageDead(_15); // scope 7 at $DIR/separate_const_switch.rs:29:8: 29:10
((_3 as Break).0: std::result::Result<std::convert::Infallible, i32>) = move _14; // scope 7 at $DIR/separate_const_switch.rs:29:8: 29:10
discriminant(_3) = 1; // scope 7 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageDead(_14); // scope 7 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageDead(_13); // scope 5 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageDead(_10); // scope 0 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageDead(_4); // scope 0 at $DIR/separate_const_switch.rs:29:9: 29:10
- _5 = discriminant(_3); // scope 0 at $DIR/separate_const_switch.rs:29:9: 29:10
- switchInt(move _5) -> [0_isize: bb1, otherwise: bb2]; // scope 0 at $DIR/separate_const_switch.rs:29:9: 29:10
+ _5 = const 1_isize; // scope 0 at $DIR/separate_const_switch.rs:29:9: 29:10
+ switchInt(const 1_isize) -> [0_isize: bb1, otherwise: bb2]; // scope 0 at $DIR/separate_const_switch.rs:29:9: 29:10
}
bb4: {
unreachable; // scope 5 at $DIR/separate_const_switch.rs:29:8: 29:10
}
bb5: {
StorageLive(_11); // scope 5 at $DIR/separate_const_switch.rs:29:8: 29:10
_11 = move ((_4 as Ok).0: i32); // scope 5 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageLive(_12); // scope 6 at $DIR/separate_const_switch.rs:29:8: 29:10
_12 = move _11; // scope 6 at $DIR/separate_const_switch.rs:29:8: 29:10
((_3 as Continue).0: i32) = move _12; // scope 6 at $DIR/separate_const_switch.rs:29:8: 29:10
discriminant(_3) = 0; // scope 6 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageDead(_12); // scope 6 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageDead(_11); // scope 5 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageDead(_10); // scope 0 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageDead(_4); // scope 0 at $DIR/separate_const_switch.rs:29:9: 29:10
- _5 = discriminant(_3); // scope 0 at $DIR/separate_const_switch.rs:29:9: 29:10
- switchInt(move _5) -> [0_isize: bb1, otherwise: bb2]; // scope 0 at $DIR/separate_const_switch.rs:29:9: 29:10
+ _5 = const 0_isize; // scope 0 at $DIR/separate_const_switch.rs:29:9: 29:10
+ switchInt(const 0_isize) -> [0_isize: bb1, otherwise: bb2]; // scope 0 at $DIR/separate_const_switch.rs:29:9: 29:10
}
}

View File

@ -0,0 +1,122 @@
// MIR for `identity` after PreCodegen
fn identity(_1: Result<i32, i32>) -> Result<i32, i32> {
debug x => _1; // in scope 0 at $DIR/separate_const_switch.rs:28:13: 28:14
let mut _0: std::result::Result<i32, i32>; // return place in scope 0 at $DIR/separate_const_switch.rs:28:37: 28:53
let mut _2: i32; // in scope 0 at $DIR/separate_const_switch.rs:29:8: 29:10
let mut _3: std::ops::ControlFlow<std::result::Result<std::convert::Infallible, i32>, i32>; // in scope 0 at $DIR/separate_const_switch.rs:29:8: 29:10
let mut _4: std::result::Result<i32, i32>; // in scope 0 at $DIR/separate_const_switch.rs:29:8: 29:9
let _5: std::result::Result<std::convert::Infallible, i32>; // in scope 0 at $DIR/separate_const_switch.rs:29:9: 29:10
let mut _6: std::result::Result<std::convert::Infallible, i32>; // in scope 0 at $DIR/separate_const_switch.rs:29:9: 29:10
let _7: i32; // in scope 0 at $DIR/separate_const_switch.rs:29:8: 29:10
scope 1 {
debug residual => _5; // in scope 1 at $DIR/separate_const_switch.rs:29:9: 29:10
scope 2 {
scope 8 (inlined <Result<i32, i32> as FromResidual<Result<Infallible, i32>>>::from_residual) { // at $DIR/separate_const_switch.rs:29:8: 29:10
debug residual => _6; // in scope 8 at $DIR/separate_const_switch.rs:29:8: 29:10
let _14: i32; // in scope 8 at $DIR/separate_const_switch.rs:29:8: 29:10
let mut _15: i32; // in scope 8 at $DIR/separate_const_switch.rs:29:8: 29:10
let mut _16: i32; // in scope 8 at $DIR/separate_const_switch.rs:29:8: 29:10
scope 9 {
debug e => _14; // in scope 9 at $DIR/separate_const_switch.rs:29:8: 29:10
scope 10 (inlined <i32 as From<i32>>::from) { // at $DIR/separate_const_switch.rs:29:8: 29:10
debug t => _16; // in scope 10 at $DIR/separate_const_switch.rs:29:8: 29:10
}
}
}
}
}
scope 3 {
debug val => _7; // in scope 3 at $DIR/separate_const_switch.rs:29:8: 29:10
scope 4 {
}
}
scope 5 (inlined <Result<i32, i32> as Try>::branch) { // at $DIR/separate_const_switch.rs:29:8: 29:10
debug self => _4; // in scope 5 at $DIR/separate_const_switch.rs:29:8: 29:10
let mut _8: isize; // in scope 5 at $DIR/separate_const_switch.rs:29:8: 29:10
let _9: i32; // in scope 5 at $DIR/separate_const_switch.rs:29:8: 29:10
let mut _10: i32; // in scope 5 at $DIR/separate_const_switch.rs:29:8: 29:10
let _11: i32; // in scope 5 at $DIR/separate_const_switch.rs:29:8: 29:10
let mut _12: std::result::Result<std::convert::Infallible, i32>; // in scope 5 at $DIR/separate_const_switch.rs:29:8: 29:10
let mut _13: i32; // in scope 5 at $DIR/separate_const_switch.rs:29:8: 29:10
scope 6 {
debug v => _9; // in scope 6 at $DIR/separate_const_switch.rs:29:8: 29:10
}
scope 7 {
debug e => _11; // in scope 7 at $DIR/separate_const_switch.rs:29:8: 29:10
}
}
bb0: {
StorageLive(_2); // scope 0 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageLive(_3); // scope 0 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageLive(_4); // scope 0 at $DIR/separate_const_switch.rs:29:8: 29:9
_4 = _1; // scope 0 at $DIR/separate_const_switch.rs:29:8: 29:9
StorageLive(_8); // scope 0 at $DIR/separate_const_switch.rs:29:8: 29:10
_8 = discriminant(_4); // scope 5 at $DIR/separate_const_switch.rs:29:8: 29:10
switchInt(move _8) -> [0_isize: bb3, 1_isize: bb1, otherwise: bb2]; // scope 5 at $DIR/separate_const_switch.rs:29:8: 29:10
}
bb1: {
StorageLive(_11); // scope 5 at $DIR/separate_const_switch.rs:29:8: 29:10
_11 = move ((_4 as Err).0: i32); // scope 5 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageLive(_12); // scope 7 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageLive(_13); // scope 7 at $DIR/separate_const_switch.rs:29:8: 29:10
_13 = move _11; // scope 7 at $DIR/separate_const_switch.rs:29:8: 29:10
((_12 as Err).0: i32) = move _13; // scope 7 at $DIR/separate_const_switch.rs:29:8: 29:10
discriminant(_12) = 1; // scope 7 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageDead(_13); // scope 7 at $DIR/separate_const_switch.rs:29:8: 29:10
((_3 as Break).0: std::result::Result<std::convert::Infallible, i32>) = move _12; // scope 7 at $DIR/separate_const_switch.rs:29:8: 29:10
discriminant(_3) = 1; // scope 7 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageDead(_12); // scope 7 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageDead(_11); // scope 5 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageDead(_8); // scope 0 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageDead(_4); // scope 0 at $DIR/separate_const_switch.rs:29:9: 29:10
StorageLive(_5); // scope 0 at $DIR/separate_const_switch.rs:29:9: 29:10
_5 = ((_3 as Break).0: std::result::Result<std::convert::Infallible, i32>); // scope 0 at $DIR/separate_const_switch.rs:29:9: 29:10
StorageLive(_6); // scope 2 at $DIR/separate_const_switch.rs:29:9: 29:10
_6 = _5; // scope 2 at $DIR/separate_const_switch.rs:29:9: 29:10
StorageLive(_14); // scope 8 at $DIR/separate_const_switch.rs:29:8: 29:10
_14 = move ((_6 as Err).0: i32); // scope 8 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageLive(_15); // scope 9 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageLive(_16); // scope 9 at $DIR/separate_const_switch.rs:29:8: 29:10
_16 = move _14; // scope 9 at $DIR/separate_const_switch.rs:29:8: 29:10
_15 = move _16; // scope 10 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageDead(_16); // scope 9 at $DIR/separate_const_switch.rs:29:8: 29:10
((_0 as Err).0: i32) = move _15; // scope 9 at $DIR/separate_const_switch.rs:29:8: 29:10
discriminant(_0) = 1; // scope 9 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageDead(_15); // scope 9 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageDead(_14); // scope 8 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageDead(_6); // scope 2 at $DIR/separate_const_switch.rs:29:9: 29:10
StorageDead(_5); // scope 0 at $DIR/separate_const_switch.rs:29:9: 29:10
StorageDead(_2); // scope 0 at $DIR/separate_const_switch.rs:29:10: 29:11
StorageDead(_3); // scope 0 at $DIR/separate_const_switch.rs:30:1: 30:2
return; // scope 0 at $DIR/separate_const_switch.rs:30:2: 30:2
}
bb2: {
unreachable; // scope 5 at $DIR/separate_const_switch.rs:29:8: 29:10
}
bb3: {
StorageLive(_9); // scope 5 at $DIR/separate_const_switch.rs:29:8: 29:10
_9 = move ((_4 as Ok).0: i32); // scope 5 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageLive(_10); // scope 6 at $DIR/separate_const_switch.rs:29:8: 29:10
_10 = move _9; // scope 6 at $DIR/separate_const_switch.rs:29:8: 29:10
((_3 as Continue).0: i32) = move _10; // scope 6 at $DIR/separate_const_switch.rs:29:8: 29:10
discriminant(_3) = 0; // scope 6 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageDead(_10); // scope 6 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageDead(_9); // scope 5 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageDead(_8); // scope 0 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageDead(_4); // scope 0 at $DIR/separate_const_switch.rs:29:9: 29:10
StorageLive(_7); // scope 0 at $DIR/separate_const_switch.rs:29:8: 29:10
_7 = ((_3 as Continue).0: i32); // scope 0 at $DIR/separate_const_switch.rs:29:8: 29:10
_2 = _7; // scope 4 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageDead(_7); // scope 0 at $DIR/separate_const_switch.rs:29:9: 29:10
((_0 as Ok).0: i32) = move _2; // scope 0 at $DIR/separate_const_switch.rs:29:5: 29:11
discriminant(_0) = 0; // scope 0 at $DIR/separate_const_switch.rs:29:5: 29:11
StorageDead(_2); // scope 0 at $DIR/separate_const_switch.rs:29:10: 29:11
StorageDead(_3); // scope 0 at $DIR/separate_const_switch.rs:30:1: 30:2
return; // scope 0 at $DIR/separate_const_switch.rs:30:2: 30:2
}
}

View File

@ -0,0 +1,150 @@
- // MIR for `identity` before SeparateConstSwitch
+ // MIR for `identity` after SeparateConstSwitch
fn identity(_1: Result<i32, i32>) -> Result<i32, i32> {
debug x => _1; // in scope 0 at $DIR/separate_const_switch.rs:28:13: 28:14
let mut _0: std::result::Result<i32, i32>; // return place in scope 0 at $DIR/separate_const_switch.rs:28:37: 28:53
let mut _2: i32; // in scope 0 at $DIR/separate_const_switch.rs:29:8: 29:10
let mut _3: std::ops::ControlFlow<std::result::Result<std::convert::Infallible, i32>, i32>; // in scope 0 at $DIR/separate_const_switch.rs:29:8: 29:10
let mut _4: std::result::Result<i32, i32>; // in scope 0 at $DIR/separate_const_switch.rs:29:8: 29:9
let mut _5: isize; // in scope 0 at $DIR/separate_const_switch.rs:29:9: 29:10
let _6: std::result::Result<std::convert::Infallible, i32>; // in scope 0 at $DIR/separate_const_switch.rs:29:9: 29:10
let mut _7: !; // in scope 0 at $DIR/separate_const_switch.rs:29:9: 29:10
let mut _8: std::result::Result<std::convert::Infallible, i32>; // in scope 0 at $DIR/separate_const_switch.rs:29:9: 29:10
let _9: i32; // in scope 0 at $DIR/separate_const_switch.rs:29:8: 29:10
scope 1 {
debug residual => _6; // in scope 1 at $DIR/separate_const_switch.rs:29:9: 29:10
scope 2 {
scope 8 (inlined <Result<i32, i32> as FromResidual<Result<Infallible, i32>>>::from_residual) { // at $DIR/separate_const_switch.rs:29:8: 29:10
debug residual => _8; // in scope 8 at $DIR/separate_const_switch.rs:29:8: 29:10
let _16: i32; // in scope 8 at $DIR/separate_const_switch.rs:29:8: 29:10
let mut _17: i32; // in scope 8 at $DIR/separate_const_switch.rs:29:8: 29:10
let mut _18: i32; // in scope 8 at $DIR/separate_const_switch.rs:29:8: 29:10
scope 9 {
debug e => _16; // in scope 9 at $DIR/separate_const_switch.rs:29:8: 29:10
scope 10 (inlined <i32 as From<i32>>::from) { // at $DIR/separate_const_switch.rs:29:8: 29:10
debug t => _18; // in scope 10 at $DIR/separate_const_switch.rs:29:8: 29:10
}
}
}
}
}
scope 3 {
debug val => _9; // in scope 3 at $DIR/separate_const_switch.rs:29:8: 29:10
scope 4 {
}
}
scope 5 (inlined <Result<i32, i32> as Try>::branch) { // at $DIR/separate_const_switch.rs:29:8: 29:10
debug self => _4; // in scope 5 at $DIR/separate_const_switch.rs:29:8: 29:10
let mut _10: isize; // in scope 5 at $DIR/separate_const_switch.rs:29:8: 29:10
let _11: i32; // in scope 5 at $DIR/separate_const_switch.rs:29:8: 29:10
let mut _12: i32; // in scope 5 at $DIR/separate_const_switch.rs:29:8: 29:10
let _13: i32; // in scope 5 at $DIR/separate_const_switch.rs:29:8: 29:10
let mut _14: std::result::Result<std::convert::Infallible, i32>; // in scope 5 at $DIR/separate_const_switch.rs:29:8: 29:10
let mut _15: i32; // in scope 5 at $DIR/separate_const_switch.rs:29:8: 29:10
scope 6 {
debug v => _11; // in scope 6 at $DIR/separate_const_switch.rs:29:8: 29:10
}
scope 7 {
debug e => _13; // in scope 7 at $DIR/separate_const_switch.rs:29:8: 29:10
}
}
bb0: {
StorageLive(_2); // scope 0 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageLive(_3); // scope 0 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageLive(_4); // scope 0 at $DIR/separate_const_switch.rs:29:8: 29:9
_4 = _1; // scope 0 at $DIR/separate_const_switch.rs:29:8: 29:9
StorageLive(_10); // scope 0 at $DIR/separate_const_switch.rs:29:8: 29:10
_10 = discriminant(_4); // scope 5 at $DIR/separate_const_switch.rs:29:8: 29:10
- switchInt(move _10) -> [0_isize: bb6, 1_isize: bb4, otherwise: bb5]; // scope 5 at $DIR/separate_const_switch.rs:29:8: 29:10
+ switchInt(move _10) -> [0_isize: bb5, 1_isize: bb3, otherwise: bb4]; // scope 5 at $DIR/separate_const_switch.rs:29:8: 29:10
}
bb1: {
- StorageDead(_10); // scope 0 at $DIR/separate_const_switch.rs:29:8: 29:10
- StorageDead(_4); // scope 0 at $DIR/separate_const_switch.rs:29:9: 29:10
- _5 = discriminant(_3); // scope 0 at $DIR/separate_const_switch.rs:29:9: 29:10
- switchInt(move _5) -> [0_isize: bb2, otherwise: bb3]; // scope 0 at $DIR/separate_const_switch.rs:29:9: 29:10
- }
-
- bb2: {
StorageLive(_9); // scope 0 at $DIR/separate_const_switch.rs:29:8: 29:10
_9 = ((_3 as Continue).0: i32); // scope 0 at $DIR/separate_const_switch.rs:29:8: 29:10
_2 = _9; // scope 4 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageDead(_9); // scope 0 at $DIR/separate_const_switch.rs:29:9: 29:10
((_0 as Ok).0: i32) = move _2; // scope 0 at $DIR/separate_const_switch.rs:29:5: 29:11
discriminant(_0) = 0; // scope 0 at $DIR/separate_const_switch.rs:29:5: 29:11
StorageDead(_2); // scope 0 at $DIR/separate_const_switch.rs:29:10: 29:11
StorageDead(_3); // scope 0 at $DIR/separate_const_switch.rs:30:1: 30:2
return; // scope 0 at $DIR/separate_const_switch.rs:30:2: 30:2
}
- bb3: {
+ bb2: {
StorageLive(_6); // scope 0 at $DIR/separate_const_switch.rs:29:9: 29:10
_6 = ((_3 as Break).0: std::result::Result<std::convert::Infallible, i32>); // scope 0 at $DIR/separate_const_switch.rs:29:9: 29:10
StorageLive(_8); // scope 2 at $DIR/separate_const_switch.rs:29:9: 29:10
_8 = _6; // scope 2 at $DIR/separate_const_switch.rs:29:9: 29:10
StorageLive(_16); // scope 8 at $DIR/separate_const_switch.rs:29:8: 29:10
_16 = move ((_8 as Err).0: i32); // scope 8 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageLive(_17); // scope 9 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageLive(_18); // scope 9 at $DIR/separate_const_switch.rs:29:8: 29:10
_18 = move _16; // scope 9 at $DIR/separate_const_switch.rs:29:8: 29:10
_17 = move _18; // scope 10 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageDead(_18); // scope 9 at $DIR/separate_const_switch.rs:29:8: 29:10
((_0 as Err).0: i32) = move _17; // scope 9 at $DIR/separate_const_switch.rs:29:8: 29:10
discriminant(_0) = 1; // scope 9 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageDead(_17); // scope 9 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageDead(_16); // scope 8 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageDead(_8); // scope 2 at $DIR/separate_const_switch.rs:29:9: 29:10
StorageDead(_6); // scope 0 at $DIR/separate_const_switch.rs:29:9: 29:10
StorageDead(_2); // scope 0 at $DIR/separate_const_switch.rs:29:10: 29:11
StorageDead(_3); // scope 0 at $DIR/separate_const_switch.rs:30:1: 30:2
return; // scope 0 at $DIR/separate_const_switch.rs:30:2: 30:2
}
- bb4: {
+ bb3: {
StorageLive(_13); // scope 5 at $DIR/separate_const_switch.rs:29:8: 29:10
_13 = move ((_4 as Err).0: i32); // scope 5 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageLive(_14); // scope 7 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageLive(_15); // scope 7 at $DIR/separate_const_switch.rs:29:8: 29:10
_15 = move _13; // scope 7 at $DIR/separate_const_switch.rs:29:8: 29:10
((_14 as Err).0: i32) = move _15; // scope 7 at $DIR/separate_const_switch.rs:29:8: 29:10
discriminant(_14) = 1; // scope 7 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageDead(_15); // scope 7 at $DIR/separate_const_switch.rs:29:8: 29:10
((_3 as Break).0: std::result::Result<std::convert::Infallible, i32>) = move _14; // scope 7 at $DIR/separate_const_switch.rs:29:8: 29:10
discriminant(_3) = 1; // scope 7 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageDead(_14); // scope 7 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageDead(_13); // scope 5 at $DIR/separate_const_switch.rs:29:8: 29:10
- goto -> bb1; // scope 5 at $DIR/separate_const_switch.rs:29:8: 29:10
+ StorageDead(_10); // scope 0 at $DIR/separate_const_switch.rs:29:8: 29:10
+ StorageDead(_4); // scope 0 at $DIR/separate_const_switch.rs:29:9: 29:10
+ _5 = discriminant(_3); // scope 0 at $DIR/separate_const_switch.rs:29:9: 29:10
+ switchInt(move _5) -> [0_isize: bb1, otherwise: bb2]; // scope 0 at $DIR/separate_const_switch.rs:29:9: 29:10
}
- bb5: {
+ bb4: {
unreachable; // scope 5 at $DIR/separate_const_switch.rs:29:8: 29:10
}
- bb6: {
+ bb5: {
StorageLive(_11); // scope 5 at $DIR/separate_const_switch.rs:29:8: 29:10
_11 = move ((_4 as Ok).0: i32); // scope 5 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageLive(_12); // scope 6 at $DIR/separate_const_switch.rs:29:8: 29:10
_12 = move _11; // scope 6 at $DIR/separate_const_switch.rs:29:8: 29:10
((_3 as Continue).0: i32) = move _12; // scope 6 at $DIR/separate_const_switch.rs:29:8: 29:10
discriminant(_3) = 0; // scope 6 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageDead(_12); // scope 6 at $DIR/separate_const_switch.rs:29:8: 29:10
StorageDead(_11); // scope 5 at $DIR/separate_const_switch.rs:29:8: 29:10
- goto -> bb1; // scope 5 at $DIR/separate_const_switch.rs:29:8: 29:10
+ StorageDead(_10); // scope 0 at $DIR/separate_const_switch.rs:29:8: 29:10
+ StorageDead(_4); // scope 0 at $DIR/separate_const_switch.rs:29:9: 29:10
+ _5 = discriminant(_3); // scope 0 at $DIR/separate_const_switch.rs:29:9: 29:10
+ switchInt(move _5) -> [0_isize: bb1, otherwise: bb2]; // scope 0 at $DIR/separate_const_switch.rs:29:9: 29:10
}
}

View File

@ -0,0 +1,35 @@
#![feature(control_flow_enum)]
#![feature(try_trait_v2)]
use std::ops::ControlFlow;
// EMIT_MIR separate_const_switch.too_complex.SeparateConstSwitch.diff
// EMIT_MIR separate_const_switch.too_complex.ConstProp.diff
// EMIT_MIR separate_const_switch.too_complex.PreCodegen.after.mir
fn too_complex(x: Result<i32, usize>) -> Option<i32> {
// The pass should break the outer match into
// two blocks that only have one parent each.
// Parents are one of the two branches of the first
// match, so a later pass can propagate constants.
match {
match x {
Ok(v) => ControlFlow::Continue(v),
Err(r) => ControlFlow::Break(r),
}
} {
ControlFlow::Continue(v) => Some(v),
ControlFlow::Break(r) => None,
}
}
// EMIT_MIR separate_const_switch.identity.SeparateConstSwitch.diff
// EMIT_MIR separate_const_switch.identity.ConstProp.diff
// EMIT_MIR separate_const_switch.identity.PreCodegen.after.mir
fn identity(x: Result<i32, i32>) -> Result<i32, i32> {
Ok(x?)
}
fn main() {
too_complex(Ok(0));
identity(Ok(0));
}

View File

@ -0,0 +1,91 @@
- // MIR for `too_complex` before ConstProp
+ // MIR for `too_complex` after ConstProp
fn too_complex(_1: Result<i32, usize>) -> Option<i32> {
debug x => _1; // in scope 0 at $DIR/separate_const_switch.rs:9:16: 9:17
let mut _0: std::option::Option<i32>; // return place in scope 0 at $DIR/separate_const_switch.rs:9:42: 9:53
let mut _2: std::ops::ControlFlow<usize, i32>; // in scope 0 at $DIR/separate_const_switch.rs:14:11: 19:6
let mut _3: isize; // in scope 0 at $DIR/separate_const_switch.rs:16:13: 16:18
let _4: i32; // in scope 0 at $DIR/separate_const_switch.rs:16:16: 16:17
let mut _5: i32; // in scope 0 at $DIR/separate_const_switch.rs:16:44: 16:45
let _6: usize; // in scope 0 at $DIR/separate_const_switch.rs:17:17: 17:18
let mut _7: usize; // in scope 0 at $DIR/separate_const_switch.rs:17:42: 17:43
let mut _8: isize; // in scope 0 at $DIR/separate_const_switch.rs:20:9: 20:33
let _9: i32; // in scope 0 at $DIR/separate_const_switch.rs:20:31: 20:32
let mut _10: i32; // in scope 0 at $DIR/separate_const_switch.rs:20:42: 20:43
let _11: usize; // in scope 0 at $DIR/separate_const_switch.rs:21:28: 21:29
scope 1 {
debug v => _4; // in scope 1 at $DIR/separate_const_switch.rs:16:16: 16:17
}
scope 2 {
debug r => _6; // in scope 2 at $DIR/separate_const_switch.rs:17:17: 17:18
}
scope 3 {
debug v => _9; // in scope 3 at $DIR/separate_const_switch.rs:20:31: 20:32
}
scope 4 {
debug r => _11; // in scope 4 at $DIR/separate_const_switch.rs:21:28: 21:29
}
bb0: {
StorageLive(_2); // scope 0 at $DIR/separate_const_switch.rs:14:11: 19:6
_3 = discriminant(_1); // scope 0 at $DIR/separate_const_switch.rs:16:13: 16:18
switchInt(move _3) -> [0_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/separate_const_switch.rs:16:13: 16:18
}
bb1: {
StorageLive(_6); // scope 0 at $DIR/separate_const_switch.rs:17:17: 17:18
_6 = ((_1 as Err).0: usize); // scope 0 at $DIR/separate_const_switch.rs:17:17: 17:18
StorageLive(_7); // scope 2 at $DIR/separate_const_switch.rs:17:42: 17:43
_7 = _6; // scope 2 at $DIR/separate_const_switch.rs:17:42: 17:43
((_2 as Break).0: usize) = move _7; // scope 2 at $DIR/separate_const_switch.rs:17:23: 17:44
discriminant(_2) = 1; // scope 2 at $DIR/separate_const_switch.rs:17:23: 17:44
StorageDead(_7); // scope 2 at $DIR/separate_const_switch.rs:17:43: 17:44
StorageDead(_6); // scope 0 at $DIR/separate_const_switch.rs:17:43: 17:44
- _8 = discriminant(_2); // scope 0 at $DIR/separate_const_switch.rs:20:9: 20:33
- switchInt(move _8) -> [0_isize: bb4, otherwise: bb3]; // scope 0 at $DIR/separate_const_switch.rs:20:9: 20:33
+ _8 = const 1_isize; // scope 0 at $DIR/separate_const_switch.rs:20:9: 20:33
+ switchInt(const 1_isize) -> [0_isize: bb4, otherwise: bb3]; // scope 0 at $DIR/separate_const_switch.rs:20:9: 20:33
}
bb2: {
StorageLive(_4); // scope 0 at $DIR/separate_const_switch.rs:16:16: 16:17
_4 = ((_1 as Ok).0: i32); // scope 0 at $DIR/separate_const_switch.rs:16:16: 16:17
StorageLive(_5); // scope 1 at $DIR/separate_const_switch.rs:16:44: 16:45
_5 = _4; // scope 1 at $DIR/separate_const_switch.rs:16:44: 16:45
((_2 as Continue).0: i32) = move _5; // scope 1 at $DIR/separate_const_switch.rs:16:22: 16:46
discriminant(_2) = 0; // scope 1 at $DIR/separate_const_switch.rs:16:22: 16:46
StorageDead(_5); // scope 1 at $DIR/separate_const_switch.rs:16:45: 16:46
StorageDead(_4); // scope 0 at $DIR/separate_const_switch.rs:16:45: 16:46
- _8 = discriminant(_2); // scope 0 at $DIR/separate_const_switch.rs:20:9: 20:33
- switchInt(move _8) -> [0_isize: bb4, otherwise: bb3]; // scope 0 at $DIR/separate_const_switch.rs:20:9: 20:33
+ _8 = const 0_isize; // scope 0 at $DIR/separate_const_switch.rs:20:9: 20:33
+ switchInt(const 0_isize) -> [0_isize: bb4, otherwise: bb3]; // scope 0 at $DIR/separate_const_switch.rs:20:9: 20:33
}
bb3: {
StorageLive(_11); // scope 0 at $DIR/separate_const_switch.rs:21:28: 21:29
_11 = ((_2 as Break).0: usize); // scope 0 at $DIR/separate_const_switch.rs:21:28: 21:29
discriminant(_0) = 0; // scope 4 at $DIR/separate_const_switch.rs:21:34: 21:38
StorageDead(_11); // scope 0 at $DIR/separate_const_switch.rs:21:37: 21:38
goto -> bb5; // scope 0 at $DIR/separate_const_switch.rs:14:5: 22:6
}
bb4: {
StorageLive(_9); // scope 0 at $DIR/separate_const_switch.rs:20:31: 20:32
_9 = ((_2 as Continue).0: i32); // scope 0 at $DIR/separate_const_switch.rs:20:31: 20:32
StorageLive(_10); // scope 3 at $DIR/separate_const_switch.rs:20:42: 20:43
_10 = _9; // scope 3 at $DIR/separate_const_switch.rs:20:42: 20:43
((_0 as Some).0: i32) = move _10; // scope 3 at $DIR/separate_const_switch.rs:20:37: 20:44
discriminant(_0) = 1; // scope 3 at $DIR/separate_const_switch.rs:20:37: 20:44
StorageDead(_10); // scope 3 at $DIR/separate_const_switch.rs:20:43: 20:44
StorageDead(_9); // scope 0 at $DIR/separate_const_switch.rs:20:43: 20:44
goto -> bb5; // scope 0 at $DIR/separate_const_switch.rs:14:5: 22:6
}
bb5: {
StorageDead(_2); // scope 0 at $DIR/separate_const_switch.rs:23:1: 23:2
return; // scope 0 at $DIR/separate_const_switch.rs:23:2: 23:2
}
}

View File

@ -0,0 +1,74 @@
// MIR for `too_complex` after PreCodegen
fn too_complex(_1: Result<i32, usize>) -> Option<i32> {
debug x => _1; // in scope 0 at $DIR/separate_const_switch.rs:9:16: 9:17
let mut _0: std::option::Option<i32>; // return place in scope 0 at $DIR/separate_const_switch.rs:9:42: 9:53
let mut _2: std::ops::ControlFlow<usize, i32>; // in scope 0 at $DIR/separate_const_switch.rs:14:11: 19:6
let mut _3: isize; // in scope 0 at $DIR/separate_const_switch.rs:16:13: 16:18
let _4: i32; // in scope 0 at $DIR/separate_const_switch.rs:16:16: 16:17
let mut _5: i32; // in scope 0 at $DIR/separate_const_switch.rs:16:44: 16:45
let _6: usize; // in scope 0 at $DIR/separate_const_switch.rs:17:17: 17:18
let mut _7: usize; // in scope 0 at $DIR/separate_const_switch.rs:17:42: 17:43
let _8: i32; // in scope 0 at $DIR/separate_const_switch.rs:20:31: 20:32
let mut _9: i32; // in scope 0 at $DIR/separate_const_switch.rs:20:42: 20:43
let _10: usize; // in scope 0 at $DIR/separate_const_switch.rs:21:28: 21:29
scope 1 {
debug v => _4; // in scope 1 at $DIR/separate_const_switch.rs:16:16: 16:17
}
scope 2 {
debug r => _6; // in scope 2 at $DIR/separate_const_switch.rs:17:17: 17:18
}
scope 3 {
debug v => _8; // in scope 3 at $DIR/separate_const_switch.rs:20:31: 20:32
}
scope 4 {
debug r => _10; // in scope 4 at $DIR/separate_const_switch.rs:21:28: 21:29
}
bb0: {
StorageLive(_2); // scope 0 at $DIR/separate_const_switch.rs:14:11: 19:6
_3 = discriminant(_1); // scope 0 at $DIR/separate_const_switch.rs:16:13: 16:18
switchInt(move _3) -> [0_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/separate_const_switch.rs:16:13: 16:18
}
bb1: {
StorageLive(_6); // scope 0 at $DIR/separate_const_switch.rs:17:17: 17:18
_6 = ((_1 as Err).0: usize); // scope 0 at $DIR/separate_const_switch.rs:17:17: 17:18
StorageLive(_7); // scope 2 at $DIR/separate_const_switch.rs:17:42: 17:43
_7 = _6; // scope 2 at $DIR/separate_const_switch.rs:17:42: 17:43
((_2 as Break).0: usize) = move _7; // scope 2 at $DIR/separate_const_switch.rs:17:23: 17:44
discriminant(_2) = 1; // scope 2 at $DIR/separate_const_switch.rs:17:23: 17:44
StorageDead(_7); // scope 2 at $DIR/separate_const_switch.rs:17:43: 17:44
StorageDead(_6); // scope 0 at $DIR/separate_const_switch.rs:17:43: 17:44
StorageLive(_10); // scope 0 at $DIR/separate_const_switch.rs:21:28: 21:29
_10 = ((_2 as Break).0: usize); // scope 0 at $DIR/separate_const_switch.rs:21:28: 21:29
discriminant(_0) = 0; // scope 4 at $DIR/separate_const_switch.rs:21:34: 21:38
StorageDead(_10); // scope 0 at $DIR/separate_const_switch.rs:21:37: 21:38
goto -> bb3; // scope 0 at $DIR/separate_const_switch.rs:14:5: 22:6
}
bb2: {
StorageLive(_4); // scope 0 at $DIR/separate_const_switch.rs:16:16: 16:17
_4 = ((_1 as Ok).0: i32); // scope 0 at $DIR/separate_const_switch.rs:16:16: 16:17
StorageLive(_5); // scope 1 at $DIR/separate_const_switch.rs:16:44: 16:45
_5 = _4; // scope 1 at $DIR/separate_const_switch.rs:16:44: 16:45
((_2 as Continue).0: i32) = move _5; // scope 1 at $DIR/separate_const_switch.rs:16:22: 16:46
discriminant(_2) = 0; // scope 1 at $DIR/separate_const_switch.rs:16:22: 16:46
StorageDead(_5); // scope 1 at $DIR/separate_const_switch.rs:16:45: 16:46
StorageDead(_4); // scope 0 at $DIR/separate_const_switch.rs:16:45: 16:46
StorageLive(_8); // scope 0 at $DIR/separate_const_switch.rs:20:31: 20:32
_8 = ((_2 as Continue).0: i32); // scope 0 at $DIR/separate_const_switch.rs:20:31: 20:32
StorageLive(_9); // scope 3 at $DIR/separate_const_switch.rs:20:42: 20:43
_9 = _8; // scope 3 at $DIR/separate_const_switch.rs:20:42: 20:43
((_0 as Some).0: i32) = move _9; // scope 3 at $DIR/separate_const_switch.rs:20:37: 20:44
discriminant(_0) = 1; // scope 3 at $DIR/separate_const_switch.rs:20:37: 20:44
StorageDead(_9); // scope 3 at $DIR/separate_const_switch.rs:20:43: 20:44
StorageDead(_8); // scope 0 at $DIR/separate_const_switch.rs:20:43: 20:44
goto -> bb3; // scope 0 at $DIR/separate_const_switch.rs:14:5: 22:6
}
bb3: {
StorageDead(_2); // scope 0 at $DIR/separate_const_switch.rs:23:1: 23:2
return; // scope 0 at $DIR/separate_const_switch.rs:23:2: 23:2
}
}

View File

@ -0,0 +1,98 @@
- // MIR for `too_complex` before SeparateConstSwitch
+ // MIR for `too_complex` after SeparateConstSwitch
fn too_complex(_1: Result<i32, usize>) -> Option<i32> {
debug x => _1; // in scope 0 at $DIR/separate_const_switch.rs:9:16: 9:17
let mut _0: std::option::Option<i32>; // return place in scope 0 at $DIR/separate_const_switch.rs:9:42: 9:53
let mut _2: std::ops::ControlFlow<usize, i32>; // in scope 0 at $DIR/separate_const_switch.rs:14:11: 19:6
let mut _3: isize; // in scope 0 at $DIR/separate_const_switch.rs:16:13: 16:18
let _4: i32; // in scope 0 at $DIR/separate_const_switch.rs:16:16: 16:17
let mut _5: i32; // in scope 0 at $DIR/separate_const_switch.rs:16:44: 16:45
let _6: usize; // in scope 0 at $DIR/separate_const_switch.rs:17:17: 17:18
let mut _7: usize; // in scope 0 at $DIR/separate_const_switch.rs:17:42: 17:43
let mut _8: isize; // in scope 0 at $DIR/separate_const_switch.rs:20:9: 20:33
let _9: i32; // in scope 0 at $DIR/separate_const_switch.rs:20:31: 20:32
let mut _10: i32; // in scope 0 at $DIR/separate_const_switch.rs:20:42: 20:43
let _11: usize; // in scope 0 at $DIR/separate_const_switch.rs:21:28: 21:29
scope 1 {
debug v => _4; // in scope 1 at $DIR/separate_const_switch.rs:16:16: 16:17
}
scope 2 {
debug r => _6; // in scope 2 at $DIR/separate_const_switch.rs:17:17: 17:18
}
scope 3 {
debug v => _9; // in scope 3 at $DIR/separate_const_switch.rs:20:31: 20:32
}
scope 4 {
debug r => _11; // in scope 4 at $DIR/separate_const_switch.rs:21:28: 21:29
}
bb0: {
StorageLive(_2); // scope 0 at $DIR/separate_const_switch.rs:14:11: 19:6
_3 = discriminant(_1); // scope 0 at $DIR/separate_const_switch.rs:16:13: 16:18
switchInt(move _3) -> [0_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/separate_const_switch.rs:16:13: 16:18
}
bb1: {
StorageLive(_6); // scope 0 at $DIR/separate_const_switch.rs:17:17: 17:18
_6 = ((_1 as Err).0: usize); // scope 0 at $DIR/separate_const_switch.rs:17:17: 17:18
StorageLive(_7); // scope 2 at $DIR/separate_const_switch.rs:17:42: 17:43
_7 = _6; // scope 2 at $DIR/separate_const_switch.rs:17:42: 17:43
((_2 as Break).0: usize) = move _7; // scope 2 at $DIR/separate_const_switch.rs:17:23: 17:44
discriminant(_2) = 1; // scope 2 at $DIR/separate_const_switch.rs:17:23: 17:44
StorageDead(_7); // scope 2 at $DIR/separate_const_switch.rs:17:43: 17:44
StorageDead(_6); // scope 0 at $DIR/separate_const_switch.rs:17:43: 17:44
- goto -> bb3; // scope 0 at $DIR/separate_const_switch.rs:15:9: 18:10
+ _8 = discriminant(_2); // scope 0 at $DIR/separate_const_switch.rs:20:9: 20:33
+ switchInt(move _8) -> [0_isize: bb4, otherwise: bb3]; // scope 0 at $DIR/separate_const_switch.rs:20:9: 20:33
}
bb2: {
StorageLive(_4); // scope 0 at $DIR/separate_const_switch.rs:16:16: 16:17
_4 = ((_1 as Ok).0: i32); // scope 0 at $DIR/separate_const_switch.rs:16:16: 16:17
StorageLive(_5); // scope 1 at $DIR/separate_const_switch.rs:16:44: 16:45
_5 = _4; // scope 1 at $DIR/separate_const_switch.rs:16:44: 16:45
((_2 as Continue).0: i32) = move _5; // scope 1 at $DIR/separate_const_switch.rs:16:22: 16:46
discriminant(_2) = 0; // scope 1 at $DIR/separate_const_switch.rs:16:22: 16:46
StorageDead(_5); // scope 1 at $DIR/separate_const_switch.rs:16:45: 16:46
StorageDead(_4); // scope 0 at $DIR/separate_const_switch.rs:16:45: 16:46
- goto -> bb3; // scope 0 at $DIR/separate_const_switch.rs:15:9: 18:10
- }
-
- bb3: {
_8 = discriminant(_2); // scope 0 at $DIR/separate_const_switch.rs:20:9: 20:33
- switchInt(move _8) -> [0_isize: bb5, otherwise: bb4]; // scope 0 at $DIR/separate_const_switch.rs:20:9: 20:33
+ switchInt(move _8) -> [0_isize: bb4, otherwise: bb3]; // scope 0 at $DIR/separate_const_switch.rs:20:9: 20:33
}
- bb4: {
+ bb3: {
StorageLive(_11); // scope 0 at $DIR/separate_const_switch.rs:21:28: 21:29
_11 = ((_2 as Break).0: usize); // scope 0 at $DIR/separate_const_switch.rs:21:28: 21:29
discriminant(_0) = 0; // scope 4 at $DIR/separate_const_switch.rs:21:34: 21:38
StorageDead(_11); // scope 0 at $DIR/separate_const_switch.rs:21:37: 21:38
- goto -> bb6; // scope 0 at $DIR/separate_const_switch.rs:14:5: 22:6
+ goto -> bb5; // scope 0 at $DIR/separate_const_switch.rs:14:5: 22:6
}
- bb5: {
+ bb4: {
StorageLive(_9); // scope 0 at $DIR/separate_const_switch.rs:20:31: 20:32
_9 = ((_2 as Continue).0: i32); // scope 0 at $DIR/separate_const_switch.rs:20:31: 20:32
StorageLive(_10); // scope 3 at $DIR/separate_const_switch.rs:20:42: 20:43
_10 = _9; // scope 3 at $DIR/separate_const_switch.rs:20:42: 20:43
((_0 as Some).0: i32) = move _10; // scope 3 at $DIR/separate_const_switch.rs:20:37: 20:44
discriminant(_0) = 1; // scope 3 at $DIR/separate_const_switch.rs:20:37: 20:44
StorageDead(_10); // scope 3 at $DIR/separate_const_switch.rs:20:43: 20:44
StorageDead(_9); // scope 0 at $DIR/separate_const_switch.rs:20:43: 20:44
- goto -> bb6; // scope 0 at $DIR/separate_const_switch.rs:14:5: 22:6
+ goto -> bb5; // scope 0 at $DIR/separate_const_switch.rs:14:5: 22:6
}
- bb6: {
+ bb5: {
StorageDead(_2); // scope 0 at $DIR/separate_const_switch.rs:23:1: 23:2
return; // scope 0 at $DIR/separate_const_switch.rs:23:2: 23:2
}
}