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 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 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 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 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}