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
//! # backend_lang
//!
//! The core of the LexerSketch native backend: a machine IR over virtual and
//! physical registers, target descriptions that say everything the backend
//! needs to know about an ISA as data, calling conventions and frame layout,
//! a parallel-move resolver, and emission to machine code through
//! [`isa_lang`]. No Cranelift, no LLVM, no system assembler.
//!
//! This release (0.2) is the foundation. Instruction selection from ir-lang
//! and SSA register allocation build on it in 0.5; until then MIR is built
//! directly, and emission takes functions whose registers are already
//! physical.
//!
//! ## Quick start
//!
//! [`emit`] is the one-call path: a MIR function on physical registers in,
//! x86-64 machine code out.
//!
//! ```
//! use backend_lang::mir::{Effects, Function, Operand};
//! 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
//!
//! - [`mir`]: [`Function`](mir::Function)s of [`Block`](mir::Block)s of
//! target instructions, with typed virtual registers, stack slots, symbols,
//! explicit implicit operands, 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. An SSA register allocator (0.5) assigns parameters and
//! arguments independently and resolves each edge with the parallel-move
//! resolver; ir-lang already uses block parameters, so instruction selection
//! maps them one to one; and there is no per-block phi list to keep in sync
//! with predecessor order.
//!
//! ## Guarantees
//!
//! - **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. Property tests run
//! it against a parallel-assignment reference, and emitted edges against
//! an interpreter of the emitted machine code.
//! - **Emission is checked.** A function is verified before emission;
//! virtual registers, mismatched tied operands, and writes to the stack or
//! frame pointer are errors, not bad code.
//! - **No reachable panics, no recursion.** Every table is a dense vector,
//! every walk a loop, every fallible path a `Result`.
//!
//! ## Features
//!
//! - `std` (default): no effect beyond isa-lang's; the crate is `no_std`
//! and needs only `alloc`.
extern crate alloc;
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.
;