Skip to main content

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}