/rust/registry/src/index.crates.io-1949cf8c6b5b557f/cranelift-codegen-0.129.1/src/write.rs
Line | Count | Source |
1 | | //! Converting Cranelift IR to text. |
2 | | //! |
3 | | //! The `write` module provides the `write_function` function which converts an IR `Function` to an |
4 | | //! equivalent textual form. This textual form can be read back by the `cranelift-reader` crate. |
5 | | |
6 | | use crate::entity::SecondaryMap; |
7 | | use crate::ir::entities::AnyEntity; |
8 | | use crate::ir::immediates::Ieee128; |
9 | | use crate::ir::pcc::Fact; |
10 | | use crate::ir::{Block, DataFlowGraph, Function, Inst, Opcode, SigRef, Type, Value, ValueDef}; |
11 | | use crate::packed_option::ReservedValue; |
12 | | use alloc::string::{String, ToString}; |
13 | | use alloc::vec::Vec; |
14 | | use core::fmt::{self, Write}; |
15 | | |
16 | | /// A `FuncWriter` used to decorate functions during printing. |
17 | | pub trait FuncWriter { |
18 | | /// Write the basic block header for the current function. |
19 | | fn write_block_header( |
20 | | &mut self, |
21 | | w: &mut dyn Write, |
22 | | func: &Function, |
23 | | block: Block, |
24 | | indent: usize, |
25 | | ) -> fmt::Result; |
26 | | |
27 | | /// Write the given `inst` to `w`. |
28 | | fn write_instruction( |
29 | | &mut self, |
30 | | w: &mut dyn Write, |
31 | | func: &Function, |
32 | | aliases: &SecondaryMap<Value, Vec<Value>>, |
33 | | inst: Inst, |
34 | | indent: usize, |
35 | | ) -> fmt::Result; |
36 | | |
37 | | /// Write the preamble to `w`. By default, this uses `write_entity_definition`. |
38 | 0 | fn write_preamble(&mut self, w: &mut dyn Write, func: &Function) -> Result<bool, fmt::Error> { |
39 | 0 | self.super_preamble(w, func) |
40 | 0 | } Unexecuted instantiation: <cranelift_codegen::print_errors::PrettyVerifierError as cranelift_codegen::write::FuncWriter>::write_preamble Unexecuted instantiation: <cranelift_codegen::write::PlainWriter as cranelift_codegen::write::FuncWriter>::write_preamble Unexecuted instantiation: <cranelift_codegen::print_errors::PrettyVerifierError as cranelift_codegen::write::FuncWriter>::write_preamble Unexecuted instantiation: <cranelift_codegen::write::PlainWriter as cranelift_codegen::write::FuncWriter>::write_preamble |
41 | | |
42 | | /// Default impl of `write_preamble` |
43 | 0 | fn super_preamble(&mut self, w: &mut dyn Write, func: &Function) -> Result<bool, fmt::Error> { |
44 | 0 | let mut any = false; |
45 | | |
46 | 0 | for (ss, slot) in func.dynamic_stack_slots.iter() { |
47 | 0 | any = true; |
48 | 0 | self.write_entity_definition(w, func, ss.into(), slot, None)?; |
49 | | } |
50 | | |
51 | 0 | for (ss, slot) in func.sized_stack_slots.iter() { |
52 | 0 | any = true; |
53 | 0 | self.write_entity_definition(w, func, ss.into(), slot, None)?; |
54 | | } |
55 | | |
56 | 0 | for (gv, gv_data) in &func.global_values { |
57 | 0 | any = true; |
58 | 0 | let maybe_fact = func.global_value_facts[gv].as_ref(); |
59 | 0 | self.write_entity_definition(w, func, gv.into(), gv_data, maybe_fact)?; |
60 | | } |
61 | | |
62 | 0 | for (mt, mt_data) in &func.memory_types { |
63 | 0 | any = true; |
64 | 0 | self.write_entity_definition(w, func, mt.into(), mt_data, None)?; |
65 | | } |
66 | | |
67 | | // Write out all signatures before functions since function declarations can refer to |
68 | | // signatures. |
69 | 0 | for (sig, sig_data) in &func.dfg.signatures { |
70 | 0 | any = true; |
71 | 0 | self.write_entity_definition(w, func, sig.into(), &sig_data, None)?; |
72 | | } |
73 | | |
74 | 0 | for (fnref, ext_func) in &func.dfg.ext_funcs { |
75 | 0 | if ext_func.signature != SigRef::reserved_value() { |
76 | 0 | any = true; |
77 | 0 | self.write_entity_definition( |
78 | 0 | w, |
79 | 0 | func, |
80 | 0 | fnref.into(), |
81 | 0 | &ext_func.display(Some(&func.params)), |
82 | 0 | None, |
83 | 0 | )?; |
84 | 0 | } |
85 | | } |
86 | | |
87 | 0 | for (&cref, cval) in func.dfg.constants.iter() { |
88 | 0 | any = true; |
89 | 0 | self.write_entity_definition(w, func, cref.into(), cval, None)?; |
90 | | } |
91 | | |
92 | 0 | if let Some(limit) = func.stack_limit { |
93 | 0 | any = true; |
94 | 0 | self.write_entity_definition(w, func, AnyEntity::StackLimit, &limit, None)?; |
95 | 0 | } |
96 | | |
97 | 0 | Ok(any) |
98 | 0 | } Unexecuted instantiation: <cranelift_codegen::print_errors::PrettyVerifierError as cranelift_codegen::write::FuncWriter>::super_preamble Unexecuted instantiation: <cranelift_codegen::write::PlainWriter as cranelift_codegen::write::FuncWriter>::super_preamble Unexecuted instantiation: <cranelift_codegen::print_errors::PrettyVerifierError as cranelift_codegen::write::FuncWriter>::super_preamble Unexecuted instantiation: <cranelift_codegen::write::PlainWriter as cranelift_codegen::write::FuncWriter>::super_preamble |
99 | | |
100 | | /// Write an entity definition defined in the preamble to `w`. |
101 | 0 | fn write_entity_definition( |
102 | 0 | &mut self, |
103 | 0 | w: &mut dyn Write, |
104 | 0 | func: &Function, |
105 | 0 | entity: AnyEntity, |
106 | 0 | value: &dyn fmt::Display, |
107 | 0 | maybe_fact: Option<&Fact>, |
108 | 0 | ) -> fmt::Result { |
109 | 0 | self.super_entity_definition(w, func, entity, value, maybe_fact) |
110 | 0 | } Unexecuted instantiation: <cranelift_codegen::write::PlainWriter as cranelift_codegen::write::FuncWriter>::write_entity_definition Unexecuted instantiation: <cranelift_codegen::write::PlainWriter as cranelift_codegen::write::FuncWriter>::write_entity_definition |
111 | | |
112 | | /// Default impl of `write_entity_definition` |
113 | 0 | fn super_entity_definition( |
114 | 0 | &mut self, |
115 | 0 | w: &mut dyn Write, |
116 | 0 | _func: &Function, |
117 | 0 | entity: AnyEntity, |
118 | 0 | value: &dyn fmt::Display, |
119 | 0 | maybe_fact: Option<&Fact>, |
120 | 0 | ) -> fmt::Result { |
121 | 0 | if let Some(fact) = maybe_fact { |
122 | 0 | writeln!(w, " {entity} ! {fact} = {value}") |
123 | | } else { |
124 | 0 | writeln!(w, " {entity} = {value}") |
125 | | } |
126 | 0 | } Unexecuted instantiation: <cranelift_codegen::write::PlainWriter as cranelift_codegen::write::FuncWriter>::super_entity_definition Unexecuted instantiation: <cranelift_codegen::write::PlainWriter as cranelift_codegen::write::FuncWriter>::super_entity_definition |
127 | | } |
128 | | |
129 | | /// A `PlainWriter` that doesn't decorate the function. |
130 | | pub struct PlainWriter; |
131 | | |
132 | | impl FuncWriter for PlainWriter { |
133 | 0 | fn write_instruction( |
134 | 0 | &mut self, |
135 | 0 | w: &mut dyn Write, |
136 | 0 | func: &Function, |
137 | 0 | aliases: &SecondaryMap<Value, Vec<Value>>, |
138 | 0 | inst: Inst, |
139 | 0 | indent: usize, |
140 | 0 | ) -> fmt::Result { |
141 | 0 | write_instruction(w, func, aliases, inst, indent) |
142 | 0 | } Unexecuted instantiation: <cranelift_codegen::write::PlainWriter as cranelift_codegen::write::FuncWriter>::write_instruction Unexecuted instantiation: <cranelift_codegen::write::PlainWriter as cranelift_codegen::write::FuncWriter>::write_instruction |
143 | | |
144 | 0 | fn write_block_header( |
145 | 0 | &mut self, |
146 | 0 | w: &mut dyn Write, |
147 | 0 | func: &Function, |
148 | 0 | block: Block, |
149 | 0 | indent: usize, |
150 | 0 | ) -> fmt::Result { |
151 | 0 | write_block_header(w, func, block, indent) |
152 | 0 | } Unexecuted instantiation: <cranelift_codegen::write::PlainWriter as cranelift_codegen::write::FuncWriter>::write_block_header Unexecuted instantiation: <cranelift_codegen::write::PlainWriter as cranelift_codegen::write::FuncWriter>::write_block_header |
153 | | } |
154 | | |
155 | | /// Write `func` to `w` as equivalent text. |
156 | | /// Use `isa` to emit ISA-dependent annotations. |
157 | 0 | pub fn write_function(w: &mut dyn Write, func: &Function) -> fmt::Result { |
158 | 0 | decorate_function(&mut PlainWriter, w, func) |
159 | 0 | } Unexecuted instantiation: cranelift_codegen::write::write_function Unexecuted instantiation: cranelift_codegen::write::write_function |
160 | | |
161 | | /// Create a reverse-alias map from a value to all aliases having that value as a direct target |
162 | 0 | fn alias_map(func: &Function) -> SecondaryMap<Value, Vec<Value>> { |
163 | 0 | let mut aliases = SecondaryMap::<_, Vec<_>>::new(); |
164 | 0 | for v in func.dfg.values() { |
165 | | // VADFS returns the immediate target of an alias |
166 | 0 | if let Some(k) = func.dfg.value_alias_dest_for_serialization(v) { |
167 | 0 | aliases[k].push(v); |
168 | 0 | } |
169 | | } |
170 | 0 | aliases |
171 | 0 | } Unexecuted instantiation: cranelift_codegen::write::alias_map Unexecuted instantiation: cranelift_codegen::write::alias_map |
172 | | |
173 | | /// Writes `func` to `w` as text. |
174 | | /// write_function_plain is passed as 'closure' to print instructions as text. |
175 | | /// pretty_function_error is passed as 'closure' to add error decoration. |
176 | 0 | pub fn decorate_function<FW: FuncWriter>( |
177 | 0 | func_w: &mut FW, |
178 | 0 | w: &mut dyn Write, |
179 | 0 | func: &Function, |
180 | 0 | ) -> fmt::Result { |
181 | 0 | write!(w, "function ")?; |
182 | 0 | write_function_spec(w, func)?; |
183 | 0 | writeln!(w, " {{")?; |
184 | 0 | let aliases = alias_map(func); |
185 | 0 | let mut any = func_w.write_preamble(w, func)?; |
186 | 0 | for block in &func.layout { |
187 | 0 | if any { |
188 | 0 | writeln!(w)?; |
189 | 0 | } |
190 | 0 | decorate_block(func_w, w, func, &aliases, block)?; |
191 | 0 | any = true; |
192 | | } |
193 | 0 | writeln!(w, "}}") |
194 | 0 | } Unexecuted instantiation: cranelift_codegen::write::decorate_function::<cranelift_codegen::write::PlainWriter> Unexecuted instantiation: cranelift_codegen::write::decorate_function::<cranelift_codegen::print_errors::PrettyVerifierError> Unexecuted instantiation: cranelift_codegen::write::decorate_function::<cranelift_codegen::write::PlainWriter> Unexecuted instantiation: cranelift_codegen::write::decorate_function::<cranelift_codegen::print_errors::PrettyVerifierError> |
195 | | |
196 | | //---------------------------------------------------------------------- |
197 | | // |
198 | | // Function spec. |
199 | | |
200 | | /// Writes the spec (name and signature) of 'func' to 'w' as text. |
201 | 0 | pub fn write_function_spec(w: &mut dyn Write, func: &Function) -> fmt::Result { |
202 | 0 | write!(w, "{}{}", func.name, func.signature) |
203 | 0 | } Unexecuted instantiation: cranelift_codegen::write::write_function_spec Unexecuted instantiation: cranelift_codegen::write::write_function_spec |
204 | | |
205 | | //---------------------------------------------------------------------- |
206 | | // |
207 | | // Basic blocks |
208 | | |
209 | 0 | fn write_arg(w: &mut dyn Write, func: &Function, arg: Value) -> fmt::Result { |
210 | 0 | let ty = func.dfg.value_type(arg); |
211 | 0 | if let Some(f) = &func.dfg.facts[arg] { |
212 | 0 | write!(w, "{arg} ! {f}: {ty}") |
213 | | } else { |
214 | 0 | write!(w, "{arg}: {ty}") |
215 | | } |
216 | 0 | } Unexecuted instantiation: cranelift_codegen::write::write_arg Unexecuted instantiation: cranelift_codegen::write::write_arg |
217 | | |
218 | | /// Write out the basic block header, outdented: |
219 | | /// |
220 | | /// block1: |
221 | | /// block1(v1: i32): |
222 | | /// block10(v4: f64, v5: i8): |
223 | | /// |
224 | 0 | pub fn write_block_header( |
225 | 0 | w: &mut dyn Write, |
226 | 0 | func: &Function, |
227 | 0 | block: Block, |
228 | 0 | indent: usize, |
229 | 0 | ) -> fmt::Result { |
230 | 0 | let cold = if func.layout.is_cold(block) { |
231 | 0 | " cold" |
232 | | } else { |
233 | 0 | "" |
234 | | }; |
235 | | |
236 | | // The `indent` is the instruction indentation. block headers are 4 spaces out from that. |
237 | 0 | write!(w, "{1:0$}{2}", indent - 4, "", block)?; |
238 | | |
239 | 0 | let mut args = func.dfg.block_params(block).iter().cloned(); |
240 | 0 | match args.next() { |
241 | 0 | None => return writeln!(w, "{cold}:"), |
242 | 0 | Some(arg) => { |
243 | 0 | write!(w, "(")?; |
244 | 0 | write_arg(w, func, arg)?; |
245 | | } |
246 | | } |
247 | | // Remaining arguments. |
248 | 0 | for arg in args { |
249 | 0 | write!(w, ", ")?; |
250 | 0 | write_arg(w, func, arg)?; |
251 | | } |
252 | 0 | writeln!(w, "){cold}:") |
253 | 0 | } Unexecuted instantiation: cranelift_codegen::write::write_block_header Unexecuted instantiation: cranelift_codegen::write::write_block_header |
254 | | |
255 | 0 | fn decorate_block<FW: FuncWriter>( |
256 | 0 | func_w: &mut FW, |
257 | 0 | w: &mut dyn Write, |
258 | 0 | func: &Function, |
259 | 0 | aliases: &SecondaryMap<Value, Vec<Value>>, |
260 | 0 | block: Block, |
261 | 0 | ) -> fmt::Result { |
262 | | // Indent all instructions if any srclocs or debug tags are present. |
263 | 0 | let indent = if func.rel_srclocs().is_empty() && func.debug_tags.is_empty() { |
264 | 0 | 4 |
265 | | } else { |
266 | 0 | 36 |
267 | | }; |
268 | | |
269 | 0 | func_w.write_block_header(w, func, block, indent)?; |
270 | 0 | for a in func.dfg.block_params(block).iter().cloned() { |
271 | 0 | write_value_aliases(w, aliases, a, indent)?; |
272 | | } |
273 | | |
274 | 0 | for inst in func.layout.block_insts(block) { |
275 | 0 | func_w.write_instruction(w, func, aliases, inst, indent)?; |
276 | | } |
277 | | |
278 | 0 | Ok(()) |
279 | 0 | } Unexecuted instantiation: cranelift_codegen::write::decorate_block::<cranelift_codegen::write::PlainWriter> Unexecuted instantiation: cranelift_codegen::write::decorate_block::<cranelift_codegen::print_errors::PrettyVerifierError> Unexecuted instantiation: cranelift_codegen::write::decorate_block::<cranelift_codegen::write::PlainWriter> Unexecuted instantiation: cranelift_codegen::write::decorate_block::<cranelift_codegen::print_errors::PrettyVerifierError> |
280 | | |
281 | | //---------------------------------------------------------------------- |
282 | | // |
283 | | // Instructions |
284 | | |
285 | | // Should `inst` be printed with a type suffix? |
286 | | // |
287 | | // Polymorphic instructions may need a suffix indicating the value of the controlling type variable |
288 | | // if it can't be trivially inferred. |
289 | | // |
290 | 0 | fn type_suffix(func: &Function, inst: Inst) -> Option<Type> { |
291 | 0 | let inst_data = &func.dfg.insts[inst]; |
292 | 0 | let constraints = inst_data.opcode().constraints(); |
293 | | |
294 | 0 | if !constraints.is_polymorphic() { |
295 | 0 | return None; |
296 | 0 | } |
297 | | |
298 | | // If the controlling type variable can be inferred from the type of the designated value input |
299 | | // operand, we don't need the type suffix. |
300 | 0 | if constraints.use_typevar_operand() { |
301 | 0 | let ctrl_var = inst_data.typevar_operand(&func.dfg.value_lists).unwrap(); |
302 | 0 | let def_block = match func.dfg.value_def(ctrl_var) { |
303 | 0 | ValueDef::Result(instr, _) => func.layout.inst_block(instr), |
304 | 0 | ValueDef::Param(block, _) => Some(block), |
305 | 0 | ValueDef::Union(..) => None, |
306 | | }; |
307 | 0 | if def_block.is_some() && def_block == func.layout.inst_block(inst) { |
308 | 0 | return None; |
309 | 0 | } |
310 | 0 | } |
311 | | |
312 | 0 | let rtype = func.dfg.ctrl_typevar(inst); |
313 | 0 | assert!( |
314 | 0 | !rtype.is_invalid(), |
315 | | "Polymorphic instruction must produce a result" |
316 | | ); |
317 | 0 | Some(rtype) |
318 | 0 | } Unexecuted instantiation: cranelift_codegen::write::type_suffix Unexecuted instantiation: cranelift_codegen::write::type_suffix |
319 | | |
320 | | /// Write out any aliases to the given target, including indirect aliases |
321 | 0 | fn write_value_aliases( |
322 | 0 | w: &mut dyn Write, |
323 | 0 | aliases: &SecondaryMap<Value, Vec<Value>>, |
324 | 0 | target: Value, |
325 | 0 | indent: usize, |
326 | 0 | ) -> fmt::Result { |
327 | 0 | let mut todo_stack = vec![target]; |
328 | 0 | while let Some(target) = todo_stack.pop() { |
329 | 0 | for &a in &aliases[target] { |
330 | 0 | writeln!(w, "{1:0$}{2} -> {3}", indent, "", a, target)?; |
331 | 0 | todo_stack.push(a); |
332 | | } |
333 | | } |
334 | | |
335 | 0 | Ok(()) |
336 | 0 | } Unexecuted instantiation: cranelift_codegen::write::write_value_aliases Unexecuted instantiation: cranelift_codegen::write::write_value_aliases |
337 | | |
338 | 0 | fn write_instruction( |
339 | 0 | w: &mut dyn Write, |
340 | 0 | func: &Function, |
341 | 0 | aliases: &SecondaryMap<Value, Vec<Value>>, |
342 | 0 | inst: Inst, |
343 | 0 | mut indent: usize, |
344 | 0 | ) -> fmt::Result { |
345 | | // Prefix containing source location, encoding, and value locations. |
346 | 0 | let mut s = String::with_capacity(16); |
347 | | |
348 | | // Source location goes first. |
349 | 0 | let srcloc = func.srcloc(inst); |
350 | 0 | if !srcloc.is_default() { |
351 | 0 | write!(s, "{srcloc} ")?; |
352 | 0 | } |
353 | | |
354 | | // Write out any debug tags. |
355 | 0 | write_debug_tags(w, &func, inst, &mut indent)?; |
356 | | |
357 | | // Write out prefix and indent the instruction. |
358 | 0 | write!(w, "{s:indent$}")?; |
359 | | |
360 | | // Write out the result values, if any. |
361 | 0 | let mut has_results = false; |
362 | 0 | for r in func.dfg.inst_results(inst) { |
363 | 0 | if !has_results { |
364 | 0 | has_results = true; |
365 | 0 | write!(w, "{r}")?; |
366 | | } else { |
367 | 0 | write!(w, ", {r}")?; |
368 | | } |
369 | 0 | if let Some(f) = &func.dfg.facts[*r] { |
370 | 0 | write!(w, " ! {f}")?; |
371 | 0 | } |
372 | | } |
373 | 0 | if has_results { |
374 | 0 | write!(w, " = ")?; |
375 | 0 | } |
376 | | |
377 | | // Then the opcode, possibly with a '.type' suffix. |
378 | 0 | let opcode = func.dfg.insts[inst].opcode(); |
379 | | |
380 | 0 | match type_suffix(func, inst) { |
381 | 0 | Some(suf) => write!(w, "{opcode}.{suf}")?, |
382 | 0 | None => write!(w, "{opcode}")?, |
383 | | } |
384 | | |
385 | 0 | write_operands(w, &func.dfg, inst)?; |
386 | 0 | writeln!(w)?; |
387 | | |
388 | | // Value aliases come out on lines after the instruction defining the referent. |
389 | 0 | for r in func.dfg.inst_results(inst) { |
390 | 0 | write_value_aliases(w, aliases, *r, indent)?; |
391 | | } |
392 | 0 | Ok(()) |
393 | 0 | } Unexecuted instantiation: cranelift_codegen::write::write_instruction Unexecuted instantiation: cranelift_codegen::write::write_instruction |
394 | | |
395 | | /// Write the operands of `inst` to `w` with a prepended space. |
396 | 0 | pub fn write_operands(w: &mut dyn Write, dfg: &DataFlowGraph, inst: Inst) -> fmt::Result { |
397 | 0 | let pool = &dfg.value_lists; |
398 | 0 | let jump_tables = &dfg.jump_tables; |
399 | 0 | let exception_tables = &dfg.exception_tables; |
400 | | use crate::ir::instructions::InstructionData::*; |
401 | 0 | let ctrl_ty = dfg.ctrl_typevar(inst); |
402 | 0 | match dfg.insts[inst] { |
403 | 0 | AtomicRmw { op, args, .. } => write!(w, " {} {}, {}", op, args[0], args[1]), |
404 | 0 | AtomicCas { args, .. } => write!(w, " {}, {}, {}", args[0], args[1], args[2]), |
405 | 0 | LoadNoOffset { flags, arg, .. } => write!(w, "{flags} {arg}"), |
406 | 0 | StoreNoOffset { flags, args, .. } => write!(w, "{} {}, {}", flags, args[0], args[1]), |
407 | 0 | Unary { arg, .. } => write!(w, " {arg}"), |
408 | 0 | UnaryImm { imm, .. } => write!(w, " {}", { |
409 | 0 | let mut imm = imm; |
410 | 0 | if ctrl_ty.bits() != 0 { |
411 | 0 | imm = imm.sign_extend_from_width(ctrl_ty.bits()); |
412 | 0 | } |
413 | 0 | imm |
414 | | }), |
415 | 0 | UnaryIeee16 { imm, .. } => write!(w, " {imm}"), |
416 | 0 | UnaryIeee32 { imm, .. } => write!(w, " {imm}"), |
417 | 0 | UnaryIeee64 { imm, .. } => write!(w, " {imm}"), |
418 | 0 | UnaryGlobalValue { global_value, .. } => write!(w, " {global_value}"), |
419 | | UnaryConst { |
420 | 0 | constant_handle, .. |
421 | 0 | } => write!(w, " {constant_handle}"), |
422 | 0 | Binary { args, .. } => write!(w, " {}, {}", args[0], args[1]), |
423 | 0 | BinaryImm8 { arg, imm, .. } => write!(w, " {arg}, {imm}"), |
424 | 0 | BinaryImm64 { arg, imm, .. } => write!(w, " {}, {}", arg, { |
425 | 0 | let mut imm = imm; |
426 | 0 | if ctrl_ty.bits() != 0 { |
427 | 0 | imm = imm.sign_extend_from_width(ctrl_ty.bits()); |
428 | 0 | } |
429 | 0 | imm |
430 | | }), |
431 | 0 | Ternary { args, .. } => write!(w, " {}, {}, {}", args[0], args[1], args[2]), |
432 | 0 | MultiAry { ref args, .. } => { |
433 | 0 | if args.is_empty() { |
434 | 0 | write!(w, "") |
435 | | } else { |
436 | 0 | write!(w, " {}", DisplayValues(args.as_slice(pool))) |
437 | | } |
438 | | } |
439 | 0 | NullAry { .. } => write!(w, " "), |
440 | 0 | TernaryImm8 { imm, args, .. } => write!(w, " {}, {}, {}", args[0], args[1], imm), |
441 | 0 | Shuffle { imm, args, .. } => { |
442 | 0 | let data = dfg.immediates.get(imm).expect( |
443 | 0 | "Expected the shuffle mask to already be inserted into the immediates table", |
444 | | ); |
445 | 0 | write!(w, " {}, {}, {}", args[0], args[1], data) |
446 | | } |
447 | 0 | IntCompare { cond, args, .. } => write!(w, " {} {}, {}", cond, args[0], args[1]), |
448 | 0 | IntCompareImm { cond, arg, imm, .. } => write!(w, " {} {}, {}", cond, arg, { |
449 | 0 | let mut imm = imm; |
450 | 0 | if ctrl_ty.bits() != 0 { |
451 | 0 | imm = imm.sign_extend_from_width(ctrl_ty.bits()); |
452 | 0 | } |
453 | 0 | imm |
454 | | }), |
455 | 0 | IntAddTrap { args, code, .. } => write!(w, " {}, {}, {}", args[0], args[1], code), |
456 | 0 | FloatCompare { cond, args, .. } => write!(w, " {} {}, {}", cond, args[0], args[1]), |
457 | 0 | Jump { destination, .. } => { |
458 | 0 | write!(w, " {}", destination.display(pool)) |
459 | | } |
460 | | Brif { |
461 | 0 | arg, |
462 | 0 | blocks: [block_then, block_else], |
463 | | .. |
464 | | } => { |
465 | 0 | write!(w, " {}, {}", arg, block_then.display(pool))?; |
466 | 0 | write!(w, ", {}", block_else.display(pool)) |
467 | | } |
468 | 0 | BranchTable { arg, table, .. } => { |
469 | 0 | write!(w, " {}, {}", arg, jump_tables[table].display(pool)) |
470 | | } |
471 | | Call { |
472 | 0 | func_ref, ref args, .. |
473 | | } => { |
474 | 0 | write!(w, " {}({})", func_ref, DisplayValues(args.as_slice(pool)))?; |
475 | 0 | write_user_stack_map_entries(w, dfg, inst) |
476 | | } |
477 | | CallIndirect { |
478 | 0 | sig_ref, ref args, .. |
479 | | } => { |
480 | 0 | let args = args.as_slice(pool); |
481 | 0 | write!( |
482 | 0 | w, |
483 | | " {}, {}({})", |
484 | | sig_ref, |
485 | 0 | args[0], |
486 | 0 | DisplayValues(&args[1..]) |
487 | 0 | )?; |
488 | 0 | write_user_stack_map_entries(w, dfg, inst) |
489 | | } |
490 | | TryCall { |
491 | 0 | func_ref, |
492 | 0 | ref args, |
493 | 0 | exception, |
494 | | .. |
495 | | } => { |
496 | 0 | write!( |
497 | 0 | w, |
498 | | " {}({}), {}", |
499 | | func_ref, |
500 | 0 | DisplayValues(args.as_slice(pool)), |
501 | 0 | exception_tables[exception].display(pool), |
502 | | ) |
503 | | } |
504 | | TryCallIndirect { |
505 | 0 | ref args, |
506 | 0 | exception, |
507 | | .. |
508 | | } => { |
509 | 0 | let args = args.as_slice(pool); |
510 | 0 | write!( |
511 | 0 | w, |
512 | | " {}({}), {}", |
513 | 0 | args[0], |
514 | 0 | DisplayValues(&args[1..]), |
515 | 0 | exception_tables[exception].display(pool), |
516 | | ) |
517 | | } |
518 | 0 | FuncAddr { func_ref, .. } => write!(w, " {func_ref}"), |
519 | | StackLoad { |
520 | 0 | stack_slot, offset, .. |
521 | 0 | } => write!(w, " {stack_slot}{offset}"), |
522 | | StackStore { |
523 | 0 | arg, |
524 | 0 | stack_slot, |
525 | 0 | offset, |
526 | | .. |
527 | 0 | } => write!(w, " {arg}, {stack_slot}{offset}"), |
528 | | DynamicStackLoad { |
529 | 0 | dynamic_stack_slot, .. |
530 | 0 | } => write!(w, " {dynamic_stack_slot}"), |
531 | | DynamicStackStore { |
532 | 0 | arg, |
533 | 0 | dynamic_stack_slot, |
534 | | .. |
535 | 0 | } => write!(w, " {arg}, {dynamic_stack_slot}"), |
536 | | Load { |
537 | 0 | flags, arg, offset, .. |
538 | 0 | } => write!(w, "{flags} {arg}{offset}"), |
539 | | Store { |
540 | 0 | flags, |
541 | 0 | args, |
542 | 0 | offset, |
543 | | .. |
544 | 0 | } => write!(w, "{} {}, {}{}", flags, args[0], args[1], offset), |
545 | 0 | Trap { code, .. } => write!(w, " {code}"), |
546 | 0 | CondTrap { arg, code, .. } => write!(w, " {arg}, {code}"), |
547 | 0 | ExceptionHandlerAddress { block, imm, .. } => write!(w, " {block}, {imm}"), |
548 | 0 | }?; |
549 | | |
550 | 0 | let mut sep = " ; "; |
551 | 0 | for arg in dfg.inst_values(inst) { |
552 | 0 | if let ValueDef::Result(src, _) = dfg.value_def(arg) { |
553 | 0 | let imm = match dfg.insts[src] { |
554 | 0 | UnaryImm { imm, .. } => { |
555 | 0 | let mut imm = imm; |
556 | 0 | if dfg.ctrl_typevar(src).bits() != 0 { |
557 | 0 | imm = imm.sign_extend_from_width(dfg.ctrl_typevar(src).bits()); |
558 | 0 | } |
559 | 0 | imm.to_string() |
560 | | } |
561 | 0 | UnaryIeee16 { imm, .. } => imm.to_string(), |
562 | 0 | UnaryIeee32 { imm, .. } => imm.to_string(), |
563 | 0 | UnaryIeee64 { imm, .. } => imm.to_string(), |
564 | | UnaryConst { |
565 | 0 | constant_handle, |
566 | | opcode: Opcode::F128const, |
567 | 0 | } => Ieee128::try_from(dfg.constants.get(constant_handle)) |
568 | 0 | .expect("16-byte f128 constant") |
569 | 0 | .to_string(), |
570 | | UnaryConst { |
571 | 0 | constant_handle, .. |
572 | 0 | } => constant_handle.to_string(), |
573 | 0 | _ => continue, |
574 | | }; |
575 | 0 | write!(w, "{sep}{arg} = {imm}")?; |
576 | 0 | sep = ", "; |
577 | 0 | } |
578 | | } |
579 | 0 | Ok(()) |
580 | 0 | } Unexecuted instantiation: cranelift_codegen::write::write_operands Unexecuted instantiation: cranelift_codegen::write::write_operands |
581 | | |
582 | 0 | fn write_debug_tags( |
583 | 0 | w: &mut dyn Write, |
584 | 0 | func: &Function, |
585 | 0 | inst: Inst, |
586 | 0 | indent: &mut usize, |
587 | 0 | ) -> fmt::Result { |
588 | 0 | let tags = func.debug_tags.get(inst); |
589 | 0 | if !tags.is_empty() { |
590 | 0 | let tags = tags |
591 | 0 | .iter() |
592 | 0 | .map(|tag| format!("{tag}"))Unexecuted instantiation: cranelift_codegen::write::write_debug_tags::{closure#0}Unexecuted instantiation: cranelift_codegen::write::write_debug_tags::{closure#0} |
593 | 0 | .collect::<Vec<_>>() |
594 | 0 | .join(", "); |
595 | 0 | let s = format!("<{tags}> "); |
596 | 0 | write!(w, "{s}")?; |
597 | 0 | *indent = indent.saturating_sub(s.len()); |
598 | 0 | } |
599 | 0 | Ok(()) |
600 | 0 | } Unexecuted instantiation: cranelift_codegen::write::write_debug_tags Unexecuted instantiation: cranelift_codegen::write::write_debug_tags |
601 | | |
602 | 0 | fn write_user_stack_map_entries(w: &mut dyn Write, dfg: &DataFlowGraph, inst: Inst) -> fmt::Result { |
603 | 0 | let entries = match dfg.user_stack_map_entries(inst) { |
604 | 0 | None => return Ok(()), |
605 | 0 | Some(es) => es, |
606 | | }; |
607 | 0 | write!(w, ", stack_map=[")?; |
608 | 0 | let mut need_comma = false; |
609 | 0 | for entry in entries { |
610 | 0 | if need_comma { |
611 | 0 | write!(w, ", ")?; |
612 | 0 | } |
613 | 0 | write!(w, "{} @ {}+{}", entry.ty, entry.slot, entry.offset)?; |
614 | 0 | need_comma = true; |
615 | | } |
616 | 0 | write!(w, "]")?; |
617 | 0 | Ok(()) |
618 | 0 | } Unexecuted instantiation: cranelift_codegen::write::write_user_stack_map_entries Unexecuted instantiation: cranelift_codegen::write::write_user_stack_map_entries |
619 | | |
620 | | /// Displayable slice of values. |
621 | | struct DisplayValues<'a>(&'a [Value]); |
622 | | |
623 | | impl<'a> fmt::Display for DisplayValues<'a> { |
624 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
625 | 0 | for (i, val) in self.0.iter().enumerate() { |
626 | 0 | if i == 0 { |
627 | 0 | write!(f, "{val}")?; |
628 | | } else { |
629 | 0 | write!(f, ", {val}")?; |
630 | | } |
631 | | } |
632 | 0 | Ok(()) |
633 | 0 | } Unexecuted instantiation: <cranelift_codegen::write::DisplayValues as core::fmt::Display>::fmt Unexecuted instantiation: <cranelift_codegen::write::DisplayValues as core::fmt::Display>::fmt |
634 | | } |
635 | | |
636 | | #[cfg(test)] |
637 | | mod tests { |
638 | | use crate::cursor::{Cursor, CursorPosition, FuncCursor}; |
639 | | use crate::ir::types; |
640 | | use crate::ir::{Function, InstBuilder, StackSlotData, StackSlotKind, UserFuncName}; |
641 | | use alloc::string::ToString; |
642 | | |
643 | | #[test] |
644 | | fn basic() { |
645 | | let mut f = Function::new(); |
646 | | assert_eq!(f.to_string(), "function u0:0() fast {\n}\n"); |
647 | | |
648 | | f.name = UserFuncName::testcase("foo"); |
649 | | assert_eq!(f.to_string(), "function %foo() fast {\n}\n"); |
650 | | |
651 | | f.create_sized_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 4, 0)); |
652 | | assert_eq!( |
653 | | f.to_string(), |
654 | | "function %foo() fast {\n ss0 = explicit_slot 4\n}\n" |
655 | | ); |
656 | | |
657 | | let block = f.dfg.make_block(); |
658 | | f.layout.append_block(block); |
659 | | assert_eq!( |
660 | | f.to_string(), |
661 | | "function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0:\n}\n" |
662 | | ); |
663 | | |
664 | | f.dfg.append_block_param(block, types::I8); |
665 | | assert_eq!( |
666 | | f.to_string(), |
667 | | "function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0(v0: i8):\n}\n" |
668 | | ); |
669 | | |
670 | | f.dfg.append_block_param(block, types::F32.by(4).unwrap()); |
671 | | assert_eq!( |
672 | | f.to_string(), |
673 | | "function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0(v0: i8, v1: f32x4):\n}\n" |
674 | | ); |
675 | | |
676 | | { |
677 | | let mut cursor = FuncCursor::new(&mut f); |
678 | | cursor.set_position(CursorPosition::After(block)); |
679 | | cursor.ins().return_(&[]) |
680 | | }; |
681 | | assert_eq!( |
682 | | f.to_string(), |
683 | | "function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0(v0: i8, v1: f32x4):\n return\n}\n" |
684 | | ); |
685 | | |
686 | | let mut f = Function::new(); |
687 | | f.create_sized_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 4, 2)); |
688 | | assert_eq!( |
689 | | f.to_string(), |
690 | | "function u0:0() fast {\n ss0 = explicit_slot 4, align = 4\n}\n" |
691 | | ); |
692 | | } |
693 | | |
694 | | #[test] |
695 | | fn aliases() { |
696 | | use crate::ir::InstBuilder; |
697 | | |
698 | | let mut func = Function::new(); |
699 | | { |
700 | | let block0 = func.dfg.make_block(); |
701 | | let mut pos = FuncCursor::new(&mut func); |
702 | | pos.insert_block(block0); |
703 | | |
704 | | // make some detached values for change_to_alias |
705 | | let v0 = pos.func.dfg.append_block_param(block0, types::I32); |
706 | | let v1 = pos.func.dfg.append_block_param(block0, types::I32); |
707 | | let v2 = pos.func.dfg.append_block_param(block0, types::I32); |
708 | | pos.func.dfg.detach_block_params(block0); |
709 | | |
710 | | // alias to a param--will be printed at beginning of block defining param |
711 | | let v3 = pos.func.dfg.append_block_param(block0, types::I32); |
712 | | pos.func.dfg.change_to_alias(v0, v3); |
713 | | |
714 | | // alias to an alias--should print attached to alias, not ultimate target |
715 | | pos.func.dfg.make_value_alias_for_serialization(v0, v2); // v0 <- v2 |
716 | | |
717 | | // alias to a result--will be printed after instruction producing result |
718 | | let _dummy0 = pos.ins().iconst(types::I32, 42); |
719 | | let v4 = pos.ins().iadd(v0, v0); |
720 | | pos.func.dfg.change_to_alias(v1, v4); |
721 | | let _dummy1 = pos.ins().iconst(types::I32, 23); |
722 | | let _v7 = pos.ins().iadd(v1, v1); |
723 | | } |
724 | | assert_eq!( |
725 | | func.to_string(), |
726 | | "function u0:0() fast {\nblock0(v3: i32):\n v0 -> v3\n v2 -> v0\n v4 = iconst.i32 42\n v5 = iadd v0, v0\n v1 -> v5\n v6 = iconst.i32 23\n v7 = iadd v1, v1\n}\n" |
727 | | ); |
728 | | } |
729 | | |
730 | | #[test] |
731 | | fn cold_blocks() { |
732 | | let mut func = Function::new(); |
733 | | { |
734 | | let mut pos = FuncCursor::new(&mut func); |
735 | | |
736 | | let block0 = pos.func.dfg.make_block(); |
737 | | pos.insert_block(block0); |
738 | | pos.func.layout.set_cold(block0); |
739 | | |
740 | | let block1 = pos.func.dfg.make_block(); |
741 | | pos.insert_block(block1); |
742 | | pos.func.dfg.append_block_param(block1, types::I32); |
743 | | pos.func.layout.set_cold(block1); |
744 | | } |
745 | | |
746 | | assert_eq!( |
747 | | func.to_string(), |
748 | | "function u0:0() fast {\nblock0 cold:\n\nblock1(v0: i32) cold:\n}\n" |
749 | | ); |
750 | | } |
751 | | } |