Skip to content

Commit

Permalink
[c_cfg] Implement importing CCFG to ctrl_flow_struct
Browse files Browse the repository at this point in the history
  • Loading branch information
HMPerson1 authored and Anton Kochkov committed Aug 9, 2018
1 parent 2fb4037 commit 40d3d41
Show file tree
Hide file tree
Showing 4 changed files with 179 additions and 23 deletions.
36 changes: 26 additions & 10 deletions src/backend/ctrl_flow_struct/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,18 @@ use std::mem;
/// - `entry` and `exit` must be a source and a sink, respectively.
/// - all nodes (except possibly `exit`) must be reachable from `entry`
/// - `exit` must not contain any code.
struct ControlFlowGraph<'cd, A: AstContext> {
graph: StableDiGraph<CfgNode<'cd, A>, CfgEdge>,
entry: NodeIndex,
exit: NodeIndex,
cctx: CondContext<'cd, A>,
actx: A,
pub struct ControlFlowGraph<'cd, A: AstContext> {
pub graph: StableDiGraph<CfgNode<'cd, A>, CfgEdge>,
pub entry: NodeIndex,
pub exit: NodeIndex,
pub cctx: CondContext<'cd, A>,
pub actx: A,
}

type NodeSet = IxBitSet<NodeIndex>;
type EdgeSet = IxBitSet<EdgeIndex>;

enum CfgNode<'cd, A: AstContext> {
pub enum CfgNode<'cd, A: AstContext> {
/// out-degree <= 1
Code(AstNode<'cd, A>),
/// out-degree >= 2
Expand All @@ -52,7 +52,7 @@ enum CfgNode<'cd, A: AstContext> {
}

#[derive(Copy, Clone, Debug)]
enum CfgEdge {
pub enum CfgEdge {
True,
False,
}
Expand All @@ -65,7 +65,7 @@ type AstNode<'cd, A> =
ast::AstNode<<A as AstContext>::Block, Condition<'cd, A>, <A as AstContext>::Variable>;

impl<'cd, A: AstContextMut> ControlFlowGraph<'cd, A> {
fn structure_whole(mut self) -> AstNode<'cd, A> {
pub fn structure_whole(mut self) -> (AstNode<'cd, A>, A) {
debug_assert!(graph_utils::is_source(&self.graph, self.entry));
debug_assert!(graph_utils::is_sink(&self.graph, self.exit));

Expand Down Expand Up @@ -134,6 +134,7 @@ impl<'cd, A: AstContextMut> ControlFlowGraph<'cd, A> {
} else {
// acyclic
let region = graph_utils::dominated_by(&self.graph, self.entry, n);
// TODO: region.remove(self.exit);
// single-block regions aren't interesting
if region.len() > 1 && !region.contains(self.exit) {
let succs = graph_utils::strict_successors_of_set(&self.graph, &region);
Expand Down Expand Up @@ -171,7 +172,7 @@ impl<'cd, A: AstContextMut> ControlFlowGraph<'cd, A> {
self.graph.remove_node(self.entry);
debug_assert!(self.graph.node_count() == 0);

ret
(ret, self.actx)
}

/// Converts the acyclic, single entry, single exit region bound by `header`
Expand Down Expand Up @@ -500,6 +501,21 @@ impl<'cd, A: AstContext> RegionAstContext<'cd, A> {
}
}

pub fn mk_code_node<A: AstContext>(block: A::Block) -> CfgNode<'static, A> {
CfgNode::Code(AstNodeC::BasicBlock(block))
}

pub fn mk_cond_node<'cd, A: AstContext>(
cctx: CondContext<'cd, A>,
cond: A::Condition,
) -> CfgNode<'cd, A> {
CfgNode::Condition(cctx.new_var(cond))
}

pub fn empty_node<A: AstContext>() -> CfgNode<'static, A> {
CfgNode::Code(AstNodeC::default())
}

impl<'cd, A> fmt::Debug for ControlFlowGraph<'cd, A>
where
A: AstContext + fmt::Debug,
Expand Down
26 changes: 13 additions & 13 deletions src/backend/ctrl_flow_struct/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ fn ast_nmg_example() {
cctx,
actx,
};
let ast = cfg.structure_whole();
let ast = cfg.structure_whole().0;
println!("{:#?}", ast);

#[allow(non_snake_case)]
Expand Down Expand Up @@ -229,7 +229,7 @@ fn ast_nmg_r1() {
cctx,
actx,
};
let ast = cfg.structure_whole();
let ast = cfg.structure_whole().0;
println!("{:#?}", ast);

let c_c1 = cctx.mk_var(v_c1);
Expand Down Expand Up @@ -291,7 +291,7 @@ fn ast_nmg_r2() {
cctx,
actx,
};
let ast = cfg.structure_whole();
let ast = cfg.structure_whole().0;
println!("{:#?}", ast);

use self::AstNodeC::*;
Expand Down Expand Up @@ -348,7 +348,7 @@ fn ast_nmg_r3() {
cctx,
actx,
};
let ast = cfg.structure_whole();
let ast = cfg.structure_whole().0;
println!("{:#?}", ast);

let c_d1 = cctx.mk_var(v_d1);
Expand Down Expand Up @@ -438,7 +438,7 @@ fn ast_switchy() {
cctx,
actx,
};
let ast = cfg.structure_whole();
let ast = cfg.structure_whole().0;
println!("{:#?}", ast);

use self::AstNodeC::*;
Expand Down Expand Up @@ -500,7 +500,7 @@ fn ast_ifelse_cascade() {
cctx,
actx,
};
let ast = cfg.structure_whole();
let ast = cfg.structure_whole().0;
println!("{:#?}", ast);

use self::AstNodeC::*;
Expand Down Expand Up @@ -554,7 +554,7 @@ fn ast_while() {
cctx,
actx,
};
let ast = cfg.structure_whole();
let ast = cfg.structure_whole().0;
println!("{:#?}", ast);

let c_c = cctx.mk_var(v_c);
Expand Down Expand Up @@ -600,7 +600,7 @@ fn ast_do_while() {
cctx,
actx,
};
let ast = cfg.structure_whole();
let ast = cfg.structure_whole().0;
println!("{:#?}", ast);

let c_c = cctx.mk_var(v_c);
Expand Down Expand Up @@ -641,7 +641,7 @@ fn ast_infinite_loop() {
cctx,
actx,
};
let ast = cfg.structure_whole();
let ast = cfg.structure_whole().0;
println!("{:#?}", ast);

use self::AstNodeC::*;
Expand Down Expand Up @@ -694,7 +694,7 @@ fn ast_complex_while_and() {
cctx,
actx,
};
let ast = cfg.structure_whole();
let ast = cfg.structure_whole().0;
println!("{:#?}", ast);

let c_c1 = cctx.mk_var(v_c1);
Expand Down Expand Up @@ -755,7 +755,7 @@ fn ast_complex_while_or() {
cctx,
actx,
};
let ast = cfg.structure_whole();
let ast = cfg.structure_whole().0;
println!("{:#?}", ast);

let c_c1 = cctx.mk_var(v_c1);
Expand Down Expand Up @@ -826,7 +826,7 @@ fn abnormal_entries() {
cctx,
actx,
};
let ast = cfg.structure_whole();
let ast = cfg.structure_whole().0;
println!("{:#?}", ast);
}

Expand Down Expand Up @@ -885,7 +885,7 @@ fn abnormal_exits() {
cctx,
actx,
};
let ast = cfg.structure_whole();
let ast = cfg.structure_whole().0;
println!("{:#?}", ast);
}

Expand Down
138 changes: 138 additions & 0 deletions src/backend/lang_c/c_cfg/ctrl_flow_struct.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
use super::{ActionEdge, ActionNode, CCFGEdge, CCFGNode, CCFG};
use backend::ctrl_flow_struct as flstr;
use backend::ctrl_flow_struct::ast_context::{AstContext, AstContextMut};
use backend::lang_c::c_ast;

use petgraph::prelude::*;
use petgraph::visit::{Dfs, EdgeFiltered, IntoNodeReferences, VisitMap};

pub fn structure_and_convert(ccfg: CCFG) -> Option<c_ast::CAST> {
let cstore = flstr::condition::Storage::new();
let flstr_cfg = import(cstore.cctx(), ccfg)?;
unimplemented!()
}

fn import<'cd>(
cctx: flstr::condition::Context<'cd, NodeIndex>,
ccfg: CCFG,
) -> Option<flstr::ControlFlowGraph<'cd, CCFG>> {
let mut new_graph = {
let ef = EdgeFiltered::from_fn(&ccfg.g, |e| {
// ignore `Normal` edges from `Goto` nodes
match (&ccfg.g[e.source()], e.weight()) {
(CCFGNode::Action(ActionNode::Goto), CCFGEdge::Action(ActionEdge::Normal)) => false,
(_, CCFGEdge::Action(_)) => true,
_ => false,
}
});
let reachable_actions = {
let mut dfs = Dfs::new(&ef, ccfg.entry);
while let Some(_) = dfs.next(&ef) {}
dfs.discovered
};

if ccfg
.g
.node_references()
.filter(|(n, _)| reachable_actions.is_visited(n))
.any(|(_, nw)| !is_action_node(nw))
{
return None;
}

try_filter_map_to_stable(
&ccfg.g,
|n, nw| {
Some(if reachable_actions.is_visited(&n) {
if let CCFGNode::Action(ActionNode::If) = nw {
Some(flstr::mk_cond_node(cctx, ccfg.branch_condition(n)?))
} else {
Some(flstr::mk_code_node(n))
}
} else {
None
})
},
|e, ew| {
// `Some(_)` <=> keep edge
// `None` <=> ignore edge
// `return None` <=> error
Some(match (&ccfg.g[ccfg.g.edge_endpoints(e).unwrap().0], ew) {
(CCFGNode::Action(ActionNode::If), CCFGEdge::Action(ActionEdge::IfThen)) => {
Some(flstr::CfgEdge::True)
}
(CCFGNode::Action(ActionNode::If), CCFGEdge::Action(ActionEdge::IfElse)) => {
return None
}
(CCFGNode::Action(ActionNode::If), CCFGEdge::Action(ActionEdge::Normal)) => {
Some(flstr::CfgEdge::False)
}
(CCFGNode::Action(ActionNode::Goto), CCFGEdge::Action(ActionEdge::GotoDst)) => {
Some(flstr::CfgEdge::True)
}
(CCFGNode::Action(ActionNode::Goto), CCFGEdge::Action(ActionEdge::Normal)) => {
None
}
(_, CCFGEdge::Action(ActionEdge::Normal)) => Some(flstr::CfgEdge::True),
(_, CCFGEdge::Value(_)) => None,
(_, CCFGEdge::Action(_)) => return None,
})
},
)?
};

let exit = new_graph.add_node(flstr::empty_node());
Some(flstr::ControlFlowGraph {
graph: new_graph,
entry: ccfg.entry,
exit,
cctx,
actx: ccfg,
})
}

impl AstContext for CCFG {
type Block = NodeIndex;
type Variable = String;
type BoolVariable = String;
type Condition = NodeIndex;
}

/// based on https://docs.rs/petgraph/0.4.12/src/petgraph/graph_impl/mod.rs.html#1293-1317
fn try_filter_map_to_stable<'a, N, E, F, G, N2, E2>(
graph: &'a Graph<N, E>,
mut node_map: F,
mut edge_map: G,
) -> Option<StableGraph<N2, E2>>
where
F: FnMut(NodeIndex, &'a N) -> Option<Option<N2>>,
G: FnMut(EdgeIndex, &'a E) -> Option<Option<E2>>,
{
let mut g = StableGraph::new();
// mapping from old node index to new node index, end represents removed.
let mut node_index_map = vec![NodeIndex::end(); graph.node_count()];
for (i, node) in graph.node_references() {
if let Some(nw) = node_map(i, node)? {
node_index_map[i.index()] = g.add_node(nw);
}
}
for edge in graph.edge_references() {
// skip edge if any endpoint was removed
let source = node_index_map[edge.source().index()];
let target = node_index_map[edge.target().index()];
if source != NodeIndex::end() && target != NodeIndex::end() {
if let Some(ew) = edge_map(edge.id(), edge.weight())? {
g.add_edge(source, target, ew);
}
}
}
Some(g)
}

fn is_action_node(e: &CCFGNode) -> bool {
if let CCFGNode::Action(_) = e {
true
} else {
false
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
//!
//! `CCFG` is CFG-like Graph so that we can do control flow structuring easily.

pub mod ctrl_flow_struct;

use std::collections::{HashMap, HashSet};
use super::c_ast;
use super::c_ast::{Ty, CAST};
Expand Down

0 comments on commit 40d3d41

Please sign in to comment.