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::{LabelValueLoc, 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: LabelValueLoc, regs: &RegInfo) -> fmt::Result281 fn write_valueloc(w: &mut dyn Write, loc: LabelValueLoc, regs: &RegInfo) -> fmt::Result {
282 match loc {
283 LabelValueLoc::ValueLoc(ValueLoc::Reg(r)) => write!(w, "{}", regs.display_regunit(r)),
284 LabelValueLoc::ValueLoc(ValueLoc::Stack(ss)) => write!(w, "{}", ss),
285 LabelValueLoc::ValueLoc(ValueLoc::Unassigned) => write!(w, "?"),
286 LabelValueLoc::Reg(r) => write!(w, "{:?}", r),
287 LabelValueLoc::SPOffset(off) => write!(w, "[sp+{}]", off),
288 }
289 }
290
write_value_range_markers( w: &mut dyn Write, val_ranges: &ValueLabelsRanges, regs: &RegInfo, offset: u32, indent: usize, ) -> fmt::Result291 fn write_value_range_markers(
292 w: &mut dyn Write,
293 val_ranges: &ValueLabelsRanges,
294 regs: &RegInfo,
295 offset: u32,
296 indent: usize,
297 ) -> fmt::Result {
298 let mut result = String::new();
299 let mut shown = HashSet::new();
300 for (val, rng) in val_ranges {
301 for i in (0..rng.len()).rev() {
302 if rng[i].start == offset {
303 write!(&mut result, " {}@", val)?;
304 write_valueloc(&mut result, rng[i].loc, regs)?;
305 shown.insert(val);
306 break;
307 }
308 }
309 }
310 for (val, rng) in val_ranges {
311 for i in (0..rng.len()).rev() {
312 if rng[i].end == offset && !shown.contains(val) {
313 write!(&mut result, " {}\u{2620}", val)?;
314 break;
315 }
316 }
317 }
318 if !result.is_empty() {
319 writeln!(w, ";{1:0$}; {2}", indent + 24, "", result)?;
320 }
321 Ok(())
322 }
323
decorate_block<FW: FuncWriter>( func_w: &mut FW, w: &mut dyn Write, func: &Function, aliases: &SecondaryMap<Value, Vec<Value>>, annotations: &DisplayFunctionAnnotations, block: Block, ) -> fmt::Result324 fn decorate_block<FW: FuncWriter>(
325 func_w: &mut FW,
326 w: &mut dyn Write,
327 func: &Function,
328 aliases: &SecondaryMap<Value, Vec<Value>>,
329 annotations: &DisplayFunctionAnnotations,
330 block: Block,
331 ) -> fmt::Result {
332 // Indent all instructions if any encodings are present.
333 let indent = if func.encodings.is_empty() && func.srclocs.is_empty() {
334 4
335 } else {
336 36
337 };
338 let isa = annotations.isa;
339
340 func_w.write_block_header(w, func, isa, block, indent)?;
341 for a in func.dfg.block_params(block).iter().cloned() {
342 write_value_aliases(w, aliases, a, indent)?;
343 }
344
345 if let Some(isa) = isa {
346 if !func.offsets.is_empty() {
347 let encinfo = isa.encoding_info();
348 let regs = &isa.register_info();
349 for (offset, inst, size) in func.inst_offsets(block, &encinfo) {
350 func_w.write_instruction(w, func, aliases, Some(isa), inst, indent)?;
351 if size > 0 {
352 if let Some(val_ranges) = annotations.value_ranges {
353 write_value_range_markers(w, val_ranges, regs, offset + size, indent)?;
354 }
355 }
356 }
357 return Ok(());
358 }
359 }
360
361 for inst in func.layout.block_insts(block) {
362 func_w.write_instruction(w, func, aliases, isa, inst, indent)?;
363 }
364
365 Ok(())
366 }
367
368 //----------------------------------------------------------------------
369 //
370 // Instructions
371
372 // Should `inst` be printed with a type suffix?
373 //
374 // Polymorphic instructions may need a suffix indicating the value of the controlling type variable
375 // if it can't be trivially inferred.
376 //
type_suffix(func: &Function, inst: Inst) -> Option<Type>377 fn type_suffix(func: &Function, inst: Inst) -> Option<Type> {
378 let inst_data = &func.dfg[inst];
379 let constraints = inst_data.opcode().constraints();
380
381 if !constraints.is_polymorphic() {
382 return None;
383 }
384
385 // If the controlling type variable can be inferred from the type of the designated value input
386 // operand, we don't need the type suffix.
387 if constraints.use_typevar_operand() {
388 let ctrl_var = inst_data.typevar_operand(&func.dfg.value_lists).unwrap();
389 let def_block = match func.dfg.value_def(ctrl_var) {
390 ValueDef::Result(instr, _) => func.layout.inst_block(instr),
391 ValueDef::Param(block, _) => Some(block),
392 };
393 if def_block.is_some() && def_block == func.layout.inst_block(inst) {
394 return None;
395 }
396 }
397
398 let rtype = func.dfg.ctrl_typevar(inst);
399 assert!(
400 !rtype.is_invalid(),
401 "Polymorphic instruction must produce a result"
402 );
403 Some(rtype)
404 }
405
406 /// 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::Result407 fn write_value_aliases(
408 w: &mut dyn Write,
409 aliases: &SecondaryMap<Value, Vec<Value>>,
410 target: Value,
411 indent: usize,
412 ) -> fmt::Result {
413 let mut todo_stack = vec![target];
414 while let Some(target) = todo_stack.pop() {
415 for &a in &aliases[target] {
416 writeln!(w, "{1:0$}{2} -> {3}", indent, "", a, target)?;
417 todo_stack.push(a);
418 }
419 }
420
421 Ok(())
422 }
423
write_instruction( w: &mut dyn Write, func: &Function, aliases: &SecondaryMap<Value, Vec<Value>>, isa: Option<&dyn TargetIsa>, inst: Inst, indent: usize, ) -> fmt::Result424 fn write_instruction(
425 w: &mut dyn Write,
426 func: &Function,
427 aliases: &SecondaryMap<Value, Vec<Value>>,
428 isa: Option<&dyn TargetIsa>,
429 inst: Inst,
430 indent: usize,
431 ) -> fmt::Result {
432 // Prefix containing source location, encoding, and value locations.
433 let mut s = String::with_capacity(16);
434
435 // Source location goes first.
436 let srcloc = func.srclocs[inst];
437 if !srcloc.is_default() {
438 write!(s, "{} ", srcloc)?;
439 }
440
441 // Write out encoding info.
442 if let Some(enc) = func.encodings.get(inst).cloned() {
443 if let Some(isa) = isa {
444 write!(s, "[{}", isa.encoding_info().display(enc))?;
445 // Write value locations, if we have them.
446 if !func.locations.is_empty() {
447 let regs = isa.register_info();
448 for &r in func.dfg.inst_results(inst) {
449 write!(s, ",{}", func.locations[r].display(®s))?
450 }
451 }
452 write!(s, "] ")?;
453 } else {
454 write!(s, "[{}] ", enc)?;
455 }
456 }
457
458 // Write out prefix and indent the instruction.
459 write!(w, "{1:0$}", indent, s)?;
460
461 // Write out the result values, if any.
462 let mut has_results = false;
463 for r in func.dfg.inst_results(inst) {
464 if !has_results {
465 has_results = true;
466 write!(w, "{}", r)?;
467 } else {
468 write!(w, ", {}", r)?;
469 }
470 }
471 if has_results {
472 write!(w, " = ")?;
473 }
474
475 // Then the opcode, possibly with a '.type' suffix.
476 let opcode = func.dfg[inst].opcode();
477
478 match type_suffix(func, inst) {
479 Some(suf) => write!(w, "{}.{}", opcode, suf)?,
480 None => write!(w, "{}", opcode)?,
481 }
482
483 write_operands(w, &func.dfg, isa, inst)?;
484 writeln!(w)?;
485
486 // Value aliases come out on lines after the instruction defining the referent.
487 for r in func.dfg.inst_results(inst) {
488 write_value_aliases(w, aliases, *r, indent)?;
489 }
490 Ok(())
491 }
492
493 /// 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::Result494 pub fn write_operands(
495 w: &mut dyn Write,
496 dfg: &DataFlowGraph,
497 isa: Option<&dyn TargetIsa>,
498 inst: Inst,
499 ) -> fmt::Result {
500 let pool = &dfg.value_lists;
501 use crate::ir::instructions::InstructionData::*;
502 match dfg[inst] {
503 AtomicRmw { op, args, .. } => write!(w, " {}, {}, {}", op, args[0], args[1]),
504 AtomicCas { args, .. } => write!(w, " {}, {}, {}", args[0], args[1], args[2]),
505 LoadNoOffset { flags, arg, .. } => write!(w, "{} {}", flags, arg),
506 StoreNoOffset { flags, args, .. } => write!(w, "{} {}, {}", flags, args[0], args[1]),
507 Unary { arg, .. } => write!(w, " {}", arg),
508 UnaryImm { imm, .. } => write!(w, " {}", imm),
509 UnaryIeee32 { imm, .. } => write!(w, " {}", imm),
510 UnaryIeee64 { imm, .. } => write!(w, " {}", imm),
511 UnaryBool { imm, .. } => write!(w, " {}", imm),
512 UnaryGlobalValue { global_value, .. } => write!(w, " {}", global_value),
513 UnaryConst {
514 constant_handle, ..
515 } => write!(w, " {}", constant_handle),
516 Binary { args, .. } => write!(w, " {}, {}", args[0], args[1]),
517 BinaryImm8 { arg, imm, .. } => write!(w, " {}, {}", arg, imm),
518 BinaryImm64 { arg, imm, .. } => write!(w, " {}, {}", arg, imm),
519 Ternary { args, .. } => write!(w, " {}, {}, {}", args[0], args[1], args[2]),
520 MultiAry { ref args, .. } => {
521 if args.is_empty() {
522 write!(w, "")
523 } else {
524 write!(w, " {}", DisplayValues(args.as_slice(pool)))
525 }
526 }
527 NullAry { .. } => write!(w, " "),
528 TernaryImm8 { imm, args, .. } => write!(w, " {}, {}, {}", args[0], args[1], imm),
529 Shuffle { mask, args, .. } => {
530 let data = dfg.immediates.get(mask).expect(
531 "Expected the shuffle mask to already be inserted into the immediates table",
532 );
533 write!(w, " {}, {}, {}", args[0], args[1], data)
534 }
535 IntCompare { cond, args, .. } => write!(w, " {} {}, {}", cond, args[0], args[1]),
536 IntCompareImm { cond, arg, imm, .. } => write!(w, " {} {}, {}", cond, arg, imm),
537 IntCond { cond, arg, .. } => write!(w, " {} {}", cond, arg),
538 FloatCompare { cond, args, .. } => write!(w, " {} {}, {}", cond, args[0], args[1]),
539 FloatCond { cond, arg, .. } => write!(w, " {} {}", cond, arg),
540 IntSelect { cond, args, .. } => {
541 write!(w, " {} {}, {}, {}", cond, args[0], args[1], args[2])
542 }
543 Jump {
544 destination,
545 ref args,
546 ..
547 } => {
548 write!(w, " {}", destination)?;
549 write_block_args(w, args.as_slice(pool))
550 }
551 Branch {
552 destination,
553 ref args,
554 ..
555 } => {
556 let args = args.as_slice(pool);
557 write!(w, " {}, {}", args[0], destination)?;
558 write_block_args(w, &args[1..])
559 }
560 BranchInt {
561 cond,
562 destination,
563 ref args,
564 ..
565 } => {
566 let args = args.as_slice(pool);
567 write!(w, " {} {}, {}", cond, args[0], destination)?;
568 write_block_args(w, &args[1..])
569 }
570 BranchFloat {
571 cond,
572 destination,
573 ref args,
574 ..
575 } => {
576 let args = args.as_slice(pool);
577 write!(w, " {} {}, {}", cond, args[0], destination)?;
578 write_block_args(w, &args[1..])
579 }
580 BranchIcmp {
581 cond,
582 destination,
583 ref args,
584 ..
585 } => {
586 let args = args.as_slice(pool);
587 write!(w, " {} {}, {}, {}", cond, args[0], args[1], destination)?;
588 write_block_args(w, &args[2..])
589 }
590 BranchTable {
591 arg,
592 destination,
593 table,
594 ..
595 } => write!(w, " {}, {}, {}", arg, destination, table),
596 BranchTableBase { table, .. } => write!(w, " {}", table),
597 BranchTableEntry {
598 args, imm, table, ..
599 } => write!(w, " {}, {}, {}, {}", args[0], args[1], imm, table),
600 IndirectJump { arg, table, .. } => write!(w, " {}, {}", arg, table),
601 Call {
602 func_ref, ref args, ..
603 } => write!(w, " {}({})", func_ref, DisplayValues(args.as_slice(pool))),
604 CallIndirect {
605 sig_ref, ref args, ..
606 } => {
607 let args = args.as_slice(pool);
608 write!(
609 w,
610 " {}, {}({})",
611 sig_ref,
612 args[0],
613 DisplayValues(&args[1..])
614 )
615 }
616 FuncAddr { func_ref, .. } => write!(w, " {}", func_ref),
617 StackLoad {
618 stack_slot, offset, ..
619 } => write!(w, " {}{}", stack_slot, offset),
620 StackStore {
621 arg,
622 stack_slot,
623 offset,
624 ..
625 } => write!(w, " {}, {}{}", arg, stack_slot, offset),
626 HeapAddr { heap, arg, imm, .. } => write!(w, " {}, {}, {}", heap, arg, imm),
627 TableAddr { table, arg, .. } => write!(w, " {}, {}", table, arg),
628 Load {
629 flags, arg, offset, ..
630 } => write!(w, "{} {}{}", flags, arg, offset),
631 LoadComplex {
632 flags,
633 ref args,
634 offset,
635 ..
636 } => {
637 let args = args.as_slice(pool);
638 write!(
639 w,
640 "{} {}{}",
641 flags,
642 DisplayValuesWithDelimiter(&args, '+'),
643 offset
644 )
645 }
646 Store {
647 flags,
648 args,
649 offset,
650 ..
651 } => write!(w, "{} {}, {}{}", flags, args[0], args[1], offset),
652 StoreComplex {
653 flags,
654 ref args,
655 offset,
656 ..
657 } => {
658 let args = args.as_slice(pool);
659 write!(
660 w,
661 "{} {}, {}{}",
662 flags,
663 args[0],
664 DisplayValuesWithDelimiter(&args[1..], '+'),
665 offset
666 )
667 }
668 RegMove { arg, src, dst, .. } => {
669 if let Some(isa) = isa {
670 let regs = isa.register_info();
671 write!(
672 w,
673 " {}, {} -> {}",
674 arg,
675 regs.display_regunit(src),
676 regs.display_regunit(dst)
677 )
678 } else {
679 write!(w, " {}, %{} -> %{}", arg, src, dst)
680 }
681 }
682 CopySpecial { src, dst, .. } => {
683 if let Some(isa) = isa {
684 let regs = isa.register_info();
685 write!(
686 w,
687 " {} -> {}",
688 regs.display_regunit(src),
689 regs.display_regunit(dst)
690 )
691 } else {
692 write!(w, " %{} -> %{}", src, dst)
693 }
694 }
695 CopyToSsa { src, .. } => {
696 if let Some(isa) = isa {
697 let regs = isa.register_info();
698 write!(w, " {}", regs.display_regunit(src))
699 } else {
700 write!(w, " %{}", src)
701 }
702 }
703 RegSpill { arg, src, dst, .. } => {
704 if let Some(isa) = isa {
705 let regs = isa.register_info();
706 write!(w, " {}, {} -> {}", arg, regs.display_regunit(src), dst)
707 } else {
708 write!(w, " {}, %{} -> {}", arg, src, dst)
709 }
710 }
711 RegFill { arg, src, dst, .. } => {
712 if let Some(isa) = isa {
713 let regs = isa.register_info();
714 write!(w, " {}, {} -> {}", arg, src, regs.display_regunit(dst))
715 } else {
716 write!(w, " {}, {} -> %{}", arg, src, dst)
717 }
718 }
719 Trap { code, .. } => write!(w, " {}", code),
720 CondTrap { arg, code, .. } => write!(w, " {}, {}", arg, code),
721 IntCondTrap {
722 cond, arg, code, ..
723 } => write!(w, " {} {}, {}", cond, arg, code),
724 FloatCondTrap {
725 cond, arg, code, ..
726 } => write!(w, " {} {}, {}", cond, arg, code),
727 }
728 }
729
730 /// Write block args using optional parantheses.
write_block_args(w: &mut dyn Write, args: &[Value]) -> fmt::Result731 fn write_block_args(w: &mut dyn Write, args: &[Value]) -> fmt::Result {
732 if args.is_empty() {
733 Ok(())
734 } else {
735 write!(w, "({})", DisplayValues(args))
736 }
737 }
738
739 /// Displayable slice of values.
740 struct DisplayValues<'a>(&'a [Value]);
741
742 impl<'a> fmt::Display for DisplayValues<'a> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result743 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
744 for (i, val) in self.0.iter().enumerate() {
745 if i == 0 {
746 write!(f, "{}", val)?;
747 } else {
748 write!(f, ", {}", val)?;
749 }
750 }
751 Ok(())
752 }
753 }
754
755 struct DisplayValuesWithDelimiter<'a>(&'a [Value], char);
756
757 impl<'a> fmt::Display for DisplayValuesWithDelimiter<'a> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result758 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
759 for (i, val) in self.0.iter().enumerate() {
760 if i == 0 {
761 write!(f, "{}", val)?;
762 } else {
763 write!(f, "{}{}", self.1, val)?;
764 }
765 }
766 Ok(())
767 }
768 }
769
770 #[cfg(test)]
771 mod tests {
772 use crate::cursor::{Cursor, CursorPosition, FuncCursor};
773 use crate::ir::types;
774 use crate::ir::{ExternalName, Function, InstBuilder, StackSlotData, StackSlotKind};
775 use alloc::string::ToString;
776
777 #[test]
basic()778 fn basic() {
779 let mut f = Function::new();
780 assert_eq!(f.to_string(), "function u0:0() fast {\n}\n");
781
782 f.name = ExternalName::testcase("foo");
783 assert_eq!(f.to_string(), "function %foo() fast {\n}\n");
784
785 f.create_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 4));
786 assert_eq!(
787 f.to_string(),
788 "function %foo() fast {\n ss0 = explicit_slot 4\n}\n"
789 );
790
791 let block = f.dfg.make_block();
792 f.layout.append_block(block);
793 assert_eq!(
794 f.to_string(),
795 "function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0:\n}\n"
796 );
797
798 f.dfg.append_block_param(block, types::I8);
799 assert_eq!(
800 f.to_string(),
801 "function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0(v0: i8):\n}\n"
802 );
803
804 f.dfg.append_block_param(block, types::F32.by(4).unwrap());
805 assert_eq!(
806 f.to_string(),
807 "function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0(v0: i8, v1: f32x4):\n}\n"
808 );
809
810 {
811 let mut cursor = FuncCursor::new(&mut f);
812 cursor.set_position(CursorPosition::After(block));
813 cursor.ins().return_(&[])
814 };
815 assert_eq!(
816 f.to_string(),
817 "function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0(v0: i8, v1: f32x4):\n return\n}\n"
818 );
819 }
820
821 #[test]
aliases()822 fn aliases() {
823 use crate::ir::InstBuilder;
824
825 let mut func = Function::new();
826 {
827 let block0 = func.dfg.make_block();
828 let mut pos = FuncCursor::new(&mut func);
829 pos.insert_block(block0);
830
831 // make some detached values for change_to_alias
832 let v0 = pos.func.dfg.append_block_param(block0, types::I32);
833 let v1 = pos.func.dfg.append_block_param(block0, types::I32);
834 let v2 = pos.func.dfg.append_block_param(block0, types::I32);
835 pos.func.dfg.detach_block_params(block0);
836
837 // alias to a param--will be printed at beginning of block defining param
838 let v3 = pos.func.dfg.append_block_param(block0, types::I32);
839 pos.func.dfg.change_to_alias(v0, v3);
840
841 // alias to an alias--should print attached to alias, not ultimate target
842 pos.func.dfg.make_value_alias_for_serialization(v0, v2); // v0 <- v2
843
844 // alias to a result--will be printed after instruction producing result
845 let _dummy0 = pos.ins().iconst(types::I32, 42);
846 let v4 = pos.ins().iadd(v0, v0);
847 pos.func.dfg.change_to_alias(v1, v4);
848 let _dummy1 = pos.ins().iconst(types::I32, 23);
849 let _v7 = pos.ins().iadd(v1, v1);
850 }
851 assert_eq!(
852 func.to_string(),
853 "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"
854 );
855 }
856 }
857