1 // Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 use rustc
::middle
::const_val
::ConstVal
;
12 use rustc
::ty
::TyCtxt
;
13 use rustc
::mir
::repr
::*;
14 use rustc
::mir
::transform
::{MirPass, Pass}
;
16 use syntax
::ast
::NodeId
;
18 use super::remove_dead_blocks
::RemoveDeadBlocks
;
20 pub struct SimplifyCfg
;
23 pub fn new() -> SimplifyCfg
{
27 fn remove_goto_chains(&self, mir
: &mut Mir
) -> bool
{
28 // Find the target at the end of the jump chain, return None if there is a loop
29 fn final_target(mir
: &Mir
, mut target
: BasicBlock
) -> Option
<BasicBlock
> {
30 // Keep track of already seen blocks to detect loops
31 let mut seen
: Vec
<BasicBlock
> = Vec
::with_capacity(8);
33 while mir
.basic_block_data(target
).statements
.is_empty() {
34 // NB -- terminator may have been swapped with `None`
35 // below, in which case we have a cycle and just want
37 if let Some(ref terminator
) = mir
.basic_block_data(target
).terminator
{
38 match terminator
.kind
{
39 TerminatorKind
::Goto { target: next }
=> {
40 if seen
.contains(&next
) {
56 let mut changed
= false;
57 for bb
in mir
.all_basic_blocks() {
58 // Temporarily take ownership of the terminator we're modifying to keep borrowck happy
59 let mut terminator
= mir
.basic_block_data_mut(bb
).terminator
.take()
60 .expect("invalid terminator state");
62 debug
!("remove_goto_chains: bb={:?} terminator={:?}", bb
, terminator
);
64 for target
in terminator
.successors_mut() {
65 let new_target
= match final_target(mir
, *target
) {
66 Some(new_target
) => new_target
,
67 None
if mir
.basic_block_data(bb
).statements
.is_empty() => bb
,
70 changed
|= *target
!= new_target
;
73 mir
.basic_block_data_mut(bb
).terminator
= Some(terminator
);
78 fn simplify_branches(&self, mir
: &mut Mir
) -> bool
{
79 let mut changed
= false;
81 for bb
in mir
.all_basic_blocks() {
82 let basic_block
= mir
.basic_block_data_mut(bb
);
83 let mut terminator
= basic_block
.terminator_mut();
84 terminator
.kind
= match terminator
.kind
{
85 TerminatorKind
::If { ref targets, .. }
if targets
.0 == targets
.1 => {
87 TerminatorKind
::Goto { target: targets.0 }
90 TerminatorKind
::If
{ ref targets
, cond
: Operand
::Constant(Constant
{
91 literal
: Literal
::Value
{
92 value
: ConstVal
::Bool(cond
)
97 TerminatorKind
::Goto { target: targets.0 }
99 TerminatorKind
::Goto { target: targets.1 }
103 TerminatorKind
::SwitchInt { ref targets, .. }
if targets
.len() == 1 => {
104 TerminatorKind
::Goto { target: targets[0] }
114 impl<'tcx
> MirPass
<'tcx
> for SimplifyCfg
{
115 fn run_pass(&mut self, tcx
: &TyCtxt
<'tcx
>, id
: NodeId
, mir
: &mut Mir
<'tcx
>) {
117 let mut changed
= true;
119 pretty
::dump_mir(tcx
, "simplify_cfg", &counter
, id
, mir
, None
);
121 changed
= self.simplify_branches(mir
);
122 changed
|= self.remove_goto_chains(mir
);
123 RemoveDeadBlocks
.run_pass(tcx
, id
, mir
);
125 // FIXME: Should probably be moved into some kind of pass manager
126 mir
.basic_blocks
.shrink_to_fit();
130 impl Pass
for SimplifyCfg {}