1use core::fmt::{self, Write};
4
5use crate::entity::{FuncId, GlobalId, Value, ValueRange};
6use crate::function::Function;
7use crate::inst::{Attrs, BlockCall, Call, CallTarget, InstData, Terminator};
8use crate::module::{Init, Module, SymbolRef};
9use crate::types::{Aggregate, PointerWidth, Type};
10
11pub(crate) fn write_quoted(f: &mut impl Write, s: &str) -> fmt::Result {
14 f.write_char('"')?;
15 for c in s.chars() {
16 match c {
17 '\\' => f.write_str("\\\\")?,
18 '"' => f.write_str("\\\"")?,
19 '\n' => f.write_str("\\n")?,
20 '\t' => f.write_str("\\t")?,
21 '\r' => f.write_str("\\r")?,
22 c if c.is_control() => write!(f, "\\u{{{:x}}}", c as u32)?,
23 c => f.write_char(c)?,
24 }
25 }
26 f.write_char('"')
27}
28
29pub(crate) fn is_bare_symbol(name: &str) -> bool {
31 let mut chars = name.chars();
32 match chars.next() {
33 Some(c) if c.is_ascii_alphabetic() || c == '_' => {}
34 _ => return false,
35 }
36 chars.all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '.' || c == '$')
37}
38
39fn write_symbol(f: &mut impl Write, name: &str) -> fmt::Result {
40 f.write_char('@')?;
41 if is_bare_symbol(name) {
42 f.write_str(name)
43 } else {
44 write_quoted(f, name)
45 }
46}
47
48fn func_name(m: &Module, func: FuncId, f: &mut impl Write) -> fmt::Result {
49 match m.decls.funcs.get(func.index()) {
50 Some(d) => write_symbol(f, &d.name),
51 None => write!(f, "@\"?{func}\""),
52 }
53}
54
55fn global_name(m: &Module, global: GlobalId, f: &mut impl Write) -> fmt::Result {
56 match m.decls.globals.get(global.index()) {
57 Some(g) => write_symbol(f, &g.name),
58 None => write!(f, "@\"?{global}\""),
59 }
60}
61
62pub(crate) fn write_const(
64 f: &mut impl Write,
65 ty: Type,
66 bits: u64,
67 ptr: PointerWidth,
68) -> fmt::Result {
69 match ty {
70 Type::Bool if bits <= 1 => f.write_str(if bits == 1 { "true" } else { "false" }),
71 Type::Ptr | Type::Ref if bits == 0 => f.write_str("null"),
72 Type::F64 => write_f64(f, f64::from_bits(bits), bits),
73 Type::F32 if bits >> 32 == 0 => write_f32(f, f32::from_bits(bits as u32), bits),
74 t if t.is_int() => {
75 let width = t.bits(ptr);
76 if width < 64 && bits >> width != 0 {
77 return write!(f, "{bits:#x}");
78 }
79 if t.is_signed_int() {
80 let shift = 64 - width;
81 write!(f, "{}", ((bits << shift) as i64) >> shift)
82 } else {
83 write!(f, "{bits}")
84 }
85 }
86 _ => write!(f, "{bits:#x}"),
87 }
88}
89
90fn write_f64(f: &mut impl Write, x: f64, bits: u64) -> fmt::Result {
91 if x.is_nan() {
92 write!(f, "nan:{bits:#x}")
93 } else if x.is_infinite() {
94 f.write_str(if x > 0.0 { "inf" } else { "-inf" })
95 } else {
96 write!(f, "{x:?}")
97 }
98}
99
100fn write_f32(f: &mut impl Write, x: f32, bits: u64) -> fmt::Result {
101 if x.is_nan() {
102 write!(f, "nan:{bits:#x}")
103 } else if x.is_infinite() {
104 f.write_str(if x > 0.0 { "inf" } else { "-inf" })
105 } else {
106 write!(f, "{x:?}")
107 }
108}
109
110fn write_values(f: &mut impl Write, func: &Function, values: &[Value]) -> fmt::Result {
111 for (i, &v) in values.iter().enumerate() {
112 if i > 0 {
113 f.write_str(", ")?;
114 }
115 write!(f, "{}", func.resolve(v))?;
116 }
117 Ok(())
118}
119
120fn write_dest(f: &mut impl Write, func: &Function, dest: &BlockCall) -> fmt::Result {
121 write!(f, "{}", dest.block)?;
122 if !dest.args.is_empty() {
123 f.write_char('(')?;
124 for (i, a) in dest.args.iter().enumerate() {
125 if i > 0 {
126 f.write_str(", ")?;
127 }
128 match a {
129 crate::inst::BlockArg::Value(v) => write!(f, "{}", func.resolve(*v))?,
130 other => write!(f, "{other}")?,
131 }
132 }
133 f.write_char(')')?;
134 }
135 Ok(())
136}
137
138fn write_call(f: &mut impl Write, m: &Module, func: &Function, call: &Call) -> fmt::Result {
139 match call.target {
140 CallTarget::Direct(callee) => func_name(m, callee, f)?,
141 CallTarget::Indirect { sig, callee } => write!(f, "{sig} {}", func.resolve(callee))?,
142 }
143 f.write_char('(')?;
144 write_values(f, func, &call.args)?;
145 f.write_char(')')?;
146 if !call.gc.is_empty() {
147 f.write_str(" gc(")?;
148 write_values(f, func, &call.gc)?;
149 f.write_char(')')?;
150 }
151 write_attrs(f, call.attrs)
152}
153
154fn write_attrs(f: &mut impl Write, attrs: Attrs) -> fmt::Result {
155 if attrs.is_empty() {
156 Ok(())
157 } else {
158 write!(f, " {attrs}")
159 }
160}
161
162fn write_op(
166 f: &mut impl Write,
167 m: &Module,
168 func: &Function,
169 data: &InstData,
170 conv_to: bool,
171) -> fmt::Result {
172 let r = |v: Value| func.resolve(v);
173 f.write_str(data.opcode())?;
174 match data {
175 InstData::Const { ty, bits } => {
176 f.write_char(' ')?;
177 write_const(f, *ty, *bits, m.decls.ptr)
178 }
179 InstData::Unary { policy, arg, .. } => write!(f, "{policy} {}", r(*arg)),
180 InstData::Binary { policy, args, .. } => {
181 write!(f, "{policy} {}, {}", r(args[0]), r(args[1]))
182 }
183 InstData::Fma { args } => write!(f, " {}, {}, {}", r(args[0]), r(args[1]), r(args[2])),
184 InstData::Compare { args, .. } => write!(f, " {}, {}", r(args[0]), r(args[1])),
185 InstData::Convert {
186 policy, to, arg, ..
187 } => {
188 write!(f, "{policy} {}", r(*arg))?;
189 if conv_to {
190 write!(f, " as {to}")?;
191 }
192 Ok(())
193 }
194 InstData::Select { cond, args } => {
195 write!(f, " {}, {}, {}", r(*cond), r(args[0]), r(args[1]))
196 }
197 InstData::StackAddr { slot } => write!(f, " {slot}"),
198 InstData::GlobalAddr { global } => {
199 f.write_char(' ')?;
200 global_name(m, *global, f)
201 }
202 InstData::FuncAddr { func: callee } => {
203 f.write_char(' ')?;
204 func_name(m, *callee, f)
205 }
206 InstData::FieldAddr { base, agg, field } => write!(f, " {}, {agg}, {field}", r(*base)),
207 InstData::ElemAddr { base, elem, index } => {
208 write!(f, " {}, {elem}, {}", r(*base), r(*index))
209 }
210 InstData::PtrOffset { base, offset } => write!(f, " {}, {}", r(*base), r(*offset)),
211 InstData::Load { addr, flags, .. } => write!(f, " {} {flags}", r(*addr)),
212 InstData::Store { value, addr, flags } => write!(f, " {}, {} {flags}", r(*value), r(*addr)),
213 InstData::AtomicLoad { addr, order, .. } => write!(f, " {} {order}", r(*addr)),
214 InstData::AtomicStore { value, addr, order } => {
215 write!(f, " {}, {} {order}", r(*value), r(*addr))
216 }
217 InstData::AtomicRmw {
218 op,
219 addr,
220 value,
221 order,
222 } => write!(f, " {} {}, {} {order}", op.name(), r(*addr), r(*value)),
223 InstData::CmpXchg {
224 addr,
225 expected,
226 new,
227 success,
228 failure,
229 } => write!(
230 f,
231 " {}, {}, {} {success} {failure}",
232 r(*addr),
233 r(*expected),
234 r(*new)
235 ),
236 InstData::Fence { order } => write!(f, " {order}"),
237 InstData::MemCopy {
238 dst,
239 src,
240 len,
241 flags,
242 }
243 | InstData::MemMove {
244 dst,
245 src,
246 len,
247 flags,
248 } => {
249 write!(f, " {}, {}, {} {flags}", r(*dst), r(*src), r(*len))
250 }
251 InstData::MemSet {
252 dst,
253 byte,
254 len,
255 flags,
256 } => {
257 write!(f, " {}, {}, {} {flags}", r(*dst), r(*byte), r(*len))
258 }
259 InstData::Call(call) => {
260 f.write_char(' ')?;
261 write_call(f, m, func, call)
262 }
263 InstData::Safepoint { gc } => {
264 f.write_str(" gc(")?;
265 write_values(f, func, gc)?;
266 f.write_char(')')
267 }
268 }
269}
270
271fn write_results(f: &mut impl Write, func: &Function, results: ValueRange) -> fmt::Result {
272 for (i, v) in results.iter().enumerate() {
273 if i > 0 {
274 f.write_str(", ")?;
275 }
276 match func.value_type(v) {
277 Some(ty) => write!(f, "{v}: {ty}")?,
278 None => write!(f, "{v}: ?")?,
279 }
280 }
281 if !results.is_empty() {
282 f.write_str(" = ")?;
283 }
284 Ok(())
285}
286
287fn write_terminator(
288 f: &mut impl Write,
289 m: &Module,
290 func: &Function,
291 term: &Terminator,
292) -> fmt::Result {
293 f.write_str(term.opcode())?;
294 match term {
295 Terminator::Jump(dest) => {
296 f.write_char(' ')?;
297 write_dest(f, func, dest)
298 }
299 Terminator::Branch {
300 cond,
301 then_dest,
302 else_dest,
303 } => {
304 write!(f, " {}, ", func.resolve(*cond))?;
305 write_dest(f, func, then_dest)?;
306 f.write_str(", ")?;
307 write_dest(f, func, else_dest)
308 }
309 Terminator::Switch {
310 value,
311 cases,
312 default,
313 } => {
314 write!(f, " {} default ", func.resolve(*value))?;
315 write_dest(f, func, default)?;
316 f.write_str(" [")?;
317 for (i, case) in cases.iter().enumerate() {
320 if i > 0 {
321 f.write_str(", ")?;
322 }
323 write!(f, "{}: ", case.value)?;
324 write_dest(f, func, &case.dest)?;
325 }
326 f.write_char(']')
327 }
328 Terminator::Return { values } => {
329 if !values.is_empty() {
330 f.write_char(' ')?;
331 write_values(f, func, values)?;
332 }
333 Ok(())
334 }
335 Terminator::Unreachable => Ok(()),
336 Terminator::Trap { code } => write!(f, " {code}"),
337 Terminator::TailCall(call) => {
338 f.write_char(' ')?;
339 write_call(f, m, func, call)
340 }
341 Terminator::Invoke {
342 call,
343 normal,
344 unwind,
345 } => {
346 f.write_char(' ')?;
347 write_call(f, m, func, call)?;
348 f.write_str(" to ")?;
349 write_dest(f, func, normal)?;
350 f.write_str(" unwind ")?;
351 write_dest(f, func, unwind)
352 }
353 Terminator::Check { op, normal, error } => {
354 f.write_char(' ')?;
355 write_op(f, m, func, op, true)?;
356 f.write_str(" to ")?;
357 write_dest(f, func, normal)?;
358 f.write_str(" error ")?;
359 write_dest(f, func, error)
360 }
361 Terminator::Resume { payload } => write!(f, " {}", func.resolve(*payload)),
362 }
363}
364
365fn write_body(f: &mut impl Write, m: &Module, func: &Function) -> fmt::Result {
366 for (i, s) in func.slots.iter().enumerate() {
367 writeln!(f, " ss{i} = slot {} align {}", s.size, s.align)?;
368 }
369 let entry = func.entry();
370 let order = core::iter::once(entry)
371 .filter(|&b| func.is_live_block(b))
372 .chain(func.blocks().filter(|&b| b != entry));
373 for b in order {
374 write!(f, "{b}")?;
375 let params = func.block_params(b);
376 if !params.is_empty() {
377 f.write_char('(')?;
378 for (i, &p) in params.iter().enumerate() {
379 if i > 0 {
380 f.write_str(", ")?;
381 }
382 match func.value_type(p) {
383 Some(ty) => write!(f, "{p}: {ty}")?,
384 None => write!(f, "{p}: ?")?,
385 }
386 }
387 f.write_char(')')?;
388 }
389 f.write_str(":\n")?;
390 for inst in func.insts(b) {
391 f.write_str(" ")?;
392 write_results(f, func, func.results(inst))?;
393 if let Some(data) = func.inst(inst) {
394 write_op(f, m, func, data, false)?;
395 }
396 f.write_char('\n')?;
397 }
398 if let Some(term) = func.terminator(b) {
399 f.write_str(" ")?;
400 write_terminator(f, m, func, term)?;
401 f.write_char('\n')?;
402 }
403 }
404 Ok(())
405}
406
407fn write_function(f: &mut impl Write, m: &Module, id: FuncId) -> fmt::Result {
408 let Some(decl) = m.decls.funcs.get(id.index()) else {
409 return Ok(());
410 };
411 f.write_str("func ")?;
412 write_symbol(f, &decl.name)?;
413 write!(f, " {} {}", decl.sig, decl.linkage.name())?;
414 write_attrs(f, decl.attrs)?;
415 match m.function(id) {
416 Some(body) => {
417 f.write_str(" {\n")?;
418 write_body(f, m, body)?;
419 f.write_str("}\n")
420 }
421 None => f.write_char('\n'),
422 }
423}
424
425pub(crate) struct FunctionDisplay<'m> {
427 pub(crate) module: &'m Module,
428 pub(crate) func: FuncId,
429}
430
431impl fmt::Display for FunctionDisplay<'_> {
432 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
433 write_function(f, self.module, self.func)
434 }
435}
436
437impl fmt::Display for Module {
438 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
441 f.write_str("module ")?;
442 write_quoted(f, &self.name)?;
443 let ptr = match self.decls.ptr {
444 PointerWidth::P32 => "ptr32",
445 PointerWidth::P64 => "ptr64",
446 };
447 writeln!(f, " {ptr}")?;
448 for (i, a) in self.decls.aggs.iter().enumerate() {
449 write!(f, "type t{i} = ")?;
450 match &a.agg {
451 Aggregate::Struct { fields, packed } => {
452 if *packed {
453 f.write_str("packed ")?;
454 }
455 f.write_str("struct {")?;
456 for (k, field) in fields.iter().enumerate() {
457 f.write_str(if k == 0 { " " } else { ", " })?;
458 write!(f, "{field}")?;
459 }
460 f.write_str(" }\n")?;
461 }
462 Aggregate::Array { elem, len } => writeln!(f, "[{elem} x {len}]")?,
463 }
464 }
465 for (i, s) in self.decls.sigs.iter().enumerate() {
466 writeln!(f, "sig s{i} = {s}")?;
467 }
468 for g in &self.decls.globals {
469 let gl = &g.global;
470 f.write_str("global ")?;
471 write_symbol(f, &g.name)?;
472 write!(
473 f,
474 " {} {} size {} align {}",
475 gl.linkage.name(),
476 if gl.mutable { "mutable" } else { "const" },
477 gl.size,
478 gl.align
479 )?;
480 match &gl.init {
481 Init::Zeroed => f.write_str(" zeroed")?,
482 Init::Bytes(bytes) => {
483 f.write_str(" bytes \"")?;
484 for b in bytes {
485 write!(f, "{b:02x}")?;
486 }
487 f.write_char('"')?;
488 }
489 }
490 for r in &gl.relocs {
491 write!(f, " reloc {} ", r.offset)?;
492 match r.target {
493 SymbolRef::Func(func) => func_name(self, func, f)?,
494 SymbolRef::Global(global) => global_name(self, global, f)?,
495 }
496 write!(f, " {:+}", r.addend)?;
497 }
498 f.write_char('\n')?;
499 }
500 for id in self.functions() {
501 if self.function(id).is_some() {
502 f.write_char('\n')?;
503 }
504 write_function(f, self, id)?;
505 }
506 Ok(())
507 }
508}
509
510#[cfg(test)]
511mod tests {
512 use super::*;
513 use alloc::string::String;
514
515 #[test]
516 fn test_quoting_escapes_specials() {
517 let mut s = String::new();
518 let _ = write_quoted(&mut s, "a\"b\\c\n\u{1}");
519 assert_eq!(s, "\"a\\\"b\\\\c\\n\\u{1}\"");
520 }
521
522 #[test]
523 fn test_bare_symbols() {
524 assert!(is_bare_symbol("main"));
525 assert!(is_bare_symbol("_start.x$1"));
526 assert!(!is_bare_symbol("1x"));
527 assert!(!is_bare_symbol(""));
528 assert!(!is_bare_symbol("a b"));
529 }
530
531 #[test]
532 fn test_const_forms() {
533 let mut s = String::new();
534 let p = PointerWidth::P64;
535 let _ = write_const(&mut s, Type::I8, 0xff, p);
536 s.push(' ');
537 let _ = write_const(&mut s, Type::U8, 0xff, p);
538 s.push(' ');
539 let _ = write_const(&mut s, Type::F64, (-0.0f64).to_bits(), p);
540 s.push(' ');
541 let _ = write_const(&mut s, Type::F64, f64::NAN.to_bits(), p);
542 s.push(' ');
543 let _ = write_const(&mut s, Type::I8, 0x1ff, p);
544 s.push(' ');
545 let _ = write_const(&mut s, Type::Ref, 0, p);
546 assert_eq!(s, "-1 255 -0.0 nan:0x7ff8000000000000 0x1ff null");
547 }
548}