1use alloc::format;
2use core::fmt;
3
4use super::{Context, Operation};
5use crate::{
6 AttributeValue, EntityWithId, SuccessorOperands, Value,
7 formatter::{Document, PrettyPrint},
8 matchers::Matcher,
9 traits::BranchOpInterface,
10};
11
12#[derive(Debug)]
13pub struct OpPrintingFlags {
14 pub print_entry_block_headers: bool,
15 pub print_intrinsic_attributes: bool,
16 pub print_source_locations: bool,
17}
18
19impl Default for OpPrintingFlags {
20 fn default() -> Self {
21 Self {
22 print_entry_block_headers: true,
23 print_intrinsic_attributes: false,
24 print_source_locations: false,
25 }
26 }
27}
28
29pub trait OpPrinter {
33 fn print(&self, flags: &OpPrintingFlags, context: &Context) -> Document;
34}
35
36impl OpPrinter for Operation {
37 #[inline]
38 fn print(&self, flags: &OpPrintingFlags, context: &Context) -> Document {
39 if let Some(op_printer) = self.as_trait::<dyn OpPrinter>() {
40 op_printer.print(flags, context)
41 } else {
42 let printer = OperationPrinter {
43 op: self,
44 flags,
45 context,
46 };
47 printer.render()
48 }
49 }
50}
51
52impl fmt::Display for Operation {
53 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
54 let flags = OpPrintingFlags::default();
55 let context = self.context();
56 let doc = self.print(&flags, context);
57 write!(f, "{doc}")
58 }
59}
60
61pub trait AttrPrinter {
62 fn print(&self, flags: &OpPrintingFlags, context: &Context) -> Document;
63}
64
65impl<T: PrettyPrint + AttributeValue> AttrPrinter for T {
66 default fn print(&self, _flags: &OpPrintingFlags, _context: &Context) -> Document {
67 PrettyPrint::render(self)
68 }
69}
70
71impl AttrPrinter for crate::Attribute {
72 fn print(&self, flags: &OpPrintingFlags, context: &Context) -> Document {
73 use crate::formatter::*;
74
75 match self.value() {
76 None => text(format!("#[{}]", self.name.as_str())),
77 Some(value) => {
78 const_text("#[")
79 + text(self.name.as_str())
80 + const_text(" = ")
81 + value.print(flags, context)
82 + const_text("]")
83 }
84 }
85 }
86}
87
88impl AttrPrinter for crate::OpFoldResult {
89 fn print(&self, flags: &OpPrintingFlags, context: &Context) -> Document {
90 use crate::formatter::*;
91
92 match self {
93 Self::Attribute(attr) => attr.print(flags, context),
94 Self::Value(value) => display(value.borrow().id()),
95 }
96 }
97}
98
99impl<T: AttrPrinter> AttrPrinter for [T] {
100 fn print(&self, flags: &OpPrintingFlags, context: &Context) -> Document {
101 use crate::formatter::*;
102
103 let mut doc = Document::Empty;
104 for (i, item) in self.iter().enumerate() {
105 if i == 0 {
106 doc += const_text(", ");
107 }
108
109 doc += item.print(flags, context);
110 }
111 doc
112 }
113}
114
115pub fn render_operation_results(op: &Operation) -> crate::formatter::Document {
116 use crate::formatter::*;
117
118 let results = op.results();
119 let doc = results.iter().fold(Document::Empty, |acc, result| {
120 if acc.is_empty() {
121 display(result.borrow().id())
122 } else {
123 acc + const_text(", ") + display(result.borrow().id())
124 }
125 });
126 if doc.is_empty() {
127 doc
128 } else {
129 doc + const_text(" = ")
130 }
131}
132
133pub fn render_operation_operands(op: &Operation) -> crate::formatter::Document {
134 use crate::formatter::*;
135
136 let operands = op.operands();
137 operands.iter().fold(Document::Empty, |acc, operand| {
138 let operand = operand.borrow();
139 let value = operand.value();
140 if acc.is_empty() {
141 display(value.id())
142 } else {
143 acc + const_text(", ") + display(value.id())
144 }
145 })
146}
147
148pub fn render_operation_result_types(op: &Operation) -> crate::formatter::Document {
149 use crate::formatter::*;
150
151 let results = op.results();
152 let result_types = results.iter().fold(Document::Empty, |acc, result| {
153 if acc.is_empty() {
154 text(format!("{}", result.borrow().ty()))
155 } else {
156 acc + const_text(", ") + text(format!("{}", result.borrow().ty()))
157 }
158 });
159 if result_types.is_empty() {
160 result_types
161 } else {
162 const_text(" : ") + result_types
163 }
164}
165
166pub fn render_regions(op: &Operation, flags: &OpPrintingFlags) -> crate::formatter::Document {
167 use crate::formatter::*;
168 const_text(" ")
169 + op.regions.iter().fold(Document::Empty, |acc, region| {
170 let doc = region.print(flags);
171 if acc.is_empty() {
172 doc
173 } else {
174 acc + const_text(" ") + doc
175 }
176 })
177 + const_text(";")
178}
179
180pub fn render_source_location(op: &Operation, context: &Context) -> crate::formatter::Document {
181 use crate::formatter::*;
182
183 if op.span.is_unknown() {
185 return Document::Empty;
186 }
187
188 let session = context.session();
190 if let Ok(source_file) = session.source_manager.get(op.span.source_id()) {
191 let location = source_file.location(op.span);
192 let filename = source_file.uri().as_str();
194 let loc_str = format!(
195 " #loc(\"{}\":{}:{})",
196 filename,
197 location.line.to_u32(),
198 location.column.to_u32()
199 );
200 return text(loc_str);
201 }
202
203 Document::Empty
204}
205
206struct OperationPrinter<'a> {
207 op: &'a Operation,
208 flags: &'a OpPrintingFlags,
209 context: &'a Context,
210}
211
212impl PrettyPrint for OperationPrinter<'_> {
224 fn render(&self) -> crate::formatter::Document {
225 use crate::formatter::*;
226
227 let doc = render_operation_results(self.op) + display(self.op.name()) + const_text(" ");
228 let doc = if let Some(value) = crate::matchers::constant().matches(self.op) {
229 doc + value.print(self.flags, self.context)
230 } else if let Some(branch) = self.op.as_trait::<dyn BranchOpInterface>() {
231 let operands = branch.operands().group(0);
233 let doc = if !operands.is_empty() {
234 operands.iter().enumerate().fold(doc, |doc, (i, operand)| {
235 let operand = operand.borrow();
236 let value = operand.value();
237 if i > 0 {
238 doc + const_text(", ") + display(value.id())
239 } else {
240 doc + display(value.id())
241 }
242 }) + const_text(" ")
243 } else {
244 doc
245 };
246 branch.successors().iter().enumerate().fold(doc, |doc, (succ_index, succ)| {
248 let doc = if succ_index > 0 {
249 doc + const_text(", ") + display(succ.block.borrow().successor())
250 } else {
251 doc + display(succ.block.borrow().successor())
252 };
253
254 let operands = branch.get_successor_operands(succ_index);
255 if !operands.is_empty() {
256 let doc = doc + const_text("(");
257 operands.forwarded().iter().enumerate().fold(doc, |doc, (i, operand)| {
258 if !operand.is_linked() {
259 if i > 0 {
260 doc + const_text(", ") + const_text("<unlinked>")
261 } else {
262 doc + const_text("<unlinked>")
263 }
264 } else {
265 let operand = operand.borrow();
266 let value = operand.value();
267 if i > 0 {
268 doc + const_text(", ") + display(value.id())
269 } else {
270 doc + display(value.id())
271 }
272 }
273 }) + const_text(")")
274 } else {
275 doc
276 }
277 })
278 } else {
279 doc + render_operation_operands(self.op)
280 };
281
282 let doc = doc + render_operation_result_types(self.op);
283
284 let attrs = self.op.attrs.iter().fold(Document::Empty, |acc, attr| {
285 if !self.flags.print_intrinsic_attributes && attr.intrinsic {
287 return acc;
288 }
289 let doc = if let Some(value) = attr.value() {
290 const_text("#[")
291 + display(attr.name)
292 + const_text(" = ")
293 + value.print(self.flags, self.context)
294 + const_text("]")
295 } else {
296 text(format!("#[{}]", &attr.name))
297 };
298 if acc.is_empty() {
299 doc
300 } else {
301 acc + const_text(" ") + doc
302 }
303 });
304
305 let doc = if attrs.is_empty() {
306 doc
307 } else {
308 doc + const_text(" ") + attrs
309 };
310
311 let doc = if self.flags.print_source_locations {
313 doc + render_source_location(self.op, self.context)
314 } else {
315 doc
316 };
317
318 if self.op.has_regions() {
319 doc + render_regions(self.op, self.flags)
320 } else {
321 doc + const_text(";")
322 }
323 }
324}