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}