feat(jit): CFG DOT出力機能を追加 (ChatGPT5実装)
- src/jit/lower/cfg_dot.rs: Control Flow GraphをGraphviz DOT形式で出力 - LowerCoreからdump_cfg_dot関数をエクスポート - NYASH_JIT_DOT環境変数でCFG可視化が可能に ChatGPT5さんによるリファクタリング作業の一環 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
60
src/jit/lower/cfg_dot.rs
Normal file
60
src/jit/lower/cfg_dot.rs
Normal file
@ -0,0 +1,60 @@
|
||||
pub fn dump_cfg_dot(func: &crate::mir::MirFunction, path: &str, phi_min: bool) -> std::io::Result<()> {
|
||||
use std::io::Write;
|
||||
let mut out = String::new();
|
||||
out.push_str(&format!("digraph \"{}\" {{\n", func.signature.name));
|
||||
out.push_str(" node [shape=box, fontsize=10];\n");
|
||||
// Derive simple bool sets: compare dsts are bool; phi of all-bool inputs are bool
|
||||
let mut bool_values: std::collections::HashSet<crate::mir::ValueId> = std::collections::HashSet::new();
|
||||
for (_bb_id, bb) in func.blocks.iter() {
|
||||
for ins in bb.instructions.iter() {
|
||||
if let crate::mir::MirInstruction::Compare { dst, .. } = ins { bool_values.insert(*dst); }
|
||||
}
|
||||
}
|
||||
let mut bool_phi: std::collections::HashSet<crate::mir::ValueId> = std::collections::HashSet::new();
|
||||
if phi_min {
|
||||
for (_bb_id, bb) in func.blocks.iter() {
|
||||
for ins in bb.instructions.iter() {
|
||||
if let crate::mir::MirInstruction::Phi { dst, inputs } = ins {
|
||||
if !inputs.is_empty() && inputs.iter().all(|(_, v)| bool_values.contains(v)) {
|
||||
bool_phi.insert(*dst);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Sort blocks for deterministic output
|
||||
let mut bb_ids: Vec<_> = func.blocks.keys().copied().collect();
|
||||
bb_ids.sort_by_key(|b| b.0);
|
||||
// Emit nodes with labels
|
||||
for bb_id in bb_ids.iter() {
|
||||
let bb = func.blocks.get(bb_id).unwrap();
|
||||
let phi_count = bb.instructions.iter().filter(|ins| matches!(ins, crate::mir::MirInstruction::Phi { .. })).count();
|
||||
let phi_b1_count = bb.instructions.iter().filter(|ins| match ins { crate::mir::MirInstruction::Phi { dst, .. } => bool_phi.contains(dst), _ => false }).count();
|
||||
let mut label = format!("bb{}", bb_id.0);
|
||||
if phi_min && phi_count > 0 {
|
||||
if phi_b1_count > 0 { label = format!("{}\\nphi:{} (b1:{})", label, phi_count, phi_b1_count); }
|
||||
else { label = format!("{}\\nphi:{}", label, phi_count); }
|
||||
}
|
||||
if *bb_id == func.entry_block { label = format!("{}\\nENTRY", label); }
|
||||
out.push_str(&format!(" n{} [label=\"{}\"];\n", bb_id.0, label));
|
||||
}
|
||||
// Emit edges based on terminators
|
||||
for bb_id in bb_ids.iter() {
|
||||
let bb = func.blocks.get(bb_id).unwrap();
|
||||
if let Some(term) = &bb.terminator {
|
||||
match term {
|
||||
crate::mir::MirInstruction::Jump { target } => {
|
||||
out.push_str(&format!(" n{} -> n{};\n", bb_id.0, target.0));
|
||||
}
|
||||
crate::mir::MirInstruction::Branch { then_bb, else_bb, .. } => {
|
||||
// Branch condition is boolean (b1)
|
||||
out.push_str(&format!(" n{} -> n{} [label=\"then cond:b1\"];\n", bb_id.0, then_bb.0));
|
||||
out.push_str(&format!(" n{} -> n{} [label=\"else cond:b1\"];\n", bb_id.0, else_bb.0));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
out.push_str("}\n");
|
||||
std::fs::write(path, out)
|
||||
}
|
||||
@ -1008,65 +1008,5 @@ impl LowerCore {
|
||||
}
|
||||
}
|
||||
|
||||
/// Emit a simple DOT graph for a MIR function's CFG.
|
||||
/// Includes block ids, successor edges, and PHI counts per block when phi_min is enabled.
|
||||
pub fn dump_cfg_dot(func: &crate::mir::MirFunction, path: &str, phi_min: bool) -> std::io::Result<()> {
|
||||
use std::io::Write;
|
||||
let mut out = String::new();
|
||||
out.push_str(&format!("digraph \"{}\" {{\n", func.signature.name));
|
||||
out.push_str(" node [shape=box, fontsize=10];\n");
|
||||
// Derive simple bool sets: compare dsts are bool; phi of all-bool inputs are bool
|
||||
let mut bool_values: std::collections::HashSet<crate::mir::ValueId> = std::collections::HashSet::new();
|
||||
for (_bb_id, bb) in func.blocks.iter() {
|
||||
for ins in bb.instructions.iter() {
|
||||
if let crate::mir::MirInstruction::Compare { dst, .. } = ins { bool_values.insert(*dst); }
|
||||
}
|
||||
}
|
||||
let mut bool_phi: std::collections::HashSet<crate::mir::ValueId> = std::collections::HashSet::new();
|
||||
if phi_min {
|
||||
for (_bb_id, bb) in func.blocks.iter() {
|
||||
for ins in bb.instructions.iter() {
|
||||
if let crate::mir::MirInstruction::Phi { dst, inputs } = ins {
|
||||
if !inputs.is_empty() && inputs.iter().all(|(_, v)| bool_values.contains(v)) {
|
||||
bool_phi.insert(*dst);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Sort blocks for deterministic output
|
||||
let mut bb_ids: Vec<_> = func.blocks.keys().copied().collect();
|
||||
bb_ids.sort_by_key(|b| b.0);
|
||||
// Emit nodes with labels
|
||||
for bb_id in bb_ids.iter() {
|
||||
let bb = func.blocks.get(bb_id).unwrap();
|
||||
let phi_count = bb.instructions.iter().filter(|ins| matches!(ins, crate::mir::MirInstruction::Phi { .. })).count();
|
||||
let phi_b1_count = bb.instructions.iter().filter(|ins| match ins { crate::mir::MirInstruction::Phi { dst, .. } => bool_phi.contains(dst), _ => false }).count();
|
||||
let mut label = format!("bb{}", bb_id.0);
|
||||
if phi_min && phi_count > 0 {
|
||||
if phi_b1_count > 0 { label = format!("{}\\nphi:{} (b1:{})", label, phi_count, phi_b1_count); }
|
||||
else { label = format!("{}\\nphi:{}", label, phi_count); }
|
||||
}
|
||||
if *bb_id == func.entry_block { label = format!("{}\\nENTRY", label); }
|
||||
out.push_str(&format!(" n{} [label=\"{}\"];\n", bb_id.0, label));
|
||||
}
|
||||
// Emit edges based on terminators
|
||||
for bb_id in bb_ids.iter() {
|
||||
let bb = func.blocks.get(bb_id).unwrap();
|
||||
if let Some(term) = &bb.terminator {
|
||||
match term {
|
||||
crate::mir::MirInstruction::Jump { target } => {
|
||||
out.push_str(&format!(" n{} -> n{};\n", bb_id.0, target.0));
|
||||
}
|
||||
crate::mir::MirInstruction::Branch { then_bb, else_bb, .. } => {
|
||||
// Branch condition is boolean (b1)
|
||||
out.push_str(&format!(" n{} -> n{} [label=\"then cond:b1\"];\n", bb_id.0, then_bb.0));
|
||||
out.push_str(&format!(" n{} -> n{} [label=\"else cond:b1\"];\n", bb_id.0, else_bb.0));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
out.push_str("}\n");
|
||||
std::fs::write(path, out)
|
||||
}
|
||||
pub use super::cfg_dot::dump_cfg_dot;
|
||||
|
||||
|
||||
@ -2,3 +2,4 @@
|
||||
pub mod core;
|
||||
pub mod builder;
|
||||
pub mod extern_thunks;
|
||||
pub mod cfg_dot;
|
||||
|
||||
Reference in New Issue
Block a user