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