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