midenc_hir/ir/
print.rs

1use alloc::format;
2use core::fmt;
3
4use super::{Context, Operation};
5use crate::{
6    formatter::{Document, PrettyPrint},
7    matchers::Matcher,
8    traits::BranchOpInterface,
9    AttributeValue, EntityWithId, SuccessorOperands, Value,
10};
11
12#[derive(Debug)]
13pub struct OpPrintingFlags {
14    pub print_entry_block_headers: bool,
15    pub print_intrinsic_attributes: bool,
16}
17
18impl Default for OpPrintingFlags {
19    fn default() -> Self {
20        Self {
21            print_entry_block_headers: true,
22            print_intrinsic_attributes: false,
23        }
24    }
25}
26
27/// The `OpPrinter` trait is expected to be implemented by all [Op] impls as a prequisite.
28///
29/// The actual implementation is typically generated as part of deriving [Op].
30pub trait OpPrinter {
31    fn print(&self, flags: &OpPrintingFlags, context: &Context) -> Document;
32}
33
34impl OpPrinter for Operation {
35    #[inline]
36    fn print(&self, flags: &OpPrintingFlags, context: &Context) -> Document {
37        if let Some(op_printer) = self.as_trait::<dyn OpPrinter>() {
38            op_printer.print(flags, context)
39        } else {
40            let printer = OperationPrinter {
41                op: self,
42                flags,
43                context,
44            };
45            printer.render()
46        }
47    }
48}
49
50impl fmt::Display for Operation {
51    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
52        let flags = OpPrintingFlags::default();
53        let context = self.context();
54        let doc = self.print(&flags, context);
55        write!(f, "{doc}")
56    }
57}
58
59pub trait AttrPrinter {
60    fn print(&self, flags: &OpPrintingFlags, context: &Context) -> Document;
61}
62
63impl<T: PrettyPrint + AttributeValue> AttrPrinter for T {
64    default fn print(&self, _flags: &OpPrintingFlags, _context: &Context) -> Document {
65        PrettyPrint::render(self)
66    }
67}
68
69impl AttrPrinter for crate::Attribute {
70    fn print(&self, flags: &OpPrintingFlags, context: &Context) -> Document {
71        use crate::formatter::*;
72
73        match self.value() {
74            None => text(format!("#[{}]", self.name.as_str())),
75            Some(value) => {
76                const_text("#[")
77                    + text(self.name.as_str())
78                    + const_text(" = ")
79                    + value.print(flags, context)
80                    + const_text("]")
81            }
82        }
83    }
84}
85
86impl AttrPrinter for crate::OpFoldResult {
87    fn print(&self, flags: &OpPrintingFlags, context: &Context) -> Document {
88        use crate::formatter::*;
89
90        match self {
91            Self::Attribute(attr) => attr.print(flags, context),
92            Self::Value(value) => display(value.borrow().id()),
93        }
94    }
95}
96
97impl<T: AttrPrinter> AttrPrinter for [T] {
98    fn print(&self, flags: &OpPrintingFlags, context: &Context) -> Document {
99        use crate::formatter::*;
100
101        let mut doc = Document::Empty;
102        for (i, item) in self.iter().enumerate() {
103            if i == 0 {
104                doc += const_text(", ");
105            }
106
107            doc += item.print(flags, context);
108        }
109        doc
110    }
111}
112
113pub fn render_operation_results(op: &Operation) -> crate::formatter::Document {
114    use crate::formatter::*;
115
116    let results = op.results();
117    let doc = results.iter().fold(Document::Empty, |acc, result| {
118        if acc.is_empty() {
119            display(result.borrow().id())
120        } else {
121            acc + const_text(", ") + display(result.borrow().id())
122        }
123    });
124    if doc.is_empty() {
125        doc
126    } else {
127        doc + const_text(" = ")
128    }
129}
130
131pub fn render_operation_operands(op: &Operation) -> crate::formatter::Document {
132    use crate::formatter::*;
133
134    let operands = op.operands();
135    operands.iter().fold(Document::Empty, |acc, operand| {
136        let operand = operand.borrow();
137        let value = operand.value();
138        if acc.is_empty() {
139            display(value.id())
140        } else {
141            acc + const_text(", ") + display(value.id())
142        }
143    })
144}
145
146pub fn render_operation_result_types(op: &Operation) -> crate::formatter::Document {
147    use crate::formatter::*;
148
149    let results = op.results();
150    let result_types = results.iter().fold(Document::Empty, |acc, result| {
151        if acc.is_empty() {
152            text(format!("{}", result.borrow().ty()))
153        } else {
154            acc + const_text(", ") + text(format!("{}", result.borrow().ty()))
155        }
156    });
157    if result_types.is_empty() {
158        result_types
159    } else {
160        const_text(" : ") + result_types
161    }
162}
163
164pub fn render_regions(op: &Operation, flags: &OpPrintingFlags) -> crate::formatter::Document {
165    use crate::formatter::*;
166    const_text(" ")
167        + op.regions.iter().fold(Document::Empty, |acc, region| {
168            let doc = region.print(flags);
169            if acc.is_empty() {
170                doc
171            } else {
172                acc + const_text(" ") + doc
173            }
174        })
175        + const_text(";")
176}
177
178struct OperationPrinter<'a> {
179    op: &'a Operation,
180    flags: &'a OpPrintingFlags,
181    context: &'a Context,
182}
183
184/// The generic format for printed operations is:
185///
186/// <%result..> = <dialect>.<op>(%operand : <operand_ty>, ..) : <result_ty..> #<attr>.. {
187///     // Region
188/// ^<block_id>(<%block_argument...>):
189///     // Block
190/// };
191///
192/// Special handling is provided for SingleRegionSingleBlock and CallableOpInterface ops:
193///
194/// * SingleRegionSingleBlock ops with no operands will have the block header elided
195impl PrettyPrint for OperationPrinter<'_> {
196    fn render(&self) -> crate::formatter::Document {
197        use crate::formatter::*;
198
199        let doc = render_operation_results(self.op) + display(self.op.name()) + const_text(" ");
200        let doc = if let Some(value) = crate::matchers::constant().matches(self.op) {
201            doc + value.print(self.flags, self.context)
202        } else if let Some(branch) = self.op.as_trait::<dyn BranchOpInterface>() {
203            // Print non-successor operands
204            let operands = branch.operands().group(0);
205            let doc = if !operands.is_empty() {
206                operands.iter().enumerate().fold(doc, |doc, (i, operand)| {
207                    let operand = operand.borrow();
208                    let value = operand.value();
209                    if i > 0 {
210                        doc + const_text(", ") + display(value.id())
211                    } else {
212                        doc + display(value.id())
213                    }
214                }) + const_text(" ")
215            } else {
216                doc
217            };
218            // Print successors
219            branch.successors().iter().enumerate().fold(doc, |doc, (succ_index, succ)| {
220                let doc = if succ_index > 0 {
221                    doc + const_text(", ") + display(succ.block.borrow().successor())
222                } else {
223                    doc + display(succ.block.borrow().successor())
224                };
225
226                let operands = branch.get_successor_operands(succ_index);
227                if !operands.is_empty() {
228                    let doc = doc + const_text("(");
229                    operands.forwarded().iter().enumerate().fold(doc, |doc, (i, operand)| {
230                        if !operand.is_linked() {
231                            if i > 0 {
232                                doc + const_text(", ") + const_text("<unlinked>")
233                            } else {
234                                doc + const_text("<unlinked>")
235                            }
236                        } else {
237                            let operand = operand.borrow();
238                            let value = operand.value();
239                            if i > 0 {
240                                doc + const_text(", ") + display(value.id())
241                            } else {
242                                doc + display(value.id())
243                            }
244                        }
245                    }) + const_text(")")
246                } else {
247                    doc
248                }
249            })
250        } else {
251            doc + render_operation_operands(self.op)
252        };
253
254        let doc = doc + render_operation_result_types(self.op);
255
256        let attrs = self.op.attrs.iter().fold(Document::Empty, |acc, attr| {
257            // Do not print intrinsic attributes unless explicitly configured
258            if !self.flags.print_intrinsic_attributes && attr.intrinsic {
259                return acc;
260            }
261            let doc = if let Some(value) = attr.value() {
262                const_text("#[")
263                    + display(attr.name)
264                    + const_text(" = ")
265                    + value.print(self.flags, self.context)
266                    + const_text("]")
267            } else {
268                text(format!("#[{}]", &attr.name))
269            };
270            if acc.is_empty() {
271                doc
272            } else {
273                acc + const_text(" ") + doc
274            }
275        });
276
277        let doc = if attrs.is_empty() {
278            doc
279        } else {
280            doc + const_text(" ") + attrs
281        };
282
283        if self.op.has_regions() {
284            doc + render_regions(self.op, self.flags)
285        } else {
286            doc + const_text(";")
287        }
288    }
289}