1747ad3c4Slazypassion //! Converting Cranelift IR to text.
2747ad3c4Slazypassion //!
3747ad3c4Slazypassion //! The `write` module provides the `write_function` function which converts an IR `Function` to an
4747ad3c4Slazypassion //! equivalent textual form. This textual form can be read back by the `cranelift-reader` crate.
5747ad3c4Slazypassion
6747ad3c4Slazypassion use crate::entity::SecondaryMap;
7747ad3c4Slazypassion use crate::ir::entities::AnyEntity;
841eca60bSbeetrees use crate::ir::immediates::Ieee128;
941eca60bSbeetrees use crate::ir::{Block, DataFlowGraph, Function, Inst, Opcode, SigRef, Type, Value, ValueDef};
10747ad3c4Slazypassion use crate::packed_option::ReservedValue;
113629bbbdSJamey Sharp use alloc::string::{String, ToString};
1210e226f9Sbjorn3 use alloc::vec::Vec;
13bb8fa40eSbjorn3 use core::fmt::{self, Write};
14747ad3c4Slazypassion
15747ad3c4Slazypassion /// A `FuncWriter` used to decorate functions during printing.
16747ad3c4Slazypassion pub trait FuncWriter {
17832666c4SRyan Hunt /// Write the basic block header for the current function.
write_block_header( &mut self, w: &mut dyn Write, func: &Function, block: Block, indent: usize, ) -> fmt::Result18832666c4SRyan Hunt fn write_block_header(
19747ad3c4Slazypassion &mut self,
20d7d48d5cSBenjamin Bouvier w: &mut dyn Write,
21747ad3c4Slazypassion func: &Function,
22832666c4SRyan Hunt block: Block,
23747ad3c4Slazypassion indent: usize,
24747ad3c4Slazypassion ) -> fmt::Result;
25747ad3c4Slazypassion
26747ad3c4Slazypassion /// Write the given `inst` to `w`.
write_instruction( &mut self, w: &mut dyn Write, func: &Function, aliases: &SecondaryMap<Value, Vec<Value>>, inst: Inst, indent: usize, ) -> fmt::Result27747ad3c4Slazypassion fn write_instruction(
28747ad3c4Slazypassion &mut self,
29d7d48d5cSBenjamin Bouvier w: &mut dyn Write,
30747ad3c4Slazypassion func: &Function,
31747ad3c4Slazypassion aliases: &SecondaryMap<Value, Vec<Value>>,
32747ad3c4Slazypassion inst: Inst,
33747ad3c4Slazypassion indent: usize,
34747ad3c4Slazypassion ) -> fmt::Result;
35747ad3c4Slazypassion
36747ad3c4Slazypassion /// Write the preamble to `w`. By default, this uses `write_entity_definition`.
write_preamble(&mut self, w: &mut dyn Write, func: &Function) -> Result<bool, fmt::Error>3743a86f14SBenjamin Bouvier fn write_preamble(&mut self, w: &mut dyn Write, func: &Function) -> Result<bool, fmt::Error> {
3843a86f14SBenjamin Bouvier self.super_preamble(w, func)
39747ad3c4Slazypassion }
40747ad3c4Slazypassion
41747ad3c4Slazypassion /// Default impl of `write_preamble`
super_preamble(&mut self, w: &mut dyn Write, func: &Function) -> Result<bool, fmt::Error>4243a86f14SBenjamin Bouvier fn super_preamble(&mut self, w: &mut dyn Write, func: &Function) -> Result<bool, fmt::Error> {
43747ad3c4Slazypassion let mut any = false;
44747ad3c4Slazypassion
459c43749dSSam Parker for (ss, slot) in func.dynamic_stack_slots.iter() {
469c43749dSSam Parker any = true;
47*2f7dbd61SChris Fallin self.write_entity_definition(w, func, ss.into(), slot)?;
489c43749dSSam Parker }
499c43749dSSam Parker
509c43749dSSam Parker for (ss, slot) in func.sized_stack_slots.iter() {
51747ad3c4Slazypassion any = true;
52*2f7dbd61SChris Fallin self.write_entity_definition(w, func, ss.into(), slot)?;
53747ad3c4Slazypassion }
54747ad3c4Slazypassion
55747ad3c4Slazypassion for (gv, gv_data) in &func.global_values {
56747ad3c4Slazypassion any = true;
57*2f7dbd61SChris Fallin self.write_entity_definition(w, func, gv.into(), gv_data)?;
581ced3e8eSChris Fallin }
591ced3e8eSChris Fallin
60747ad3c4Slazypassion // Write out all signatures before functions since function declarations can refer to
61747ad3c4Slazypassion // signatures.
62747ad3c4Slazypassion for (sig, sig_data) in &func.dfg.signatures {
63747ad3c4Slazypassion any = true;
64*2f7dbd61SChris Fallin self.write_entity_definition(w, func, sig.into(), &sig_data)?;
65747ad3c4Slazypassion }
66747ad3c4Slazypassion
67747ad3c4Slazypassion for (fnref, ext_func) in &func.dfg.ext_funcs {
68747ad3c4Slazypassion if ext_func.signature != SigRef::reserved_value() {
69747ad3c4Slazypassion any = true;
708a9b1a90SBenjamin Bouvier self.write_entity_definition(
718a9b1a90SBenjamin Bouvier w,
728a9b1a90SBenjamin Bouvier func,
738a9b1a90SBenjamin Bouvier fnref.into(),
748a9b1a90SBenjamin Bouvier &ext_func.display(Some(&func.params)),
758a9b1a90SBenjamin Bouvier )?;
76747ad3c4Slazypassion }
77747ad3c4Slazypassion }
78747ad3c4Slazypassion
790672d1dcSAndrew Brown for (&cref, cval) in func.dfg.constants.iter() {
800672d1dcSAndrew Brown any = true;
81*2f7dbd61SChris Fallin self.write_entity_definition(w, func, cref.into(), cval)?;
820672d1dcSAndrew Brown }
830672d1dcSAndrew Brown
84c9a0ba81SAlex Crichton if let Some(limit) = func.stack_limit {
85c9a0ba81SAlex Crichton any = true;
86*2f7dbd61SChris Fallin self.write_entity_definition(w, func, AnyEntity::StackLimit, &limit)?;
87c9a0ba81SAlex Crichton }
88c9a0ba81SAlex Crichton
89747ad3c4Slazypassion Ok(any)
90747ad3c4Slazypassion }
91747ad3c4Slazypassion
92747ad3c4Slazypassion /// 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::Result93747ad3c4Slazypassion fn write_entity_definition(
94747ad3c4Slazypassion &mut self,
95d7d48d5cSBenjamin Bouvier w: &mut dyn Write,
96747ad3c4Slazypassion func: &Function,
97747ad3c4Slazypassion entity: AnyEntity,
98d7d48d5cSBenjamin Bouvier value: &dyn fmt::Display,
99747ad3c4Slazypassion ) -> fmt::Result {
100*2f7dbd61SChris Fallin self.super_entity_definition(w, func, entity, value)
101747ad3c4Slazypassion }
102747ad3c4Slazypassion
103747ad3c4Slazypassion /// Default impl of `write_entity_definition`
super_entity_definition( &mut self, w: &mut dyn Write, _func: &Function, entity: AnyEntity, value: &dyn fmt::Display, ) -> fmt::Result104747ad3c4Slazypassion fn super_entity_definition(
105747ad3c4Slazypassion &mut self,
106d7d48d5cSBenjamin Bouvier w: &mut dyn Write,
107099102d9SAlex Crichton _func: &Function,
108747ad3c4Slazypassion entity: AnyEntity,
109d7d48d5cSBenjamin Bouvier value: &dyn fmt::Display,
110747ad3c4Slazypassion ) -> fmt::Result {
111a0442ea0SHamir Mahal writeln!(w, " {entity} = {value}")
112747ad3c4Slazypassion }
113747ad3c4Slazypassion }
114747ad3c4Slazypassion
115747ad3c4Slazypassion /// A `PlainWriter` that doesn't decorate the function.
116747ad3c4Slazypassion pub struct PlainWriter;
117747ad3c4Slazypassion
118747ad3c4Slazypassion impl FuncWriter for PlainWriter {
write_instruction( &mut self, w: &mut dyn Write, func: &Function, aliases: &SecondaryMap<Value, Vec<Value>>, inst: Inst, indent: usize, ) -> fmt::Result119747ad3c4Slazypassion fn write_instruction(
120747ad3c4Slazypassion &mut self,
121d7d48d5cSBenjamin Bouvier w: &mut dyn Write,
122747ad3c4Slazypassion func: &Function,
123747ad3c4Slazypassion aliases: &SecondaryMap<Value, Vec<Value>>,
124747ad3c4Slazypassion inst: Inst,
125747ad3c4Slazypassion indent: usize,
126747ad3c4Slazypassion ) -> fmt::Result {
12743a86f14SBenjamin Bouvier write_instruction(w, func, aliases, inst, indent)
128747ad3c4Slazypassion }
129747ad3c4Slazypassion
write_block_header( &mut self, w: &mut dyn Write, func: &Function, block: Block, indent: usize, ) -> fmt::Result130832666c4SRyan Hunt fn write_block_header(
131747ad3c4Slazypassion &mut self,
132d7d48d5cSBenjamin Bouvier w: &mut dyn Write,
133747ad3c4Slazypassion func: &Function,
134832666c4SRyan Hunt block: Block,
135747ad3c4Slazypassion indent: usize,
136747ad3c4Slazypassion ) -> fmt::Result {
13743a86f14SBenjamin Bouvier write_block_header(w, func, block, indent)
138747ad3c4Slazypassion }
139747ad3c4Slazypassion }
140747ad3c4Slazypassion
141747ad3c4Slazypassion /// Write `func` to `w` as equivalent text.
142747ad3c4Slazypassion /// Use `isa` to emit ISA-dependent annotations.
write_function(w: &mut dyn Write, func: &Function) -> fmt::Result14343a86f14SBenjamin Bouvier pub fn write_function(w: &mut dyn Write, func: &Function) -> fmt::Result {
14443a86f14SBenjamin Bouvier decorate_function(&mut PlainWriter, w, func)
145747ad3c4Slazypassion }
146747ad3c4Slazypassion
147747ad3c4Slazypassion /// 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>>148747ad3c4Slazypassion fn alias_map(func: &Function) -> SecondaryMap<Value, Vec<Value>> {
149747ad3c4Slazypassion let mut aliases = SecondaryMap::<_, Vec<_>>::new();
150747ad3c4Slazypassion for v in func.dfg.values() {
151747ad3c4Slazypassion // VADFS returns the immediate target of an alias
152747ad3c4Slazypassion if let Some(k) = func.dfg.value_alias_dest_for_serialization(v) {
153747ad3c4Slazypassion aliases[k].push(v);
154747ad3c4Slazypassion }
155747ad3c4Slazypassion }
156747ad3c4Slazypassion aliases
157747ad3c4Slazypassion }
158747ad3c4Slazypassion
159747ad3c4Slazypassion /// Writes `func` to `w` as text.
160747ad3c4Slazypassion /// write_function_plain is passed as 'closure' to print instructions as text.
161747ad3c4Slazypassion /// 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, ) -> fmt::Result162747ad3c4Slazypassion pub fn decorate_function<FW: FuncWriter>(
163747ad3c4Slazypassion func_w: &mut FW,
164d7d48d5cSBenjamin Bouvier w: &mut dyn Write,
165747ad3c4Slazypassion func: &Function,
166747ad3c4Slazypassion ) -> fmt::Result {
167747ad3c4Slazypassion write!(w, "function ")?;
1683da7fc8eSSingleAccretion write_function_spec(w, func)?;
169747ad3c4Slazypassion writeln!(w, " {{")?;
170747ad3c4Slazypassion let aliases = alias_map(func);
17143a86f14SBenjamin Bouvier let mut any = func_w.write_preamble(w, func)?;
172832666c4SRyan Hunt for block in &func.layout {
173747ad3c4Slazypassion if any {
174747ad3c4Slazypassion writeln!(w)?;
175747ad3c4Slazypassion }
17643a86f14SBenjamin Bouvier decorate_block(func_w, w, func, &aliases, block)?;
177747ad3c4Slazypassion any = true;
178747ad3c4Slazypassion }
179747ad3c4Slazypassion writeln!(w, "}}")
180747ad3c4Slazypassion }
181747ad3c4Slazypassion
182747ad3c4Slazypassion //----------------------------------------------------------------------
183747ad3c4Slazypassion //
184747ad3c4Slazypassion // Function spec.
185747ad3c4Slazypassion
1863da7fc8eSSingleAccretion /// Writes the spec (name and signature) of 'func' to 'w' as text.
write_function_spec(w: &mut dyn Write, func: &Function) -> fmt::Result1873da7fc8eSSingleAccretion pub fn write_function_spec(w: &mut dyn Write, func: &Function) -> fmt::Result {
18843a86f14SBenjamin Bouvier write!(w, "{}{}", func.name, func.signature)
189747ad3c4Slazypassion }
190747ad3c4Slazypassion
191747ad3c4Slazypassion //----------------------------------------------------------------------
192747ad3c4Slazypassion //
193747ad3c4Slazypassion // Basic blocks
194747ad3c4Slazypassion
write_arg(w: &mut dyn Write, func: &Function, arg: Value) -> fmt::Result19543a86f14SBenjamin Bouvier fn write_arg(w: &mut dyn Write, func: &Function, arg: Value) -> fmt::Result {
196f466aa26SChris Fallin let ty = func.dfg.value_type(arg);
197a0442ea0SHamir Mahal write!(w, "{arg}: {ty}")
198f466aa26SChris Fallin }
199747ad3c4Slazypassion
200747ad3c4Slazypassion /// Write out the basic block header, outdented:
201747ad3c4Slazypassion ///
202832666c4SRyan Hunt /// block1:
203832666c4SRyan Hunt /// block1(v1: i32):
204689f7d48STrevor Elliott /// block10(v4: f64, v5: i8):
205747ad3c4Slazypassion ///
write_block_header( w: &mut dyn Write, func: &Function, block: Block, indent: usize, ) -> fmt::Result206832666c4SRyan Hunt pub fn write_block_header(
207d7d48d5cSBenjamin Bouvier w: &mut dyn Write,
208747ad3c4Slazypassion func: &Function,
209832666c4SRyan Hunt block: Block,
210747ad3c4Slazypassion indent: usize,
211747ad3c4Slazypassion ) -> fmt::Result {
21251649d56SChris Fallin let cold = if func.layout.is_cold(block) {
21351649d56SChris Fallin " cold"
21451649d56SChris Fallin } else {
21551649d56SChris Fallin ""
21651649d56SChris Fallin };
21751649d56SChris Fallin
218832666c4SRyan Hunt // The `indent` is the instruction indentation. block headers are 4 spaces out from that.
219832666c4SRyan Hunt write!(w, "{1:0$}{2}", indent - 4, "", block)?;
220747ad3c4Slazypassion
221832666c4SRyan Hunt let mut args = func.dfg.block_params(block).iter().cloned();
222747ad3c4Slazypassion match args.next() {
223a0442ea0SHamir Mahal None => return writeln!(w, "{cold}:"),
224747ad3c4Slazypassion Some(arg) => {
225747ad3c4Slazypassion write!(w, "(")?;
22643a86f14SBenjamin Bouvier write_arg(w, func, arg)?;
227747ad3c4Slazypassion }
228747ad3c4Slazypassion }
229747ad3c4Slazypassion // Remaining arguments.
230747ad3c4Slazypassion for arg in args {
231747ad3c4Slazypassion write!(w, ", ")?;
23243a86f14SBenjamin Bouvier write_arg(w, func, arg)?;
233747ad3c4Slazypassion }
234a0442ea0SHamir Mahal writeln!(w, "){cold}:")
235747ad3c4Slazypassion }
236747ad3c4Slazypassion
decorate_block<FW: FuncWriter>( func_w: &mut FW, w: &mut dyn Write, func: &Function, aliases: &SecondaryMap<Value, Vec<Value>>, block: Block, ) -> fmt::Result237832666c4SRyan Hunt fn decorate_block<FW: FuncWriter>(
238747ad3c4Slazypassion func_w: &mut FW,
239d7d48d5cSBenjamin Bouvier w: &mut dyn Write,
240747ad3c4Slazypassion func: &Function,
241747ad3c4Slazypassion aliases: &SecondaryMap<Value, Vec<Value>>,
242832666c4SRyan Hunt block: Block,
243747ad3c4Slazypassion ) -> fmt::Result {
244a3d6e407SChris Fallin // Indent all instructions if any srclocs or debug tags are present.
245a3d6e407SChris Fallin let indent = if func.rel_srclocs().is_empty() && func.debug_tags.is_empty() {
246a3d6e407SChris Fallin 4
247a3d6e407SChris Fallin } else {
248a3d6e407SChris Fallin 36
249a3d6e407SChris Fallin };
250747ad3c4Slazypassion
25143a86f14SBenjamin Bouvier func_w.write_block_header(w, func, block, indent)?;
252832666c4SRyan Hunt for a in func.dfg.block_params(block).iter().cloned() {
253747ad3c4Slazypassion write_value_aliases(w, aliases, a, indent)?;
254747ad3c4Slazypassion }
2558f95c517SYury Delendik
256832666c4SRyan Hunt for inst in func.layout.block_insts(block) {
25743a86f14SBenjamin Bouvier func_w.write_instruction(w, func, aliases, inst, indent)?;
258747ad3c4Slazypassion }
259747ad3c4Slazypassion
260747ad3c4Slazypassion Ok(())
261747ad3c4Slazypassion }
262747ad3c4Slazypassion
263747ad3c4Slazypassion //----------------------------------------------------------------------
264747ad3c4Slazypassion //
265747ad3c4Slazypassion // Instructions
266747ad3c4Slazypassion
267747ad3c4Slazypassion // Should `inst` be printed with a type suffix?
268747ad3c4Slazypassion //
269747ad3c4Slazypassion // Polymorphic instructions may need a suffix indicating the value of the controlling type variable
270747ad3c4Slazypassion // if it can't be trivially inferred.
271747ad3c4Slazypassion //
type_suffix(func: &Function, inst: Inst) -> Option<Type>272747ad3c4Slazypassion fn type_suffix(func: &Function, inst: Inst) -> Option<Type> {
27325bf8e0eSTrevor Elliott let inst_data = &func.dfg.insts[inst];
274747ad3c4Slazypassion let constraints = inst_data.opcode().constraints();
275747ad3c4Slazypassion
276747ad3c4Slazypassion if !constraints.is_polymorphic() {
277747ad3c4Slazypassion return None;
278747ad3c4Slazypassion }
279747ad3c4Slazypassion
280747ad3c4Slazypassion // If the controlling type variable can be inferred from the type of the designated value input
281747ad3c4Slazypassion // operand, we don't need the type suffix.
282747ad3c4Slazypassion if constraints.use_typevar_operand() {
283747ad3c4Slazypassion let ctrl_var = inst_data.typevar_operand(&func.dfg.value_lists).unwrap();
284832666c4SRyan Hunt let def_block = match func.dfg.value_def(ctrl_var) {
285832666c4SRyan Hunt ValueDef::Result(instr, _) => func.layout.inst_block(instr),
286832666c4SRyan Hunt ValueDef::Param(block, _) => Some(block),
287f980defeSChris Fallin ValueDef::Union(..) => None,
288747ad3c4Slazypassion };
289832666c4SRyan Hunt if def_block.is_some() && def_block == func.layout.inst_block(inst) {
290747ad3c4Slazypassion return None;
291747ad3c4Slazypassion }
292747ad3c4Slazypassion }
293747ad3c4Slazypassion
294747ad3c4Slazypassion let rtype = func.dfg.ctrl_typevar(inst);
295747ad3c4Slazypassion assert!(
296747ad3c4Slazypassion !rtype.is_invalid(),
297747ad3c4Slazypassion "Polymorphic instruction must produce a result"
298747ad3c4Slazypassion );
299747ad3c4Slazypassion Some(rtype)
300747ad3c4Slazypassion }
301747ad3c4Slazypassion
302747ad3c4Slazypassion /// 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::Result303747ad3c4Slazypassion fn write_value_aliases(
304d7d48d5cSBenjamin Bouvier w: &mut dyn Write,
305747ad3c4Slazypassion aliases: &SecondaryMap<Value, Vec<Value>>,
306747ad3c4Slazypassion target: Value,
307747ad3c4Slazypassion indent: usize,
308747ad3c4Slazypassion ) -> fmt::Result {
309747ad3c4Slazypassion let mut todo_stack = vec![target];
310747ad3c4Slazypassion while let Some(target) = todo_stack.pop() {
311747ad3c4Slazypassion for &a in &aliases[target] {
312747ad3c4Slazypassion writeln!(w, "{1:0$}{2} -> {3}", indent, "", a, target)?;
313747ad3c4Slazypassion todo_stack.push(a);
314747ad3c4Slazypassion }
315747ad3c4Slazypassion }
316747ad3c4Slazypassion
317747ad3c4Slazypassion Ok(())
318747ad3c4Slazypassion }
319747ad3c4Slazypassion
write_instruction( w: &mut dyn Write, func: &Function, aliases: &SecondaryMap<Value, Vec<Value>>, inst: Inst, mut indent: usize, ) -> fmt::Result320747ad3c4Slazypassion fn write_instruction(
321d7d48d5cSBenjamin Bouvier w: &mut dyn Write,
322747ad3c4Slazypassion func: &Function,
323747ad3c4Slazypassion aliases: &SecondaryMap<Value, Vec<Value>>,
324747ad3c4Slazypassion inst: Inst,
325a3d6e407SChris Fallin mut indent: usize,
326747ad3c4Slazypassion ) -> fmt::Result {
327747ad3c4Slazypassion // Prefix containing source location, encoding, and value locations.
328747ad3c4Slazypassion let mut s = String::with_capacity(16);
329747ad3c4Slazypassion
330747ad3c4Slazypassion // Source location goes first.
3318a9b1a90SBenjamin Bouvier let srcloc = func.srcloc(inst);
332747ad3c4Slazypassion if !srcloc.is_default() {
333a0442ea0SHamir Mahal write!(s, "{srcloc} ")?;
334747ad3c4Slazypassion }
335747ad3c4Slazypassion
336a3d6e407SChris Fallin // Write out any debug tags.
337a3d6e407SChris Fallin write_debug_tags(w, &func, inst, &mut indent)?;
338a3d6e407SChris Fallin
339747ad3c4Slazypassion // Write out prefix and indent the instruction.
340a0442ea0SHamir Mahal write!(w, "{s:indent$}")?;
341747ad3c4Slazypassion
342747ad3c4Slazypassion // Write out the result values, if any.
343747ad3c4Slazypassion let mut has_results = false;
344747ad3c4Slazypassion for r in func.dfg.inst_results(inst) {
345747ad3c4Slazypassion if !has_results {
346747ad3c4Slazypassion has_results = true;
347a0442ea0SHamir Mahal write!(w, "{r}")?;
348747ad3c4Slazypassion } else {
349a0442ea0SHamir Mahal write!(w, ", {r}")?;
350747ad3c4Slazypassion }
351747ad3c4Slazypassion }
352747ad3c4Slazypassion if has_results {
353747ad3c4Slazypassion write!(w, " = ")?;
354747ad3c4Slazypassion }
355747ad3c4Slazypassion
356747ad3c4Slazypassion // Then the opcode, possibly with a '.type' suffix.
35725bf8e0eSTrevor Elliott let opcode = func.dfg.insts[inst].opcode();
358747ad3c4Slazypassion
359747ad3c4Slazypassion match type_suffix(func, inst) {
360a0442ea0SHamir Mahal Some(suf) => write!(w, "{opcode}.{suf}")?,
361a0442ea0SHamir Mahal None => write!(w, "{opcode}")?,
362747ad3c4Slazypassion }
363747ad3c4Slazypassion
36443a86f14SBenjamin Bouvier write_operands(w, &func.dfg, inst)?;
365747ad3c4Slazypassion writeln!(w)?;
366747ad3c4Slazypassion
367747ad3c4Slazypassion // Value aliases come out on lines after the instruction defining the referent.
368747ad3c4Slazypassion for r in func.dfg.inst_results(inst) {
369747ad3c4Slazypassion write_value_aliases(w, aliases, *r, indent)?;
370747ad3c4Slazypassion }
371747ad3c4Slazypassion Ok(())
372747ad3c4Slazypassion }
373747ad3c4Slazypassion
374747ad3c4Slazypassion /// Write the operands of `inst` to `w` with a prepended space.
write_operands(w: &mut dyn Write, dfg: &DataFlowGraph, inst: Inst) -> fmt::Result37543a86f14SBenjamin Bouvier pub fn write_operands(w: &mut dyn Write, dfg: &DataFlowGraph, inst: Inst) -> fmt::Result {
376747ad3c4Slazypassion let pool = &dfg.value_lists;
37715fe9c7cSTrevor Elliott let jump_tables = &dfg.jump_tables;
37894ec88eaSChris Fallin let exception_tables = &dfg.exception_tables;
379747ad3c4Slazypassion use crate::ir::instructions::InstructionData::*;
3800683b84bSNick Fitzgerald let ctrl_ty = dfg.ctrl_typevar(inst);
38125bf8e0eSTrevor Elliott match dfg.insts[inst] {
382138148a5SJamey Sharp AtomicRmw { op, args, .. } => write!(w, " {} {}, {}", op, args[0], args[1]),
383138148a5SJamey Sharp AtomicCas { args, .. } => write!(w, " {}, {}, {}", args[0], args[1], args[2]),
384a0442ea0SHamir Mahal LoadNoOffset { flags, arg, .. } => write!(w, "{flags} {arg}"),
385138148a5SJamey Sharp StoreNoOffset { flags, args, .. } => write!(w, "{} {}, {}", flags, args[0], args[1]),
386a0442ea0SHamir Mahal Unary { arg, .. } => write!(w, " {arg}"),
3870683b84bSNick Fitzgerald UnaryImm { imm, .. } => write!(w, " {}", {
3880683b84bSNick Fitzgerald let mut imm = imm;
3890683b84bSNick Fitzgerald if ctrl_ty.bits() != 0 {
3900683b84bSNick Fitzgerald imm = imm.sign_extend_from_width(ctrl_ty.bits());
3910683b84bSNick Fitzgerald }
3920683b84bSNick Fitzgerald imm
3930683b84bSNick Fitzgerald }),
394a0442ea0SHamir Mahal UnaryIeee16 { imm, .. } => write!(w, " {imm}"),
395a0442ea0SHamir Mahal UnaryIeee32 { imm, .. } => write!(w, " {imm}"),
396a0442ea0SHamir Mahal UnaryIeee64 { imm, .. } => write!(w, " {imm}"),
397a0442ea0SHamir Mahal UnaryGlobalValue { global_value, .. } => write!(w, " {global_value}"),
3980672d1dcSAndrew Brown UnaryConst {
3990672d1dcSAndrew Brown constant_handle, ..
400a0442ea0SHamir Mahal } => write!(w, " {constant_handle}"),
401138148a5SJamey Sharp Binary { args, .. } => write!(w, " {}, {}", args[0], args[1]),
402a0442ea0SHamir Mahal BinaryImm8 { arg, imm, .. } => write!(w, " {arg}, {imm}"),
4030683b84bSNick Fitzgerald BinaryImm64 { arg, imm, .. } => write!(w, " {}, {}", arg, {
4040683b84bSNick Fitzgerald let mut imm = imm;
4050683b84bSNick Fitzgerald if ctrl_ty.bits() != 0 {
4060683b84bSNick Fitzgerald imm = imm.sign_extend_from_width(ctrl_ty.bits());
4070683b84bSNick Fitzgerald }
4080683b84bSNick Fitzgerald imm
4090683b84bSNick Fitzgerald }),
410138148a5SJamey Sharp Ternary { args, .. } => write!(w, " {}, {}, {}", args[0], args[1], args[2]),
411747ad3c4Slazypassion MultiAry { ref args, .. } => {
412747ad3c4Slazypassion if args.is_empty() {
413747ad3c4Slazypassion write!(w, "")
414747ad3c4Slazypassion } else {
415138148a5SJamey Sharp write!(w, " {}", DisplayValues(args.as_slice(pool)))
416747ad3c4Slazypassion }
417747ad3c4Slazypassion }
418747ad3c4Slazypassion NullAry { .. } => write!(w, " "),
419138148a5SJamey Sharp TernaryImm8 { imm, args, .. } => write!(w, " {}, {}, {}", args[0], args[1], imm),
4202fbd57e9Sbjorn3 Shuffle { imm, args, .. } => {
4212fbd57e9Sbjorn3 let data = dfg.immediates.get(imm).expect(
422af1499ceSAndrew Brown "Expected the shuffle mask to already be inserted into the immediates table",
423af1499ceSAndrew Brown );
424138148a5SJamey Sharp write!(w, " {}, {}, {}", args[0], args[1], data)
425af1499ceSAndrew Brown }
426138148a5SJamey Sharp IntCompare { cond, args, .. } => write!(w, " {} {}, {}", cond, args[0], args[1]),
4270683b84bSNick Fitzgerald IntCompareImm { cond, arg, imm, .. } => write!(w, " {} {}, {}", cond, arg, {
4280683b84bSNick Fitzgerald let mut imm = imm;
4290683b84bSNick Fitzgerald if ctrl_ty.bits() != 0 {
4300683b84bSNick Fitzgerald imm = imm.sign_extend_from_width(ctrl_ty.bits());
4310683b84bSNick Fitzgerald }
4320683b84bSNick Fitzgerald imm
4330683b84bSNick Fitzgerald }),
434138148a5SJamey Sharp IntAddTrap { args, code, .. } => write!(w, " {}, {}, {}", args[0], args[1], code),
435138148a5SJamey Sharp FloatCompare { cond, args, .. } => write!(w, " {} {}, {}", cond, args[0], args[1]),
4361e6c13d8STrevor Elliott Jump { destination, .. } => {
437e82995f0STrevor Elliott write!(w, " {}", destination.display(pool))
438747ad3c4Slazypassion }
439b58a197dSTrevor Elliott Brif {
440b58a197dSTrevor Elliott arg,
441b58a197dSTrevor Elliott blocks: [block_then, block_else],
442b58a197dSTrevor Elliott ..
443b58a197dSTrevor Elliott } => {
444138148a5SJamey Sharp write!(w, " {}, {}", arg, block_then.display(pool))?;
445e82995f0STrevor Elliott write!(w, ", {}", block_else.display(pool))
446b58a197dSTrevor Elliott }
44780c147d9STrevor Elliott BranchTable { arg, table, .. } => {
448138148a5SJamey Sharp write!(w, " {}, {}", arg, jump_tables[table].display(pool))
44980c147d9STrevor Elliott }
450747ad3c4Slazypassion Call {
451747ad3c4Slazypassion func_ref, ref args, ..
45259de3a32SNick Fitzgerald } => {
45359de3a32SNick Fitzgerald write!(w, " {}({})", func_ref, DisplayValues(args.as_slice(pool)))?;
45459de3a32SNick Fitzgerald write_user_stack_map_entries(w, dfg, inst)
45559de3a32SNick Fitzgerald }
456747ad3c4Slazypassion CallIndirect {
457747ad3c4Slazypassion sig_ref, ref args, ..
458747ad3c4Slazypassion } => {
459747ad3c4Slazypassion let args = args.as_slice(pool);
460138148a5SJamey Sharp write!(
461138148a5SJamey Sharp w,
462138148a5SJamey Sharp " {}, {}({})",
463138148a5SJamey Sharp sig_ref,
464138148a5SJamey Sharp args[0],
465138148a5SJamey Sharp DisplayValues(&args[1..])
46659de3a32SNick Fitzgerald )?;
46759de3a32SNick Fitzgerald write_user_stack_map_entries(w, dfg, inst)
468747ad3c4Slazypassion }
46994ec88eaSChris Fallin TryCall {
47094ec88eaSChris Fallin func_ref,
47194ec88eaSChris Fallin ref args,
47294ec88eaSChris Fallin exception,
47394ec88eaSChris Fallin ..
47494ec88eaSChris Fallin } => {
47594ec88eaSChris Fallin write!(
47694ec88eaSChris Fallin w,
47794ec88eaSChris Fallin " {}({}), {}",
47894ec88eaSChris Fallin func_ref,
47994ec88eaSChris Fallin DisplayValues(args.as_slice(pool)),
48094ec88eaSChris Fallin exception_tables[exception].display(pool),
481d70a517cSNick Fitzgerald )?;
482d70a517cSNick Fitzgerald write_user_stack_map_entries(w, dfg, inst)
48394ec88eaSChris Fallin }
48494ec88eaSChris Fallin TryCallIndirect {
48594ec88eaSChris Fallin ref args,
48694ec88eaSChris Fallin exception,
48794ec88eaSChris Fallin ..
48894ec88eaSChris Fallin } => {
48994ec88eaSChris Fallin let args = args.as_slice(pool);
49094ec88eaSChris Fallin write!(
49194ec88eaSChris Fallin w,
49294ec88eaSChris Fallin " {}({}), {}",
49394ec88eaSChris Fallin args[0],
49494ec88eaSChris Fallin DisplayValues(&args[1..]),
49594ec88eaSChris Fallin exception_tables[exception].display(pool),
496d70a517cSNick Fitzgerald )?;
497d70a517cSNick Fitzgerald write_user_stack_map_entries(w, dfg, inst)
49894ec88eaSChris Fallin }
499a0442ea0SHamir Mahal FuncAddr { func_ref, .. } => write!(w, " {func_ref}"),
500747ad3c4Slazypassion StackLoad {
501747ad3c4Slazypassion stack_slot, offset, ..
502a0442ea0SHamir Mahal } => write!(w, " {stack_slot}{offset}"),
503747ad3c4Slazypassion StackStore {
504747ad3c4Slazypassion arg,
505747ad3c4Slazypassion stack_slot,
506747ad3c4Slazypassion offset,
507747ad3c4Slazypassion ..
508a0442ea0SHamir Mahal } => write!(w, " {arg}, {stack_slot}{offset}"),
5099c43749dSSam Parker DynamicStackLoad {
5109c43749dSSam Parker dynamic_stack_slot, ..
511a0442ea0SHamir Mahal } => write!(w, " {dynamic_stack_slot}"),
5129c43749dSSam Parker DynamicStackStore {
5139c43749dSSam Parker arg,
5149c43749dSSam Parker dynamic_stack_slot,
5159c43749dSSam Parker ..
516a0442ea0SHamir Mahal } => write!(w, " {arg}, {dynamic_stack_slot}"),
517747ad3c4Slazypassion Load {
518747ad3c4Slazypassion flags, arg, offset, ..
519a0442ea0SHamir Mahal } => write!(w, "{flags} {arg}{offset}"),
520747ad3c4Slazypassion Store {
521747ad3c4Slazypassion flags,
522747ad3c4Slazypassion args,
523747ad3c4Slazypassion offset,
524747ad3c4Slazypassion ..
525138148a5SJamey Sharp } => write!(w, "{} {}, {}{}", flags, args[0], args[1], offset),
526a0442ea0SHamir Mahal Trap { code, .. } => write!(w, " {code}"),
527a0442ea0SHamir Mahal CondTrap { arg, code, .. } => write!(w, " {arg}, {code}"),
5284c01ee2fSChris Fallin ExceptionHandlerAddress { block, imm, .. } => write!(w, " {block}, {imm}"),
5293629bbbdSJamey Sharp }?;
5303629bbbdSJamey Sharp
5313629bbbdSJamey Sharp let mut sep = " ; ";
5321e6c13d8STrevor Elliott for arg in dfg.inst_values(inst) {
5333629bbbdSJamey Sharp if let ValueDef::Result(src, _) = dfg.value_def(arg) {
53425bf8e0eSTrevor Elliott let imm = match dfg.insts[src] {
5350683b84bSNick Fitzgerald UnaryImm { imm, .. } => {
5360683b84bSNick Fitzgerald let mut imm = imm;
5370683b84bSNick Fitzgerald if dfg.ctrl_typevar(src).bits() != 0 {
5380683b84bSNick Fitzgerald imm = imm.sign_extend_from_width(dfg.ctrl_typevar(src).bits());
5390683b84bSNick Fitzgerald }
5400683b84bSNick Fitzgerald imm.to_string()
5410683b84bSNick Fitzgerald }
54241eca60bSbeetrees UnaryIeee16 { imm, .. } => imm.to_string(),
5433629bbbdSJamey Sharp UnaryIeee32 { imm, .. } => imm.to_string(),
5443629bbbdSJamey Sharp UnaryIeee64 { imm, .. } => imm.to_string(),
5453629bbbdSJamey Sharp UnaryConst {
54641eca60bSbeetrees constant_handle,
54741eca60bSbeetrees opcode: Opcode::F128const,
54841eca60bSbeetrees } => Ieee128::try_from(dfg.constants.get(constant_handle))
54941eca60bSbeetrees .expect("16-byte f128 constant")
55041eca60bSbeetrees .to_string(),
55141eca60bSbeetrees UnaryConst {
5523629bbbdSJamey Sharp constant_handle, ..
5533629bbbdSJamey Sharp } => constant_handle.to_string(),
5543629bbbdSJamey Sharp _ => continue,
5553629bbbdSJamey Sharp };
556a0442ea0SHamir Mahal write!(w, "{sep}{arg} = {imm}")?;
5573629bbbdSJamey Sharp sep = ", ";
558747ad3c4Slazypassion }
559747ad3c4Slazypassion }
5603629bbbdSJamey Sharp Ok(())
5613629bbbdSJamey Sharp }
562747ad3c4Slazypassion
write_debug_tags( w: &mut dyn Write, func: &Function, inst: Inst, indent: &mut usize, ) -> fmt::Result563a3d6e407SChris Fallin fn write_debug_tags(
564a3d6e407SChris Fallin w: &mut dyn Write,
565a3d6e407SChris Fallin func: &Function,
566a3d6e407SChris Fallin inst: Inst,
567a3d6e407SChris Fallin indent: &mut usize,
568a3d6e407SChris Fallin ) -> fmt::Result {
569a3d6e407SChris Fallin let tags = func.debug_tags.get(inst);
570a3d6e407SChris Fallin if !tags.is_empty() {
571a3d6e407SChris Fallin let tags = tags
572a3d6e407SChris Fallin .iter()
573a3d6e407SChris Fallin .map(|tag| format!("{tag}"))
574a3d6e407SChris Fallin .collect::<Vec<_>>()
575a3d6e407SChris Fallin .join(", ");
576a3d6e407SChris Fallin let s = format!("<{tags}> ");
577a3d6e407SChris Fallin write!(w, "{s}")?;
578a3d6e407SChris Fallin *indent = indent.saturating_sub(s.len());
579a3d6e407SChris Fallin }
580a3d6e407SChris Fallin Ok(())
581a3d6e407SChris Fallin }
582a3d6e407SChris Fallin
write_user_stack_map_entries(w: &mut dyn Write, dfg: &DataFlowGraph, inst: Inst) -> fmt::Result58359de3a32SNick Fitzgerald fn write_user_stack_map_entries(w: &mut dyn Write, dfg: &DataFlowGraph, inst: Inst) -> fmt::Result {
58459de3a32SNick Fitzgerald let entries = match dfg.user_stack_map_entries(inst) {
58559de3a32SNick Fitzgerald None => return Ok(()),
58659de3a32SNick Fitzgerald Some(es) => es,
58759de3a32SNick Fitzgerald };
58859de3a32SNick Fitzgerald write!(w, ", stack_map=[")?;
58959de3a32SNick Fitzgerald let mut need_comma = false;
59059de3a32SNick Fitzgerald for entry in entries {
59159de3a32SNick Fitzgerald if need_comma {
59259de3a32SNick Fitzgerald write!(w, ", ")?;
59359de3a32SNick Fitzgerald }
59459de3a32SNick Fitzgerald write!(w, "{} @ {}+{}", entry.ty, entry.slot, entry.offset)?;
59559de3a32SNick Fitzgerald need_comma = true;
59659de3a32SNick Fitzgerald }
59759de3a32SNick Fitzgerald write!(w, "]")?;
59859de3a32SNick Fitzgerald Ok(())
59959de3a32SNick Fitzgerald }
60059de3a32SNick Fitzgerald
601747ad3c4Slazypassion /// Displayable slice of values.
602747ad3c4Slazypassion struct DisplayValues<'a>(&'a [Value]);
603747ad3c4Slazypassion
604747ad3c4Slazypassion impl<'a> fmt::Display for DisplayValues<'a> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result605747ad3c4Slazypassion fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
606747ad3c4Slazypassion for (i, val) in self.0.iter().enumerate() {
607747ad3c4Slazypassion if i == 0 {
608a0442ea0SHamir Mahal write!(f, "{val}")?;
609747ad3c4Slazypassion } else {
610a0442ea0SHamir Mahal write!(f, ", {val}")?;
611747ad3c4Slazypassion }
612747ad3c4Slazypassion }
613747ad3c4Slazypassion Ok(())
614747ad3c4Slazypassion }
615747ad3c4Slazypassion }
616747ad3c4Slazypassion
617747ad3c4Slazypassion #[cfg(test)]
618747ad3c4Slazypassion mod tests {
619747ad3c4Slazypassion use crate::cursor::{Cursor, CursorPosition, FuncCursor};
620747ad3c4Slazypassion use crate::ir::types;
6218a9b1a90SBenjamin Bouvier use crate::ir::{Function, InstBuilder, StackSlotData, StackSlotKind, UserFuncName};
62210e226f9Sbjorn3 use alloc::string::ToString;
623747ad3c4Slazypassion
624747ad3c4Slazypassion #[test]
basic()625747ad3c4Slazypassion fn basic() {
626747ad3c4Slazypassion let mut f = Function::new();
627747ad3c4Slazypassion assert_eq!(f.to_string(), "function u0:0() fast {\n}\n");
628747ad3c4Slazypassion
6298a9b1a90SBenjamin Bouvier f.name = UserFuncName::testcase("foo");
630747ad3c4Slazypassion assert_eq!(f.to_string(), "function %foo() fast {\n}\n");
631747ad3c4Slazypassion
63245bc7366SChris Fallin f.create_sized_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 4, 0));
633747ad3c4Slazypassion assert_eq!(
634747ad3c4Slazypassion f.to_string(),
635747ad3c4Slazypassion "function %foo() fast {\n ss0 = explicit_slot 4\n}\n"
636747ad3c4Slazypassion );
637747ad3c4Slazypassion
638832666c4SRyan Hunt let block = f.dfg.make_block();
639832666c4SRyan Hunt f.layout.append_block(block);
640747ad3c4Slazypassion assert_eq!(
641747ad3c4Slazypassion f.to_string(),
642832666c4SRyan Hunt "function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0:\n}\n"
643747ad3c4Slazypassion );
644747ad3c4Slazypassion
645832666c4SRyan Hunt f.dfg.append_block_param(block, types::I8);
646747ad3c4Slazypassion assert_eq!(
647747ad3c4Slazypassion f.to_string(),
648832666c4SRyan Hunt "function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0(v0: i8):\n}\n"
649747ad3c4Slazypassion );
650747ad3c4Slazypassion
651832666c4SRyan Hunt f.dfg.append_block_param(block, types::F32.by(4).unwrap());
652747ad3c4Slazypassion assert_eq!(
653747ad3c4Slazypassion f.to_string(),
654832666c4SRyan Hunt "function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0(v0: i8, v1: f32x4):\n}\n"
655747ad3c4Slazypassion );
656747ad3c4Slazypassion
657747ad3c4Slazypassion {
658747ad3c4Slazypassion let mut cursor = FuncCursor::new(&mut f);
659832666c4SRyan Hunt cursor.set_position(CursorPosition::After(block));
660747ad3c4Slazypassion cursor.ins().return_(&[])
661747ad3c4Slazypassion };
662747ad3c4Slazypassion assert_eq!(
663747ad3c4Slazypassion f.to_string(),
664832666c4SRyan Hunt "function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0(v0: i8, v1: f32x4):\n return\n}\n"
665747ad3c4Slazypassion );
66645bc7366SChris Fallin
66745bc7366SChris Fallin let mut f = Function::new();
66845bc7366SChris Fallin f.create_sized_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 4, 2));
66945bc7366SChris Fallin assert_eq!(
67045bc7366SChris Fallin f.to_string(),
67145bc7366SChris Fallin "function u0:0() fast {\n ss0 = explicit_slot 4, align = 4\n}\n"
67245bc7366SChris Fallin );
673747ad3c4Slazypassion }
674747ad3c4Slazypassion
675747ad3c4Slazypassion #[test]
aliases()676747ad3c4Slazypassion fn aliases() {
677747ad3c4Slazypassion use crate::ir::InstBuilder;
678747ad3c4Slazypassion
679747ad3c4Slazypassion let mut func = Function::new();
680747ad3c4Slazypassion {
681832666c4SRyan Hunt let block0 = func.dfg.make_block();
682747ad3c4Slazypassion let mut pos = FuncCursor::new(&mut func);
683832666c4SRyan Hunt pos.insert_block(block0);
684747ad3c4Slazypassion
685747ad3c4Slazypassion // make some detached values for change_to_alias
686832666c4SRyan Hunt let v0 = pos.func.dfg.append_block_param(block0, types::I32);
687832666c4SRyan Hunt let v1 = pos.func.dfg.append_block_param(block0, types::I32);
688832666c4SRyan Hunt let v2 = pos.func.dfg.append_block_param(block0, types::I32);
689832666c4SRyan Hunt pos.func.dfg.detach_block_params(block0);
690747ad3c4Slazypassion
691832666c4SRyan Hunt // alias to a param--will be printed at beginning of block defining param
692832666c4SRyan Hunt let v3 = pos.func.dfg.append_block_param(block0, types::I32);
693747ad3c4Slazypassion pos.func.dfg.change_to_alias(v0, v3);
694747ad3c4Slazypassion
695747ad3c4Slazypassion // alias to an alias--should print attached to alias, not ultimate target
696747ad3c4Slazypassion pos.func.dfg.make_value_alias_for_serialization(v0, v2); // v0 <- v2
697747ad3c4Slazypassion
698747ad3c4Slazypassion // alias to a result--will be printed after instruction producing result
699747ad3c4Slazypassion let _dummy0 = pos.ins().iconst(types::I32, 42);
700747ad3c4Slazypassion let v4 = pos.ins().iadd(v0, v0);
701747ad3c4Slazypassion pos.func.dfg.change_to_alias(v1, v4);
702747ad3c4Slazypassion let _dummy1 = pos.ins().iconst(types::I32, 23);
703747ad3c4Slazypassion let _v7 = pos.ins().iadd(v1, v1);
704747ad3c4Slazypassion }
705747ad3c4Slazypassion assert_eq!(
706747ad3c4Slazypassion func.to_string(),
707138148a5SJamey Sharp "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"
708747ad3c4Slazypassion );
709747ad3c4Slazypassion }
71051649d56SChris Fallin
71151649d56SChris Fallin #[test]
cold_blocks()71251649d56SChris Fallin fn cold_blocks() {
71351649d56SChris Fallin let mut func = Function::new();
71451649d56SChris Fallin {
71551649d56SChris Fallin let mut pos = FuncCursor::new(&mut func);
71651649d56SChris Fallin
71751649d56SChris Fallin let block0 = pos.func.dfg.make_block();
71851649d56SChris Fallin pos.insert_block(block0);
71951649d56SChris Fallin pos.func.layout.set_cold(block0);
72051649d56SChris Fallin
72151649d56SChris Fallin let block1 = pos.func.dfg.make_block();
72251649d56SChris Fallin pos.insert_block(block1);
72351649d56SChris Fallin pos.func.dfg.append_block_param(block1, types::I32);
72451649d56SChris Fallin pos.func.layout.set_cold(block1);
72551649d56SChris Fallin }
72651649d56SChris Fallin
72751649d56SChris Fallin assert_eq!(
72851649d56SChris Fallin func.to_string(),
72951649d56SChris Fallin "function u0:0() fast {\nblock0 cold:\n\nblock1(v0: i32) cold:\n}\n"
73051649d56SChris Fallin );
73151649d56SChris Fallin }
732747ad3c4Slazypassion }
733