1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
//! # backend_lang
//!
//! The LexerSketch native backend: instruction selection from ir-lang 2.0,
//! SSA register allocation with exact stack maps, and emission to x86-64
//! machine code through [`isa_lang`], on top of a machine IR over virtual and
//! physical registers, target descriptions that state everything the backend
//! needs to know about an ISA as data, SysV and Win64 calling conventions and
//! frame layout, and a parallel-move resolver. No Cranelift, no LLVM, no
//! system assembler.
//!
//! This release (0.3) compiles the integer, control-flow, memory, call, and
//! checked-arithmetic subset of ir-lang. Floating point waits on isa-lang's
//! SSE forms, AArch64 on its AArch64 forms, and unwind tables on object
//! files that carry them; each is refused with an error that says so, never
//! miscompiled.
//!
//! ## Quick start
//!
//! [`compile`] is the one-call path: an ir-lang module in, machine code with
//! symbols and relocations out.
//!
//! ```
//! use backend_lang::Options;
//! use ir_lang::{Linkage, Module, Overflow, Signature, Type};
//!
//! // fn add(x: i64, y: i64) -> i64 { x + y }
//! let mut m = Module::new("demo");
//! let sig = Signature::new(&[Type::I64, Type::I64], &[Type::I64]);
//! let f = m.declare_function("add", &sig, Linkage::Export)?;
//! let mut b = m.build(f)?;
//! let (x, y) = (b.param(0)?, b.param(1)?);
//! let s = b.add(x, y, Overflow::Wrap)?;
//! b.ret(&[s])?;
//!
//! let c = backend_lang::compile(&m, &Options::sysv().with_frame_pointer(false))?;
//! // SysV: x arrives in rdi, y in rsi; the sum leaves in rax.
//! // add rdi, rsi; mov rax, rdi; ret
//! assert_eq!(c.code(), [0x48, 0x01, 0xF7, 0x48, 0x89, 0xF8, 0xC3]);
//! assert_eq!(c.function("add").map(|f| f.offset()), Some(0));
//! assert_eq!(c.exports(), ["add"]);
//! # Ok::<(), Box<dyn std::error::Error>>(())
//! ```
//!
//! [`emit`] is the lower-level path: a MIR function on physical registers
//! (written by hand, or after [`regalloc::allocate`]) in, machine code out.
//!
//! ```
//! use backend_lang::mir::{Effects, Function};
//! use backend_lang::x86_64::{op, SYSV, RAX, RDI};
//!
//! // fn double(x) -> x + x, SysV: x arrives in rdi, the result leaves in rax.
//! let mut f = Function::new("double", &SYSV);
//! let entry = f.block()?;
//! f.add_param(entry, RDI.into())?;
//! f.push(entry, op::MOV64, &[RAX.into(), RDI.into()])?;
//! f.push(entry, op::ADD64, &[RAX.into(), RAX.into(), RDI.into()])?;
//! f.push_with(entry, op::RET, &[], Effects::new().uses(&[RAX]))?;
//! f.omit_frame_pointer();
//!
//! let code = backend_lang::emit(&f)?;
//! // mov rax, rdi; add rax, rdi; ret
//! assert_eq!(code, [0x48, 0x89, 0xF8, 0x48, 0x01, 0xF8, 0xC3]);
//! # Ok::<(), Box<dyn std::error::Error>>(())
//! ```
//!
//! ## The pieces
//!
//! - [`compile`], [`Options`], [`Compiled`]: the whole pipeline for a module.
//! - [`isel`]: instruction selection, one ir-lang function to MIR on
//! virtual registers.
//! - [`regalloc`]: SSA register allocation, virtual registers to physical
//! registers and stack slots.
//! - [`mir`]: [`Function`](mir::Function)s of [`Block`](mir::Block)s of
//! target instructions, with typed virtual registers, stack slots, symbols,
//! explicit implicit operands, call-site records, and a deterministic text
//! form.
//! - [`target`]: the [`Target`](target::Target) description (registers,
//! classes, instructions with their operand rules and implicit operands),
//! [`abi`](target::abi) (argument and return registers, callee-saved sets,
//! stack alignment, shadow space, red zone, probing), and
//! [`frame`](target::frame) (frame layout and prologue/epilogue plans).
//! - [`moves`]: the parallel-move resolver every parallel copy goes through.
//! - [`x86_64`]: the x86-64 description, SysV and Win64, and the
//! [`Emitter`](x86_64::Emitter).
//!
//! ## Block parameters, not phi nodes
//!
//! Values cross control-flow edges as block parameters: a branch passes
//! arguments, and the target block's parameters receive them all at once.
//! This is the same information as phi nodes, kept at the edge where it is
//! used: the parallel copy a phi implies on each incoming edge is written
//! down on that edge. The register allocator assigns parameters and
//! arguments independently and resolves each edge with the parallel-move
//! resolver; ir-lang uses block parameters too, so instruction selection
//! maps them one to one; and there is no per-block phi list to keep in sync
//! with predecessor order.
//!
//! ## Guarantees
//!
//! - **Compiled code computes what the IR says.** Random ir-lang programs
//! (every integer operation under every policy, error edges, loops,
//! switches, memory, calls, tail calls) are compiled and run against
//! ir-lang's reference interpreter, in an x86-64 interpreter under SysV and
//! Win64 and natively on x86-64 Linux and Windows.
//! - **Stack maps are exact.** At every safepoint the recorded root slots are
//! exactly its GC list (IR.md ยง9), and no reference is in a register while
//! the collector runs; a moving collector in the tests walks real frames by
//! these records.
//! - **Parallel copies are exact.** Every edge, `copy`, and `pcopy` goes
//! through [`moves::MoveResolver`], which handles any permutation, cycles
//! of any length, fan-out, registers and stack slots mixed, and
//! immediates, with the fewest moves under its rules.
//! - **Unsupported is an error, not bad code.** What this release does not
//! select is reported as [`isel::IselError::Unsupported`] naming what it
//! waits on.
//! - **No reachable panics, no recursion.** Every table is a dense vector,
//! every walk a loop, every fallible path a `Result`. Passes are linear in
//! the function (one sort here and there) except liveness, whose cost is
//! the size of the live sets and has a budget
//! ([`regalloc::Options::live_budget`]).
//!
//! ## Features
//!
//! - `std` (default): no effect beyond isa-lang's and ir-lang's; the crate is
//! `no_std` and needs only `alloc`.
extern crate alloc;
pub use ;
pub use ir_lang;
pub use isa_lang;
pub use ;
use Vec;
/// Emits one function to machine code: the one-call path.
///
/// The function must be on physical registers (after register allocation,
/// or written that way). It is verified, its frame is laid out under its
/// calling convention, and every instruction is encoded. Calls to other
/// functions need [`x86_64::Emitter`], which places several functions
/// together; a function may call itself by its own name here.
///
/// # Errors
///
/// An [`x86_64::EmitError`] naming the problem.
///
/// # Examples
///
/// ```
/// use backend_lang::mir::{Function, Operand};
/// use backend_lang::x86_64::{op, SYSV, RAX};
///
/// let mut f = Function::new("one", &SYSV);
/// let b = f.block()?;
/// f.push(b, op::MOV32RI, &[RAX.into(), Operand::Imm(1)])?;
/// f.push(b, op::RET, &[])?;
/// let code = backend_lang::emit(&f)?;
/// assert_eq!(code.last(), Some(&0xC3));
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
/// Compiles and runs the `rust` code blocks in `README.md` and `docs/API.md` as
/// part of `cargo test`, so the published examples cannot drift from the API.
///
/// Present only while collecting doctests (`#[cfg(doctest)]`); it is not part of
/// the public surface and does not appear in the built library or its docs.
;