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