improvement(semantic/cfg): better CFG API (#3472)

This commit is contained in:
rzvxa 2024-06-06 07:55:37 +00:00
parent 3c7ee85ce4
commit 61bae74f76
3 changed files with 37 additions and 21 deletions

View file

@ -228,12 +228,7 @@ impl Rule for RulesOfHooks {
return;
}
if !petgraph::algo::has_path_connecting(
&semantic.cfg().graph,
func_cfg_id,
node_cfg_id,
None,
) {
if !ctx.semantic().cfg().is_reachabale(func_cfg_id, node_cfg_id) {
// There should always be a control flow path between a parent and child node.
// If there is none it means we always do an early exit before reaching our hook call.
// In some cases it might mean that we are operating on an invalid `cfg` but in either

View file

@ -111,7 +111,8 @@ fn main() -> std::io::Result<()> {
}
});
format!(
"xlabel = \"nodes [{}]\\l\", label = \"bb{}\n{}\"",
"xlabel = \"nodes{} [{}]\\l\", label = \"bb{}\n{}\"",
node.1,
nodes,
node.1,
semantic.semantic.cfg().basic_blocks[*node.1]

View file

@ -1,14 +1,13 @@
mod builder;
mod dot;
use itertools::Itertools;
use oxc_span::CompactStr;
use oxc_syntax::operator::{
AssignmentOperator, BinaryOperator, LogicalOperator, UnaryOperator, UpdateOperator,
};
use petgraph::{
stable_graph::NodeIndex,
visit::{depth_first_search, Dfs, DfsEvent, Walker},
visit::{depth_first_search, Control, DfsEvent},
Graph,
};
@ -192,19 +191,28 @@ impl ControlFlowGraph {
}
pub fn is_reachabale(&self, from: BasicBlockId, to: BasicBlockId) -> bool {
if from == to {
return true;
}
let graph = &self.graph;
let mut dfs = Dfs::empty(graph);
dfs.reset(graph);
dfs.move_to(from);
dfs.iter(graph)
.take_while_inclusive(|it| {
!self
.basic_block(*it)
.instructions()
.iter()
.any(|it| matches!(it, Instruction { kind: InstructionKind::Unreachable, .. }))
})
.any(|x| x == to)
depth_first_search(&self.graph, Some(from), |event| match event {
DfsEvent::TreeEdge(a, b) => {
let unreachable = graph.edges_connecting(a, b).all(|edge| {
matches!(edge.weight(), EdgeType::NewFunction | EdgeType::Unreachable)
});
if unreachable {
Control::Prune
} else if b == to {
return Control::Break(true);
} else {
Control::Continue
}
}
_ => Control::Continue,
})
.break_value()
.unwrap_or(false)
}
pub fn is_cyclic(&self, node: BasicBlockId) -> bool {
@ -214,6 +222,18 @@ impl ControlFlowGraph {
})
.is_err()
}
pub fn has_conditional_path(&self, from: BasicBlockId, to: BasicBlockId) -> bool {
let graph = &self.graph;
// All nodes should be able to reach the `to` node, Otherwise we have a conditional/branching flow.
petgraph::algo::dijkstra(graph, from, Some(to), |e| match e.weight() {
EdgeType::NewFunction => 1,
EdgeType::Jump | EdgeType::Unreachable | EdgeType::Backedge | EdgeType::Normal => 0,
})
.into_iter()
.filter(|(_, val)| *val == 0)
.any(|(f, _)| !self.is_reachabale(f, to))
}
}
pub struct PreservedExpressionState {