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
//! The [`Backend`] abstraction and the [`Bytecode`] reference backend.
use Function;
use crateCodegenError;
use cratelower;
use crateProgram;
/// A code generator: lowers a function in SSA form to a concrete target representation.
///
/// This is the abstraction the crate is built around. A backend reads the IR a
/// front-end produced and turns it into the form a particular target consumes. The
/// [`Bytecode`] backend shipped here emits a flat [`Program`]; a backend layered on a
/// native code generator later — LLVM, Cranelift — produces that generator's own module
/// type, named by the [`Output`](Backend::Output) associated type. Drawing the boundary
/// as a trait is what lets a front-end be written against codegen without committing to
/// any one target.
///
/// A backend does not own the function and keeps no per-call state, so a single instance
/// compiles many functions and is cheap to pass around and share.
///
/// # Implementing a backend
///
/// A backend's `compile` is responsible for rejecting input it cannot lower. The
/// convention the bytecode backend follows is to call
/// [`Function::validate`](ir_lang::Function::validate) first and return
/// [`CodegenError::InvalidIr`] on failure, so the lowering proper only ever runs on
/// well-formed SSA.
///
/// # Examples
///
/// Compile with the shipped backend through the trait:
///
/// ```
/// use codegen_lang::{Backend, Bytecode};
/// use ir_lang::{Builder, Type};
///
/// let mut b = Builder::new("noop", &[], Type::Unit);
/// b.ret(None);
///
/// let program = Bytecode.compile(&b.finish()).expect("noop is well-formed");
/// assert_eq!(program.name(), "noop");
/// ```
/// The reference backend: lowers a function to a flat, register-based [`Program`].
///
/// `Bytecode` is the concrete target shipped with the crate and the one
/// [`compile`] uses. It validates the function, then lowers each block in order to a
/// linear op stream — small enough to read, serialize, and execute, which makes it the
/// natural target for testing a front-end's output before a native backend exists.
///
/// It is a zero-sized type with no configuration; construct it directly or with
/// [`Default`].
///
/// # Examples
///
/// ```
/// use codegen_lang::{Backend, Bytecode};
/// use ir_lang::{Builder, BinOp, Type};
///
/// // fn inc(x: int) -> int { x + 1 }
/// let mut b = Builder::new("inc", &[Type::Int], Type::Int);
/// let x = b.block_params(b.entry())[0];
/// let one = b.iconst(1);
/// let sum = b.bin(BinOp::Add, x, one);
/// b.ret(Some(sum));
///
/// let program = Bytecode.compile(&b.finish()).expect("inc is well-formed");
/// assert_eq!(program.register_count(), 3); // x, the constant, the sum
/// ```
;
/// Lowers a function to bytecode with the default [`Bytecode`] backend.
///
/// A shortcut for `Bytecode.compile(func)` for the common case where the bytecode target
/// is the one you want. Use [`Bytecode`] through the [`Backend`] trait when a generic
/// over backends is what you need.
///
/// # Errors
///
/// Returns [`CodegenError::InvalidIr`] if `func` is not well-formed SSA.
///
/// # Examples
///
/// ```
/// use codegen_lang::compile;
/// use ir_lang::{Builder, Type, UnOp};
///
/// // fn negate(x: int) -> int { -x }
/// let mut b = Builder::new("negate", &[Type::Int], Type::Int);
/// let x = b.block_params(b.entry())[0];
/// let neg = b.un(UnOp::Neg, x);
/// b.ret(Some(neg));
///
/// let program = compile(&b.finish()).expect("negate is well-formed");
/// assert_eq!(program.name(), "negate");
/// ```