Skip to main content

bcgen_lang/
output.rs

1//! The result of compiling a unit.
2
3use alloc::vec::Vec;
4
5use alloc::string::String;
6
7use bytecode_lang::{FuncId, GlobalId, Module, TypeId};
8use hir_lang::{ItemId, NodeRef};
9
10/// A compiled unit: the LSB module, its entry points, and the map from every
11/// instruction back to the HIR node it was compiled from.
12///
13/// # Examples
14///
15/// ```
16/// use hir_lang::{Builder, Name};
17/// use intern_lang::Interner;
18///
19/// let mut names = Interner::new();
20/// let mut b = Builder::new();
21/// let one = b.int(1);
22/// let body = b.block(&[], Some(one));
23/// let f = b.func(Name::new(names.intern("one")), &[], body);
24/// let root = b.module(None, &[f]);
25/// let hir = b.finish(root)?;
26///
27/// let out = bcgen_lang::compile(&hir, &names)?;
28/// let func = out.function(f).unwrap();
29/// // Every instruction maps back to a node of the unit.
30/// for pc in 0..out.module().function(func).unwrap().code().len() {
31///     assert!(out.origin(func, pc as u32).is_some());
32/// }
33/// # Ok::<(), Box<dyn std::error::Error>>(())
34/// ```
35#[derive(Clone, Debug, PartialEq, Eq)]
36pub struct Compiled {
37    pub(crate) module: Module,
38    pub(crate) entry: FuncId,
39    pub(crate) start: Option<FuncId>,
40    pub(crate) result: Option<GlobalId>,
41    pub(crate) functions: Vec<(ItemId, FuncId)>,
42    pub(crate) origins: Vec<Vec<NodeRef>>,
43    pub(crate) classes: Vec<(ItemId, TypeId)>,
44    pub(crate) methods: Vec<((ItemId, ItemId), FuncId)>,
45    pub(crate) destructor: Option<String>,
46}
47
48impl Compiled {
49    /// The LSB module.
50    ///
51    /// # Examples
52    ///
53    /// ```
54    /// let hir = { let mut b = hir_lang::Builder::new(); let r = b.module(None, &[]); b.finish(r)? };
55    /// let out = bcgen_lang::compile(&hir, &intern_lang::Interner::new())?;
56    /// assert_eq!(out.module().functions().len(), 1); // the entry
57    /// # Ok::<(), Box<dyn std::error::Error>>(())
58    /// ```
59    #[must_use]
60    pub fn module(&self) -> &Module {
61        &self.module
62    }
63
64    /// The LSB module, by value (to hand to a VM or an encoder).
65    ///
66    /// # Examples
67    ///
68    /// ```
69    /// let hir = { let mut b = hir_lang::Builder::new(); let r = b.module(None, &[]); b.finish(r)? };
70    /// let module = bcgen_lang::compile(&hir, &intern_lang::Interner::new())?.into_module();
71    /// assert!(!bytecode_lang::encode(&module).is_empty());
72    /// # Ok::<(), Box<dyn std::error::Error>>(())
73    /// ```
74    #[must_use]
75    pub fn into_module(self) -> Module {
76        self.module
77    }
78
79    /// The entry function, `() -> dyn`, exported as `<entry>`: it returns the
80    /// value of the module body (nil without one). Under
81    /// [`BodyMode::Start`](crate::BodyMode::Start) the body runs once, in the
82    /// start function, and the entry reads its kept value; under
83    /// [`BodyMode::Entry`](crate::BodyMode::Entry) the entry is the body.
84    ///
85    /// # Examples
86    ///
87    /// ```
88    /// let hir = { let mut b = hir_lang::Builder::new(); let r = b.module(None, &[]); b.finish(r)? };
89    /// let out = bcgen_lang::compile(&hir, &intern_lang::Interner::new())?;
90    /// assert!(out.module().function(out.entry()).is_some());
91    /// # Ok::<(), Box<dyn std::error::Error>>(())
92    /// ```
93    #[must_use]
94    pub fn entry(&self) -> FuncId {
95        self.entry
96    }
97
98    /// The module's start function, if it has one: global and constant
99    /// initializers, defaults evaluated once, and (under `BodyMode::Start`)
100    /// the body.
101    ///
102    /// # Examples
103    ///
104    /// ```
105    /// let hir = { let mut b = hir_lang::Builder::new(); let r = b.module(None, &[]); b.finish(r)? };
106    /// assert_eq!(bcgen_lang::compile(&hir, &intern_lang::Interner::new())?.start(), None);
107    /// # Ok::<(), Box<dyn std::error::Error>>(())
108    /// ```
109    #[must_use]
110    pub fn start(&self) -> Option<FuncId> {
111        self.start
112    }
113
114    /// The global holding the module body's value under `BodyMode::Start`.
115    ///
116    /// # Examples
117    ///
118    /// ```
119    /// let hir = { let mut b = hir_lang::Builder::new(); let r = b.module(None, &[]); b.finish(r)? };
120    /// assert!(bcgen_lang::compile(&hir, &intern_lang::Interner::new())?.result_global().is_none());
121    /// # Ok::<(), Box<dyn std::error::Error>>(())
122    /// ```
123    #[must_use]
124    pub fn result_global(&self) -> Option<GlobalId> {
125        self.result
126    }
127
128    /// The function compiled for a HIR function item: its parameters are
129    /// `dyn` (a by-reference parameter is a `ref cell dyn` holding an LSB
130    /// reference), followed by an `i64` presence mask when any parameter has
131    /// a default; it returns `dyn` (a generator's returns its coroutine). It
132    /// carries the item's LSB parameter list, so it can be called typed
133    /// (`call`, `Vm::run`) or bound dynamically. Root-level functions are
134    /// exported under their names.
135    ///
136    /// # Examples
137    ///
138    /// See [`Compiled`].
139    #[must_use]
140    pub fn function(&self, item: ItemId) -> Option<FuncId> {
141        self.functions
142            .iter()
143            .find(|(i, _)| *i == item)
144            .map(|&(_, f)| f)
145    }
146
147    /// The HIR node instruction `pc` of `func` was compiled from.
148    ///
149    /// # Examples
150    ///
151    /// See [`Compiled`].
152    #[must_use]
153    pub fn origin(&self, func: FuncId, pc: u32) -> Option<NodeRef> {
154        self.origins
155            .get(func.index())
156            .and_then(|o| o.get(pc as usize))
157            .copied()
158    }
159
160    /// The HIR node of every instruction of `func`, indexed by pc.
161    ///
162    /// # Examples
163    ///
164    /// ```
165    /// let hir = { let mut b = hir_lang::Builder::new(); let r = b.module(None, &[]); b.finish(r)? };
166    /// let out = bcgen_lang::compile(&hir, &intern_lang::Interner::new())?;
167    /// assert_eq!(out.origins(out.entry()).len(), out.module().function(out.entry()).unwrap().code().len());
168    /// # Ok::<(), Box<dyn std::error::Error>>(())
169    /// ```
170    #[must_use]
171    pub fn origins(&self, func: FuncId) -> &[NodeRef] {
172        self.origins.get(func.index()).map_or(&[], Vec::as_slice)
173    }
174
175    /// The LSB struct type of a class or interface item (mixins have none).
176    ///
177    /// # Examples
178    ///
179    /// ```
180    /// use hir_lang::{Builder, ClassDef, Generics, Item, ItemKind, List, Name};
181    /// use intern_lang::Interner;
182    ///
183    /// let mut names = Interner::new();
184    /// let mut b = Builder::new();
185    /// let class = ClassDef {
186    ///     generics: Generics::default(),
187    ///     bases: List::EMPTY,
188    ///     interfaces: List::EMPTY,
189    ///     fields: List::EMPTY,
190    ///     items: List::EMPTY,
191    ///     is_abstract: false,
192    ///     is_final: false,
193    ///     mixin: false,
194    /// };
195    /// let c = b.item(Item::new(Some(Name::new(names.intern("Point"))), ItemKind::Class(class)));
196    /// let root = b.module(None, &[c]);
197    /// let out = bcgen_lang::compile(&b.finish(root)?, &names)?;
198    /// let ty = out.class_type(c).unwrap();
199    /// assert!(matches!(out.module().types()[ty.index()], bytecode_lang::TypeDef::Struct(_)));
200    /// # Ok::<(), Box<dyn std::error::Error>>(())
201    /// ```
202    #[must_use]
203    pub fn class_type(&self, class: hir_lang::ItemId) -> Option<TypeId> {
204        self.classes
205            .iter()
206            .find(|(i, _)| *i == class)
207            .map(|&(_, t)| t)
208    }
209
210    /// The function a method item was compiled to for a class (a mixin's
211    /// method has one per class using it; an abstract method has none).
212    ///
213    /// # Examples
214    ///
215    /// See `tests/classes.rs`; a class without methods has none:
216    ///
217    /// ```
218    /// let hir = { let mut b = hir_lang::Builder::new(); let r = b.module(None, &[]); b.finish(r)? };
219    /// let out = bcgen_lang::compile(&hir, &intern_lang::Interner::new())?;
220    /// assert_eq!(out.method(hir.root(), hir.root()), None);
221    /// # Ok::<(), Box<dyn std::error::Error>>(())
222    /// ```
223    #[must_use]
224    pub fn method(&self, class: hir_lang::ItemId, method: hir_lang::ItemId) -> Option<FuncId> {
225        self.methods
226            .iter()
227            .find(|(k, _)| *k == (class, method))
228            .map(|&(_, f)| f)
229    }
230
231    /// The LSB method name of the destructor
232    /// ([`Options::destructor`](crate::Options::destructor), `__destruct()`
233    /// in the PHP profile), when a class of the unit defines one. LSB format
234    /// 2 has no destructor flag: the host passes this name to bvm-lang's
235    /// `Vm::set_destructor` on a VM made with the reference-counting memory
236    /// profile, so destructors run where PHP runs them.
237    ///
238    /// # Examples
239    ///
240    /// ```
241    /// let hir = { let mut b = hir_lang::Builder::new(); let r = b.module(None, &[]); b.finish(r)? };
242    /// let out = bcgen_lang::compile(&hir, &intern_lang::Interner::new())?;
243    /// assert_eq!(out.destructor(), None);
244    /// # Ok::<(), Box<dyn std::error::Error>>(())
245    /// ```
246    #[must_use]
247    pub fn destructor(&self) -> Option<&str> {
248        self.destructor.as_deref()
249    }
250}