aot_lang/compiler.rs
1//! The compiler that ties the pipeline together: lower each function to object
2//! code, then link the objects into one image.
3
4use crate::encode::encode;
5use crate::error::AotError;
6use alloc::string::String;
7use alloc::vec::Vec;
8use codegen_lang::{Program, compile as lower};
9use ir_lang::Function;
10use linker_lang::{Image, Linker, Object};
11
12/// The section every compiled function's code is placed in.
13const TEXT: &str = ".text";
14
15/// Builds the linker object for one lowered program: its encoded code in the
16/// [`TEXT`] section, with a symbol at the start named for the function.
17fn object_from_program(program: &Program) -> Object {
18 let mut object = Object::new(program.name());
19 object.section(TEXT, encode(program));
20 object.define(program.name(), TEXT, 0);
21 object
22}
23
24/// Ahead-of-time compile a single function into a linked [`Image`], with the
25/// function itself as the entry point.
26///
27/// This is the shortcut for the common case of compiling one function. It lowers
28/// `func` to object code, links it at base address `0`, and sets the image's entry
29/// to the function. Reach for [`Compiler`] when you have several functions to place
30/// in one image, or need to choose the base address or entry point.
31///
32/// # Parameters
33///
34/// - `func` — the function to compile, in SSA form as produced by `ir_lang::Builder`.
35/// It is validated during lowering.
36///
37/// # Errors
38///
39/// Returns [`AotError::Codegen`] if `func` does not pass `Function::validate`. The
40/// link step cannot fail for a single well-formed function, so no [`AotError::Link`]
41/// arises here.
42///
43/// # Examples
44///
45/// Compile a function and read back its code and entry point:
46///
47/// ```
48/// use aot_lang::compile;
49/// use ir_lang::{Builder, BinOp, Type};
50///
51/// // fn square(x: int) -> int { x * x }
52/// let mut b = Builder::new("square", &[Type::Int], Type::Int);
53/// let x = b.block_params(b.entry())[0];
54/// let sq = b.bin(BinOp::Mul, x, x);
55/// b.ret(Some(sq));
56///
57/// let image = compile(&b.finish()).expect("square is well-formed");
58///
59/// // The entry point is `square`, laid out at the default base address of 0.
60/// assert_eq!(image.entry(), Some(0));
61/// assert_eq!(image.symbol("square"), Some(0));
62/// // Its object code is the `.text` section's bytes.
63/// assert!(!image.section(".text").unwrap().data().is_empty());
64/// ```
65///
66/// A function that does not validate is rejected:
67///
68/// ```
69/// use aot_lang::{compile, AotError};
70/// use ir_lang::{Builder, Type};
71///
72/// // Promises an int return but never returns one.
73/// let func = Builder::new("bad", &[], Type::Int).finish();
74/// assert!(matches!(compile(&func), Err(AotError::Codegen(_))));
75/// ```
76pub fn compile(func: &Function) -> Result<Image, AotError> {
77 let program = lower(func)?;
78 let image = Linker::new()
79 .entry(program.name())
80 .link(&[object_from_program(&program)])?;
81 Ok(image)
82}
83
84/// Accumulates functions and links them into one image.
85///
86/// A `Compiler` compiles each function you [`add`](Compiler::add) to object code
87/// and holds it; [`link`](Compiler::link) places them all in a single [`Image`].
88/// This is the multi-function counterpart to the free [`compile`] function: use it
89/// to build an image out of several functions, to choose the
90/// [`base_address`](Compiler::base_address) the image is laid out at, or to name the
91/// [`entry`](Compiler::entry) point.
92///
93/// Functions are laid out in the order they are added. Each defines a symbol under
94/// its own name, so two functions with the same name collide — the link then fails
95/// with [`AotError::Link`].
96///
97/// Implements [`Default`], equivalent to [`Compiler::new`]: base address `0`, no
98/// entry point.
99///
100/// # Examples
101///
102/// Place two functions in one image, based at a load address, with a chosen entry
103/// point:
104///
105/// ```
106/// use aot_lang::Compiler;
107/// use ir_lang::{Builder, BinOp, Type};
108///
109/// // fn add(a: int, b: int) -> int { a + b }
110/// let mut add = Builder::new("add", &[Type::Int, Type::Int], Type::Int);
111/// let a = add.block_params(add.entry())[0];
112/// let b = add.block_params(add.entry())[1];
113/// let sum = add.bin(BinOp::Add, a, b);
114/// add.ret(Some(sum));
115///
116/// // fn one() -> int { 1 }
117/// let mut one = Builder::new("one", &[], Type::Int);
118/// let c = one.iconst(1);
119/// one.ret(Some(c));
120///
121/// let mut compiler = Compiler::new();
122/// compiler.base_address(0x40_0000).entry("add");
123/// compiler.add(&add.finish()).unwrap();
124/// compiler.add(&one.finish()).unwrap();
125/// let image = compiler.link().unwrap();
126///
127/// // `add` is the entry, placed first at the base address; `one` follows it.
128/// assert_eq!(image.entry(), Some(0x40_0000));
129/// assert_eq!(image.symbol("add"), Some(0x40_0000));
130/// assert!(image.symbol("one").unwrap() > 0x40_0000);
131/// ```
132#[derive(Default)]
133pub struct Compiler {
134 objects: Vec<Object>,
135 base_address: u64,
136 entry: Option<String>,
137}
138
139impl Compiler {
140 /// Creates an empty compiler with the default configuration: base address `0`
141 /// and no entry point. Same as [`Compiler::default`].
142 #[must_use]
143 pub fn new() -> Self {
144 Self::default()
145 }
146
147 /// Sets the address the image is laid out from. The first function is placed
148 /// here and the rest follow; every resolved symbol address is relative to it.
149 ///
150 /// # Examples
151 ///
152 /// ```
153 /// use aot_lang::Compiler;
154 /// use ir_lang::{Builder, Type};
155 ///
156 /// let mut f = Builder::new("f", &[], Type::Unit);
157 /// f.ret(None);
158 ///
159 /// let mut compiler = Compiler::new();
160 /// compiler.base_address(0x1000);
161 /// compiler.add(&f.finish()).unwrap();
162 /// assert_eq!(compiler.link().unwrap().symbol("f"), Some(0x1000));
163 /// ```
164 pub fn base_address(&mut self, addr: u64) -> &mut Self {
165 self.base_address = addr;
166 self
167 }
168
169 /// Sets the symbol whose address becomes the image's entry point. The symbol
170 /// must name a function that is added before [`link`](Compiler::link), or the
171 /// link fails with [`AotError::Link`].
172 ///
173 /// # Examples
174 ///
175 /// ```
176 /// use aot_lang::Compiler;
177 /// use ir_lang::{Builder, Type};
178 ///
179 /// let mut main = Builder::new("main", &[], Type::Unit);
180 /// main.ret(None);
181 ///
182 /// let mut compiler = Compiler::new();
183 /// compiler.entry("main");
184 /// compiler.add(&main.finish()).unwrap();
185 /// assert_eq!(compiler.link().unwrap().entry(), Some(0));
186 /// ```
187 pub fn entry(&mut self, symbol: impl Into<String>) -> &mut Self {
188 self.entry = Some(symbol.into());
189 self
190 }
191
192 /// Compiles `func` to object code and appends it to the image being built.
193 ///
194 /// The function is lowered immediately, so a malformed one is reported here
195 /// rather than deferred to [`link`](Compiler::link). Functions keep the order in
196 /// which they are added, which is the order they are placed in the image.
197 ///
198 /// # Errors
199 ///
200 /// Returns [`AotError::Codegen`] if `func` does not pass `Function::validate`.
201 ///
202 /// # Examples
203 ///
204 /// ```
205 /// use aot_lang::Compiler;
206 /// use ir_lang::{Builder, Type};
207 ///
208 /// let mut compiler = Compiler::new();
209 /// for name in ["a", "b", "c"] {
210 /// let mut f = Builder::new(name, &[], Type::Unit);
211 /// f.ret(None);
212 /// compiler.add(&f.finish()).unwrap();
213 /// }
214 /// assert_eq!(compiler.link().unwrap().symbols().count(), 3);
215 /// ```
216 pub fn add(&mut self, func: &Function) -> Result<&mut Self, AotError> {
217 let program = lower(func)?;
218 self.objects.push(object_from_program(&program));
219 Ok(self)
220 }
221
222 /// Links every added function into one [`Image`].
223 ///
224 /// The functions are placed end to end from the [base
225 /// address](Compiler::base_address), each defining a symbol under its name, and
226 /// the [entry point](Compiler::entry) — if one was set — resolves to its address.
227 ///
228 /// # Errors
229 ///
230 /// Returns [`AotError::Link`] if two functions share a name
231 /// (`LinkError::DuplicateSymbol`), or if the configured entry point was never
232 /// added (`LinkError::UndefinedEntry`).
233 ///
234 /// # Examples
235 ///
236 /// ```
237 /// use aot_lang::Compiler;
238 /// use ir_lang::{Builder, Type};
239 ///
240 /// let mut f = Builder::new("f", &[], Type::Unit);
241 /// f.ret(None);
242 ///
243 /// let mut compiler = Compiler::new();
244 /// compiler.add(&f.finish()).unwrap();
245 /// let image = compiler.link().unwrap();
246 /// assert_eq!(image.len(), 1); // one `.text` section
247 /// ```
248 pub fn link(&self) -> Result<Image, AotError> {
249 let mut linker = Linker::new().base_address(self.base_address);
250 if let Some(entry) = &self.entry {
251 linker = linker.entry(entry);
252 }
253 linker.link(&self.objects).map_err(AotError::from)
254 }
255}
256
257#[cfg(test)]
258#[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
259mod tests {
260 use super::*;
261 use crate::AotError;
262 use ir_lang::{BinOp, Builder, Type};
263 use linker_lang::LinkError;
264
265 fn unit_fn(name: &str) -> Function {
266 let mut b = Builder::new(name, &[], Type::Unit);
267 b.ret(None);
268 b.finish()
269 }
270
271 #[test]
272 fn test_compile_single_sets_entry_to_the_function() {
273 let mut b = Builder::new("inc", &[Type::Int], Type::Int);
274 let x = b.block_params(b.entry())[0];
275 let one = b.iconst(1);
276 let y = b.bin(BinOp::Add, x, one);
277 b.ret(Some(y));
278
279 let image = compile(&b.finish()).unwrap();
280 assert_eq!(image.entry(), Some(0));
281 assert_eq!(image.symbol("inc"), Some(0));
282 assert!(!image.section(".text").unwrap().data().is_empty());
283 }
284
285 #[test]
286 fn test_compile_rejects_invalid_function() {
287 let func = Builder::new("bad", &[], Type::Int).finish();
288 assert!(matches!(compile(&func), Err(AotError::Codegen(_))));
289 }
290
291 #[test]
292 fn test_compiler_lays_out_functions_in_add_order() {
293 let mut compiler = Compiler::new();
294 compiler.base_address(0x2000).entry("first");
295 compiler.add(&unit_fn("first")).unwrap();
296 compiler.add(&unit_fn("second")).unwrap();
297
298 let image = compiler.link().unwrap();
299 assert_eq!(image.entry(), Some(0x2000));
300 assert_eq!(image.symbol("first"), Some(0x2000));
301 assert!(image.symbol("second").unwrap() > 0x2000);
302 }
303
304 #[test]
305 fn test_compiler_duplicate_name_is_link_error() {
306 let mut compiler = Compiler::new();
307 compiler.add(&unit_fn("dup")).unwrap();
308 compiler.add(&unit_fn("dup")).unwrap();
309
310 match compiler.link() {
311 Err(AotError::Link(LinkError::DuplicateSymbol { name })) => assert_eq!(name, "dup"),
312 other => panic!("expected duplicate-symbol link error, got {other:?}"),
313 }
314 }
315
316 #[test]
317 fn test_compiler_undefined_entry_is_link_error() {
318 let mut compiler = Compiler::new();
319 compiler.entry("missing");
320 compiler.add(&unit_fn("present")).unwrap();
321
322 assert!(matches!(
323 compiler.link(),
324 Err(AotError::Link(LinkError::UndefinedEntry { .. }))
325 ));
326 }
327
328 #[test]
329 fn test_compiler_default_has_no_entry() {
330 let mut compiler = Compiler::default();
331 compiler.add(&unit_fn("f")).unwrap();
332 assert_eq!(compiler.link().unwrap().entry(), None);
333 }
334}