Skip to main content

rucc_ir/
print.rs

1//! The printer: a module as text.
2//!
3//! Design: `spec/08-ir.md` section 8.8.
4//!
5//! The printer and the parser round-trip byte for byte, which is what makes the IR testable on
6//! its own, what makes `-fdump-ir=after-<pass>` worth reading, and what lets a fuzzer make IR
7//! directly rather than through the front end. The printer is written first because the parser
8//! has to read what it writes.
9//!
10//! # What decides the text
11//!
12//! Nothing printed here is a fact about the tables the module happens to be in. Values are
13//! numbered in the order they are printed rather than by their index, blocks likewise, and a
14//! signature is written out at the call rather than as a number into a side table. So printing
15//! a module, parsing it back and printing it again gives the same bytes even when the second
16//! module's tables are laid out differently from the first's, which is the property that makes
17//! the round trip worth testing at all.
18//!
19//! A type is written on an instruction only where it cannot be worked out from the operands:
20//! on one that takes none, and on one whose result is a different type from its first operand.
21//! Everything else would be a second copy of something already on the line above, and a second
22//! copy is a thing that can disagree.
23//!
24//! Spans are not printed. Debug information has its own form and it is written in a later
25//! milestone; the round trip is a claim about the text, not about the source locations behind
26//! it.
27
28use std::fmt::Write as _;
29
30use rucc_base::{Interner, Symbol};
31
32use crate::func::Func;
33use crate::inst::{
34    Abi, Block, BlockCall, Imm, Inst, InstData, MemInfo, Meta, Param, Signature, Value,
35};
36use crate::module::{Alias, Datum, Global, Module, Reloc};
37use crate::{Extra, FORMAT_VERSION, Linkage, MemOrder, Opcode, Type, Visibility};
38
39/// Whether the opcode says what it produces without a type having to be written down.
40///
41/// A comparison produces `i1`, one per lane of what it compared. The two that produce an
42/// address produce an address. A call produces what its signature says, and the signature is
43/// written out on the same line. Everything else either takes an operand of the type it
44/// produces, in which case that operand says it, or has the type written after the opcode.
45///
46/// The printer and the parser share this, because a rule the two of them state separately is a
47/// rule they will eventually state differently.
48pub(crate) fn implied_result(opcode: Opcode) -> bool {
49    matches!(
50        opcode,
51        Opcode::ICmp
52            | Opcode::FCmp
53            | Opcode::GlobalAddr
54            | Opcode::BlockAddr
55            | Opcode::Alloca
56            | Opcode::Call
57            | Opcode::CallIndirect
58            | Opcode::TailCall
59    )
60}
61
62/// The whole module, as text.
63#[must_use]
64pub fn print(module: &Module, names: &Interner) -> String {
65    let mut printer = Printer::new(module, names);
66    printer.module();
67    printer.finish()
68}
69
70/// One function of a module, as text, for a dump of a single function.
71#[must_use]
72pub fn print_func(module: &Module, func: &Func, names: &Interner) -> String {
73    let mut printer = Printer::new(module, names);
74    printer.func(func);
75    printer.finish()
76}
77
78/// A module being written out.
79#[derive(Debug)]
80pub struct Printer<'a> {
81    module: &'a Module,
82    names: &'a Interner,
83    out: String,
84    // The number each value and each block is printed as, in print order, indexed by the index
85    // it has in the function being printed. `u32::MAX` for one that has not been reached,
86    // which only happens in a function the verifier would turn down.
87    values: Vec<u32>,
88    blocks: Vec<u32>,
89}
90
91impl<'a> Printer<'a> {
92    /// A printer over one module, whose names are in `names`.
93    #[must_use]
94    pub fn new(module: &'a Module, names: &'a Interner) -> Printer<'a> {
95        Printer { module, names, out: String::new(), values: Vec::new(), blocks: Vec::new() }
96    }
97
98    /// The text written so far.
99    #[must_use]
100    pub fn finish(self) -> String {
101        self.out
102    }
103
104    /// The header, then the globals, the aliases, the functions and the metadata.
105    pub fn module(&mut self) {
106        let module = self.module;
107        let name = self.names.resolve(module.name);
108        // Writing to a `String` cannot fail, which is why the result is dropped here and at
109        // every other `write!` in this file rather than turned into a panic to reason about.
110        let _ = writeln!(self.out, "; ModuleID = '{name}'");
111        let _ = writeln!(self.out, "; format {FORMAT_VERSION}");
112        let _ = writeln!(self.out, "target triple = \"{}\"", module.triple);
113        let _ = writeln!(self.out, "target datalayout = \"{}\"", module.datalayout);
114
115        if module.globals().next().is_some() {
116            self.out.push('\n');
117            for id in module.globals() {
118                self.global(&module[id]);
119            }
120        }
121        if module.aliases().next().is_some() {
122            self.out.push('\n');
123            for id in module.aliases() {
124                self.alias(&module[id]);
125            }
126        }
127        for id in module.funcs() {
128            self.out.push('\n');
129            self.func(&module[id]);
130        }
131        if module.metadata().next().is_some() {
132            self.out.push('\n');
133            for meta in module.metadata() {
134                self.meta_node(meta);
135            }
136        }
137    }
138
139    // Globals and aliases.
140
141    /// One global variable, on one line.
142    fn global(&mut self, global: &Global) {
143        let _ = write!(self.out, "global @{} : ", self.names.resolve(global.name));
144        match self.scalar_init(global) {
145            // The shorthand for the common case, which is a global holding one number. It is
146            // used only when the type accounts for the whole size, so that reading it back
147            // gives the size again without its having been written down.
148            Some((ty, imm)) => {
149                let _ = write!(self.out, "{ty} = ");
150                self.imm(imm, ty);
151            }
152            None => {
153                let _ = write!(self.out, "bytes {}", global.size);
154                if let Some(init) = global.init {
155                    self.out.push_str(" = { ");
156                    for (index, &datum) in self.module[init].iter().enumerate() {
157                        if index > 0 {
158                            self.out.push_str(", ");
159                        }
160                        self.datum(datum);
161                    }
162                    self.out.push_str(" }");
163                }
164            }
165        }
166        let _ = write!(self.out, ", align {}", global.align);
167        self.linkage(global.linkage, global.visibility);
168        if let Some(model) = global.tls {
169            let _ = write!(self.out, ", tls({})", model.name());
170        }
171        if global.constant {
172            self.out.push_str(", constant");
173        }
174        self.section(global.section);
175        self.out.push('\n');
176    }
177
178    /// The type and the value of a global that holds exactly one scalar filling it.
179    fn scalar_init(&self, global: &Global) -> Option<(Type, Imm)> {
180        let init = global.init?;
181        let [datum] = self.module[init] else { return None };
182        let Datum::Scalar { ty, value } = datum else { return None };
183        (datum.size(self.module) == global.size).then(|| (ty, self.module[value]))
184    }
185
186    /// One piece of a global's image.
187    fn datum(&mut self, datum: Datum) {
188        match datum {
189            Datum::Zero(bytes) => {
190                let _ = write!(self.out, "zero {bytes}");
191            }
192            Datum::Bytes(range) => {
193                self.out.push_str("bytes ");
194                let bytes = &self.module[range];
195                self.string(bytes);
196            }
197            Datum::Scalar { ty, value } => {
198                let _ = write!(self.out, "{ty} ");
199                self.imm(self.module[value], ty);
200            }
201            Datum::Addr(reloc) => {
202                let Reloc { symbol, addend, size } = self.module[reloc];
203                let _ = write!(self.out, "addr.{size} @{}", self.names.resolve(symbol));
204                match addend.signum() {
205                    1 => {
206                        let _ = write!(self.out, " + {addend}");
207                    }
208                    -1 => {
209                        // Written as a subtraction rather than as a negative addend, because
210                        // `+ -8` is a thing nobody reads twice the same way. `i64::MIN` has no
211                        // positive counterpart, so it keeps the sign it came with.
212                        let _ = match addend.checked_neg() {
213                            Some(amount) => write!(self.out, " - {amount}"),
214                            None => write!(self.out, " + {addend}"),
215                        };
216                    }
217                    _ => {}
218                }
219            }
220        }
221    }
222
223    /// One alias, on one line.
224    fn alias(&mut self, alias: &Alias) {
225        let _ = write!(
226            self.out,
227            "{} @{} = @{}",
228            alias.kind.name(),
229            self.names.resolve(alias.name),
230            self.names.resolve(alias.target)
231        );
232        self.linkage(alias.linkage, alias.visibility);
233        self.out.push('\n');
234    }
235
236    // Functions.
237
238    /// One function: its signature, then its blocks, or a semicolon if it has none.
239    pub fn func(&mut self, func: &Func) {
240        self.number(func);
241        let _ = write!(self.out, "func @{}", self.names.resolve(func.name));
242        self.signature(func.signature());
243        self.linkage(func.linkage, func.visibility);
244        if !func.attrs.is_default() {
245            let _ = write!(self.out, ", {}", func.attrs);
246        }
247        self.section(func.section);
248        if func.is_declaration() {
249            self.out.push_str(";\n");
250            return;
251        }
252        self.out.push_str(" {\n");
253        for (index, block) in func.blocks().enumerate() {
254            if index > 0 {
255                self.out.push('\n');
256            }
257            self.block(func, block);
258        }
259        self.out.push_str("}\n");
260    }
261
262    /// Gives every value and every block of a function the number it is printed as.
263    ///
264    /// In print order, which is what makes the text a fact about the function's shape rather
265    /// than about which order its tables were filled in.
266    fn number(&mut self, func: &Func) {
267        let counts = func.counts();
268        self.values.clear();
269        self.values.resize(counts.values, u32::MAX);
270        self.blocks.clear();
271        self.blocks.resize(counts.blocks, u32::MAX);
272        let mut next = 0;
273        for (index, block) in func.blocks().enumerate() {
274            self.blocks[block.index()] = index as u32;
275            for &param in &func[block].params {
276                self.values[param.index()] = next;
277                next += 1;
278            }
279            for inst in func.insts(block) {
280                for result in func[inst].results() {
281                    self.values[result.index()] = next;
282                    next += 1;
283                }
284            }
285        }
286    }
287
288    /// The parameter and result types of a function or a call, with what the ABI asks of each.
289    fn signature(&mut self, signature: &Signature) {
290        self.out.push('(');
291        for (index, param) in signature.params.iter().enumerate() {
292            if index > 0 {
293                self.out.push_str(", ");
294            }
295            self.param(param);
296        }
297        if signature.variadic {
298            if !signature.params.is_empty() {
299                self.out.push_str(", ");
300            }
301            self.out.push_str("...");
302        }
303        self.out.push(')');
304        match signature.returns.as_slice() {
305            [] => {}
306            [param] => {
307                self.out.push_str(" -> ");
308                self.param(param);
309            }
310            params => {
311                self.out.push_str(" -> (");
312                for (index, param) in params.iter().enumerate() {
313                    if index > 0 {
314                        self.out.push_str(", ");
315                    }
316                    self.param(param);
317                }
318                self.out.push(')');
319            }
320        }
321    }
322
323    /// One parameter: its type, and what the ABI asks of it when that is anything.
324    fn param(&mut self, param: &Param) {
325        let _ = write!(self.out, "{}", param.ty);
326        let _ = match param.abi {
327            Abi::Plain => Ok(()),
328            Abi::Sext => write!(self.out, " sext"),
329            Abi::Zext => write!(self.out, " zext"),
330            Abi::ByVal { size, align } => write!(self.out, " byval({size}, align {align})"),
331            Abi::Sret { size, align } => write!(self.out, " sret({size}, align {align})"),
332        };
333    }
334
335    /// One block: its label with its parameters, then its instructions.
336    fn block(&mut self, func: &Func, block: Block) {
337        let _ = write!(self.out, "block{}", self.blocks[block.index()]);
338        let params = &func[block].params;
339        if !params.is_empty() {
340            self.out.push('(');
341            for (index, &param) in params.iter().enumerate() {
342                if index > 0 {
343                    self.out.push_str(", ");
344                }
345                self.value(param);
346                let _ = write!(self.out, ": {}", func[param].ty);
347            }
348            self.out.push(')');
349        }
350        self.out.push_str(":\n");
351        for inst in func.insts(block) {
352            self.inst(func, inst);
353        }
354    }
355
356    /// One instruction, indented, on one line.
357    fn inst(&mut self, func: &Func, inst: Inst) {
358        let data = func[inst];
359        self.out.push_str("    ");
360        for (index, result) in data.results().enumerate() {
361            if index > 0 {
362                self.out.push_str(", ");
363            }
364            self.value(result);
365        }
366        if data.results > 0 {
367            self.out.push_str(" = ");
368        }
369        self.out.push_str(data.opcode.name());
370        self.result_types(func, &data);
371        let _ = write!(self.out, "{}", data.flags);
372        self.operands(func, &data);
373        self.out.push('\n');
374    }
375
376    /// The type suffix, where the operands do not already say what the result is.
377    fn result_types(&mut self, func: &Func, data: &InstData) {
378        let results: Vec<Value> = data.results().collect();
379        match results.as_slice() {
380            [] => {}
381            _ if implied_result(data.opcode) => {}
382            [result] => {
383                let ty = func[*result].ty;
384                let takes_the_same = func[data.args].first().is_some_and(|&arg| func[arg].ty == ty);
385                if !takes_the_same {
386                    let _ = write!(self.out, ".{ty}");
387                }
388            }
389            // The handful that produce two. Both are written, because neither of them follows
390            // from the operands in a way worth remembering a rule for.
391            types => {
392                self.out.push_str(".(");
393                for (index, &result) in types.iter().enumerate() {
394                    if index > 0 {
395                        self.out.push_str(", ");
396                    }
397                    let _ = write!(self.out, "{}", func[result].ty);
398                }
399                self.out.push(')');
400            }
401        }
402    }
403
404    /// Everything to the right of the opcode.
405    fn operands(&mut self, func: &Func, data: &InstData) {
406        let args = &func[data.args];
407        match data.extra {
408            Extra::None => self.value_list_spaced(args),
409            Extra::Imm(imm) => {
410                self.out.push(' ');
411                let ty = data.first_result.map_or(Type::VOID, |result| func[result].ty);
412                self.imm(func[imm], ty);
413            }
414            Extra::Symbol(symbol) => {
415                let _ = write!(self.out, " @{}", self.names.resolve(symbol));
416                if !args.is_empty() {
417                    self.out.push('(');
418                    self.value_list(args);
419                    self.out.push(')');
420                }
421            }
422            Extra::IntPred(pred) => {
423                let _ = write!(self.out, " {}", pred.name());
424                self.value_list_spaced(args);
425            }
426            Extra::FloatPred(pred) => {
427                let _ = write!(self.out, " {}", pred.name());
428                self.value_list_spaced(args);
429            }
430            Extra::Mem(mem) => {
431                match (data.opcode, args) {
432                    // A store reads left to right like the assignment it came from, which is
433                    // worth one special case in the printer and one in the parser.
434                    (Opcode::Store | Opcode::AtomicStore, [value, addr]) => {
435                        self.out.push(' ');
436                        self.value(*value);
437                        self.out.push_str(" -> ");
438                        self.value(*addr);
439                    }
440                    _ => self.value_list_spaced(args),
441                }
442                self.mem(func[mem]);
443            }
444            Extra::Rmw(op, mem) => {
445                let _ = write!(self.out, " {}", op.name());
446                self.value_list_spaced(args);
447                self.mem(func[mem]);
448            }
449            Extra::Order(order) => {
450                let _ = write!(self.out, " {}", order.name());
451            }
452            Extra::Targets(targets) => {
453                // A conditional branch names its condition first and then both arms. A jump
454                // has no operands at all and is its target.
455                if !args.is_empty() {
456                    self.value_list_spaced(args);
457                    self.out.push(',');
458                }
459                for (index, &call) in func[targets].iter().enumerate() {
460                    self.out.push_str(if index > 0 { ", " } else { " " });
461                    self.block_call(func, call);
462                }
463            }
464            Extra::Call(call) => {
465                let info = func[call];
466                let rest = match info.callee {
467                    Some(callee) => {
468                        let _ = write!(self.out, " @{}", self.names.resolve(callee));
469                        args
470                    }
471                    // An indirect call takes the address it calls as its first operand, and
472                    // the rest are the arguments.
473                    None => {
474                        self.out.push(' ');
475                        match args.split_first() {
476                            Some((&addr, rest)) => {
477                                self.value(addr);
478                                rest
479                            }
480                            None => {
481                                self.out.push_str("%?");
482                                &[]
483                            }
484                        }
485                    }
486                };
487                self.out.push('(');
488                self.value_list(rest);
489                self.out.push_str(") : ");
490                self.signature(&func[info.signature]);
491            }
492            Extra::Switch(switch) => {
493                let info = func[switch];
494                let ty = args.first().map_or(Type::VOID, |&arg| func[arg].ty);
495                self.value_list_spaced(args);
496                if let Some((&default, cases)) = func[info.targets].split_first() {
497                    self.out.push_str(", ");
498                    self.block_call(func, default);
499                    self.out.push_str(", [");
500                    for (index, (&case, &value)) in cases.iter().zip(&func[info.cases]).enumerate()
501                    {
502                        if index > 0 {
503                            self.out.push_str(", ");
504                        }
505                        self.imm(value, ty);
506                        self.out.push_str(" => ");
507                        self.block_call(func, case);
508                    }
509                    self.out.push(']');
510                }
511            }
512            Extra::Asm(asm) => {
513                let info = func[asm];
514                self.out.push(' ');
515                self.string(self.names.resolve(info.template).as_bytes());
516                self.out.push_str(", ");
517                self.string(self.names.resolve(info.constraints).as_bytes());
518                self.out.push_str(", ");
519                self.string(self.names.resolve(info.clobbers).as_bytes());
520                self.out.push('(');
521                self.value_list(args);
522                self.out.push(')');
523                if !info.targets.is_empty() {
524                    self.out.push_str(", labels [");
525                    for (index, &call) in func[info.targets].iter().enumerate() {
526                        if index > 0 {
527                            self.out.push_str(", ");
528                        }
529                        self.block_call(func, call);
530                    }
531                    self.out.push(']');
532                }
533            }
534        }
535    }
536
537    /// The operands, separated by commas, with a leading space when there are any.
538    fn value_list_spaced(&mut self, args: &[Value]) {
539        if args.is_empty() {
540            return;
541        }
542        self.out.push(' ');
543        self.value_list(args);
544    }
545
546    /// The operands, separated by commas, with nothing in front.
547    fn value_list(&mut self, args: &[Value]) {
548        for (index, &arg) in args.iter().enumerate() {
549            if index > 0 {
550                self.out.push_str(", ");
551            }
552            self.value(arg);
553        }
554    }
555
556    /// A branch target, with the values it passes.
557    fn block_call(&mut self, func: &Func, call: BlockCall) {
558        let _ = write!(self.out, "block{}", self.blocks[call.block.index()]);
559        let args = &func[call.args];
560        if !args.is_empty() {
561            self.out.push('(');
562            self.value_list(args);
563            self.out.push(')');
564        }
565    }
566
567    /// What an access carries beyond its address.
568    fn mem(&mut self, info: MemInfo) {
569        if info.size != 0 {
570            let _ = write!(self.out, ", size {}", info.size);
571        }
572        let _ = write!(self.out, ", align {}", info.align);
573        if info.order != MemOrder::NotAtomic {
574            let _ = write!(self.out, ", {}", info.order.name());
575        }
576        if let Some(tbaa) = info.tbaa {
577            let _ = write!(self.out, ", tbaa !{}", tbaa.index());
578        }
579    }
580
581    /// One value, as the number it was given in print order.
582    fn value(&mut self, value: Value) {
583        match self.values.get(value.index()).copied() {
584            Some(number) if number != u32::MAX => {
585                let _ = write!(self.out, "%{number}");
586            }
587            // A use with no definition anywhere ahead of it. The verifier turns this down, and
588            // printing something rather than panicking is what makes the printer usable for
589            // finding out why.
590            _ => self.out.push_str("%?"),
591        }
592    }
593
594    /// One constant, read as the type it is a constant of.
595    fn imm(&mut self, imm: Imm, ty: Type) {
596        let scalar = if ty.is_vector() { ty.lane() } else { ty };
597        if scalar.is_float() {
598            // The bit pattern, because a decimal that reads back as the same value needs a
599            // printer this compiler has not written yet, and because a NaN payload survives.
600            let _ = write!(self.out, "{:#x}", imm.bits());
601        } else if scalar.is_int() {
602            let _ = write!(self.out, "{}", imm.signed(scalar));
603        } else {
604            let _ = write!(self.out, "{:#x}", imm.bits());
605        }
606    }
607
608    /// One metadata node, on one line.
609    fn meta_node(&mut self, meta: Meta) {
610        let node = self.module[meta];
611        let _ = write!(self.out, "!{} = tbaa ", meta.index());
612        self.string(self.names.resolve(node.name).as_bytes());
613        if let Some(parent) = node.parent {
614            let _ = write!(self.out, ", parent !{}", parent.index());
615        }
616        let _ = write!(self.out, ", offset {}", node.offset);
617        self.out.push('\n');
618    }
619
620    /// The linkage and, where it is not the ordinary one, the visibility.
621    fn linkage(&mut self, linkage: Linkage, visibility: Visibility) {
622        let _ = write!(self.out, ", linkage({})", linkage.name());
623        if visibility != Visibility::Default {
624            let _ = write!(self.out, ", visibility({})", visibility.name());
625        }
626    }
627
628    /// The section, where one was asked for.
629    fn section(&mut self, section: Option<Symbol>) {
630        if let Some(section) = section {
631            self.out.push_str(", section ");
632            self.string(self.names.resolve(section).as_bytes());
633        }
634    }
635
636    /// A byte string, quoted, with everything outside printable ASCII in hexadecimal.
637    fn string(&mut self, bytes: &[u8]) {
638        self.out.push('"');
639        for &byte in bytes {
640            match byte {
641                b'"' => self.out.push_str("\\\""),
642                b'\\' => self.out.push_str("\\\\"),
643                0x20..=0x7e => self.out.push(byte as char),
644                _ => {
645                    let _ = write!(self.out, "\\{byte:02x}");
646                }
647            }
648        }
649        self.out.push('"');
650    }
651}
652
653#[cfg(test)]
654mod tests {
655    use rucc_base::Interner;
656    use rucc_target::{Arch, Env, Os, TargetInfo, Triple};
657
658    use super::*;
659    use crate::func::Builder;
660    use crate::inst::{AsmInfo, CallInfo, MetaNode, SwitchInfo};
661    use crate::module::{AliasKind, TlsModel};
662    use crate::{AttrSet, Attrs, Flags, FloatPred, FpContract, IntPred, RmwOp};
663
664    fn target() -> TargetInfo {
665        TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
666    }
667
668    #[test]
669    fn the_example_in_the_spec() {
670        let mut names = Interner::new();
671        let mut module = Module::new(names.intern("example.c"), &target());
672
673        let char_node = module.add_meta(MetaNode {
674            name: names.intern("omnipotent char"),
675            parent: None,
676            offset: 0,
677        });
678        let int_node = module.add_meta(MetaNode {
679            name: names.intern("int"),
680            parent: Some(char_node),
681            offset: 0,
682        });
683
684        let i32_ = Type::int(32);
685        let zero_bits = module.add_imm(Imm::int(0, i32_));
686        let init = module.push_data(&[Datum::Scalar { ty: i32_, value: zero_bits }]);
687        let mut counter = Global::new(names.intern("counter"), 4, 4);
688        counter.linkage = Linkage::Internal;
689        counter.init = Some(init);
690        module.add_global(counter);
691
692        let mut func = Func::new(
693            names.intern("sum"),
694            Signature::new().with_params(&[i32_]).with_returns(&[i32_]),
695        );
696        func.attrs = Attrs { set: AttrSet::NOUNWIND, fp_contract: FpContract::On };
697        let entry = func.create_block();
698        let n = func.append_param(entry, i32_);
699        let header = func.create_block();
700        let acc = func.append_param(header, i32_);
701        let i = func.append_param(header, i32_);
702        let exit = func.create_block();
703        let result = func.append_param(exit, i32_);
704
705        let mut b = Builder::new(&mut func, entry);
706        let zero = b.iconst(i32_, 0);
707        let cmp = b.icmp(IntPred::Sle, n, zero);
708        b.br_if(cmp, exit, &[zero], header, &[zero, zero]);
709
710        let mut b = Builder::new(&mut func, header);
711        let one = b.iconst(i32_, 1);
712        let next = b.binary(Opcode::Add, i, one, Flags::NSW);
713        let total = b.binary(Opcode::Add, acc, next, Flags::NSW);
714        let done = b.icmp(IntPred::Sge, next, n);
715        b.br_if(done, exit, &[total], header, &[total, next]);
716
717        let mut b = Builder::new(&mut func, exit);
718        let address = b.value(
719            InstData {
720                extra: Extra::Symbol(names.intern("counter")),
721                ..InstData::new(Opcode::GlobalAddr)
722            },
723            Type::PTR,
724        );
725        b.store(
726            result,
727            address,
728            MemInfo { size: 0, align: 4, order: MemOrder::NotAtomic, tbaa: Some(int_node) },
729            Flags::NONE,
730        );
731        b.ret(&[result]);
732        module.add_func(func);
733
734        assert_eq!(print(&module, &names), crate::fixtures::EXAMPLE);
735    }
736
737    #[test]
738    fn one_of_almost_everything() {
739        let mut names = Interner::new();
740        let mut module = Module::new(names.intern("zoo.c"), &target());
741        let int_node =
742            module.add_meta(MetaNode { name: names.intern("int"), parent: None, offset: 0 });
743
744        let i32_ = Type::int(32);
745        let i64_ = Type::int(64);
746        let f64_ = Type::float(crate::Float::F64);
747        let mut func = Func::new(
748            names.intern("zoo"),
749            Signature::new().with_params(&[i32_, Type::PTR]).with_returns(&[i32_]),
750        );
751        let entry = func.create_block();
752        let n = func.append_param(entry, i32_);
753        let p = func.append_param(entry, Type::PTR);
754        let middle = func.create_block();
755        let other = func.create_block();
756        let exit = func.create_block();
757        let taken = func.append_param(exit, i32_);
758        let arrival = func.create_block();
759
760        let mut b = Builder::new(&mut func, entry);
761        let minus_one = b.iconst(i64_, -1);
762        let half = b.fconst(f64_, 0x3ff8_0000_0000_0000);
763        let seven = b.func().add_imm(Imm::int(7, i32_));
764        let vector = b.value(
765            InstData { extra: Extra::Imm(seven), ..InstData::new(Opcode::Splat) },
766            Type::vector(i32_, 4),
767        );
768        let stack = b.func().add_mem(MemInfo {
769            size: 16,
770            align: 8,
771            order: MemOrder::NotAtomic,
772            tbaa: None,
773        });
774        let slot = b.value(
775            InstData { extra: Extra::Mem(stack), ..InstData::new(Opcode::Alloca) },
776            Type::PTR,
777        );
778        let args = b.func().push_values(&[slot, minus_one]);
779        let addr = b.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR);
780        let plain = MemInfo { size: 0, align: 4, order: MemOrder::NotAtomic, tbaa: Some(int_node) };
781        let loaded = b.load(i32_, addr, plain, Flags::NONE);
782        b.store(loaded, addr, plain, Flags::VOLATILE);
783
784        let atomic =
785            b.func().add_mem(MemInfo { size: 0, align: 4, order: MemOrder::SeqCst, tbaa: None });
786        let args = b.func().push_values(&[addr, n]);
787        let old = b.value(
788            InstData {
789                args,
790                extra: Extra::Rmw(RmwOp::Add, atomic),
791                ..InstData::new(Opcode::AtomicRmw)
792            },
793            i32_,
794        );
795        let args = b.func().push_values(&[addr, old, n]);
796        b.inst(
797            InstData { args, extra: Extra::Mem(atomic), ..InstData::new(Opcode::Cmpxchg) },
798            &[i32_, Type::I1],
799        );
800        b.inst(
801            InstData { extra: Extra::Order(MemOrder::SeqCst), ..InstData::new(Opcode::Fence) },
802            &[],
803        );
804        b.unary(Opcode::SExt, n, i64_);
805        b.fcmp(FloatPred::Oeq, half, half, Flags::NONE);
806        let args = b.func().push_values(&[n, n]);
807        b.inst(InstData { args, ..InstData::new(Opcode::SAddOverflow) }, &[i32_, Type::I1]);
808        let puts = b.func().add_signature(
809            Signature::new().with_params(&[Type::PTR]).with_returns(&[i32_]).variadic(),
810        );
811        b.call(names.intern("puts"), puts, &[p]);
812        let indirect =
813            b.func().add_signature(Signature::new().with_params(&[i32_]).with_returns(&[i32_]));
814        let info = b.func().add_call(CallInfo { callee: None, signature: indirect });
815        let args = b.func().push_values(&[p, n]);
816        b.value(
817            InstData { args, extra: Extra::Call(info), ..InstData::new(Opcode::CallIndirect) },
818            i32_,
819        );
820        let copy = b.func().add_mem(MemInfo {
821            size: 16,
822            align: 8,
823            order: MemOrder::NotAtomic,
824            tbaa: None,
825        });
826        let args = b.func().push_values(&[slot, p]);
827        b.inst(InstData { args, extra: Extra::Mem(copy), ..InstData::new(Opcode::Memcpy) }, &[]);
828        let asm = b.func().add_asm(AsmInfo {
829            template: names.intern("pause"),
830            constraints: names.intern(""),
831            clobbers: names.intern("memory"),
832            targets: crate::inst::BlockCallList::EMPTY,
833        });
834        b.inst(
835            InstData {
836                flags: Flags::VOLATILE,
837                extra: Extra::Asm(asm),
838                ..InstData::new(Opcode::InlineAsm)
839            },
840            &[],
841        );
842        let args = b.func().push_values(&[vector]);
843        b.value(
844            InstData {
845                args,
846                extra: Extra::Symbol(names.intern("x86.sse2.pmovmskb")),
847                ..InstData::new(Opcode::TargetIntrinsic)
848            },
849            i32_,
850        );
851        b.jump(middle, &[]);
852
853        let mut b = Builder::new(&mut func, middle);
854        let cases = b.func().push_imms(&[Imm::int(0, i32_), Imm::int(-1, i32_)]);
855        let default = BlockCall { block: other, args: crate::inst::ValueList::EMPTY };
856        let first = BlockCall { block: exit, args: b.func().push_values(&[n]) };
857        let second = BlockCall { block: other, args: crate::inst::ValueList::EMPTY };
858        let targets = b.func().push_block_calls(&[default, first, second]);
859        let switch = b.func().add_switch(SwitchInfo { targets, cases });
860        let args = b.func().push_values(&[n]);
861        b.inst(
862            InstData { args, extra: Extra::Switch(switch), ..InstData::new(Opcode::Switch) },
863            &[],
864        );
865
866        let mut b = Builder::new(&mut func, other);
867        let address = b.block_addr(arrival);
868        b.indirect_br(address, &[arrival]);
869
870        let mut b = Builder::new(&mut func, exit);
871        b.ret(&[taken]);
872
873        let mut b = Builder::new(&mut func, arrival);
874        let call = BlockCall { block: exit, args: b.func().push_values(&[n]) };
875        let targets = b.func().push_block_calls(&[call]);
876        let goto = b.func().add_asm(AsmInfo {
877            template: names.intern("jmp %l0"),
878            constraints: names.intern(""),
879            clobbers: names.intern(""),
880            targets,
881        });
882        b.inst(InstData { extra: Extra::Asm(goto), ..InstData::new(Opcode::InlineAsm) }, &[]);
883
884        module.add_func(func);
885
886        assert_eq!(print(&module, &names), crate::fixtures::ZOO);
887    }
888
889    #[test]
890    fn the_shapes_a_symbol_comes_in() {
891        let mut names = Interner::new();
892        let mut module = Module::new(names.intern("data.c"), &target());
893
894        let i32_ = Type::int(32);
895        let text = module.push_bytes(b"hi\x00\xff\"\\");
896        let entry_name = names.intern("hi.str");
897        let forward = module.add_reloc(Reloc { symbol: entry_name, addend: 8, size: 8 });
898        let backward = module.add_reloc(Reloc { symbol: entry_name, addend: -8, size: 8 });
899        let seven = module.add_imm(Imm::int(7, i32_));
900        let image = module.push_data(&[
901            Datum::Bytes(text),
902            Datum::Zero(2),
903            Datum::Scalar { ty: i32_, value: seven },
904            Datum::Addr(forward),
905            Datum::Addr(backward),
906        ]);
907        let mut table = Global::new(names.intern("table"), 28, 8);
908        table.init = Some(image);
909        table.constant = true;
910        table.section = Some(names.intern(".rodata.rel"));
911        module.add_global(table);
912
913        let mut errno = Global::new(names.intern("errno"), 4, 4);
914        errno.tls = Some(TlsModel::InitialExec);
915        errno.visibility = Visibility::Hidden;
916        module.add_global(errno);
917
918        let mut alias = Alias::new(names.intern("total"), names.intern("table"));
919        alias.linkage = Linkage::Weak;
920        module.add_alias(alias);
921        let mut memcpy = Alias::new(names.intern("memcpy"), names.intern("memcpy.resolve"));
922        memcpy.kind = AliasKind::IFunc;
923        memcpy.visibility = Visibility::Protected;
924        module.add_alias(memcpy);
925
926        let mut puts = Func::new(
927            names.intern("puts"),
928            Signature::new().with_params(&[Type::PTR]).with_returns(&[i32_]),
929        );
930        puts.linkage = Linkage::External;
931        puts.attrs.set = AttrSet::NOUNWIND | AttrSet::WILLRETURN;
932        module.add_func(puts);
933
934        let mut helper =
935            Func::new(names.intern("helper"), Signature::new().with_returns(&[i32_, i32_]));
936        helper.linkage = Linkage::Internal;
937        helper.section = Some(names.intern(".text.hot"));
938        helper.attrs.set = AttrSet::READNONE | AttrSet::ALWAYS_INLINE;
939        let block = helper.create_block();
940        let mut b = Builder::new(&mut helper, block);
941        let one = b.iconst(i32_, 1);
942        b.ret(&[one, one]);
943        module.add_func(helper);
944
945        assert_eq!(print(&module, &names), crate::fixtures::SYMBOLS);
946    }
947
948    #[test]
949    fn a_signature_writes_what_the_abi_asks_of_each_parameter() {
950        let mut names = Interner::new();
951        let module = Module::new(names.intern("abi.c"), &target());
952        let mut func = Func::new(
953            names.intern("f"),
954            Signature::new()
955                .and_param(Param::with_abi(Type::PTR, Abi::Sret { size: 24, align: 8 }))
956                .and_param(Param::with_abi(Type::PTR, Abi::ByVal { size: 16, align: 8 }))
957                .and_param(Param::with_abi(Type::int(8), Abi::Zext))
958                .and_param(Param::new(Type::int(32))),
959        );
960        let entry = func.create_block();
961        for param in [Type::PTR, Type::PTR, Type::int(8), Type::int(32)] {
962            func.append_param(entry, param);
963        }
964        let mut b = Builder::new(&mut func, entry);
965        b.ret(&[]);
966
967        assert_eq!(
968            print_func(&module, &func, &names),
969            "\
970func @f(ptr sret(24, align 8), ptr byval(16, align 8), i8 zext, i32), linkage(external) {
971block0(%0: ptr, %1: ptr, %2: i8, %3: i32):
972    return
973}
974"
975        );
976    }
977
978    #[test]
979    fn numbering_follows_the_text_and_not_the_tables() {
980        // The blocks are laid out entry, middle, exit, and their contents are built in the
981        // opposite order, so every index in the tables runs against the order they print in.
982        // The numbers in the text have to come out in reading order anyway, because that is
983        // what makes printing a module, parsing it and printing it again give the same bytes.
984        let mut names = Interner::new();
985        let mut module = Module::new(names.intern("order.c"), &target());
986        let i32_ = Type::int(32);
987        let mut func = Func::new(names.intern("f"), Signature::new().with_returns(&[i32_]));
988        let entry = func.create_block();
989        let middle = func.create_block();
990        let exit = func.create_block();
991        let arrived = func.append_param(exit, i32_);
992
993        let mut b = Builder::new(&mut func, exit);
994        b.ret(&[arrived]);
995        let mut b = Builder::new(&mut func, middle);
996        let two = b.iconst(i32_, 2);
997        b.jump(exit, &[two]);
998        let mut b = Builder::new(&mut func, entry);
999        b.jump(middle, &[]);
1000        module.add_func(func);
1001
1002        assert_eq!(
1003            print_func(&module, &module[module.funcs().next().unwrap()], &names),
1004            "\
1005func @f() -> i32, linkage(external) {
1006block0:
1007    jump block1
1008
1009block1:
1010    %0 = iconst.i32 2
1011    jump block2(%0)
1012
1013block2(%1: i32):
1014    return %1
1015}
1016"
1017        );
1018    }
1019}