solar_codegen/backend/evm/ir/
display.rs1use super::*;
4use solar_data_structures::fmt::FmtIteratorExt;
5
6impl EvmIrModule {
7 pub fn to_text(&self) -> impl fmt::Display + '_ {
9 fmt::from_fn(move |f| {
10 write!(
11 f,
12 "{}",
13 self.blocks.iter_enumerated().format_with("", |f, (block_id, block)| {
14 write!(f, "{}", display_block(self, block_id, block))
15 })
16 )
17 })
18 }
19}
20
21fn display_block<'a>(
22 module: &'a EvmIrModule,
23 block_id: EvmIrBlockId,
24 block: &'a EvmIrBlock,
25) -> impl fmt::Display + 'a {
26 fmt::from_fn(move |f| {
27 let entry = if module.entry_block == Some(block_id) { " (entry)" } else { "" };
28 let cold = if block.metadata.hotness == EvmIrBlockHotness::Cold { " [cold]" } else { "" };
29 write!(f, "{}{}{}", block.label, entry, cold)?;
30 if !block.entry_stack.is_empty() {
31 write!(f, " (in ")?;
32 for (i, &value) in block.entry_stack.iter().enumerate() {
33 if i != 0 {
34 write!(f, ", ")?;
35 }
36 write!(f, "{}", display_value(module, value))?;
37 }
38 write!(f, ")")?;
39 }
40 writeln!(f, ":")?;
41 for inst in &block.instructions {
42 writeln!(f, " {}", display_instruction(module, inst))?;
43 }
44 if let Some(term) = &block.terminator {
45 writeln!(f, " {}", display_terminator(module, term))?;
46 }
47 Ok(())
48 })
49}
50
51fn display_instruction<'a>(
52 module: &'a EvmIrModule,
53 inst: &'a EvmIrInstruction,
54) -> impl fmt::Display + 'a {
55 fmt::from_fn(move |f| {
56 if let Some(result) = inst.result {
57 write!(f, "{} = ", display_value(module, result))?;
58 }
59 write!(f, "{}", inst.mnemonic())?;
60 if !inst.operands.is_empty() {
61 write!(
62 f,
63 " {}",
64 inst.operands.iter().map(|operand| display_operand(module, operand)).format(", ")
65 )?;
66 }
67 write!(
68 f,
69 "{}",
70 display_metadata(&inst.metadata, Some(default_instruction_stack_effect(inst)))
71 )
72 })
73}
74
75fn display_terminator<'a>(
76 module: &'a EvmIrModule,
77 term: &'a EvmIrTerminator,
78) -> impl fmt::Display + 'a {
79 fmt::from_fn(move |f| {
80 match &term.kind {
81 EvmIrTerminatorKind::Fallthrough(target) => {
82 write!(f, "fallthrough {}", display_block_id(module, *target))?;
83 }
84 EvmIrTerminatorKind::Jump(target) => {
85 write!(f, "jump {}", display_block_id(module, *target))?;
86 }
87 EvmIrTerminatorKind::Branch { condition, then_block, else_block } => {
88 write!(
89 f,
90 "br {}, {}, {}",
91 display_operand(module, condition),
92 display_block_id(module, *then_block),
93 display_block_id(module, *else_block)
94 )?;
95 }
96 EvmIrTerminatorKind::Switch { value, default, cases } => {
97 write!(
98 f,
99 "switch {}, default {}, [",
100 display_operand(module, value),
101 display_block_id(module, *default)
102 )?;
103 write!(
104 f,
105 "{}",
106 cases.iter().format_with(", ", |f, (case, target)| {
107 write!(
108 f,
109 "{} => {}",
110 display_operand(module, case),
111 display_block_id(module, *target)
112 )
113 })
114 )?;
115 write!(f, "]")?;
116 }
117 EvmIrTerminatorKind::Return { offset, size } => {
118 write!(
119 f,
120 "return {}, {}",
121 display_operand(module, offset),
122 display_operand(module, size)
123 )?;
124 }
125 EvmIrTerminatorKind::Revert { offset, size } => {
126 write!(
127 f,
128 "revert {}, {}",
129 display_operand(module, offset),
130 display_operand(module, size)
131 )?;
132 }
133 EvmIrTerminatorKind::Stop => write!(f, "stop")?,
134 EvmIrTerminatorKind::Invalid => write!(f, "invalid")?,
135 EvmIrTerminatorKind::SelfDestruct { recipient } => {
136 write!(f, "selfdestruct {}", display_operand(module, recipient))?;
137 }
138 EvmIrTerminatorKind::RawOpcode(opcode) => {
139 write!(f, "terminal 0x{opcode:02x}")?;
140 }
141 }
142 write!(
143 f,
144 "{}",
145 display_metadata(&term.metadata, Some(default_terminator_stack_effect(&term.kind)))
146 )
147 })
148}
149
150fn display_metadata(
151 metadata: &EvmIrMetadata,
152 default_stack: Option<EvmIrStackEffect>,
153) -> impl fmt::Display + '_ {
154 enum Field<'a> {
155 Stack(EvmIrStackEffect),
156 Attr(&'a EvmIrMetadataItem),
157 }
158
159 fn display_field(field: Field<'_>) -> impl fmt::Display + '_ {
160 fmt::from_fn(move |f| match field {
161 Field::Stack(effect) => write!(f, "stack={}->{}", effect.inputs, effect.outputs),
162 Field::Attr(item) => {
163 write!(f, "{}", item.key)?;
164 if let Some(value) = &item.value {
165 write!(f, "={value}")?;
166 }
167 Ok(())
168 }
169 })
170 }
171
172 fmt::from_fn(move |f| {
173 if metadata.is_empty() {
174 return Ok(());
175 }
176 let mut fields =
177 Vec::with_capacity(metadata.attrs.len() + usize::from(metadata.stack.is_some()));
178 if let Some(stack) = metadata.stack
179 && Some(stack) != default_stack
180 {
181 fields.push(Field::Stack(stack));
182 }
183 fields.extend(metadata.attrs.iter().map(Field::Attr));
184 if fields.is_empty() {
185 return Ok(());
186 }
187 write!(f, " !meta({})", fields.into_iter().map(display_field).format(", "))
188 })
189}
190
191fn display_operand<'a>(
192 module: &'a EvmIrModule,
193 operand: &'a EvmIrOperand,
194) -> impl fmt::Display + 'a {
195 fmt::from_fn(move |f| match operand {
196 EvmIrOperand::Value(value) => write!(f, "{}", display_value(module, *value)),
197 EvmIrOperand::Immediate(value) => write!(f, "{}", display_u256(*value)),
198 EvmIrOperand::Block(block) => write!(f, "{}", display_block_id(module, *block)),
199 EvmIrOperand::Symbol(symbol) => write!(f, "{symbol}"),
200 })
201}
202
203fn display_value(module: &EvmIrModule, value: EvmIrValueId) -> impl fmt::Display + '_ {
204 fmt::from_fn(move |f| write!(f, "%{}", module.values[value].name))
205}
206
207fn display_block_id(module: &EvmIrModule, block: EvmIrBlockId) -> impl fmt::Display + '_ {
208 fmt::from_fn(move |f| write!(f, "{}", module.blocks[block].label))
209}
210
211fn display_u256(value: U256) -> impl fmt::Display {
212 fmt::from_fn(move |f| {
213 if let Ok(value) = u64::try_from(value)
214 && value < 1000
215 {
216 write!(f, "{value}")
217 } else {
218 write!(f, "{value:#x}")
219 }
220 })
221}