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