feat(semantic/cfg): propagate unreachable edges through subgraphs. (#3648)

From this:

![Screenshot 2024-06-12 230152](https://github.com/oxc-project/oxc/assets/3788964/b489cee8-62ec-4483-9c9b-6e3d9858fec2)

To this:

![Screenshot 2024-06-12 230031](https://github.com/oxc-project/oxc/assets/3788964/674a6e42-5311-4ca6-940b-65d8e3ccc0f4)
This commit is contained in:
rzvxa 2024-06-13 08:34:12 +00:00
parent d9c5b33394
commit 9c31ed9178
22 changed files with 100 additions and 60 deletions

View file

@ -5,7 +5,10 @@ use oxc_allocator::Allocator;
use oxc_parser::Parser;
use oxc_semantic::{DebugDot, DisplayDot, EdgeType, SemanticBuilder};
use oxc_span::SourceType;
use petgraph::dot::{Config, Dot};
use petgraph::{
dot::{Config, Dot},
visit::EdgeRef,
};
// Instruction:
// 1. create a `test.js`,
@ -91,8 +94,10 @@ fn main() -> std::io::Result<()> {
&|_graph, edge| {
let weight = edge.weight();
let label = format!("label = \"{weight:?}\"");
if matches!(weight, EdgeType::Unreachable) {
format!("{label}, style = \"dotted\"")
if matches!(weight, EdgeType::Unreachable)
|| semantic.semantic.cfg().basic_block(edge.source()).unreachable
{
format!("{label}, style = \"dotted\" ")
} else {
label
}

View file

@ -4,6 +4,7 @@ use crate::ReturnInstructionKind;
use context::Ctx;
pub use context::{CtxCursor, CtxFlags};
use petgraph::Direction;
use super::{
AstNodeId, BasicBlock, BasicBlockId, CompactStr, ControlFlowGraph, EdgeType, ErrorEdgeKind,
@ -56,6 +57,17 @@ impl<'a> ControlFlowGraphBuilder<'a> {
self.basic_block_mut(self.current_node_ix)
}
/// # Panics
pub fn basic_block(&self, basic_block: BasicBlockId) -> &BasicBlock {
let idx = *self
.graph
.node_weight(basic_block)
.expect("expected `self.current_node_ix` to be a valid node index in self.graph");
self.basic_blocks
.get(idx)
.expect("expected `self.current_node_ix` to be a valid node index in self.graph")
}
/// # Panics
pub fn basic_block_mut(&mut self, basic_block: BasicBlockId) -> &mut BasicBlock {
let idx = *self
@ -99,6 +111,20 @@ impl<'a> ControlFlowGraphBuilder<'a> {
}
pub fn add_edge(&mut self, a: BasicBlockId, b: BasicBlockId, weight: EdgeType) {
if matches!(weight, EdgeType::NewFunction) {
self.basic_block_mut(b).unreachable = false;
} else if self.basic_block(a).unreachable {
if self.graph.edges_directed(b, Direction::Incoming).count() == 0 {
self.basic_block_mut(b).unreachable = true;
}
} else if !self
.basic_block(b)
.instructions()
.iter()
.any(|it| matches!(it, Instruction { kind: InstructionKind::Unreachable, .. }))
{
self.basic_block_mut(b).unreachable = false;
}
self.graph.add_edge(a, b, weight);
}
@ -193,12 +219,13 @@ impl<'a> ControlFlowGraphBuilder<'a> {
pub fn append_unreachable(&mut self) {
let current_node_ix = self.current_node_ix;
let basic_block_with_unreachable_graph_ix = self.new_basic_block_normal();
self.push_instruction(InstructionKind::Unreachable, None);
self.current_basic_block().unreachable = true;
self.add_edge(
current_node_ix,
basic_block_with_unreachable_graph_ix,
EdgeType::Unreachable,
);
self.push_instruction(InstructionKind::Unreachable, None);
}
#[inline]

View file

@ -3,7 +3,10 @@ use oxc_ast::{
AstKind,
};
use oxc_syntax::node::AstNodeId;
use petgraph::dot::{Config, Dot};
use petgraph::{
dot::{Config, Dot},
visit::EdgeRef,
};
use crate::{
AstNode, AstNodes, BasicBlock, ControlFlowGraph, EdgeType, Instruction, InstructionKind,
@ -56,7 +59,9 @@ impl DisplayDot for ControlFlowGraph {
&|_graph, edge| {
let weight = edge.weight();
let label = format!("label = \"{weight:?}\" ");
if matches!(weight, EdgeType::Unreachable) {
if matches!(weight, EdgeType::Unreachable)
|| self.basic_block(edge.source()).unreachable
{
format!("{label}, style = \"dotted\" ")
} else {
label
@ -115,7 +120,9 @@ impl DebugDot for ControlFlowGraph {
&|_graph, edge| {
let weight = edge.weight();
let label = format!("label = \"{weight:?}\" ");
if matches!(weight, EdgeType::Unreachable) {
if matches!(weight, EdgeType::Unreachable)
|| self.basic_block(edge.source()).unreachable
{
format!("{label}, style = \"dotted\" ")
} else {
label

View file

@ -116,11 +116,12 @@ pub enum CallType {
#[derive(Debug)]
pub struct BasicBlock {
pub instructions: Vec<Instruction>,
pub unreachable: bool,
}
impl BasicBlock {
fn new() -> Self {
BasicBlock { instructions: Vec::new() }
BasicBlock { instructions: Vec::new(), unreachable: false }
}
pub fn instructions(&self) -> &Vec<Instruction> {

View file

@ -9,9 +9,9 @@ digraph {
2 [ label = "unreachable" ]
3 [ label = "" ]
1 -> 0 [ label = "Error(Implicit)" ]
2 -> 0 [ label = "Error(Implicit)" ]
2 -> 0 [ label = "Error(Implicit)" , style = "dotted" ]
1 -> 2 [ label = "Unreachable" , style = "dotted" ]
3 -> 0 [ label = "Error(Implicit)" ]
2 -> 3 [ label = "Normal" ]
2 -> 3 [ label = "Normal" , style = "dotted" ]
1 -> 3 [ label = "Jump" ]
}

View file

@ -13,7 +13,7 @@ digraph {
1 -> 0 [ label = "Error(Implicit)" ]
3 -> 2 [ label = "Error(Implicit)" ]
1 -> 3 [ label = "NewFunction" ]
4 -> 2 [ label = "Error(Implicit)" ]
4 -> 2 [ label = "Error(Implicit)" , style = "dotted" ]
3 -> 4 [ label = "Unreachable" , style = "dotted" ]
5 -> 0 [ label = "Error(Implicit)" ]
1 -> 5 [ label = "Normal" ]

View file

@ -25,14 +25,14 @@ digraph {
6 -> 2 [ label = "Error(Implicit)" ]
4 -> 6 [ label = "Normal" ]
7 -> 2 [ label = "Error(Implicit)" ]
8 -> 2 [ label = "Error(Implicit)" ]
8 -> 2 [ label = "Error(Implicit)" , style = "dotted" ]
7 -> 8 [ label = "Unreachable" , style = "dotted" ]
9 -> 2 [ label = "Error(Implicit)" ]
9 -> 2 [ label = "Error(Implicit)" , style = "dotted" ]
10 -> 2 [ label = "Error(Implicit)" ]
6 -> 7 [ label = "Normal" ]
8 -> 9 [ label = "Normal" ]
9 -> 10 [ label = "Normal" ]
9 -> 7 [ label = "Backedge" ]
8 -> 9 [ label = "Normal" , style = "dotted" ]
9 -> 10 [ label = "Normal" , style = "dotted" ]
9 -> 7 [ label = "Backedge" , style = "dotted" ]
7 -> 10 [ label = "Jump" ]
11 -> 2 [ label = "Error(Implicit)" ]
3 -> 5 [ label = "Normal" ]

View file

@ -13,7 +13,7 @@ digraph {
1 -> 0 [ label = "Error(Implicit)" ]
3 -> 2 [ label = "Error(Implicit)" ]
1 -> 3 [ label = "NewFunction" ]
4 -> 2 [ label = "Error(Implicit)" ]
4 -> 2 [ label = "Error(Implicit)" , style = "dotted" ]
3 -> 4 [ label = "Unreachable" , style = "dotted" ]
5 -> 0 [ label = "Error(Implicit)" ]
1 -> 5 [ label = "Normal" ]

View file

@ -28,18 +28,18 @@ digraph {
4 -> 6 [ label = "Normal" ]
5 -> 6 [ label = "Normal" ]
7 -> 2 [ label = "Error(Implicit)" ]
8 -> 2 [ label = "Error(Implicit)" ]
8 -> 2 [ label = "Error(Implicit)" , style = "dotted" ]
7 -> 8 [ label = "Unreachable" , style = "dotted" ]
9 -> 2 [ label = "Error(Implicit)" ]
10 -> 2 [ label = "Error(Implicit)" ]
10 -> 2 [ label = "Error(Implicit)" , style = "dotted" ]
9 -> 10 [ label = "Unreachable" , style = "dotted" ]
11 -> 2 [ label = "Error(Implicit)" ]
11 -> 2 [ label = "Error(Implicit)" , style = "dotted" ]
3 -> 4 [ label = "Normal" ]
8 -> 11 [ label = "Normal" ]
8 -> 11 [ label = "Normal" , style = "dotted" ]
6 -> 7 [ label = "Jump" ]
3 -> 9 [ label = "Normal" ]
10 -> 11 [ label = "Normal" ]
12 -> 2 [ label = "Error(Implicit)" ]
10 -> 11 [ label = "Normal" , style = "dotted" ]
12 -> 2 [ label = "Error(Implicit)" , style = "dotted" ]
11 -> 12 [ label = "Unreachable" , style = "dotted" ]
13 -> 0 [ label = "Error(Implicit)" ]
1 -> 13 [ label = "Normal" ]

View file

@ -30,21 +30,21 @@ digraph {
6 -> 2 [ label = "Error(Implicit)" ]
7 -> 2 [ label = "Error(Implicit)" ]
8 -> 2 [ label = "Error(Implicit)" ]
9 -> 2 [ label = "Error(Implicit)" ]
9 -> 2 [ label = "Error(Implicit)" , style = "dotted" ]
8 -> 9 [ label = "Unreachable" , style = "dotted" ]
10 -> 2 [ label = "Error(Implicit)" ]
11 -> 2 [ label = "Error(Implicit)" ]
12 -> 2 [ label = "Error(Implicit)" ]
13 -> 2 [ label = "Error(Implicit)" ]
13 -> 2 [ label = "Error(Implicit)" , style = "dotted" ]
12 -> 13 [ label = "Unreachable" , style = "dotted" ]
14 -> 2 [ label = "Error(Implicit)" ]
10 -> 11 [ label = "Normal" ]
13 -> 14 [ label = "Normal" ]
13 -> 14 [ label = "Normal" , style = "dotted" ]
11 -> 12 [ label = "Jump" ]
10 -> 14 [ label = "Normal" ]
15 -> 2 [ label = "Error(Implicit)" ]
6 -> 7 [ label = "Normal" ]
9 -> 15 [ label = "Normal" ]
9 -> 15 [ label = "Normal" , style = "dotted" ]
7 -> 8 [ label = "Jump" ]
6 -> 10 [ label = "Normal" ]
14 -> 15 [ label = "Normal" ]

View file

@ -21,11 +21,11 @@ digraph {
2 -> 4 [ label = "Normal" ]
5 -> 0 [ label = "Error(Implicit)" ]
6 -> 0 [ label = "Error(Implicit)" ]
7 -> 0 [ label = "Error(Implicit)" ]
7 -> 0 [ label = "Error(Implicit)" , style = "dotted" ]
6 -> 7 [ label = "Unreachable" , style = "dotted" ]
8 -> 0 [ label = "Error(Implicit)" ]
4 -> 5 [ label = "Normal" ]
7 -> 8 [ label = "Normal" ]
7 -> 8 [ label = "Normal" , style = "dotted" ]
5 -> 6 [ label = "Jump" ]
4 -> 8 [ label = "Normal" ]
9 -> 0 [ label = "Error(Implicit)" ]

View file

@ -22,20 +22,20 @@ digraph {
1 -> 3 [ label = "NewFunction" ]
5 -> 2 [ label = "Error(Implicit)" ]
5 -> 4 [ label = "Finalize" ]
6 -> 2 [ label = "Error(Implicit)" ]
6 -> 4 [ label = "Finalize" ]
6 -> 2 [ label = "Error(Implicit)" , style = "dotted" ]
6 -> 4 [ label = "Finalize" , style = "dotted" ]
5 -> 6 [ label = "Unreachable" , style = "dotted" ]
7 -> 2 [ label = "Error(Implicit)" ]
4 -> 7 [ label = "Normal" ]
8 -> 2 [ label = "Error(Implicit)" ]
8 -> 2 [ label = "Error(Implicit)" , style = "dotted" ]
7 -> 8 [ label = "Unreachable" , style = "dotted" ]
9 -> 2 [ label = "Error(Implicit)" ]
9 -> 2 [ label = "Error(Implicit)" , style = "dotted" ]
3 -> 5 [ label = "Normal" ]
8 -> 9 [ label = "Join" ]
8 -> 9 [ label = "Join" , style = "dotted" ]
10 -> 2 [ label = "Error(Implicit)" ]
9 -> 10 [ label = "Normal" ]
9 -> 10 [ label = "Normal" , style = "dotted" ]
7 -> 10 [ label = "Jump" ]
11 -> 2 [ label = "Error(Implicit)" ]
11 -> 2 [ label = "Error(Implicit)" , style = "dotted" ]
10 -> 11 [ label = "Unreachable" , style = "dotted" ]
12 -> 0 [ label = "Error(Implicit)" ]
1 -> 12 [ label = "Normal" ]

View file

@ -14,9 +14,9 @@ digraph {
1 -> 0 [ label = "Error(Implicit)" ]
3 -> 2 [ label = "Error(Implicit)" ]
1 -> 3 [ label = "NewFunction" ]
4 -> 2 [ label = "Error(Implicit)" ]
4 -> 2 [ label = "Error(Implicit)" , style = "dotted" ]
3 -> 4 [ label = "Unreachable" , style = "dotted" ]
5 -> 2 [ label = "Error(Implicit)" ]
5 -> 2 [ label = "Error(Implicit)" , style = "dotted" ]
4 -> 5 [ label = "Unreachable" , style = "dotted" ]
6 -> 0 [ label = "Error(Implicit)" ]
1 -> 6 [ label = "Normal" ]

View file

@ -14,9 +14,9 @@ digraph {
1 -> 0 [ label = "Error(Implicit)" ]
3 -> 2 [ label = "Error(Implicit)" ]
1 -> 3 [ label = "NewFunction" ]
4 -> 2 [ label = "Error(Implicit)" ]
4 -> 2 [ label = "Error(Implicit)" , style = "dotted" ]
3 -> 4 [ label = "Unreachable" , style = "dotted" ]
5 -> 2 [ label = "Error(Implicit)" ]
5 -> 2 [ label = "Error(Implicit)" , style = "dotted" ]
4 -> 5 [ label = "Unreachable" , style = "dotted" ]
6 -> 0 [ label = "Error(Implicit)" ]
1 -> 6 [ label = "Normal" ]

View file

@ -13,7 +13,7 @@ digraph {
1 -> 0 [ label = "Error(Implicit)" ]
3 -> 2 [ label = "Error(Implicit)" ]
1 -> 3 [ label = "NewFunction" ]
4 -> 2 [ label = "Error(Implicit)" ]
4 -> 2 [ label = "Error(Implicit)" , style = "dotted" ]
3 -> 4 [ label = "Unreachable" , style = "dotted" ]
5 -> 0 [ label = "Error(Implicit)" ]
1 -> 5 [ label = "Normal" ]

View file

@ -13,7 +13,7 @@ digraph {
1 -> 0 [ label = "Error(Implicit)" ]
3 -> 2 [ label = "Error(Implicit)" ]
1 -> 3 [ label = "NewFunction" ]
4 -> 2 [ label = "Error(Implicit)" ]
4 -> 2 [ label = "Error(Implicit)" , style = "dotted" ]
3 -> 4 [ label = "Unreachable" , style = "dotted" ]
5 -> 0 [ label = "Error(Implicit)" ]
1 -> 5 [ label = "Normal" ]

View file

@ -13,7 +13,7 @@ digraph {
1 -> 0 [ label = "Error(Implicit)" ]
3 -> 2 [ label = "Error(Implicit)" ]
1 -> 3 [ label = "NewFunction" ]
4 -> 2 [ label = "Error(Implicit)" ]
4 -> 2 [ label = "Error(Implicit)" , style = "dotted" ]
3 -> 4 [ label = "Unreachable" , style = "dotted" ]
5 -> 0 [ label = "Error(Implicit)" ]
1 -> 5 [ label = "Normal" ]

View file

@ -13,7 +13,7 @@ digraph {
1 -> 0 [ label = "Error(Implicit)" ]
3 -> 2 [ label = "Error(Implicit)" ]
1 -> 3 [ label = "NewFunction" ]
4 -> 2 [ label = "Error(Implicit)" ]
4 -> 2 [ label = "Error(Implicit)" , style = "dotted" ]
3 -> 4 [ label = "Unreachable" , style = "dotted" ]
5 -> 0 [ label = "Error(Implicit)" ]
1 -> 5 [ label = "Normal" ]

View file

@ -21,13 +21,13 @@ digraph {
4 -> 0 [ label = "Error(Implicit)" ]
5 -> 0 [ label = "Error(Implicit)" ]
4 -> 5 [ label = "Jump" ]
6 -> 0 [ label = "Error(Implicit)" ]
6 -> 0 [ label = "Error(Implicit)" , style = "dotted" ]
5 -> 6 [ label = "Unreachable" , style = "dotted" ]
7 -> 0 [ label = "Error(Implicit)" ]
8 -> 0 [ label = "Error(Implicit)" ]
7 -> 8 [ label = "Jump" ]
4 -> 7 [ label = "Normal" ]
6 -> 7 [ label = "Normal" ]
6 -> 7 [ label = "Normal" , style = "dotted" ]
3 -> 4 [ label = "Normal" ]
3 -> 7 [ label = "Normal" ]
9 -> 0 [ label = "Error(Implicit)" ]

View file

@ -33,7 +33,7 @@ digraph {
4 -> 2 [ label = "Error(Implicit)" ]
5 -> 2 [ label = "Error(Implicit)" ]
4 -> 5 [ label = "Jump" ]
6 -> 2 [ label = "Error(Implicit)" ]
6 -> 2 [ label = "Error(Implicit)" , style = "dotted" ]
5 -> 6 [ label = "Unreachable" , style = "dotted" ]
7 -> 2 [ label = "Error(Implicit)" ]
8 -> 2 [ label = "Error(Implicit)" ]
@ -41,12 +41,12 @@ digraph {
9 -> 2 [ label = "Error(Implicit)" ]
10 -> 2 [ label = "Error(Implicit)" ]
9 -> 10 [ label = "Jump" ]
11 -> 2 [ label = "Error(Implicit)" ]
11 -> 2 [ label = "Error(Implicit)" , style = "dotted" ]
10 -> 11 [ label = "Unreachable" , style = "dotted" ]
12 -> 2 [ label = "Error(Implicit)" ]
13 -> 2 [ label = "Error(Implicit)" ]
12 -> 13 [ label = "Jump" ]
14 -> 2 [ label = "Error(Implicit)" ]
14 -> 2 [ label = "Error(Implicit)" , style = "dotted" ]
13 -> 14 [ label = "Unreachable" , style = "dotted" ]
15 -> 2 [ label = "Error(Implicit)" ]
16 -> 2 [ label = "Error(Implicit)" ]
@ -54,14 +54,14 @@ digraph {
17 -> 2 [ label = "Error(Implicit)" ]
18 -> 2 [ label = "Error(Implicit)" ]
17 -> 18 [ label = "Jump" ]
19 -> 2 [ label = "Error(Implicit)" ]
19 -> 2 [ label = "Error(Implicit)" , style = "dotted" ]
18 -> 19 [ label = "Unreachable" , style = "dotted" ]
4 -> 7 [ label = "Normal" ]
4 -> 9 [ label = "Normal" ]
4 -> 12 [ label = "Normal" ]
4 -> 15 [ label = "Normal" ]
4 -> 17 [ label = "Normal" ]
6 -> 7 [ label = "Normal" ]
6 -> 7 [ label = "Normal" , style = "dotted" ]
3 -> 4 [ label = "Normal" ]
7 -> 9 [ label = "Normal" ]
7 -> 12 [ label = "Normal" ]
@ -72,21 +72,21 @@ digraph {
9 -> 12 [ label = "Normal" ]
9 -> 15 [ label = "Normal" ]
9 -> 17 [ label = "Normal" ]
11 -> 12 [ label = "Normal" ]
11 -> 12 [ label = "Normal" , style = "dotted" ]
3 -> 9 [ label = "Normal" ]
12 -> 15 [ label = "Normal" ]
12 -> 17 [ label = "Normal" ]
14 -> 15 [ label = "Normal" ]
14 -> 15 [ label = "Normal" , style = "dotted" ]
3 -> 12 [ label = "Normal" ]
15 -> 17 [ label = "Normal" ]
16 -> 17 [ label = "Normal" ]
3 -> 15 [ label = "Normal" ]
3 -> 17 [ label = "Normal" ]
20 -> 2 [ label = "Error(Implicit)" ]
19 -> 20 [ label = "Normal" ]
19 -> 20 [ label = "Normal" , style = "dotted" ]
5 -> 20 [ label = "Jump" ]
10 -> 20 [ label = "Jump" ]
21 -> 2 [ label = "Error(Implicit)" ]
21 -> 2 [ label = "Error(Implicit)" , style = "dotted" ]
20 -> 21 [ label = "Unreachable" , style = "dotted" ]
22 -> 0 [ label = "Error(Implicit)" ]
1 -> 22 [ label = "Normal" ]

View file

@ -17,13 +17,13 @@ digraph {
1 -> 0 [ label = "Error(Implicit)" ]
3 -> 2 [ label = "Error(Implicit)" ]
1 -> 3 [ label = "NewFunction" ]
4 -> 2 [ label = "Error(Implicit)" ]
4 -> 2 [ label = "Error(Implicit)" , style = "dotted" ]
3 -> 4 [ label = "Unreachable" , style = "dotted" ]
5 -> 0 [ label = "Error(Implicit)" ]
1 -> 5 [ label = "Normal" ]
7 -> 6 [ label = "Error(Implicit)" ]
5 -> 7 [ label = "NewFunction" ]
8 -> 6 [ label = "Error(Implicit)" ]
8 -> 6 [ label = "Error(Implicit)" , style = "dotted" ]
7 -> 8 [ label = "Unreachable" , style = "dotted" ]
9 -> 0 [ label = "Error(Implicit)" ]
5 -> 9 [ label = "Normal" ]

View file

@ -26,14 +26,14 @@ digraph {
4 -> 6 [ label = "Normal" ]
5 -> 6 [ label = "Normal" ]
7 -> 2 [ label = "Error(Implicit)" ]
8 -> 2 [ label = "Error(Implicit)" ]
8 -> 2 [ label = "Error(Implicit)" , style = "dotted" ]
7 -> 8 [ label = "Unreachable" , style = "dotted" ]
9 -> 2 [ label = "Error(Implicit)" ]
3 -> 4 [ label = "Normal" ]
4 -> 7 [ label = "Jump" ]
8 -> 4 [ label = "Backedge" ]
8 -> 4 [ label = "Backedge" , style = "dotted" ]
4 -> 9 [ label = "Normal" ]
10 -> 2 [ label = "Error(Implicit)" ]
10 -> 2 [ label = "Error(Implicit)" , style = "dotted" ]
9 -> 10 [ label = "Unreachable" , style = "dotted" ]
11 -> 0 [ label = "Error(Implicit)" ]
1 -> 11 [ label = "Normal" ]