rucc_mir/lib.rs
1//! The machine IR, still in SSA, and its printer and parser.
2//!
3//! Design: `spec/10-backend.md`. Layer rank 9, see `spec/18-package-layout.md`.
4//!
5//! MIR is the second representation. It is still a control flow graph of blocks and it is still
6//! in SSA form, but the instructions are the target's instructions and the operands are
7//! registers drawn from the target's register classes. Instruction selection produces it, the
8//! allocator rewrites it, and the encoder reads it.
9//!
10//! Nothing in this crate knows what any instruction means. An opcode is a name, an operand is a
11//! register with a class and a role, and that is all a pass over MIR needs in order to move
12//! instructions, split live ranges or insert a spill. What the opcodes mean is in the target's
13//! rule set, which is what the selector was compiled from, and in the encoder, which is
14//! generated from the same description. That is what `spec/10-backend.md` section 10.8 means
15//! when it says no pipeline crate holds target-specific code.
16//!
17//! # The shape of an instruction
18//!
19//! An opcode, an operand vector, an optional immediate, an optional memory addressing mode, and
20//! the symbol it names. Twenty-four bytes. The operands are in one order, the ones the
21//! instruction writes and then the ones it reads, with the registers a memory operand names
22//! last, and [`InstBuilder`] is what keeps them that way.
23//!
24//! Where an instruction goes is on its block rather than on the instruction, in the order the
25//! terminator's own arms run, which is regalloc2's arrangement and the one the allocator
26//! interface in `spec/10-backend.md` section 10.4 follows. Where an instruction came from is a
27//! parallel array, reached by [`Func::span`], for the same reason `rucc-ir` puts it there.
28//!
29//! # Before and after allocation
30//!
31//! ```text
32//! mfunc @scale { mfunc @scale {
33//! block0(%0:gpr, %1:gpr): block0:
34//! %2:gpr = x64.mov_ri 4 $rcx = x64.mov_ri 4
35//! %3:gpr(reuse 1) = x64.imul_rr ... $rax = x64.imul_rr $rax, $rcx
36//! ```
37//!
38//! Both are the same text form, printed by [`print()`] and read by [`parse()`], and both round-trip
39//! byte for byte. That is what `--emit=mir` and `--emit=mir-final` are, and it is what lets a
40//! test of the allocator state its input by writing one down.
41//!
42//! # Status
43//!
44//! The representation, the printer and the parser are here. What comes next in `M3` is the
45//! lowering that produces MIR, the frame layout and the allocator that rewrite it, and the
46//! encoder that reads it. Two things a call needs are deliberately not here yet, because they
47//! belong with the ABI lowering that is the next piece rather than with the representation: the
48//! set of registers a call clobbers, and the stack slots a frame is made of.
49//!
50//! Every crate in the workspace is published, and publishing implies a promise. This one is
51//! tier 3: its Rust API is explicitly unstable and will change without a major version bump.
52//! Depend on the `rucc` binary's behaviour, not on this.
53
54#![doc(html_root_url = "https://docs.rs/rucc-mir/0.3.13")]
55
56#[cfg(test)]
57mod fixtures;
58mod func;
59mod inst;
60mod parse;
61mod print;
62
63pub use func::{Func, InstBuilder, defs};
64pub use inst::{
65 Amode, Block, BlockCall, BlockData, Imm, ImmRef, Inst, InstData, Mem, MemRef, Opcode, Operand,
66 OperandList, Param, Reg,
67};
68// An operand's role and its constraint are a target's description of an instruction before they
69// are anything in the machine IR, so they are written down in `rucc-target` where a target
70// description can reach them. They are still part of this crate's vocabulary, because the
71// machine IR is where every pass reads them.
72pub use parse::{ParseError, parse};
73pub use print::{Printer, print, print_func};
74pub use rucc_target::{Constraint, Role};
75
76/// The milestone in `spec/17-milestones.md` that fills this crate in.
77pub const MILESTONE: &str = "M3";