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
27pub 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
184impl 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 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 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 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}