Skip to main content

llvm_ir/
printer.rs

1//! LLVM IR text format printer (`.ll` file emitter).
2
3use crate::basic_block::BasicBlock;
4use crate::context::{BlockId, ConstId, Context, InstrId, TypeId, ValueRef};
5use crate::function::Function;
6use crate::instruction::{FastMathFlags, InstrKind};
7use crate::module::Module;
8use crate::types::{FloatKind, StructType, TypeData};
9use crate::value::ConstantData;
10use std::fmt::Write as FmtWrite;
11
12/// Public API for `Printer`.
13pub struct Printer<'a> {
14    // `ctx` field.
15    ctx: &'a Context,
16}
17
18impl<'a> Printer<'a> {
19    /// Public API for `new`.
20    pub fn new(ctx: &'a Context) -> Self {
21        Printer { ctx }
22    }
23
24    /// Public API for `print_module`.
25    pub fn print_module(&self, module: &Module) -> String {
26        let mut out = String::new();
27
28        // Module header
29        if let Some(ref sf) = module.source_filename {
30            writeln!(out, "source_filename = {:?}", sf).unwrap();
31        }
32        if let Some(ref dl) = module.data_layout {
33            writeln!(out, "target datalayout = {:?}", dl).unwrap();
34        }
35        if let Some(ref tt) = module.target_triple {
36            writeln!(out, "target triple = {:?}", tt).unwrap();
37        }
38
39        // Named struct type definitions
40        if !module.named_types.is_empty() {
41            writeln!(out).unwrap();
42            for (name, ty) in &module.named_types {
43                write!(out, "%{} = type ", name).unwrap();
44                if let TypeData::Struct(st) = self.ctx.get_type(*ty) {
45                    self.write_struct_body(&mut out, st);
46                } else {
47                    self.write_type(&mut out, *ty);
48                }
49                writeln!(out).unwrap();
50            }
51        }
52
53        // Global variables
54        if !module.globals.is_empty() {
55            writeln!(out).unwrap();
56            for gv in &module.globals {
57                let linkage = gv.linkage.as_str();
58                if !linkage.is_empty() {
59                    write!(out, "@{} = {} ", gv.name, linkage).unwrap();
60                } else {
61                    write!(out, "@{} = ", gv.name).unwrap();
62                }
63                if gv.is_constant {
64                    write!(out, "constant ").unwrap();
65                } else {
66                    write!(out, "global ").unwrap();
67                }
68                self.write_type(&mut out, gv.ty);
69                if let Some(init) = gv.initializer {
70                    write!(out, " ").unwrap();
71                    self.write_const_value(&mut out, init);
72                } else {
73                    write!(out, " undef").unwrap();
74                }
75                writeln!(out).unwrap();
76            }
77        }
78
79        // Functions
80        for func in &module.functions {
81            writeln!(out).unwrap();
82            self.write_function(&mut out, func);
83        }
84
85        if !module.named_metadata.is_empty() || !module.metadata_nodes.is_empty() {
86            writeln!(out).unwrap();
87            for (name, value) in &module.named_metadata {
88                writeln!(out, "!{} = {}", name, value).unwrap();
89            }
90            let mut ids: Vec<u32> = module.metadata_nodes.keys().copied().collect();
91            ids.sort_unstable();
92            for id in ids {
93                if let Some(value) = module.metadata_node(id) {
94                    writeln!(out, "!{} = {}", id, value).unwrap();
95                }
96            }
97        }
98
99        out
100    }
101
102    // -----------------------------------------------------------------------
103    // Type printing
104    // -----------------------------------------------------------------------
105
106    fn write_type(&self, out: &mut String, ty: TypeId) {
107        match self.ctx.get_type(ty) {
108            TypeData::Void => out.push_str("void"),
109            TypeData::Integer(bits) => {
110                write!(out, "i{}", bits).unwrap();
111            }
112            TypeData::Float(kind) => out.push_str(float_kind_str(*kind)),
113            TypeData::Pointer => out.push_str("ptr"),
114            TypeData::Label => out.push_str("label"),
115            TypeData::Metadata => out.push_str("metadata"),
116            TypeData::Array { element, len } => {
117                let elem = *element;
118                let l = *len;
119                write!(out, "[{} x ", l).unwrap();
120                self.write_type(out, elem);
121                out.push(']');
122            }
123            TypeData::Vector {
124                element,
125                len,
126                scalable,
127            } => {
128                let elem = *element;
129                let l = *len;
130                let sc = *scalable;
131                if sc {
132                    write!(out, "<vscale x {} x ", l).unwrap();
133                } else {
134                    write!(out, "<{} x ", l).unwrap();
135                }
136                self.write_type(out, elem);
137                out.push('>');
138            }
139            TypeData::Struct(st) => {
140                if let Some(ref name) = st.name.clone() {
141                    write!(out, "%{}", name).unwrap();
142                } else {
143                    let st = st.clone();
144                    self.write_struct_body(out, &st);
145                }
146            }
147            TypeData::Function(ft) => {
148                let ft = ft.clone();
149                self.write_type(out, ft.ret);
150                out.push_str(" (");
151                for (i, &p) in ft.params.iter().enumerate() {
152                    if i > 0 {
153                        out.push_str(", ");
154                    }
155                    self.write_type(out, p);
156                }
157                if ft.variadic {
158                    if !ft.params.is_empty() {
159                        out.push_str(", ");
160                    }
161                    out.push_str("...");
162                }
163                out.push(')');
164            }
165        }
166    }
167
168    fn write_struct_body(&self, out: &mut String, st: &StructType) {
169        if st.packed {
170            out.push('<');
171        }
172        out.push_str("{ ");
173        for (i, &f) in st.fields.iter().enumerate() {
174            if i > 0 {
175                out.push_str(", ");
176            }
177            self.write_type(out, f);
178        }
179        out.push_str(" }");
180        if st.packed {
181            out.push('>');
182        }
183    }
184
185    // -----------------------------------------------------------------------
186    // Constant printing
187    // -----------------------------------------------------------------------
188
189    /// Print `type value` e.g. `i32 42`.
190    fn write_const_with_type(&self, out: &mut String, id: ConstId) {
191        let ty = self.ctx.type_of_const(id);
192        self.write_type(out, ty);
193        out.push(' ');
194        self.write_const_value(out, id);
195    }
196
197    fn write_const_value(&self, out: &mut String, id: ConstId) {
198        match self.ctx.get_const(id) {
199            ConstantData::Int { val, .. } => {
200                write!(out, "{}", *val as i64).unwrap();
201            }
202            ConstantData::IntWide { words, .. } => {
203                // Emit as hex for simplicity.
204                out.push_str("0x");
205                for w in words.iter().rev() {
206                    write!(out, "{:016x}", w).unwrap();
207                }
208            }
209            ConstantData::Float { ty, bits } => {
210                match self.ctx.get_type(*ty) {
211                    TypeData::Float(FloatKind::Single) => {
212                        let f = f32::from_bits(*bits as u32);
213                        // Use LLVM hex format for exact round-trip.
214                        let d = f as f64;
215                        write!(out, "0x{:016X}", d.to_bits()).unwrap();
216                    }
217                    _ => {
218                        // Always use hex float for exact round-trip.
219                        write!(out, "0x{:016X}", bits).unwrap();
220                    }
221                }
222            }
223            ConstantData::Null(_) => out.push_str("null"),
224            ConstantData::Undef(_) => out.push_str("undef"),
225            ConstantData::Poison(_) => out.push_str("poison"),
226            ConstantData::ZeroInitializer(_) => out.push_str("zeroinitializer"),
227            ConstantData::Array { elements, .. } => {
228                out.push('[');
229                for (i, &e) in elements.iter().enumerate() {
230                    if i > 0 {
231                        out.push_str(", ");
232                    }
233                    self.write_const_with_type(out, e);
234                }
235                out.push(']');
236            }
237            ConstantData::Struct { fields, .. } => {
238                out.push_str("{ ");
239                for (i, &f) in fields.iter().enumerate() {
240                    if i > 0 {
241                        out.push_str(", ");
242                    }
243                    self.write_const_with_type(out, f);
244                }
245                out.push_str(" }");
246            }
247            ConstantData::Vector { elements, .. } => {
248                out.push('<');
249                for (i, &e) in elements.iter().enumerate() {
250                    if i > 0 {
251                        out.push_str(", ");
252                    }
253                    self.write_const_with_type(out, e);
254                }
255                out.push('>');
256            }
257            ConstantData::GlobalRef { name, .. } => {
258                write!(out, "@{}", name).unwrap();
259            }
260        }
261    }
262
263    // -----------------------------------------------------------------------
264    // ValueRef printing
265    // -----------------------------------------------------------------------
266
267    /// Print a typed value ref: `i32 %x` or `i32 42`.
268    fn write_typed_value(&self, out: &mut String, vref: ValueRef, func: &Function) {
269        let ty = self.type_of_vref(vref, func);
270        self.write_type(out, ty);
271        out.push(' ');
272        self.write_value(out, vref, func);
273    }
274
275    fn write_value(&self, out: &mut String, vref: ValueRef, func: &Function) {
276        match vref {
277            ValueRef::Instruction(id) => {
278                if let Some(ref name) = func.instr(id).name {
279                    write!(out, "%{}", name).unwrap();
280                } else {
281                    write!(out, "%v{}", id.0).unwrap();
282                }
283            }
284            ValueRef::Argument(id) => {
285                let arg = func.arg(id);
286                if arg.name.is_empty() {
287                    write!(out, "%{}", id.0).unwrap();
288                } else {
289                    write!(out, "%{}", arg.name).unwrap();
290                }
291            }
292            ValueRef::Constant(id) => self.write_const_value(out, id),
293            ValueRef::Global(id) => {
294                write!(out, "@g{}", id.0).unwrap();
295            }
296        }
297    }
298
299    fn type_of_vref(&self, vref: ValueRef, func: &Function) -> TypeId {
300        match vref {
301            ValueRef::Instruction(id) => func.instr(id).ty,
302            ValueRef::Argument(id) => func.arg(id).ty,
303            ValueRef::Constant(id) => self.ctx.type_of_const(id),
304            ValueRef::Global(_) => self.ctx.ptr_ty,
305        }
306    }
307
308    // -----------------------------------------------------------------------
309    // Function printing
310    // -----------------------------------------------------------------------
311
312    fn write_function(&self, out: &mut String, func: &Function) {
313        if func.is_declaration {
314            out.push_str("declare ");
315        } else {
316            out.push_str("define ");
317        }
318        // Linkage (skip for external which is default).
319        let lk = func.linkage.as_str();
320        if !lk.is_empty() {
321            write!(out, "{} ", lk).unwrap();
322        }
323
324        // Return type from function type.
325        if let crate::types::TypeData::Function(ft) = self.ctx.get_type(func.ty) {
326            let ret = ft.ret;
327            let params = ft.params.clone();
328            let variadic = ft.variadic;
329            self.write_type(out, ret);
330            write!(out, " @{}(", func.name).unwrap();
331            for (i, arg) in func.args.iter().enumerate() {
332                if i > 0 {
333                    out.push_str(", ");
334                }
335                self.write_type(out, arg.ty);
336                if !arg.name.is_empty() {
337                    write!(out, " %{}", arg.name).unwrap();
338                }
339            }
340            // If there are fewer args than param types (anonymous decl args), fill in.
341            let n_printed = func.args.len();
342            if n_printed < params.len() {
343                for (i, &param_ty) in params.iter().enumerate().skip(n_printed) {
344                    if i > 0 {
345                        out.push_str(", ");
346                    }
347                    self.write_type(out, param_ty);
348                }
349            }
350            if variadic {
351                if !func.args.is_empty() || !params.is_empty() {
352                    out.push_str(", ");
353                }
354                out.push_str("...");
355            }
356        } else {
357            panic!(
358                "printer: function '{}' has non-function type {:?}",
359                func.name,
360                self.ctx.get_type(func.ty)
361            );
362        }
363        out.push(')');
364
365        if func.is_declaration {
366            writeln!(out).unwrap();
367            return;
368        }
369
370        writeln!(out, " {{").unwrap();
371
372        for (bi, bb) in func.blocks.iter().enumerate() {
373            self.write_block(out, bb, func, BlockId(bi as u32));
374        }
375
376        writeln!(out, "}}").unwrap();
377    }
378
379    fn write_block(&self, out: &mut String, bb: &BasicBlock, func: &Function, _bid: BlockId) {
380        writeln!(out, "{}:", bb.name).unwrap();
381        for id in bb.instrs() {
382            self.write_instr(out, id, func);
383        }
384    }
385
386    fn write_instr(&self, out: &mut String, id: InstrId, func: &Function) {
387        let instr = func.instr(id);
388        out.push_str("  ");
389
390        // Result name if non-void.
391        if let Some(ref name) = instr.name {
392            if !name.is_empty() {
393                write!(out, "%{} = ", name).unwrap();
394            }
395        }
396
397        // Emit fast-math flags helper.
398        fn fmf_str(f: &FastMathFlags) -> String {
399            let mut s = String::new();
400            if f.fast {
401                s.push_str("fast ");
402                return s;
403            }
404            if f.nnan {
405                s.push_str("nnan ");
406            }
407            if f.ninf {
408                s.push_str("ninf ");
409            }
410            if f.nsz {
411                s.push_str("nsz ");
412            }
413            if f.arcp {
414                s.push_str("arcp ");
415            }
416            if f.contract {
417                s.push_str("contract ");
418            }
419            if f.afn {
420                s.push_str("afn ");
421            }
422            if f.reassoc {
423                s.push_str("reassoc ");
424            }
425            s
426        }
427
428        match &instr.kind {
429            InstrKind::Add { flags, lhs, rhs } => {
430                out.push_str("add ");
431                if flags.nuw {
432                    out.push_str("nuw ");
433                }
434                if flags.nsw {
435                    out.push_str("nsw ");
436                }
437                self.write_typed_value(out, *lhs, func);
438                out.push_str(", ");
439                self.write_value(out, *rhs, func);
440            }
441            InstrKind::Sub { flags, lhs, rhs } => {
442                out.push_str("sub ");
443                if flags.nuw {
444                    out.push_str("nuw ");
445                }
446                if flags.nsw {
447                    out.push_str("nsw ");
448                }
449                self.write_typed_value(out, *lhs, func);
450                out.push_str(", ");
451                self.write_value(out, *rhs, func);
452            }
453            InstrKind::Mul { flags, lhs, rhs } => {
454                out.push_str("mul ");
455                if flags.nuw {
456                    out.push_str("nuw ");
457                }
458                if flags.nsw {
459                    out.push_str("nsw ");
460                }
461                self.write_typed_value(out, *lhs, func);
462                out.push_str(", ");
463                self.write_value(out, *rhs, func);
464            }
465            InstrKind::UDiv { exact, lhs, rhs } => {
466                out.push_str("udiv ");
467                if *exact {
468                    out.push_str("exact ");
469                }
470                self.write_typed_value(out, *lhs, func);
471                out.push_str(", ");
472                self.write_value(out, *rhs, func);
473            }
474            InstrKind::SDiv { exact, lhs, rhs } => {
475                out.push_str("sdiv ");
476                if *exact {
477                    out.push_str("exact ");
478                }
479                self.write_typed_value(out, *lhs, func);
480                out.push_str(", ");
481                self.write_value(out, *rhs, func);
482            }
483            InstrKind::URem { lhs, rhs } => {
484                out.push_str("urem ");
485                self.write_typed_value(out, *lhs, func);
486                out.push_str(", ");
487                self.write_value(out, *rhs, func);
488            }
489            InstrKind::SRem { lhs, rhs } => {
490                out.push_str("srem ");
491                self.write_typed_value(out, *lhs, func);
492                out.push_str(", ");
493                self.write_value(out, *rhs, func);
494            }
495            InstrKind::And { lhs, rhs } => {
496                out.push_str("and ");
497                self.write_typed_value(out, *lhs, func);
498                out.push_str(", ");
499                self.write_value(out, *rhs, func);
500            }
501            InstrKind::Or { lhs, rhs } => {
502                out.push_str("or ");
503                self.write_typed_value(out, *lhs, func);
504                out.push_str(", ");
505                self.write_value(out, *rhs, func);
506            }
507            InstrKind::Xor { lhs, rhs } => {
508                out.push_str("xor ");
509                self.write_typed_value(out, *lhs, func);
510                out.push_str(", ");
511                self.write_value(out, *rhs, func);
512            }
513            InstrKind::Shl { flags, lhs, rhs } => {
514                out.push_str("shl ");
515                if flags.nuw {
516                    out.push_str("nuw ");
517                }
518                if flags.nsw {
519                    out.push_str("nsw ");
520                }
521                self.write_typed_value(out, *lhs, func);
522                out.push_str(", ");
523                self.write_value(out, *rhs, func);
524            }
525            InstrKind::LShr { exact, lhs, rhs } => {
526                out.push_str("lshr ");
527                if *exact {
528                    out.push_str("exact ");
529                }
530                self.write_typed_value(out, *lhs, func);
531                out.push_str(", ");
532                self.write_value(out, *rhs, func);
533            }
534            InstrKind::AShr { exact, lhs, rhs } => {
535                out.push_str("ashr ");
536                if *exact {
537                    out.push_str("exact ");
538                }
539                self.write_typed_value(out, *lhs, func);
540                out.push_str(", ");
541                self.write_value(out, *rhs, func);
542            }
543            InstrKind::FAdd { flags, lhs, rhs } => {
544                write!(out, "fadd {}", fmf_str(flags)).unwrap();
545                self.write_typed_value(out, *lhs, func);
546                out.push_str(", ");
547                self.write_value(out, *rhs, func);
548            }
549            InstrKind::FSub { flags, lhs, rhs } => {
550                write!(out, "fsub {}", fmf_str(flags)).unwrap();
551                self.write_typed_value(out, *lhs, func);
552                out.push_str(", ");
553                self.write_value(out, *rhs, func);
554            }
555            InstrKind::FMul { flags, lhs, rhs } => {
556                write!(out, "fmul {}", fmf_str(flags)).unwrap();
557                self.write_typed_value(out, *lhs, func);
558                out.push_str(", ");
559                self.write_value(out, *rhs, func);
560            }
561            InstrKind::FDiv { flags, lhs, rhs } => {
562                write!(out, "fdiv {}", fmf_str(flags)).unwrap();
563                self.write_typed_value(out, *lhs, func);
564                out.push_str(", ");
565                self.write_value(out, *rhs, func);
566            }
567            InstrKind::FRem { flags, lhs, rhs } => {
568                write!(out, "frem {}", fmf_str(flags)).unwrap();
569                self.write_typed_value(out, *lhs, func);
570                out.push_str(", ");
571                self.write_value(out, *rhs, func);
572            }
573            InstrKind::FNeg { flags, operand } => {
574                write!(out, "fneg {}", fmf_str(flags)).unwrap();
575                self.write_typed_value(out, *operand, func);
576            }
577            InstrKind::ICmp { pred, lhs, rhs } => {
578                write!(out, "icmp {} ", pred.as_str()).unwrap();
579                self.write_typed_value(out, *lhs, func);
580                out.push_str(", ");
581                self.write_value(out, *rhs, func);
582            }
583            InstrKind::FCmp {
584                flags,
585                pred,
586                lhs,
587                rhs,
588            } => {
589                write!(out, "fcmp {}{} ", fmf_str(flags), pred.as_str()).unwrap();
590                self.write_typed_value(out, *lhs, func);
591                out.push_str(", ");
592                self.write_value(out, *rhs, func);
593            }
594            InstrKind::Alloca {
595                alloc_ty,
596                num_elements,
597                align,
598            } => {
599                out.push_str("alloca ");
600                self.write_type(out, *alloc_ty);
601                if let Some(ne) = num_elements {
602                    out.push_str(", ");
603                    self.write_typed_value(out, *ne, func);
604                }
605                if let Some(a) = align {
606                    write!(out, ", align {}", a).unwrap();
607                }
608            }
609            InstrKind::Load {
610                ty,
611                ptr,
612                align,
613                volatile,
614            } => {
615                out.push_str("load ");
616                if *volatile {
617                    out.push_str("volatile ");
618                }
619                self.write_type(out, *ty);
620                out.push_str(", ");
621                self.write_typed_value(out, *ptr, func);
622                if let Some(a) = align {
623                    write!(out, ", align {}", a).unwrap();
624                }
625            }
626            InstrKind::Store {
627                val,
628                ptr,
629                align,
630                volatile,
631            } => {
632                out.push_str("store ");
633                if *volatile {
634                    out.push_str("volatile ");
635                }
636                self.write_typed_value(out, *val, func);
637                out.push_str(", ");
638                self.write_typed_value(out, *ptr, func);
639                if let Some(a) = align {
640                    write!(out, ", align {}", a).unwrap();
641                }
642            }
643            InstrKind::GetElementPtr {
644                inbounds,
645                base_ty,
646                ptr,
647                indices,
648            } => {
649                out.push_str("getelementptr ");
650                if *inbounds {
651                    out.push_str("inbounds ");
652                }
653                self.write_type(out, *base_ty);
654                out.push_str(", ");
655                self.write_typed_value(out, *ptr, func);
656                for idx in indices {
657                    out.push_str(", ");
658                    self.write_typed_value(out, *idx, func);
659                }
660            }
661            InstrKind::Trunc { val, to }
662            | InstrKind::ZExt { val, to }
663            | InstrKind::SExt { val, to }
664            | InstrKind::FPTrunc { val, to }
665            | InstrKind::FPExt { val, to }
666            | InstrKind::FPToUI { val, to }
667            | InstrKind::FPToSI { val, to }
668            | InstrKind::UIToFP { val, to }
669            | InstrKind::SIToFP { val, to }
670            | InstrKind::PtrToInt { val, to }
671            | InstrKind::IntToPtr { val, to }
672            | InstrKind::BitCast { val, to }
673            | InstrKind::AddrSpaceCast { val, to } => {
674                out.push_str(instr.kind.opcode());
675                out.push(' ');
676                self.write_typed_value(out, *val, func);
677                out.push_str(" to ");
678                self.write_type(out, *to);
679            }
680            InstrKind::Freeze { val } => {
681                out.push_str("freeze ");
682                self.write_typed_value(out, *val, func);
683            }
684            InstrKind::Select {
685                cond,
686                then_val,
687                else_val,
688            } => {
689                out.push_str("select ");
690                self.write_typed_value(out, *cond, func);
691                out.push_str(", ");
692                self.write_typed_value(out, *then_val, func);
693                out.push_str(", ");
694                self.write_typed_value(out, *else_val, func);
695            }
696            InstrKind::Phi { ty, incoming } => {
697                out.push_str("phi ");
698                self.write_type(out, *ty);
699                let inc = incoming.clone();
700                for (i, (val, block)) in inc.iter().enumerate() {
701                    if i > 0 {
702                        out.push_str(", ");
703                    } else {
704                        out.push(' ');
705                    }
706                    out.push_str("[ ");
707                    self.write_value(out, *val, func);
708                    out.push_str(", %");
709                    out.push_str(&func.block(*block).name);
710                    out.push_str(" ]");
711                }
712            }
713            InstrKind::ExtractValue { aggregate, indices } => {
714                out.push_str("extractvalue ");
715                self.write_typed_value(out, *aggregate, func);
716                for &i in indices {
717                    write!(out, ", {}", i).unwrap();
718                }
719            }
720            InstrKind::InsertValue {
721                aggregate,
722                val,
723                indices,
724            } => {
725                out.push_str("insertvalue ");
726                self.write_typed_value(out, *aggregate, func);
727                out.push_str(", ");
728                self.write_typed_value(out, *val, func);
729                for &i in indices {
730                    write!(out, ", {}", i).unwrap();
731                }
732            }
733            InstrKind::ExtractElement { vec, idx } => {
734                out.push_str("extractelement ");
735                self.write_typed_value(out, *vec, func);
736                out.push_str(", ");
737                self.write_typed_value(out, *idx, func);
738            }
739            InstrKind::InsertElement { vec, val, idx } => {
740                out.push_str("insertelement ");
741                self.write_typed_value(out, *vec, func);
742                out.push_str(", ");
743                self.write_typed_value(out, *val, func);
744                out.push_str(", ");
745                self.write_typed_value(out, *idx, func);
746            }
747            InstrKind::ShuffleVector { v1, v2, mask } => {
748                out.push_str("shufflevector ");
749                self.write_typed_value(out, *v1, func);
750                out.push_str(", ");
751                self.write_typed_value(out, *v2, func);
752                // Mask: LLVM requires the type annotation, e.g. `<4 x i32> <i32 0, ...>`.
753                write!(out, ", <{} x i32> <", mask.len()).unwrap();
754                for (i, &m) in mask.iter().enumerate() {
755                    if i > 0 {
756                        out.push_str(", ");
757                    }
758                    write!(out, "i32 {}", m).unwrap();
759                }
760                out.push('>');
761            }
762            InstrKind::Call {
763                tail,
764                callee_ty,
765                callee,
766                args,
767            } => {
768                match tail {
769                    crate::instruction::TailCallKind::Tail => out.push_str("tail "),
770                    crate::instruction::TailCallKind::MustTail => out.push_str("musttail "),
771                    crate::instruction::TailCallKind::NoTail => out.push_str("notail "),
772                    crate::instruction::TailCallKind::None => {}
773                }
774                out.push_str("call ");
775                // Print return type from callee_ty.
776                if let crate::types::TypeData::Function(ft) = self.ctx.get_type(*callee_ty) {
777                    let ret = ft.ret;
778                    self.write_type(out, ret);
779                    out.push(' ');
780                }
781                self.write_value(out, *callee, func);
782                out.push('(');
783                let call_args = args.clone();
784                for (i, &arg) in call_args.iter().enumerate() {
785                    if i > 0 {
786                        out.push_str(", ");
787                    }
788                    self.write_typed_value(out, arg, func);
789                }
790                out.push(')');
791            }
792            InstrKind::Ret { val: None } => {
793                out.push_str("ret void");
794            }
795            InstrKind::Ret { val: Some(v) } => {
796                out.push_str("ret ");
797                self.write_typed_value(out, *v, func);
798            }
799            InstrKind::Br { dest } => {
800                write!(out, "br label %{}", func.block(*dest).name).unwrap();
801            }
802            InstrKind::CondBr {
803                cond,
804                then_dest,
805                else_dest,
806            } => {
807                out.push_str("br ");
808                self.write_typed_value(out, *cond, func);
809                write!(
810                    out,
811                    ", label %{}, label %{}",
812                    func.block(*then_dest).name,
813                    func.block(*else_dest).name
814                )
815                .unwrap();
816            }
817            InstrKind::Switch {
818                val,
819                default,
820                cases,
821            } => {
822                out.push_str("switch ");
823                self.write_typed_value(out, *val, func);
824                writeln!(out, ", label %{} [", func.block(*default).name).unwrap();
825                let sw_cases = cases.clone();
826                for (case_val, dest) in &sw_cases {
827                    out.push_str("    ");
828                    self.write_typed_value(out, *case_val, func);
829                    writeln!(out, ", label %{}", func.block(*dest).name).unwrap();
830                }
831                out.push_str("  ]");
832            }
833            InstrKind::Unreachable => {
834                out.push_str("unreachable");
835            }
836        }
837
838        if let Some(attachments) = func.instr_metadata(id) {
839            for (key, value) in attachments {
840                write!(out, ", !{} {}", key, value).unwrap();
841            }
842        } else if let Some(loc_id) = func.instr_dbg_loc(id) {
843            write!(out, ", !dbg !{}", loc_id).unwrap();
844        }
845        writeln!(out).unwrap();
846    }
847}
848
849fn float_kind_str(kind: FloatKind) -> &'static str {
850    match kind {
851        FloatKind::Half => "half",
852        FloatKind::BFloat => "bfloat",
853        FloatKind::Single => "float",
854        FloatKind::Double => "double",
855        FloatKind::Fp128 => "fp128",
856        FloatKind::X86Fp80 => "x86_fp80",
857    }
858}
859
860#[cfg(test)]
861mod tests {
862    use super::*;
863    use crate::basic_block::BasicBlock;
864    use crate::context::Context;
865    use crate::function::Function;
866    use crate::instruction::{InstrKind, Instruction, IntArithFlags};
867    use crate::module::Module;
868    use crate::value::Linkage;
869
870    #[test]
871    fn print_simple_function() {
872        let mut ctx = Context::new();
873        let fn_ty = ctx.mk_fn_type(ctx.i32_ty, vec![ctx.i32_ty, ctx.i32_ty], false);
874        let args = vec![
875            crate::value::Argument {
876                name: "a".into(),
877                ty: ctx.i32_ty,
878                index: 0,
879            },
880            crate::value::Argument {
881                name: "b".into(),
882                ty: ctx.i32_ty,
883                index: 1,
884            },
885        ];
886        let mut func = Function::new("add", fn_ty, args, Linkage::External);
887
888        let a_ref = ValueRef::Argument(crate::context::ArgId(0));
889        let b_ref = ValueRef::Argument(crate::context::ArgId(1));
890
891        let mut bb = BasicBlock::new("entry");
892        let add_instr = Instruction::new(
893            Some("result".into()),
894            ctx.i32_ty,
895            InstrKind::Add {
896                flags: IntArithFlags::default(),
897                lhs: a_ref,
898                rhs: b_ref,
899            },
900        );
901        let iid = func.alloc_instr(add_instr);
902        bb.append_instr(iid);
903
904        let ret_instr = Instruction::new(
905            None,
906            ctx.void_ty,
907            InstrKind::Ret {
908                val: Some(ValueRef::Instruction(iid)),
909            },
910        );
911        let rid = func.alloc_instr(ret_instr);
912        bb.set_terminator(rid);
913
914        let _bid = func.add_block(bb);
915
916        let mut module = Module::new("test");
917        module.add_function(func);
918
919        let printer = Printer::new(&ctx);
920        let output = printer.print_module(&module);
921        assert!(output.contains("define i32 @add("));
922        assert!(output.contains("%result = add i32 %a, %b"));
923        assert!(output.contains("ret i32 %result"));
924    }
925}