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::{
9 Block, DataFlowGraph, DisplayFunctionAnnotations, Function, Inst, SigRef, Type, Value,
10 ValueDef, ValueLoc,
11 };
12 use crate::isa::{RegInfo, TargetIsa};
13 use crate::packed_option::ReservedValue;
14 use crate::value_label::ValueLabelsRanges;
15 use crate::HashSet;
16 use alloc::string::String;
17 use alloc::vec::Vec;
18 use core::fmt::{self, Write};
19
20 /// A `FuncWriter` used to decorate functions during printing.
21 pub trait FuncWriter {
22 /// Write the basic block header for the current function.
write_block_header( &mut self, w: &mut dyn Write, func: &Function, isa: Option<&dyn TargetIsa>, block: Block, indent: usize, ) -> fmt::Result23 fn write_block_header(
24 &mut self,
25 w: &mut dyn Write,
26 func: &Function,
27 isa: Option<&dyn TargetIsa>,
28 block: Block,
29 indent: usize,
30 ) -> fmt::Result;
31
32 /// Write the given `inst` to `w`.
write_instruction( &mut self, w: &mut dyn Write, func: &Function, aliases: &SecondaryMap<Value, Vec<Value>>, isa: Option<&dyn TargetIsa>, inst: Inst, indent: usize, ) -> fmt::Result33 fn write_instruction(
34 &mut self,
35 w: &mut dyn Write,
36 func: &Function,
37 aliases: &SecondaryMap<Value, Vec<Value>>,
38 isa: Option<&dyn TargetIsa>,
39 inst: Inst,
40 indent: usize,
41 ) -> fmt::Result;
42
43 /// Write the preamble to `w`. By default, this uses `write_entity_definition`.
write_preamble( &mut self, w: &mut dyn Write, func: &Function, regs: Option<&RegInfo>, ) -> Result<bool, fmt::Error>44 fn write_preamble(
45 &mut self,
46 w: &mut dyn Write,
47 func: &Function,
48 regs: Option<&RegInfo>,
49 ) -> Result<bool, fmt::Error> {
50 self.super_preamble(w, func, regs)
51 }
52
53 /// Default impl of `write_preamble`
super_preamble( &mut self, w: &mut dyn Write, func: &Function, regs: Option<&RegInfo>, ) -> Result<bool, fmt::Error>54 fn super_preamble(
55 &mut self,
56 w: &mut dyn Write,
57 func: &Function,
58 regs: Option<&RegInfo>,
59 ) -> Result<bool, fmt::Error> {
60 let mut any = false;
61
62 for (ss, slot) in func.stack_slots.iter() {
63 any = true;
64 self.write_entity_definition(w, func, ss.into(), slot)?;
65 }
66
67 for (gv, gv_data) in &func.global_values {
68 any = true;
69 self.write_entity_definition(w, func, gv.into(), gv_data)?;
70 }
71
72 for (heap, heap_data) in &func.heaps {
73 if !heap_data.index_type.is_invalid() {
74 any = true;
75 self.write_entity_definition(w, func, heap.into(), heap_data)?;
76 }
77 }
78
79 for (table, table_data) in &func.tables {
80 if !table_data.index_type.is_invalid() {
81 any = true;
82 self.write_entity_definition(w, func, table.into(), table_data)?;
83 }
84 }
85
86 // Write out all signatures before functions since function declarations can refer to
87 // signatures.
88 for (sig, sig_data) in &func.dfg.signatures {
89 any = true;
90 self.write_entity_definition(w, func, sig.into(), &sig_data.display(regs))?;
91 }
92
93 for (fnref, ext_func) in &func.dfg.ext_funcs {
94 if ext_func.signature != SigRef::reserved_value() {
95 any = true;
96 self.write_entity_definition(w, func, fnref.into(), ext_func)?;
97 }
98 }
99
100 for (jt, jt_data) in &func.jump_tables {
101 any = true;
102 self.write_entity_definition(w, func, jt.into(), jt_data)?;
103 }
104
105 for (&cref, cval) in func.dfg.constants.iter() {
106 any = true;
107 self.write_entity_definition(w, func, cref.into(), cval)?;
108 }
109
110 if let Some(limit) = func.stack_limit {
111 any = true;
112 self.write_entity_definition(w, func, AnyEntity::StackLimit, &limit)?;
113 }
114
115 Ok(any)
116 }
117
118 /// Write an entity definition defined in the preamble to `w`.
write_entity_definition( &mut self, w: &mut dyn Write, func: &Function, entity: AnyEntity, value: &dyn fmt::Display, ) -> fmt::Result119 fn write_entity_definition(
120 &mut self,
121 w: &mut dyn Write,
122 func: &Function,
123 entity: AnyEntity,
124 value: &dyn fmt::Display,
125 ) -> fmt::Result {
126 self.super_entity_definition(w, func, entity, value)
127 }
128
129 /// Default impl of `write_entity_definition`
130 #[allow(unused_variables)]
super_entity_definition( &mut self, w: &mut dyn Write, func: &Function, entity: AnyEntity, value: &dyn fmt::Display, ) -> fmt::Result131 fn super_entity_definition(
132 &mut self,
133 w: &mut dyn Write,
134 func: &Function,
135 entity: AnyEntity,
136 value: &dyn fmt::Display,
137 ) -> fmt::Result {
138 writeln!(w, " {} = {}", entity, value)
139 }
140 }
141
142 /// A `PlainWriter` that doesn't decorate the function.
143 pub struct PlainWriter;
144
145 impl FuncWriter for PlainWriter {
write_instruction( &mut self, w: &mut dyn Write, func: &Function, aliases: &SecondaryMap<Value, Vec<Value>>, isa: Option<&dyn TargetIsa>, inst: Inst, indent: usize, ) -> fmt::Result146 fn write_instruction(
147 &mut self,
148 w: &mut dyn Write,
149 func: &Function,
150 aliases: &SecondaryMap<Value, Vec<Value>>,
151 isa: Option<&dyn TargetIsa>,
152 inst: Inst,
153 indent: usize,
154 ) -> fmt::Result {
155 write_instruction(w, func, aliases, isa, inst, indent)
156 }
157
write_block_header( &mut self, w: &mut dyn Write, func: &Function, isa: Option<&dyn TargetIsa>, block: Block, indent: usize, ) -> fmt::Result158 fn write_block_header(
159 &mut self,
160 w: &mut dyn Write,
161 func: &Function,
162 isa: Option<&dyn TargetIsa>,
163 block: Block,
164 indent: usize,
165 ) -> fmt::Result {
166 write_block_header(w, func, isa, block, indent)
167 }
168 }
169
170 /// Write `func` to `w` as equivalent text.
171 /// Use `isa` to emit ISA-dependent annotations.
write_function( w: &mut dyn Write, func: &Function, annotations: &DisplayFunctionAnnotations, ) -> fmt::Result172 pub fn write_function(
173 w: &mut dyn Write,
174 func: &Function,
175 annotations: &DisplayFunctionAnnotations,
176 ) -> fmt::Result {
177 decorate_function(&mut PlainWriter, w, func, annotations)
178 }
179
180 /// Create a reverse-alias map from a value to all aliases having that value as a direct target
alias_map(func: &Function) -> SecondaryMap<Value, Vec<Value>>181 fn alias_map(func: &Function) -> SecondaryMap<Value, Vec<Value>> {
182 let mut aliases = SecondaryMap::<_, Vec<_>>::new();
183 for v in func.dfg.values() {
184 // VADFS returns the immediate target of an alias
185 if let Some(k) = func.dfg.value_alias_dest_for_serialization(v) {
186 aliases[k].push(v);
187 }
188 }
189 aliases
190 }
191
192 /// Writes `func` to `w` as text.
193 /// write_function_plain is passed as 'closure' to print instructions as text.
194 /// pretty_function_error is passed as 'closure' to add error decoration.
decorate_function<FW: FuncWriter>( func_w: &mut FW, w: &mut dyn Write, func: &Function, annotations: &DisplayFunctionAnnotations, ) -> fmt::Result195 pub fn decorate_function<FW: FuncWriter>(
196 func_w: &mut FW,
197 w: &mut dyn Write,
198 func: &Function,
199 annotations: &DisplayFunctionAnnotations,
200 ) -> fmt::Result {
201 let regs = annotations.isa.map(TargetIsa::register_info);
202 let regs = regs.as_ref();
203
204 write!(w, "function ")?;
205 write_spec(w, func, regs)?;
206 writeln!(w, " {{")?;
207 let aliases = alias_map(func);
208 let mut any = func_w.write_preamble(w, func, regs)?;
209 for block in &func.layout {
210 if any {
211 writeln!(w)?;
212 }
213 decorate_block(func_w, w, func, &aliases, annotations, block)?;
214 any = true;
215 }
216 writeln!(w, "}}")
217 }
218
219 //----------------------------------------------------------------------
220 //
221 // Function spec.
222
write_spec(w: &mut dyn Write, func: &Function, regs: Option<&RegInfo>) -> fmt::Result223 fn write_spec(w: &mut dyn Write, func: &Function, regs: Option<&RegInfo>) -> fmt::Result {
224 write!(w, "{}{}", func.name, func.signature.display(regs))
225 }
226
227 //----------------------------------------------------------------------
228 //
229 // Basic blocks
230
write_arg( w: &mut dyn Write, func: &Function, regs: Option<&RegInfo>, arg: Value, ) -> fmt::Result231 fn write_arg(
232 w: &mut dyn Write,
233 func: &Function,
234 regs: Option<&RegInfo>,
235 arg: Value,
236 ) -> fmt::Result {
237 write!(w, "{}: {}", arg, func.dfg.value_type(arg))?;
238 let loc = func.locations[arg];
239 if loc.is_assigned() {
240 write!(w, " [{}]", loc.display(regs))?
241 }
242
243 Ok(())
244 }
245
246 /// Write out the basic block header, outdented:
247 ///
248 /// block1:
249 /// block1(v1: i32):
250 /// block10(v4: f64, v5: b1):
251 ///
write_block_header( w: &mut dyn Write, func: &Function, isa: Option<&dyn TargetIsa>, block: Block, indent: usize, ) -> fmt::Result252 pub fn write_block_header(
253 w: &mut dyn Write,
254 func: &Function,
255 isa: Option<&dyn TargetIsa>,
256 block: Block,
257 indent: usize,
258 ) -> fmt::Result {
259 // The `indent` is the instruction indentation. block headers are 4 spaces out from that.
260 write!(w, "{1:0$}{2}", indent - 4, "", block)?;
261
262 let regs = isa.map(TargetIsa::register_info);
263 let regs = regs.as_ref();
264
265 let mut args = func.dfg.block_params(block).iter().cloned();
266 match args.next() {
267 None => return writeln!(w, ":"),
268 Some(arg) => {
269 write!(w, "(")?;
270 write_arg(w, func, regs, arg)?;
271 }
272 }
273 // Remaining arguments.
274 for arg in args {
275 write!(w, ", ")?;
276 write_arg(w, func, regs, arg)?;
277 }
278 writeln!(w, "):")
279 }
280
write_valueloc(w: &mut dyn Write, loc: ValueLoc, regs: &RegInfo) -> fmt::Result281 fn write_valueloc(w: &mut dyn Write, loc: ValueLoc, regs: &RegInfo) -> fmt::Result {
282 match loc {
283 ValueLoc::Reg(r) => write!(w, "{}", regs.display_regunit(r)),
284 ValueLoc::Stack(ss) => write!(w, "{}", ss),
285 ValueLoc::Unassigned => write!(w, "?"),
286 }
287 }
288
write_value_range_markers( w: &mut dyn Write, val_ranges: &ValueLabelsRanges, regs: &RegInfo, offset: u32, indent: usize, ) -> fmt::Result289 fn write_value_range_markers(
290 w: &mut dyn Write,
291 val_ranges: &ValueLabelsRanges,
292 regs: &RegInfo,
293 offset: u32,
294 indent: usize,
295 ) -> fmt::Result {
296 let mut result = String::new();
297 let mut shown = HashSet::new();
298 for (val, rng) in val_ranges {
299 for i in (0..rng.len()).rev() {
300 if rng[i].start == offset {
301 write!(&mut result, " {}@", val)?;
302 write_valueloc(&mut result, rng[i].loc, regs)?;
303 shown.insert(val);
304 break;
305 }
306 }
307 }
308 for (val, rng) in val_ranges {
309 for i in (0..rng.len()).rev() {
310 if rng[i].end == offset && !shown.contains(val) {
311 write!(&mut result, " {}\u{2620}", val)?;
312 break;
313 }
314 }
315 }
316 if !result.is_empty() {
317 writeln!(w, ";{1:0$}; {2}", indent + 24, "", result)?;
318 }
319 Ok(())
320 }
321
decorate_block<FW: FuncWriter>( func_w: &mut FW, w: &mut dyn Write, func: &Function, aliases: &SecondaryMap<Value, Vec<Value>>, annotations: &DisplayFunctionAnnotations, block: Block, ) -> fmt::Result322 fn decorate_block<FW: FuncWriter>(
323 func_w: &mut FW,
324 w: &mut dyn Write,
325 func: &Function,
326 aliases: &SecondaryMap<Value, Vec<Value>>,
327 annotations: &DisplayFunctionAnnotations,
328 block: Block,
329 ) -> fmt::Result {
330 // Indent all instructions if any encodings are present.
331 let indent = if func.encodings.is_empty() && func.srclocs.is_empty() {
332 4
333 } else {
334 36
335 };
336 let isa = annotations.isa;
337
338 func_w.write_block_header(w, func, isa, block, indent)?;
339 for a in func.dfg.block_params(block).iter().cloned() {
340 write_value_aliases(w, aliases, a, indent)?;
341 }
342
343 if let Some(isa) = isa {
344 if !func.offsets.is_empty() {
345 let encinfo = isa.encoding_info();
346 let regs = &isa.register_info();
347 for (offset, inst, size) in func.inst_offsets(block, &encinfo) {
348 func_w.write_instruction(w, func, aliases, Some(isa), inst, indent)?;
349 if size > 0 {
350 if let Some(val_ranges) = annotations.value_ranges {
351 write_value_range_markers(w, val_ranges, regs, offset + size, indent)?;
352 }
353 }
354 }
355 return Ok(());
356 }
357 }
358
359 for inst in func.layout.block_insts(block) {
360 func_w.write_instruction(w, func, aliases, isa, inst, indent)?;
361 }
362
363 Ok(())
364 }
365
366 //----------------------------------------------------------------------
367 //
368 // Instructions
369
370 // Should `inst` be printed with a type suffix?
371 //
372 // Polymorphic instructions may need a suffix indicating the value of the controlling type variable
373 // if it can't be trivially inferred.
374 //
type_suffix(func: &Function, inst: Inst) -> Option<Type>375 fn type_suffix(func: &Function, inst: Inst) -> Option<Type> {
376 let inst_data = &func.dfg[inst];
377 let constraints = inst_data.opcode().constraints();
378
379 if !constraints.is_polymorphic() {
380 return None;
381 }
382
383 // If the controlling type variable can be inferred from the type of the designated value input
384 // operand, we don't need the type suffix.
385 if constraints.use_typevar_operand() {
386 let ctrl_var = inst_data.typevar_operand(&func.dfg.value_lists).unwrap();
387 let def_block = match func.dfg.value_def(ctrl_var) {
388 ValueDef::Result(instr, _) => func.layout.inst_block(instr),
389 ValueDef::Param(block, _) => Some(block),
390 };
391 if def_block.is_some() && def_block == func.layout.inst_block(inst) {
392 return None;
393 }
394 }
395
396 let rtype = func.dfg.ctrl_typevar(inst);
397 assert!(
398 !rtype.is_invalid(),
399 "Polymorphic instruction must produce a result"
400 );
401 Some(rtype)
402 }
403
404 /// Write out any aliases to the given target, including indirect aliases
write_value_aliases( w: &mut dyn Write, aliases: &SecondaryMap<Value, Vec<Value>>, target: Value, indent: usize, ) -> fmt::Result405 fn write_value_aliases(
406 w: &mut dyn Write,
407 aliases: &SecondaryMap<Value, Vec<Value>>,
408 target: Value,
409 indent: usize,
410 ) -> fmt::Result {
411 let mut todo_stack = vec![target];
412 while let Some(target) = todo_stack.pop() {
413 for &a in &aliases[target] {
414 writeln!(w, "{1:0$}{2} -> {3}", indent, "", a, target)?;
415 todo_stack.push(a);
416 }
417 }
418
419 Ok(())
420 }
421
write_instruction( w: &mut dyn Write, func: &Function, aliases: &SecondaryMap<Value, Vec<Value>>, isa: Option<&dyn TargetIsa>, inst: Inst, indent: usize, ) -> fmt::Result422 fn write_instruction(
423 w: &mut dyn Write,
424 func: &Function,
425 aliases: &SecondaryMap<Value, Vec<Value>>,
426 isa: Option<&dyn TargetIsa>,
427 inst: Inst,
428 indent: usize,
429 ) -> fmt::Result {
430 // Prefix containing source location, encoding, and value locations.
431 let mut s = String::with_capacity(16);
432
433 // Source location goes first.
434 let srcloc = func.srclocs[inst];
435 if !srcloc.is_default() {
436 write!(s, "{} ", srcloc)?;
437 }
438
439 // Write out encoding info.
440 if let Some(enc) = func.encodings.get(inst).cloned() {
441 if let Some(isa) = isa {
442 write!(s, "[{}", isa.encoding_info().display(enc))?;
443 // Write value locations, if we have them.
444 if !func.locations.is_empty() {
445 let regs = isa.register_info();
446 for &r in func.dfg.inst_results(inst) {
447 write!(s, ",{}", func.locations[r].display(®s))?
448 }
449 }
450 write!(s, "] ")?;
451 } else {
452 write!(s, "[{}] ", enc)?;
453 }
454 }
455
456 // Write out prefix and indent the instruction.
457 write!(w, "{1:0$}", indent, s)?;
458
459 // Write out the result values, if any.
460 let mut has_results = false;
461 for r in func.dfg.inst_results(inst) {
462 if !has_results {
463 has_results = true;
464 write!(w, "{}", r)?;
465 } else {
466 write!(w, ", {}", r)?;
467 }
468 }
469 if has_results {
470 write!(w, " = ")?;
471 }
472
473 // Then the opcode, possibly with a '.type' suffix.
474 let opcode = func.dfg[inst].opcode();
475
476 match type_suffix(func, inst) {
477 Some(suf) => write!(w, "{}.{}", opcode, suf)?,
478 None => write!(w, "{}", opcode)?,
479 }
480
481 write_operands(w, &func.dfg, isa, inst)?;
482 writeln!(w)?;
483
484 // Value aliases come out on lines after the instruction defining the referent.
485 for r in func.dfg.inst_results(inst) {
486 write_value_aliases(w, aliases, *r, indent)?;
487 }
488 Ok(())
489 }
490
491 /// Write the operands of `inst` to `w` with a prepended space.
write_operands( w: &mut dyn Write, dfg: &DataFlowGraph, isa: Option<&dyn TargetIsa>, inst: Inst, ) -> fmt::Result492 pub fn write_operands(
493 w: &mut dyn Write,
494 dfg: &DataFlowGraph,
495 isa: Option<&dyn TargetIsa>,
496 inst: Inst,
497 ) -> fmt::Result {
498 let pool = &dfg.value_lists;
499 use crate::ir::instructions::InstructionData::*;
500 match dfg[inst] {
501 Unary { arg, .. } => write!(w, " {}", arg),
502 UnaryImm { imm, .. } => write!(w, " {}", imm),
503 UnaryIeee32 { imm, .. } => write!(w, " {}", imm),
504 UnaryIeee64 { imm, .. } => write!(w, " {}", imm),
505 UnaryBool { imm, .. } => write!(w, " {}", imm),
506 UnaryGlobalValue { global_value, .. } => write!(w, " {}", global_value),
507 UnaryConst {
508 constant_handle, ..
509 } => write!(w, " {}", constant_handle),
510 Binary { args, .. } => write!(w, " {}, {}", args[0], args[1]),
511 BinaryImm8 { arg, imm, .. } => write!(w, " {}, {}", arg, imm),
512 BinaryImm64 { arg, imm, .. } => write!(w, " {}, {}", arg, imm),
513 Ternary { args, .. } => write!(w, " {}, {}, {}", args[0], args[1], args[2]),
514 MultiAry { ref args, .. } => {
515 if args.is_empty() {
516 write!(w, "")
517 } else {
518 write!(w, " {}", DisplayValues(args.as_slice(pool)))
519 }
520 }
521 NullAry { .. } => write!(w, " "),
522 TernaryImm8 { imm, args, .. } => write!(w, " {}, {}, {}", args[0], args[1], imm),
523 Shuffle { mask, args, .. } => {
524 let data = dfg.immediates.get(mask).expect(
525 "Expected the shuffle mask to already be inserted into the immediates table",
526 );
527 write!(w, " {}, {}, {}", args[0], args[1], data)
528 }
529 IntCompare { cond, args, .. } => write!(w, " {} {}, {}", cond, args[0], args[1]),
530 IntCompareImm { cond, arg, imm, .. } => write!(w, " {} {}, {}", cond, arg, imm),
531 IntCond { cond, arg, .. } => write!(w, " {} {}", cond, arg),
532 FloatCompare { cond, args, .. } => write!(w, " {} {}, {}", cond, args[0], args[1]),
533 FloatCond { cond, arg, .. } => write!(w, " {} {}", cond, arg),
534 IntSelect { cond, args, .. } => {
535 write!(w, " {} {}, {}, {}", cond, args[0], args[1], args[2])
536 }
537 Jump {
538 destination,
539 ref args,
540 ..
541 } => {
542 write!(w, " {}", destination)?;
543 write_block_args(w, args.as_slice(pool))
544 }
545 Branch {
546 destination,
547 ref args,
548 ..
549 } => {
550 let args = args.as_slice(pool);
551 write!(w, " {}, {}", args[0], destination)?;
552 write_block_args(w, &args[1..])
553 }
554 BranchInt {
555 cond,
556 destination,
557 ref args,
558 ..
559 } => {
560 let args = args.as_slice(pool);
561 write!(w, " {} {}, {}", cond, args[0], destination)?;
562 write_block_args(w, &args[1..])
563 }
564 BranchFloat {
565 cond,
566 destination,
567 ref args,
568 ..
569 } => {
570 let args = args.as_slice(pool);
571 write!(w, " {} {}, {}", cond, args[0], destination)?;
572 write_block_args(w, &args[1..])
573 }
574 BranchIcmp {
575 cond,
576 destination,
577 ref args,
578 ..
579 } => {
580 let args = args.as_slice(pool);
581 write!(w, " {} {}, {}, {}", cond, args[0], args[1], destination)?;
582 write_block_args(w, &args[2..])
583 }
584 BranchTable {
585 arg,
586 destination,
587 table,
588 ..
589 } => write!(w, " {}, {}, {}", arg, destination, table),
590 BranchTableBase { table, .. } => write!(w, " {}", table),
591 BranchTableEntry {
592 args, imm, table, ..
593 } => write!(w, " {}, {}, {}, {}", args[0], args[1], imm, table),
594 IndirectJump { arg, table, .. } => write!(w, " {}, {}", arg, table),
595 Call {
596 func_ref, ref args, ..
597 } => write!(w, " {}({})", func_ref, DisplayValues(args.as_slice(pool))),
598 CallIndirect {
599 sig_ref, ref args, ..
600 } => {
601 let args = args.as_slice(pool);
602 write!(
603 w,
604 " {}, {}({})",
605 sig_ref,
606 args[0],
607 DisplayValues(&args[1..])
608 )
609 }
610 FuncAddr { func_ref, .. } => write!(w, " {}", func_ref),
611 StackLoad {
612 stack_slot, offset, ..
613 } => write!(w, " {}{}", stack_slot, offset),
614 StackStore {
615 arg,
616 stack_slot,
617 offset,
618 ..
619 } => write!(w, " {}, {}{}", arg, stack_slot, offset),
620 HeapAddr { heap, arg, imm, .. } => write!(w, " {}, {}, {}", heap, arg, imm),
621 TableAddr { table, arg, .. } => write!(w, " {}, {}", table, arg),
622 Load {
623 flags, arg, offset, ..
624 } => write!(w, "{} {}{}", flags, arg, offset),
625 LoadComplex {
626 flags,
627 ref args,
628 offset,
629 ..
630 } => {
631 let args = args.as_slice(pool);
632 write!(
633 w,
634 "{} {}{}",
635 flags,
636 DisplayValuesWithDelimiter(&args, '+'),
637 offset
638 )
639 }
640 Store {
641 flags,
642 args,
643 offset,
644 ..
645 } => write!(w, "{} {}, {}{}", flags, args[0], args[1], offset),
646 StoreComplex {
647 flags,
648 ref args,
649 offset,
650 ..
651 } => {
652 let args = args.as_slice(pool);
653 write!(
654 w,
655 "{} {}, {}{}",
656 flags,
657 args[0],
658 DisplayValuesWithDelimiter(&args[1..], '+'),
659 offset
660 )
661 }
662 RegMove { arg, src, dst, .. } => {
663 if let Some(isa) = isa {
664 let regs = isa.register_info();
665 write!(
666 w,
667 " {}, {} -> {}",
668 arg,
669 regs.display_regunit(src),
670 regs.display_regunit(dst)
671 )
672 } else {
673 write!(w, " {}, %{} -> %{}", arg, src, dst)
674 }
675 }
676 CopySpecial { src, dst, .. } => {
677 if let Some(isa) = isa {
678 let regs = isa.register_info();
679 write!(
680 w,
681 " {} -> {}",
682 regs.display_regunit(src),
683 regs.display_regunit(dst)
684 )
685 } else {
686 write!(w, " %{} -> %{}", src, dst)
687 }
688 }
689 CopyToSsa { src, .. } => {
690 if let Some(isa) = isa {
691 let regs = isa.register_info();
692 write!(w, " {}", regs.display_regunit(src))
693 } else {
694 write!(w, " %{}", src)
695 }
696 }
697 RegSpill { arg, src, dst, .. } => {
698 if let Some(isa) = isa {
699 let regs = isa.register_info();
700 write!(w, " {}, {} -> {}", arg, regs.display_regunit(src), dst)
701 } else {
702 write!(w, " {}, %{} -> {}", arg, src, dst)
703 }
704 }
705 RegFill { arg, src, dst, .. } => {
706 if let Some(isa) = isa {
707 let regs = isa.register_info();
708 write!(w, " {}, {} -> {}", arg, src, regs.display_regunit(dst))
709 } else {
710 write!(w, " {}, {} -> %{}", arg, src, dst)
711 }
712 }
713 Trap { code, .. } => write!(w, " {}", code),
714 CondTrap { arg, code, .. } => write!(w, " {}, {}", arg, code),
715 IntCondTrap {
716 cond, arg, code, ..
717 } => write!(w, " {} {}, {}", cond, arg, code),
718 FloatCondTrap {
719 cond, arg, code, ..
720 } => write!(w, " {} {}, {}", cond, arg, code),
721 }
722 }
723
724 /// Write block args using optional parantheses.
write_block_args(w: &mut dyn Write, args: &[Value]) -> fmt::Result725 fn write_block_args(w: &mut dyn Write, args: &[Value]) -> fmt::Result {
726 if args.is_empty() {
727 Ok(())
728 } else {
729 write!(w, "({})", DisplayValues(args))
730 }
731 }
732
733 /// Displayable slice of values.
734 struct DisplayValues<'a>(&'a [Value]);
735
736 impl<'a> fmt::Display for DisplayValues<'a> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result737 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
738 for (i, val) in self.0.iter().enumerate() {
739 if i == 0 {
740 write!(f, "{}", val)?;
741 } else {
742 write!(f, ", {}", val)?;
743 }
744 }
745 Ok(())
746 }
747 }
748
749 struct DisplayValuesWithDelimiter<'a>(&'a [Value], char);
750
751 impl<'a> fmt::Display for DisplayValuesWithDelimiter<'a> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result752 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
753 for (i, val) in self.0.iter().enumerate() {
754 if i == 0 {
755 write!(f, "{}", val)?;
756 } else {
757 write!(f, "{}{}", self.1, val)?;
758 }
759 }
760 Ok(())
761 }
762 }
763
764 #[cfg(test)]
765 mod tests {
766 use crate::cursor::{Cursor, CursorPosition, FuncCursor};
767 use crate::ir::types;
768 use crate::ir::{ExternalName, Function, InstBuilder, StackSlotData, StackSlotKind};
769 use alloc::string::ToString;
770
771 #[test]
basic()772 fn basic() {
773 let mut f = Function::new();
774 assert_eq!(f.to_string(), "function u0:0() fast {\n}\n");
775
776 f.name = ExternalName::testcase("foo");
777 assert_eq!(f.to_string(), "function %foo() fast {\n}\n");
778
779 f.create_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 4));
780 assert_eq!(
781 f.to_string(),
782 "function %foo() fast {\n ss0 = explicit_slot 4\n}\n"
783 );
784
785 let block = f.dfg.make_block();
786 f.layout.append_block(block);
787 assert_eq!(
788 f.to_string(),
789 "function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0:\n}\n"
790 );
791
792 f.dfg.append_block_param(block, types::I8);
793 assert_eq!(
794 f.to_string(),
795 "function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0(v0: i8):\n}\n"
796 );
797
798 f.dfg.append_block_param(block, types::F32.by(4).unwrap());
799 assert_eq!(
800 f.to_string(),
801 "function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0(v0: i8, v1: f32x4):\n}\n"
802 );
803
804 {
805 let mut cursor = FuncCursor::new(&mut f);
806 cursor.set_position(CursorPosition::After(block));
807 cursor.ins().return_(&[])
808 };
809 assert_eq!(
810 f.to_string(),
811 "function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0(v0: i8, v1: f32x4):\n return\n}\n"
812 );
813 }
814
815 #[test]
aliases()816 fn aliases() {
817 use crate::ir::InstBuilder;
818
819 let mut func = Function::new();
820 {
821 let block0 = func.dfg.make_block();
822 let mut pos = FuncCursor::new(&mut func);
823 pos.insert_block(block0);
824
825 // make some detached values for change_to_alias
826 let v0 = pos.func.dfg.append_block_param(block0, types::I32);
827 let v1 = pos.func.dfg.append_block_param(block0, types::I32);
828 let v2 = pos.func.dfg.append_block_param(block0, types::I32);
829 pos.func.dfg.detach_block_params(block0);
830
831 // alias to a param--will be printed at beginning of block defining param
832 let v3 = pos.func.dfg.append_block_param(block0, types::I32);
833 pos.func.dfg.change_to_alias(v0, v3);
834
835 // alias to an alias--should print attached to alias, not ultimate target
836 pos.func.dfg.make_value_alias_for_serialization(v0, v2); // v0 <- v2
837
838 // alias to a result--will be printed after instruction producing result
839 let _dummy0 = pos.ins().iconst(types::I32, 42);
840 let v4 = pos.ins().iadd(v0, v0);
841 pos.func.dfg.change_to_alias(v1, v4);
842 let _dummy1 = pos.ins().iconst(types::I32, 23);
843 let _v7 = pos.ins().iadd(v1, v1);
844 }
845 assert_eq!(
846 func.to_string(),
847 "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"
848 );
849 }
850 }
851