ruka_mir/browser_graph/
emit_program.rs

1use cranelift_entity::EntityRef;
2
3use crate::naming::FunctionNames;
4use crate::{MirBlock, MirFuncId, MirFunction, MirProgram, MirTerminator};
5
6use super::formatting::{
7    format_block_summary, format_edge_label, format_function_params, format_jump_edge_label,
8    format_local_kind, format_local_list, format_local_repr, format_terminator,
9};
10use super::instr_format::format_instr;
11use super::*;
12
13impl<'a> BrowserMirGraphBuilder<'a> {
14    /// Create a browser graph builder for one MIR program.
15    pub(super) fn new(program: &'a MirProgram) -> Self {
16        Self {
17            program,
18            program_names: ProgramNames::from_program(program),
19            next_synthetic_id: 0,
20            next_edge_id: 0,
21            nodes: Vec::new(),
22            edges: Vec::new(),
23        }
24    }
25
26    /// Emit the full MIR program graph and return the root node id.
27    pub(super) fn emit_program(&mut self) -> String {
28        let program_id = self.node(
29            "mir-program".to_owned(),
30            "MirProgram",
31            Some(format!("{} functions", self.program.functions.len())),
32            "program",
33            vec![
34                format!("functions={}", self.program.functions.len()),
35                format!("structs={}", self.program.structs.len()),
36            ],
37        );
38
39        for decl in &self.program.structs {
40            let fields = decl
41                .fields
42                .iter()
43                .map(|field| format!("{}: {:?}", field.name, field.ty))
44                .collect::<Vec<_>>()
45                .join(", ");
46            self.nodes[0]
47                .details
48                .push(format!("struct {} {{ {} }}", decl.name, fields));
49        }
50
51        for (func_id, function) in self.program.functions.iter() {
52            self.emit_function(&program_id, func_id, function);
53        }
54
55        program_id
56    }
57
58    /// Emit one MIR function subgraph.
59    fn emit_function(&mut self, program_id: &str, func_id: MirFuncId, function: &MirFunction) {
60        let function_names = FunctionNames::from_function(function);
61        let func_ident = self.program_names.function_ident(func_id);
62        let function_id = self.synthetic_node(
63            "function",
64            &function.name,
65            Some(format!(
66                "entry b{} | {} blocks",
67                function.entry.index(),
68                function.blocks.len()
69            )),
70            "function",
71            vec![
72                "kind=MirFunction".to_owned(),
73                format!("name={}", function.name),
74                format!("id={func_ident}"),
75                format!("entry=b{}", function.entry.index()),
76                format!("arity={}", function.arity),
77                format!("return={}", function.return_ty),
78                format!(
79                    "params={}",
80                    format_function_params(function, &function_names)
81                ),
82                format!("locals={}", function.locals.len()),
83                format!("blocks={}", function.blocks.len()),
84            ],
85        );
86        self.edge(program_id, &function_id, Some("function"), "membership");
87
88        for (local_id, info) in function.locals.iter() {
89            let local_ident = function_names.local_ident(local_id);
90            self.append_detail(
91                &function_id,
92                format!(
93                    "local {}: {} {} {} ({})",
94                    local_ident,
95                    format_local_kind(info.kind),
96                    format_local_repr(info.repr),
97                    info.ty,
98                    info.debug_name.as_deref().unwrap_or("-")
99                ),
100            );
101        }
102        for binding in function.param_bindings() {
103            self.append_detail(
104                &function_id,
105                format!(
106                    "param {}: source={} local={} materialized={} ty={}",
107                    function_names.local_ident(binding.local_id),
108                    format_local_repr(binding.source_repr()),
109                    format_local_repr(binding.local_repr()),
110                    if binding.requires_materialization() {
111                        "yes"
112                    } else {
113                        "no"
114                    },
115                    binding.semantic_ty(),
116                ),
117            );
118        }
119
120        let mut block_node_ids = Vec::with_capacity(function.blocks.len());
121        for (block_id, block) in function.blocks.iter() {
122            let block_node_id =
123                self.emit_block(func_id, function, block_id.index(), block, &function_names);
124            if block_id == function.entry {
125                self.edge(&function_id, &block_node_id, Some("entry"), "membership");
126            }
127            block_node_ids.push(block_node_id);
128        }
129
130        for (block_id, block) in function.blocks.iter() {
131            let from = &block_node_ids[block_id.index()];
132            match &block.terminator {
133                MirTerminator::Jump { target, args } => {
134                    let to = &block_node_ids[target.index()];
135                    self.edge(
136                        from,
137                        to,
138                        format_jump_edge_label(args, &function_names).as_deref(),
139                        "cfg",
140                    );
141                }
142                MirTerminator::Branch {
143                    then_target,
144                    then_args,
145                    else_target,
146                    else_args,
147                    ..
148                } => {
149                    let then_to = &block_node_ids[then_target.index()];
150                    self.edge(
151                        from,
152                        then_to,
153                        Some(&format_edge_label("then", then_args, &function_names)),
154                        "cfg",
155                    );
156                    let else_to = &block_node_ids[else_target.index()];
157                    self.edge(
158                        from,
159                        else_to,
160                        Some(&format_edge_label("else", else_args, &function_names)),
161                        "cfg",
162                    );
163                }
164                MirTerminator::Return { .. } => {}
165            }
166        }
167    }
168
169    /// Emit one MIR block node.
170    fn emit_block(
171        &mut self,
172        func_id: MirFuncId,
173        function: &MirFunction,
174        block_index: usize,
175        block: &MirBlock,
176        function_names: &FunctionNames,
177    ) -> String {
178        let summary = Some(format_block_summary(
179            function,
180            block_index,
181            block,
182            function_names,
183        ));
184        let mut details = vec![format!(
185            "params={}",
186            format_local_list(&block.params, function_names)
187        )];
188        for (index, instr) in block.instrs.iter().enumerate() {
189            details.push(format!(
190                "{index:02}. {}",
191                format_instr(function, instr, function_names, &self.program_names)
192            ));
193        }
194        details.push(format!(
195            "terminator={}",
196            format_terminator(&block.terminator, function_names)
197        ));
198        self.node(
199            format!("f{}-b{block_index}", func_id.index()),
200            &format!("b{block_index}"),
201            summary,
202            "block",
203            details,
204        )
205    }
206}