bytecode_lang/builder.rs
1//! Building modules: [`ModuleBuilder`] and [`FunctionBuilder`].
2//!
3//! The builders own every structural guarantee a code generator should not
4//! have to think about: strings, structural types, and constants are
5//! deduplicated; registers are numbered and typed as they are declared;
6//! branches are written against [`Label`]s and resolved when the function is
7//! added, so **a built function never contains an unresolved or out-of-range
8//! branch target**; and [`FunctionBuilder::parallel_move`] turns a set of
9//! simultaneous moves (block arguments, the bytecode stand-in for SSA phis)
10//! into a correct sequence, breaking cycles through a temporary (the defect
11//! class of family issue H01, where `jump H(b, a)` was emitted as two plain
12//! moves and lost a value).
13//!
14//! Builder methods that cannot fail on their own (declaring a register,
15//! emitting an instruction) never return `Result`. When one of them does
16//! hit a limit, or is handed a label from another function, the builder
17//! records the first such error and reports it from
18//! [`ModuleBuilder::add_function`] or [`ModuleBuilder::finish`], so a
19//! code generator can emit straight-line code and check once.
20
21use alloc::boxed::Box;
22use alloc::collections::{BTreeMap, BTreeSet};
23use alloc::vec::Vec;
24use core::fmt;
25
26use crate::call::{ArgKind, CallShape, ParamError, ParamList, ShapeError};
27use crate::encode::section_sizes;
28use crate::ids::{
29 ConstId, FuncId, GlobalId, ImportId, NameRef, Reg, ShapeId, StrId, TableId, Target, TypeId,
30 TypeRef, UpvalIdx,
31};
32use crate::inst::Inst;
33use crate::module::{
34 Callee, Const, Export, ExportItem, Function, Global, Handler, Hook, HookBinding, Import,
35 JumpTable, LineRow, LocalVar, Module, Strings,
36};
37use crate::policy::IntTy;
38use crate::types::{FuncType, TypeDef, ValType};
39
40/// Why a function or module could not be built.
41///
42/// # Examples
43///
44/// ```
45/// use bytecode_lang::{BuildError, ModuleBuilder};
46///
47/// let mut m = ModuleBuilder::new();
48/// let mut f = m.function("f", &[], &[]);
49/// let nowhere = f.label();
50/// f.jmp(nowhere); // never bound
51/// let err = m.add_function(f).unwrap_err();
52/// assert!(matches!(err, BuildError::UnboundLabel { .. }));
53/// ```
54#[derive(Clone, Copy, PartialEq, Eq, Debug)]
55#[non_exhaustive]
56pub enum BuildError {
57 /// A branch, table, handler, or local uses a label that was never bound.
58 UnboundLabel {
59 /// The function.
60 func: FuncId,
61 /// The label's number within the function.
62 label: u32,
63 },
64 /// A label was bound twice.
65 LabelRebound {
66 /// The function.
67 func: FuncId,
68 /// The label's number within the function.
69 label: u32,
70 },
71 /// A label made by another function's builder was used.
72 ForeignLabel {
73 /// The function whose builder saw the label.
74 func: FuncId,
75 },
76 /// A branch target is not an instruction of the function: an emitted
77 /// instruction names a target past the end, or a label used by a branch
78 /// is bound after the last instruction.
79 TargetOutOfRange {
80 /// The function.
81 func: FuncId,
82 /// The target.
83 target: u32,
84 },
85 /// A try region or local range ends before it starts.
86 InvalidRange {
87 /// The function.
88 func: FuncId,
89 },
90 /// A per-function table is full: registers, captures, name refs, or
91 /// type refs (65,536 each), parameters (255), or instructions, jump
92 /// tables, handlers (`u32::MAX`).
93 TooMany {
94 /// The function.
95 func: FuncId,
96 /// Which table: `"registers"`, `"captures"`, ...
97 what: &'static str,
98 },
99 /// A register was named that the function never declared.
100 UnknownRegister {
101 /// The function.
102 func: FuncId,
103 /// The register.
104 reg: Reg,
105 },
106 /// An instruction with `overflow = promote` writes a register declared
107 /// with a static type. `promote` produces an `f64` where an integer does
108 /// not fit, so its result must be `dyn` (OPS §2).
109 PromoteNotDynamic {
110 /// The function.
111 func: FuncId,
112 /// The instruction's pc.
113 pc: u32,
114 },
115 /// Two moves of one parallel move write the same register.
116 ConflictingMoves {
117 /// The function.
118 func: FuncId,
119 /// The register written twice.
120 dst: Reg,
121 },
122 /// A function's or an import's [`ParamList`] breaks a rule of its own
123 /// or does not fit its signature.
124 InvalidParams {
125 /// The function or import.
126 owner: Callee,
127 /// What is wrong.
128 error: ParamError,
129 },
130 /// A [`CallShape`] is malformed.
131 InvalidShape {
132 /// The function.
133 func: FuncId,
134 /// What is wrong.
135 error: ShapeError,
136 },
137 /// A function builder does not belong to this module builder, or was
138 /// already added.
139 ForeignFunction(FuncId),
140 /// A declared function was never added.
141 UndefinedFunction(FuncId),
142 /// A reserved type was never defined.
143 UndefinedType(TypeId),
144 /// An aggregate constant refers to itself or a later constant.
145 ConstForwardRef(ConstId),
146 /// A module-level table would pass `u32::MAX` entries or bytes
147 /// (`what` names it).
148 ModuleFull(&'static str),
149 /// An encoded section would exceed 4 GiB, which its length field cannot
150 /// express.
151 TooLarge {
152 /// The section id (see `specs/LSB.md` §6).
153 section: u32,
154 },
155}
156
157impl fmt::Display for BuildError {
158 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
159 match self {
160 BuildError::UnboundLabel { func, label } => {
161 write!(f, "label {label} of {func} is used but never bound")
162 }
163 BuildError::LabelRebound { func, label } => {
164 write!(f, "label {label} of {func} is bound twice")
165 }
166 BuildError::ForeignLabel { func } => {
167 write!(f, "{func} was given a label from another function")
168 }
169 BuildError::TargetOutOfRange { func, target } => {
170 write!(f, "branch target @{target} is past the end of {func}")
171 }
172 BuildError::InvalidRange { func } => {
173 write!(
174 f,
175 "a try region or local range of {func} ends before it starts"
176 )
177 }
178 BuildError::TooMany { func, what } => write!(f, "{func} has too many {what}"),
179 BuildError::UnknownRegister { func, reg } => {
180 write!(f, "{func} names undeclared register {reg}")
181 }
182 BuildError::PromoteNotDynamic { func, pc } => write!(
183 f,
184 "instruction {pc} of {func} uses overflow = promote but its destination is not dyn"
185 ),
186 BuildError::ConflictingMoves { func, dst } => {
187 write!(f, "a parallel move in {func} writes {dst} twice")
188 }
189 BuildError::InvalidParams { owner, error } => {
190 write!(f, "the parameter list of {owner} is invalid: {error}")
191 }
192 BuildError::InvalidShape { func, error } => {
193 write!(f, "a call shape of {func} is invalid: {error}")
194 }
195 BuildError::ForeignFunction(func) => {
196 write!(
197 f,
198 "{func} does not belong to this module builder or was added twice"
199 )
200 }
201 BuildError::UndefinedFunction(func) => {
202 write!(f, "{func} was declared but never added")
203 }
204 BuildError::UndefinedType(ty) => write!(f, "{ty} was reserved but never defined"),
205 BuildError::ConstForwardRef(id) => write!(
206 f,
207 "an aggregate constant refers to {id}, which is not an earlier constant"
208 ),
209 BuildError::ModuleFull(what) => write!(f, "the module's {what} table is full"),
210 BuildError::TooLarge { section } => {
211 write!(f, "section {section} would exceed 4 GiB")
212 }
213 }
214 }
215}
216
217impl core::error::Error for BuildError {}
218
219/// A position in a function's code, named before it is known.
220///
221/// Make one with [`FunctionBuilder::label`], use it in branches, tables,
222/// handlers, and local ranges, and [`bind`](FunctionBuilder::bind) it to the
223/// next instruction's position exactly once. A label belongs to the builder
224/// that made it.
225///
226/// # Examples
227///
228/// ```
229/// use bytecode_lang::{Inst, ModuleBuilder, Target};
230///
231/// let mut m = ModuleBuilder::new();
232/// let mut f = m.function("f", &[], &[]);
233/// let end = f.label();
234/// f.jmp(end);
235/// f.emit(Inst::Nop {});
236/// f.bind(end);
237/// f.ret_void();
238/// let id = m.add_function(f).unwrap();
239/// let module = m.finish().unwrap();
240/// assert_eq!(module.function(id).unwrap().code()[0], Inst::Jmp { target: Target(2) });
241/// ```
242#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
243pub struct Label {
244 func: FuncId,
245 index: u32,
246}
247
248/// Builds one function. Created by [`ModuleBuilder::function`], added back
249/// with [`ModuleBuilder::add_function`].
250///
251/// The parameters are registers `r0..rN` with the declared parameter types;
252/// [`reg`](FunctionBuilder::reg) declares more, in order, so consecutive
253/// calls return consecutive registers (what call windows need).
254///
255/// # Examples
256///
257/// ```
258/// use bytecode_lang::{Inst, IntOp, IntTy, ModuleBuilder, ValType};
259///
260/// // fn count(n: i64) -> i64 { let i = 0; while i < n { i = i + 1 } i }
261/// let mut m = ModuleBuilder::new();
262/// let mut f = m.function("count", &[ValType::I64], &[ValType::I64]);
263/// let n = f.param(0);
264/// let (i, one, more) = (f.reg(ValType::I64), f.reg(ValType::I64), f.reg(ValType::Bool));
265/// let i64op = IntOp::new(IntTy::I64);
266/// f.emit(Inst::LoadInt { dst: i, val: 0, ty: IntTy::I64 });
267/// f.emit(Inst::LoadInt { dst: one, val: 1, ty: IntTy::I64 });
268/// let (head, done) = (f.label(), f.label());
269/// f.bind(head);
270/// f.emit(Inst::ILt { dst: more, lhs: i, rhs: n, ty: IntTy::I64 });
271/// f.jmp_if_not(more, done);
272/// f.emit(Inst::IAdd { dst: i, lhs: i, rhs: one, op: i64op });
273/// f.emit(Inst::Safepoint {});
274/// f.jmp(head);
275/// f.bind(done);
276/// f.ret(i);
277/// m.add_function(f).unwrap();
278/// let module = m.finish().unwrap();
279/// assert_eq!(module.functions()[0].code().len(), 8);
280/// ```
281#[derive(Clone, Debug)]
282pub struct FunctionBuilder {
283 id: FuncId,
284 name: StrId,
285 sig: TypeId,
286 regs: Vec<ValType>,
287 params: u16,
288 captures: Vec<ValType>,
289 names: Vec<StrId>,
290 name_index: BTreeMap<StrId, NameRef>,
291 type_refs: Vec<TypeId>,
292 type_index: BTreeMap<TypeId, TypeRef>,
293 param_list: Option<ParamList>,
294 shapes: Vec<CallShape>,
295 shape_index: BTreeMap<CallShape, ShapeId>,
296 code: Vec<Inst>,
297 labels: Vec<Option<u32>>,
298 /// Branches emitted against a label: (pc, label).
299 fixups: Vec<(u32, Label)>,
300 tables: Vec<(Vec<Label>, Label)>,
301 handlers: Vec<(Label, Label, Label, Reg)>,
302 locals: Vec<(Reg, StrId, Label, Label)>,
303 lines: Vec<LineRow>,
304 location: Option<(StrId, u32, u32)>,
305 temps: BTreeMap<ValType, Reg>,
306 error: Option<BuildError>,
307}
308
309/// The placeholder target of a branch awaiting its label; never valid, so
310/// an unpatched branch could not slip through the final range check.
311const UNRESOLVED: Target = Target(u32::MAX);
312
313impl FunctionBuilder {
314 fn new(id: FuncId, name: StrId, sig: TypeId, params: &[ValType]) -> Self {
315 let mut f = FunctionBuilder {
316 id,
317 name,
318 sig,
319 regs: Vec::with_capacity(params.len()),
320 params: 0,
321 captures: Vec::new(),
322 names: Vec::new(),
323 name_index: BTreeMap::new(),
324 type_refs: Vec::new(),
325 type_index: BTreeMap::new(),
326 param_list: None,
327 shapes: Vec::new(),
328 shape_index: BTreeMap::new(),
329 code: Vec::new(),
330 labels: Vec::new(),
331 fixups: Vec::new(),
332 tables: Vec::new(),
333 handlers: Vec::new(),
334 locals: Vec::new(),
335 lines: Vec::new(),
336 location: None,
337 temps: BTreeMap::new(),
338 error: None,
339 };
340 if params.len() > usize::from(u8::MAX) {
341 f.fail(BuildError::TooMany {
342 func: id,
343 what: "parameters",
344 });
345 }
346 for &ty in params {
347 let _param = f.reg(ty);
348 }
349 f.params = u16::try_from(params.len()).unwrap_or(u16::MAX);
350 f
351 }
352
353 /// Records the first error; later ones are consequences of it.
354 fn fail(&mut self, error: BuildError) {
355 if self.error.is_none() {
356 self.error = Some(error);
357 }
358 }
359
360 fn too_many(&mut self, what: &'static str) {
361 self.fail(BuildError::TooMany {
362 func: self.id,
363 what,
364 });
365 }
366
367 /// The id this function will have in the module.
368 ///
369 /// # Examples
370 ///
371 /// ```
372 /// use bytecode_lang::{FuncId, ModuleBuilder};
373 ///
374 /// let mut m = ModuleBuilder::new();
375 /// let f = m.function("f", &[], &[]);
376 /// assert_eq!(f.id(), FuncId(0));
377 /// ```
378 #[must_use]
379 pub fn id(&self) -> FuncId {
380 self.id
381 }
382
383 /// The register holding parameter `index`: parameters occupy the first
384 /// registers, in order, so this is `Reg(index)`.
385 ///
386 /// # Examples
387 ///
388 /// ```
389 /// use bytecode_lang::{ModuleBuilder, Reg, ValType};
390 ///
391 /// let mut m = ModuleBuilder::new();
392 /// let f = m.function("f", &[ValType::I64, ValType::F64], &[]);
393 /// assert_eq!(f.param(1), Reg(1));
394 /// assert_eq!(f.param_count(), 2);
395 /// ```
396 #[must_use]
397 pub fn param(&self, index: u16) -> Reg {
398 Reg(index)
399 }
400
401 /// The number of parameters.
402 ///
403 /// # Examples
404 ///
405 /// ```
406 /// use bytecode_lang::{ModuleBuilder, ValType};
407 ///
408 /// let mut m = ModuleBuilder::new();
409 /// assert_eq!(m.function("f", &[ValType::Dyn], &[]).param_count(), 1);
410 /// ```
411 #[must_use]
412 pub fn param_count(&self) -> u16 {
413 self.params
414 }
415
416 /// Declares a register of type `ty` and returns it. Registers are
417 /// numbered in declaration order.
418 ///
419 /// # Examples
420 ///
421 /// ```
422 /// use bytecode_lang::{ModuleBuilder, Reg, ValType};
423 ///
424 /// let mut m = ModuleBuilder::new();
425 /// let mut f = m.function("f", &[ValType::I64], &[]);
426 /// assert_eq!(f.reg(ValType::Dyn), Reg(1));
427 /// assert_eq!(f.reg(ValType::Dyn), Reg(2));
428 /// ```
429 pub fn reg(&mut self, ty: ValType) -> Reg {
430 match u16::try_from(self.regs.len()) {
431 Ok(index) => {
432 self.regs.push(ty);
433 Reg(index)
434 }
435 Err(_) => {
436 self.too_many("registers");
437 Reg(u16::MAX)
438 }
439 }
440 }
441
442 /// Declares one register per type, consecutively, and returns the
443 /// first: a call window (`dst` then the arguments) in one step.
444 ///
445 /// # Examples
446 ///
447 /// ```
448 /// use bytecode_lang::{ModuleBuilder, Reg, ValType};
449 ///
450 /// let mut m = ModuleBuilder::new();
451 /// let mut f = m.function("f", &[], &[]);
452 /// let window = f.regs(&[ValType::I64, ValType::I64, ValType::I64]);
453 /// assert_eq!(window, Reg(0));
454 /// assert_eq!(f.reg(ValType::Bool), Reg(3));
455 /// ```
456 pub fn regs(&mut self, types: &[ValType]) -> Reg {
457 let first = Reg(u16::try_from(self.regs.len()).unwrap_or(u16::MAX));
458 for &ty in types {
459 let _reg = self.reg(ty);
460 }
461 first
462 }
463
464 /// Declares a captured value of type `ty` (read with
465 /// [`Inst::GetUpval`]) and returns its index.
466 ///
467 /// # Examples
468 ///
469 /// ```
470 /// use bytecode_lang::{ModuleBuilder, UpvalIdx, ValType};
471 ///
472 /// let mut m = ModuleBuilder::new();
473 /// let mut f = m.function("f", &[], &[]);
474 /// assert_eq!(f.capture(ValType::I64), UpvalIdx(0));
475 /// assert_eq!(f.capture(ValType::Dyn), UpvalIdx(1));
476 /// ```
477 pub fn capture(&mut self, ty: ValType) -> UpvalIdx {
478 match u16::try_from(self.captures.len()) {
479 Ok(index) => {
480 self.captures.push(ty);
481 UpvalIdx(index)
482 }
483 Err(_) => {
484 self.too_many("captures");
485 UpvalIdx(u16::MAX)
486 }
487 }
488 }
489
490 /// The function-local [`NameRef`] for the string `name`, adding it to
491 /// the function's name list on first use.
492 ///
493 /// # Examples
494 ///
495 /// ```
496 /// use bytecode_lang::{ModuleBuilder, NameRef};
497 ///
498 /// let mut m = ModuleBuilder::new();
499 /// let (x, y) = (m.string("x"), m.string("y"));
500 /// let mut f = m.function("f", &[], &[]);
501 /// assert_eq!(f.name_ref(y), NameRef(0));
502 /// assert_eq!(f.name_ref(x), NameRef(1));
503 /// assert_eq!(f.name_ref(y), NameRef(0));
504 /// ```
505 pub fn name_ref(&mut self, name: StrId) -> NameRef {
506 if let Some(&r) = self.name_index.get(&name) {
507 return r;
508 }
509 match u16::try_from(self.names.len()) {
510 Ok(index) => {
511 self.names.push(name);
512 let r = NameRef(index);
513 let _previous = self.name_index.insert(name, r);
514 r
515 }
516 Err(_) => {
517 self.too_many("name refs");
518 NameRef(u16::MAX)
519 }
520 }
521 }
522
523 /// The function-local [`TypeRef`] for the type `ty`, adding it to the
524 /// function's type list on first use.
525 ///
526 /// # Examples
527 ///
528 /// ```
529 /// use bytecode_lang::{ModuleBuilder, TypeDef, TypeRef, ValType};
530 ///
531 /// let mut m = ModuleBuilder::new();
532 /// let arr = m.add_type(TypeDef::Array(ValType::Dyn));
533 /// let mut f = m.function("f", &[], &[]);
534 /// assert_eq!(f.type_ref(arr), TypeRef(0));
535 /// assert_eq!(f.type_ref(arr), TypeRef(0));
536 /// ```
537 pub fn type_ref(&mut self, ty: TypeId) -> TypeRef {
538 if let Some(&r) = self.type_index.get(&ty) {
539 return r;
540 }
541 match u16::try_from(self.type_refs.len()) {
542 Ok(index) => {
543 self.type_refs.push(ty);
544 let r = TypeRef(index);
545 let _previous = self.type_index.insert(ty, r);
546 r
547 }
548 Err(_) => {
549 self.too_many("type refs");
550 TypeRef(u16::MAX)
551 }
552 }
553 }
554
555 /// Gives the function a dynamic-call signature: parameter names, kinds,
556 /// by-reference flags, and defaults, which `dcall`/`dcall_shape` bind
557 /// arguments to (`specs/LSB.md` §5.15). The list is checked when the
558 /// function is added: its own rules ([`ParamList::validate`]) and that it
559 /// fits the signature ([`ParamList::fits`]: one parameter per entry, and
560 /// a trailing `i64` presence mask when an entry has a default).
561 ///
562 /// # Examples
563 ///
564 /// ```
565 /// use bytecode_lang::{ModuleBuilder, Param, ParamKind, ParamList, ValType};
566 ///
567 /// // PHP: function f($x, ...$rest) — every function value of a dynamic
568 /// // language takes its arguments this way.
569 /// let mut m = ModuleBuilder::new();
570 /// let x = m.string("x");
571 /// let mut f = m.function("f", &[ValType::Dyn, ValType::Dyn], &[]);
572 /// f.set_params(ParamList::new(vec![Param::normal(x), Param::new(ParamKind::RestMap, None)]));
573 /// f.ret_void();
574 /// assert!(m.add_function(f).is_ok());
575 /// ```
576 pub fn set_params(&mut self, list: ParamList) {
577 self.param_list = Some(list);
578 }
579
580 /// The function-local [`ShapeId`] for the call shape `args`, adding it
581 /// to the function's shape table on first use. A malformed shape (see
582 /// [`CallShape::validate`]) is recorded and reported when the function
583 /// is added.
584 ///
585 /// # Examples
586 ///
587 /// ```
588 /// use bytecode_lang::{ArgKind, ModuleBuilder, ShapeId};
589 ///
590 /// let mut m = ModuleBuilder::new();
591 /// let flag = m.string("flag");
592 /// let mut f = m.function("f", &[], &[]);
593 /// let s = f.call_shape(&[ArgKind::Spread, ArgKind::Named(flag)]);
594 /// assert_eq!(s, ShapeId(0));
595 /// assert_eq!(f.call_shape(&[ArgKind::Spread, ArgKind::Named(flag)]), s);
596 /// ```
597 pub fn call_shape(&mut self, args: &[ArgKind]) -> ShapeId {
598 let shape = CallShape::new(args.to_vec());
599 if let Some(&id) = self.shape_index.get(&shape) {
600 return id;
601 }
602 if let Err(error) = shape.validate() {
603 self.fail(BuildError::InvalidShape {
604 func: self.id,
605 error,
606 });
607 return ShapeId(u16::MAX);
608 }
609 match u16::try_from(self.shapes.len()) {
610 Ok(index) => {
611 let id = ShapeId(index);
612 let _previous = self.shape_index.insert(shape.clone(), id);
613 self.shapes.push(shape);
614 id
615 }
616 Err(_) => {
617 self.too_many("call shapes");
618 ShapeId(u16::MAX)
619 }
620 }
621 }
622
623 /// Emits `dcall_shape dst, callee, csN` with the shape `args` (interned
624 /// by [`call_shape`](Self::call_shape)): a dynamic call whose window
625 /// `dst+1 ..= dst+args.len()` holds positional, named, and spread
626 /// arguments.
627 ///
628 /// # Examples
629 ///
630 /// ```
631 /// use bytecode_lang::{ArgKind, Inst, ModuleBuilder, ShapeId, ValType};
632 ///
633 /// let mut m = ModuleBuilder::new();
634 /// let x = m.string("x");
635 /// let mut f = m.function("f", &[ValType::Dyn], &[]);
636 /// let window = f.regs(&[ValType::Dyn, ValType::Dyn]);
637 /// f.dcall_shape(window, f.param(0), &[ArgKind::Named(x)]);
638 /// f.ret_void();
639 /// let id = m.add_function(f).unwrap();
640 /// let module = m.finish().unwrap();
641 /// assert_eq!(
642 /// module.function(id).unwrap().code()[0],
643 /// Inst::DCallShape { dst: window, callee: bytecode_lang::Reg(0), shape: ShapeId(0) },
644 /// );
645 /// ```
646 pub fn dcall_shape(&mut self, dst: Reg, callee: Reg, args: &[ArgKind]) -> u32 {
647 let shape = self.call_shape(args);
648 self.emit(Inst::DCallShape { dst, callee, shape })
649 }
650
651 /// Sets the source position of the instructions emitted from now on.
652 /// A line-table row is added at the next instruction only if the
653 /// position changed.
654 ///
655 /// # Examples
656 ///
657 /// ```
658 /// use bytecode_lang::{Inst, ModuleBuilder};
659 ///
660 /// let mut m = ModuleBuilder::new();
661 /// let file = m.string("a.mox");
662 /// let mut f = m.function("f", &[], &[]);
663 /// f.set_location(file, 1, 1);
664 /// f.emit(Inst::Nop {});
665 /// f.emit(Inst::Nop {}); // same position: no new row
666 /// f.set_location(file, 2, 1);
667 /// f.ret_void();
668 /// let id = m.add_function(f).unwrap();
669 /// let module = m.finish().unwrap();
670 /// let pcs: Vec<u32> = module.function(id).unwrap().lines().iter().map(|r| r.pc).collect();
671 /// assert_eq!(pcs, [0, 2]);
672 /// ```
673 pub fn set_location(&mut self, file: StrId, line: u32, column: u32) {
674 self.location = Some((file, line, column));
675 }
676
677 /// The position the next instruction will have.
678 ///
679 /// # Examples
680 ///
681 /// ```
682 /// use bytecode_lang::{Inst, ModuleBuilder};
683 ///
684 /// let mut m = ModuleBuilder::new();
685 /// let mut f = m.function("f", &[], &[]);
686 /// assert_eq!(f.pc(), 0);
687 /// f.emit(Inst::Nop {});
688 /// assert_eq!(f.pc(), 1);
689 /// ```
690 #[must_use]
691 pub fn pc(&self) -> u32 {
692 u32::try_from(self.code.len()).unwrap_or(u32::MAX)
693 }
694
695 /// Appends `inst` and returns its pc.
696 ///
697 /// A `jmp`, `jmp_if`, or `jmp_if_not` emitted this way carries a raw
698 /// [`Target`]; it is checked against the final code length when the
699 /// function is added. Prefer [`jmp`](Self::jmp) and friends, which take
700 /// labels.
701 ///
702 /// # Examples
703 ///
704 /// ```
705 /// use bytecode_lang::{Inst, ModuleBuilder};
706 ///
707 /// let mut m = ModuleBuilder::new();
708 /// let mut f = m.function("f", &[], &[]);
709 /// assert_eq!(f.emit(Inst::Safepoint {}), 0);
710 /// assert_eq!(f.emit(Inst::RetVoid {}), 1);
711 /// ```
712 pub fn emit(&mut self, inst: Inst) -> u32 {
713 let pc = self.pc();
714 if pc == u32::MAX {
715 self.too_many("instructions");
716 return pc;
717 }
718 if let Some((file, line, column)) = self.location {
719 let same = self
720 .lines
721 .last()
722 .is_some_and(|r| (r.file, r.line, r.column) == (file, line, column));
723 if !same {
724 self.lines.push(LineRow {
725 pc,
726 file,
727 line,
728 column,
729 });
730 }
731 }
732 self.code.push(inst);
733 pc
734 }
735
736 /// Emits `mov dst, src`.
737 ///
738 /// # Examples
739 ///
740 /// ```
741 /// use bytecode_lang::{Inst, ModuleBuilder, Reg, ValType};
742 ///
743 /// let mut m = ModuleBuilder::new();
744 /// let mut f = m.function("f", &[ValType::I64], &[]);
745 /// let copy = f.reg(ValType::I64);
746 /// f.mov(copy, Reg(0));
747 /// f.ret_void();
748 /// let id = m.add_function(f).unwrap();
749 /// let module = m.finish().unwrap();
750 /// assert_eq!(module.function(id).unwrap().code()[0], Inst::Mov { dst: copy, src: Reg(0) });
751 /// ```
752 pub fn mov(&mut self, dst: Reg, src: Reg) -> u32 {
753 self.emit(Inst::Mov { dst, src })
754 }
755
756 /// Emits `ret src`.
757 ///
758 /// # Examples
759 ///
760 /// ```
761 /// use bytecode_lang::{Inst, ModuleBuilder, ValType};
762 ///
763 /// let mut m = ModuleBuilder::new();
764 /// let mut f = m.function("f", &[ValType::Dyn], &[ValType::Dyn]);
765 /// let x = f.param(0);
766 /// assert_eq!(f.ret(x), 0);
767 /// ```
768 pub fn ret(&mut self, src: Reg) -> u32 {
769 self.emit(Inst::Ret { src })
770 }
771
772 /// Emits `ret_void`.
773 ///
774 /// # Examples
775 ///
776 /// ```
777 /// use bytecode_lang::ModuleBuilder;
778 ///
779 /// let mut m = ModuleBuilder::new();
780 /// let mut f = m.function("f", &[], &[]);
781 /// assert_eq!(f.ret_void(), 0);
782 /// ```
783 pub fn ret_void(&mut self) -> u32 {
784 self.emit(Inst::RetVoid {})
785 }
786
787 /// A new, unbound label.
788 ///
789 /// # Examples
790 ///
791 /// ```
792 /// use bytecode_lang::ModuleBuilder;
793 ///
794 /// let mut m = ModuleBuilder::new();
795 /// let mut f = m.function("f", &[], &[]);
796 /// let (a, b) = (f.label(), f.label());
797 /// assert_ne!(a, b);
798 /// ```
799 pub fn label(&mut self) -> Label {
800 let index = match u32::try_from(self.labels.len()) {
801 Ok(index) => index,
802 Err(_) => {
803 self.too_many("labels");
804 u32::MAX
805 }
806 };
807 self.labels.push(None);
808 Label {
809 func: self.id,
810 index,
811 }
812 }
813
814 /// Whether `label` was made by this builder; records an error if not.
815 fn owns(&mut self, label: Label) -> bool {
816 let ok = label.func == self.id && (label.index as usize) < self.labels.len();
817 if !ok {
818 self.fail(BuildError::ForeignLabel { func: self.id });
819 }
820 ok
821 }
822
823 /// Binds `label` to the position of the next instruction emitted.
824 /// Binding a label twice is an error.
825 ///
826 /// # Examples
827 ///
828 /// ```
829 /// use bytecode_lang::{BuildError, ModuleBuilder};
830 ///
831 /// let mut m = ModuleBuilder::new();
832 /// let mut f = m.function("f", &[], &[]);
833 /// let l = f.label();
834 /// f.bind(l);
835 /// f.bind(l);
836 /// f.ret_void();
837 /// assert!(matches!(m.add_function(f), Err(BuildError::LabelRebound { .. })));
838 /// ```
839 pub fn bind(&mut self, label: Label) {
840 if !self.owns(label) {
841 return;
842 }
843 let pc = self.pc();
844 match self.labels.get_mut(label.index as usize) {
845 Some(slot @ None) => *slot = Some(pc),
846 _ => self.fail(BuildError::LabelRebound {
847 func: self.id,
848 label: label.index,
849 }),
850 }
851 }
852
853 fn branch(&mut self, inst: Inst, label: Label) -> u32 {
854 let owned = self.owns(label);
855 let pc = self.emit(inst);
856 if owned {
857 self.fixups.push((pc, label));
858 }
859 pc
860 }
861
862 /// Emits `jmp label`.
863 ///
864 /// # Examples
865 ///
866 /// ```
867 /// use bytecode_lang::{Inst, ModuleBuilder, Target};
868 ///
869 /// let mut m = ModuleBuilder::new();
870 /// let mut f = m.function("f", &[], &[]);
871 /// let top = f.label();
872 /// f.bind(top);
873 /// f.emit(Inst::Safepoint {});
874 /// f.jmp(top);
875 /// let id = m.add_function(f).unwrap();
876 /// let module = m.finish().unwrap();
877 /// assert_eq!(module.function(id).unwrap().code()[1], Inst::Jmp { target: Target(0) });
878 /// ```
879 pub fn jmp(&mut self, label: Label) -> u32 {
880 self.branch(Inst::Jmp { target: UNRESOLVED }, label)
881 }
882
883 /// Emits `jmp_if cond, label`.
884 ///
885 /// # Examples
886 ///
887 /// ```
888 /// use bytecode_lang::{Inst, ModuleBuilder, Target, ValType};
889 ///
890 /// let mut m = ModuleBuilder::new();
891 /// let mut f = m.function("f", &[ValType::Bool], &[]);
892 /// let c = f.param(0);
893 /// let out = f.label();
894 /// f.jmp_if(c, out);
895 /// f.bind(out);
896 /// f.ret_void();
897 /// let id = m.add_function(f).unwrap();
898 /// let module = m.finish().unwrap();
899 /// assert_eq!(module.function(id).unwrap().code()[0], Inst::JmpIf { cond: c, target: Target(1) });
900 /// ```
901 pub fn jmp_if(&mut self, cond: Reg, label: Label) -> u32 {
902 self.branch(
903 Inst::JmpIf {
904 cond,
905 target: UNRESOLVED,
906 },
907 label,
908 )
909 }
910
911 /// Emits `jmp_if_not cond, label`.
912 ///
913 /// # Examples
914 ///
915 /// ```
916 /// use bytecode_lang::{Inst, ModuleBuilder, Target, ValType};
917 ///
918 /// let mut m = ModuleBuilder::new();
919 /// let mut f = m.function("f", &[ValType::Bool], &[]);
920 /// let c = f.param(0);
921 /// let out = f.label();
922 /// f.jmp_if_not(c, out);
923 /// f.bind(out);
924 /// f.ret_void();
925 /// let id = m.add_function(f).unwrap();
926 /// let module = m.finish().unwrap();
927 /// assert_eq!(module.function(id).unwrap().code()[0], Inst::JmpIfNot { cond: c, target: Target(1) });
928 /// ```
929 pub fn jmp_if_not(&mut self, cond: Reg, label: Label) -> u32 {
930 self.branch(
931 Inst::JmpIfNot {
932 cond,
933 target: UNRESOLVED,
934 },
935 label,
936 )
937 }
938
939 /// Emits `switch.ty src, jtN` with a new jump table: `targets[v]` for a
940 /// selector `v` in range, `default` otherwise. Returns the table's id.
941 ///
942 /// # Examples
943 ///
944 /// ```
945 /// use bytecode_lang::{IntTy, ModuleBuilder, TableId, ValType};
946 ///
947 /// let mut m = ModuleBuilder::new();
948 /// let mut f = m.function("f", &[ValType::U8], &[]);
949 /// let sel = f.param(0);
950 /// let (zero, other) = (f.label(), f.label());
951 /// assert_eq!(f.switch(IntTy::U8, sel, &[zero], other), TableId(0));
952 /// f.bind(zero);
953 /// f.bind(other);
954 /// f.ret_void();
955 /// assert!(m.add_function(f).is_ok());
956 /// ```
957 pub fn switch(&mut self, ty: IntTy, src: Reg, targets: &[Label], default: Label) -> TableId {
958 let all_owned = targets.iter().all(|&l| self.owns(l)) & self.owns(default);
959 let table = match u32::try_from(self.tables.len()) {
960 Ok(index) => TableId(index),
961 Err(_) => {
962 self.too_many("jump tables");
963 TableId(u32::MAX)
964 }
965 };
966 if all_owned {
967 self.tables.push((targets.to_vec(), default));
968 }
969 let _pc = self.emit(Inst::Switch { src, table, ty });
970 table
971 }
972
973 /// Declares a try region: an error raised at a pc in `start..end`
974 /// continues at `handler` with the error value in `catch` (a `dyn`
975 /// register). Declare inner regions before the regions that enclose
976 /// them; the first matching region wins.
977 ///
978 /// # Examples
979 ///
980 /// ```
981 /// use bytecode_lang::{Inst, ModuleBuilder, Reg, ValType};
982 ///
983 /// let mut m = ModuleBuilder::new();
984 /// let mut f = m.function("f", &[ValType::Dyn], &[]);
985 /// let err = f.reg(ValType::Dyn);
986 /// let (start, end, handler) = (f.label(), f.label(), f.label());
987 /// f.bind(start);
988 /// f.emit(Inst::Throw { src: Reg(0) });
989 /// f.bind(end);
990 /// f.bind(handler);
991 /// f.ret_void();
992 /// f.try_region(start, end, handler, err);
993 /// let id = m.add_function(f).unwrap();
994 /// let module = m.finish().unwrap();
995 /// assert_eq!(module.function(id).unwrap().handlers()[0].catch, err);
996 /// ```
997 pub fn try_region(&mut self, start: Label, end: Label, handler: Label, catch: Reg) {
998 if self.owns(start) & self.owns(end) & self.owns(handler) {
999 self.handlers.push((start, end, handler, catch));
1000 }
1001 }
1002
1003 /// Names the register `reg` as the source variable `name` over the
1004 /// range `start..end`, for debuggers.
1005 ///
1006 /// # Examples
1007 ///
1008 /// ```
1009 /// use bytecode_lang::{ModuleBuilder, ValType};
1010 ///
1011 /// let mut m = ModuleBuilder::new();
1012 /// let name = m.string("count");
1013 /// let mut f = m.function("f", &[ValType::I64], &[]);
1014 /// let (s, e) = (f.label(), f.label());
1015 /// f.bind(s);
1016 /// f.ret_void();
1017 /// f.bind(e);
1018 /// f.local(f.param(0), name, s, e);
1019 /// let id = m.add_function(f).unwrap();
1020 /// let module = m.finish().unwrap();
1021 /// assert_eq!(module.function(id).unwrap().locals()[0].name, name);
1022 /// ```
1023 pub fn local(&mut self, reg: Reg, name: StrId, start: Label, end: Label) {
1024 if self.owns(start) & self.owns(end) {
1025 self.locals.push((reg, name, start, end));
1026 }
1027 }
1028
1029 /// A temporary of type `ty` for breaking move cycles, guaranteed not to
1030 /// be any register in `named` (the registers the current move set
1031 /// mentions). One temporary per type is cached and reused, since it is
1032 /// only live inside one move sequence; if a move set names the cached
1033 /// one (or names undeclared registers a fresh one would collide with),
1034 /// fresh registers are declared until one is free. That terminates
1035 /// within `named.len() + 1` declarations.
1036 fn temp(&mut self, ty: ValType, named: &BTreeSet<Reg>) -> Reg {
1037 if let Some(&r) = self.temps.get(&ty) {
1038 if !named.contains(&r) {
1039 return r;
1040 }
1041 }
1042 loop {
1043 let r = self.reg(ty);
1044 if self.error.is_some() || !named.contains(&r) {
1045 let _previous = self.temps.insert(ty, r);
1046 return r;
1047 }
1048 }
1049 }
1050
1051 /// Emits moves that copy every `src` to its `dst` **simultaneously**:
1052 /// each destination ends up with the value its source held before any
1053 /// move ran, even when sources and destinations overlap or form cycles
1054 /// (`(a, b), (b, a)` swaps). Moves whose source is their destination
1055 /// are dropped. Cycles are broken through a temporary register of the
1056 /// saved register's type. Two moves writing one register are an error.
1057 ///
1058 /// This is the correct lowering of block arguments (the bytecode form of
1059 /// SSA phis): a back edge that permutes its own parameters must not be
1060 /// emitted as plain sequential moves.
1061 ///
1062 /// # Examples
1063 ///
1064 /// ```
1065 /// use bytecode_lang::{Inst, ModuleBuilder, Reg, ValType};
1066 ///
1067 /// let mut m = ModuleBuilder::new();
1068 /// let mut f = m.function("swap", &[ValType::I64, ValType::I64], &[]);
1069 /// let (a, b) = (f.param(0), f.param(1));
1070 /// f.parallel_move(&[(a, b), (b, a)]);
1071 /// f.ret_void();
1072 /// let id = m.add_function(f).unwrap();
1073 /// let module = m.finish().unwrap();
1074 /// let tmp = Reg(2); // allocated to break the cycle
1075 /// assert_eq!(
1076 /// &module.function(id).unwrap().code()[..3],
1077 /// &[
1078 /// Inst::Mov { dst: tmp, src: a },
1079 /// Inst::Mov { dst: a, src: b },
1080 /// Inst::Mov { dst: b, src: tmp },
1081 /// ],
1082 /// );
1083 /// ```
1084 pub fn parallel_move(&mut self, moves: &[(Reg, Reg)]) {
1085 // Pending moves (dst, src), self-moves dropped.
1086 let mut pending: Vec<(Reg, Reg)> = moves.iter().copied().filter(|(d, s)| d != s).collect();
1087 // Every register the move set mentions: a temporary must be none of them.
1088 let named: BTreeSet<Reg> = moves.iter().flat_map(|&(d, s)| [d, s]).collect();
1089 let n = pending.len();
1090 // dst -> index of the move writing it (destinations are unique).
1091 let mut writer: BTreeMap<Reg, usize> = BTreeMap::new();
1092 for (i, &(dst, _)) in pending.iter().enumerate() {
1093 if writer.insert(dst, i).is_some() {
1094 self.fail(BuildError::ConflictingMoves { func: self.id, dst });
1095 return;
1096 }
1097 }
1098 // src -> moves reading it, and how many of those are still pending.
1099 let mut readers: BTreeMap<Reg, Vec<usize>> = BTreeMap::new();
1100 for (i, &(_, src)) in pending.iter().enumerate() {
1101 readers.entry(src).or_default().push(i);
1102 }
1103 let mut live_readers: BTreeMap<Reg, usize> =
1104 readers.iter().map(|(&r, v)| (r, v.len())).collect();
1105 let mut done = alloc::vec![false; n];
1106 // A move is ready when nothing pending still needs its destination.
1107 let mut ready: Vec<usize> = (0..n)
1108 .filter(|&i| {
1109 pending
1110 .get(i)
1111 .is_some_and(|&(d, _)| !live_readers.contains_key(&d))
1112 })
1113 .collect();
1114 let mut remaining = n;
1115 let mut scan = 0usize;
1116 while remaining > 0 {
1117 while let Some(i) = ready.pop() {
1118 let Some(&(dst, src)) = pending.get(i) else {
1119 continue;
1120 };
1121 let _pc = self.emit(Inst::Mov { dst, src });
1122 if let Some(flag) = done.get_mut(i) {
1123 *flag = true;
1124 }
1125 remaining -= 1;
1126 // `src` lost a reader; if none are left and `src` is itself
1127 // a pending destination, that move may now run.
1128 if let Some(count) = live_readers.get_mut(&src) {
1129 *count -= 1;
1130 if *count == 0 {
1131 let _gone = live_readers.remove(&src);
1132 if let Some(&w) = writer.get(&src) {
1133 if !done.get(w).copied().unwrap_or(true) {
1134 ready.push(w);
1135 }
1136 }
1137 }
1138 }
1139 }
1140 if remaining == 0 {
1141 break;
1142 }
1143 // Every pending move now lies on a cycle (each destination still
1144 // has exactly one pending reader). Save one destination in a
1145 // temporary and point its reader at the temporary; the cycle
1146 // becomes a chain and drains through `ready`.
1147 while done.get(scan).copied().unwrap_or(false) {
1148 scan += 1;
1149 }
1150 let Some(&(saved, _)) = pending.get(scan) else {
1151 return;
1152 };
1153 let Some(&ty) = self.regs.get(saved.index()) else {
1154 self.fail(BuildError::UnknownRegister {
1155 func: self.id,
1156 reg: saved,
1157 });
1158 return;
1159 };
1160 let tmp = self.temp(ty, &named);
1161 let _pc = self.emit(Inst::Mov {
1162 dst: tmp,
1163 src: saved,
1164 });
1165 if let Some(list) = readers.get(&saved) {
1166 for &j in list {
1167 if !done.get(j).copied().unwrap_or(true) {
1168 if let Some(m) = pending.get_mut(j) {
1169 m.1 = tmp;
1170 }
1171 }
1172 }
1173 }
1174 let _gone = live_readers.remove(&saved);
1175 ready.push(scan);
1176 }
1177 }
1178
1179 /// Resolves labels and produces the function.
1180 fn finish(mut self) -> Result<(FuncId, Function), BuildError> {
1181 if let Some(error) = self.error {
1182 return Err(error);
1183 }
1184 let func = self.id;
1185 if let Some(list) = &self.param_list {
1186 let sig_params = self.regs.get(..usize::from(self.params)).unwrap_or(&[]);
1187 if let Err(error) = list.validate().and_then(|()| list.fits(sig_params)) {
1188 return Err(BuildError::InvalidParams {
1189 owner: Callee::Func(func),
1190 error,
1191 });
1192 }
1193 }
1194 let len = self.pc();
1195 let labels = core::mem::take(&mut self.labels);
1196 let resolve =
1197 |label: Label| -> Result<u32, BuildError> {
1198 labels.get(label.index as usize).copied().flatten().ok_or(
1199 BuildError::UnboundLabel {
1200 func,
1201 label: label.index,
1202 },
1203 )
1204 };
1205 let in_code = |pc: u32| -> Result<Target, BuildError> {
1206 if pc < len {
1207 Ok(Target(pc))
1208 } else {
1209 Err(BuildError::TargetOutOfRange { func, target: pc })
1210 }
1211 };
1212
1213 for &(pc, label) in &self.fixups {
1214 let target = in_code(resolve(label)?)?;
1215 if let Some(inst) = self.code.get_mut(pc as usize) {
1216 *inst = match *inst {
1217 Inst::Jmp { .. } => Inst::Jmp { target },
1218 Inst::JmpIf { cond, .. } => Inst::JmpIf { cond, target },
1219 Inst::JmpIfNot { cond, .. } => Inst::JmpIfNot { cond, target },
1220 other => other,
1221 };
1222 }
1223 }
1224 // One pass over the code, one dispatch per instruction:
1225 // - every branch, label-built or emitted raw, must land on an
1226 // instruction (this is the guarantee; the fix-ups above only fill
1227 // the placeholders);
1228 // - `overflow = promote` yields an f64 where an integer does not fit,
1229 // so it is only valid with a `dyn` destination. Where the
1230 // destination's declared type is known here, enforce that; an
1231 // undeclared register is left to the verifier.
1232 for (pc, inst) in self.code.iter().enumerate() {
1233 let (target, promote) = inst.build_checks();
1234 if let Some(target) = target {
1235 let _target = in_code(target.0)?;
1236 }
1237 if promote {
1238 let declared = inst.dst().and_then(|d| self.regs.get(d.index()));
1239 if declared.is_some_and(|&ty| ty != ValType::Dyn) {
1240 let pc = u32::try_from(pc).unwrap_or(u32::MAX);
1241 return Err(BuildError::PromoteNotDynamic { func, pc });
1242 }
1243 }
1244 }
1245
1246 let mut tables = Vec::with_capacity(self.tables.len());
1247 for (targets, default) in &self.tables {
1248 let mut resolved = Vec::with_capacity(targets.len());
1249 for &l in targets {
1250 resolved.push(in_code(resolve(l)?)?);
1251 }
1252 tables.push(JumpTable {
1253 targets: resolved,
1254 default: in_code(resolve(*default)?)?,
1255 });
1256 }
1257
1258 let mut handlers = Vec::with_capacity(self.handlers.len());
1259 for &(start, end, handler, catch) in &self.handlers {
1260 let (start, end) = (resolve(start)?, resolve(end)?);
1261 if start > end {
1262 return Err(BuildError::InvalidRange { func });
1263 }
1264 let target = in_code(resolve(handler)?)?;
1265 handlers.push(Handler {
1266 start,
1267 end,
1268 target,
1269 catch,
1270 });
1271 }
1272
1273 let mut locals = Vec::with_capacity(self.locals.len());
1274 for &(reg, name, start, end) in &self.locals {
1275 let (start, end) = (resolve(start)?, resolve(end)?);
1276 if start > end {
1277 return Err(BuildError::InvalidRange { func });
1278 }
1279 locals.push(LocalVar {
1280 reg,
1281 name,
1282 start,
1283 end,
1284 });
1285 }
1286
1287 Ok((
1288 func,
1289 Function {
1290 name: self.name,
1291 sig: self.sig,
1292 params: self.param_list,
1293 regs: self.regs,
1294 captures: self.captures,
1295 names: self.names,
1296 type_refs: self.type_refs,
1297 tables,
1298 shapes: self.shapes,
1299 handlers,
1300 code: self.code,
1301 lines: self.lines,
1302 locals,
1303 },
1304 ))
1305 }
1306}
1307
1308/// Builds a [`Module`].
1309///
1310/// Strings, structural types (everything but structs), and constants are
1311/// deduplicated. Ids are dense and assigned in creation order. Functions are
1312/// declared with [`function`](Self::function), which fixes their
1313/// [`FuncId`] up front so they can call each other in any order, and are
1314/// added with [`add_function`](Self::add_function).
1315///
1316/// # Examples
1317///
1318/// ```
1319/// use bytecode_lang::{Const, ExportItem, Inst, ModuleBuilder, ValType};
1320///
1321/// let mut m = ModuleBuilder::new();
1322/// let greeting = m.string("hello");
1323/// let k = m.constant(Const::Str(greeting));
1324/// let mut f = m.function("greet", &[], &[ValType::Str]);
1325/// let s = f.reg(ValType::Str);
1326/// f.emit(Inst::LoadConst { dst: s, k });
1327/// f.ret(s);
1328/// let greet = m.add_function(f).unwrap();
1329/// m.export("greet", ExportItem::Func(greet));
1330/// let module = m.finish().unwrap();
1331/// assert_eq!(module.string(greeting), Some("hello"));
1332/// ```
1333#[derive(Clone, Debug, Default)]
1334pub struct ModuleBuilder {
1335 strings: Strings,
1336 string_index: BTreeMap<Box<str>, StrId>,
1337 types: Vec<Option<TypeDef>>,
1338 type_index: BTreeMap<TypeDef, TypeId>,
1339 consts: Vec<Const>,
1340 const_index: BTreeMap<Const, ConstId>,
1341 imports: Vec<Import>,
1342 globals: Vec<Global>,
1343 functions: Vec<Option<Function>>,
1344 /// The (name, signature) each declared function slot was created with.
1345 declared: Vec<(StrId, TypeId)>,
1346 exports: Vec<Export>,
1347 hooks: BTreeMap<Hook, Callee>,
1348 name: Option<StrId>,
1349 start: Option<FuncId>,
1350 error: Option<BuildError>,
1351}
1352
1353/// The next dense id for a table of `len` entries, or `None` when the
1354/// table is full.
1355fn next_id(len: usize) -> Option<u32> {
1356 u32::try_from(len).ok().filter(|&id| id != u32::MAX)
1357}
1358
1359impl ModuleBuilder {
1360 /// An empty module builder.
1361 ///
1362 /// # Examples
1363 ///
1364 /// ```
1365 /// use bytecode_lang::ModuleBuilder;
1366 ///
1367 /// let module = ModuleBuilder::new().finish().unwrap();
1368 /// assert!(module.functions().is_empty());
1369 /// ```
1370 #[must_use]
1371 pub fn new() -> Self {
1372 Self::default()
1373 }
1374
1375 fn fail(&mut self, error: BuildError) {
1376 if self.error.is_none() {
1377 self.error = Some(error);
1378 }
1379 }
1380
1381 /// The id of the string `s`, adding it on first use.
1382 ///
1383 /// # Examples
1384 ///
1385 /// ```
1386 /// use bytecode_lang::{ModuleBuilder, StrId};
1387 ///
1388 /// let mut m = ModuleBuilder::new();
1389 /// assert_eq!(m.string("a"), StrId(0));
1390 /// assert_eq!(m.string("b"), StrId(1));
1391 /// assert_eq!(m.string("a"), StrId(0));
1392 /// ```
1393 pub fn string(&mut self, s: &str) -> StrId {
1394 if let Some(&id) = self.string_index.get(s) {
1395 return id;
1396 }
1397 let Some(id) = next_id(self.strings.len()) else {
1398 self.fail(BuildError::ModuleFull("string"));
1399 return StrId(u32::MAX);
1400 };
1401 if !self.strings.push(s) {
1402 self.fail(BuildError::ModuleFull("string"));
1403 return StrId(u32::MAX);
1404 }
1405 let _previous = self.string_index.insert(Box::from(s), StrId(id));
1406 StrId(id)
1407 }
1408
1409 /// Adds a type and returns its id. Structural types (functions, arrays,
1410 /// maps, cells, iterators) are deduplicated; every struct is a new type.
1411 ///
1412 /// # Examples
1413 ///
1414 /// ```
1415 /// use bytecode_lang::{ModuleBuilder, StructDef, TypeDef, ValType};
1416 ///
1417 /// let mut m = ModuleBuilder::new();
1418 /// let a = m.add_type(TypeDef::Array(ValType::I64));
1419 /// assert_eq!(m.add_type(TypeDef::Array(ValType::I64)), a);
1420 /// let s1 = m.add_type(TypeDef::Struct(StructDef::default()));
1421 /// let s2 = m.add_type(TypeDef::Struct(StructDef::default()));
1422 /// assert_ne!(s1, s2);
1423 /// ```
1424 pub fn add_type(&mut self, def: TypeDef) -> TypeId {
1425 if def.is_structural() {
1426 if let Some(&id) = self.type_index.get(&def) {
1427 return id;
1428 }
1429 }
1430 let Some(id) = next_id(self.types.len()) else {
1431 self.fail(BuildError::ModuleFull("type"));
1432 return TypeId(u32::MAX);
1433 };
1434 if def.is_structural() {
1435 let _previous = self.type_index.insert(def.clone(), TypeId(id));
1436 }
1437 self.types.push(Some(def));
1438 TypeId(id)
1439 }
1440
1441 /// Reserves a type id to be defined later with
1442 /// [`define_type`](Self::define_type): how a struct refers to itself
1443 /// (a list node's `next` field) or two structs refer to each other.
1444 ///
1445 /// # Examples
1446 ///
1447 /// ```
1448 /// use bytecode_lang::{Field, ModuleBuilder, StructDef, TypeDef, ValType};
1449 ///
1450 /// let mut m = ModuleBuilder::new();
1451 /// let node = m.reserve_type();
1452 /// let (name, next) = (m.string("Node"), m.string("next"));
1453 /// m.define_type(node, TypeDef::Struct(StructDef {
1454 /// name,
1455 /// parent: None,
1456 /// fields: vec![Field { name: next, ty: ValType::Ref(node) }],
1457 /// methods: vec![],
1458 /// }));
1459 /// assert!(m.finish().is_ok());
1460 /// ```
1461 pub fn reserve_type(&mut self) -> TypeId {
1462 let Some(id) = next_id(self.types.len()) else {
1463 self.fail(BuildError::ModuleFull("type"));
1464 return TypeId(u32::MAX);
1465 };
1466 self.types.push(None);
1467 TypeId(id)
1468 }
1469
1470 /// Defines a type reserved with [`reserve_type`](Self::reserve_type).
1471 /// Defining an id that was not reserved, or defining it twice, is
1472 /// recorded as [`UndefinedType`](BuildError::UndefinedType).
1473 ///
1474 /// # Examples
1475 ///
1476 /// ```
1477 /// use bytecode_lang::{ModuleBuilder, TypeDef, ValType};
1478 ///
1479 /// let mut m = ModuleBuilder::new();
1480 /// let t = m.reserve_type();
1481 /// m.define_type(t, TypeDef::Cell(ValType::Dyn));
1482 /// assert_eq!(m.finish().unwrap().type_def(t), Some(&TypeDef::Cell(ValType::Dyn)));
1483 /// ```
1484 pub fn define_type(&mut self, id: TypeId, def: TypeDef) {
1485 match self.types.get_mut(id.index()) {
1486 Some(slot @ None) => *slot = Some(def),
1487 _ => self.fail(BuildError::UndefinedType(id)),
1488 }
1489 }
1490
1491 /// The id of the function type `(params) -> (results)`.
1492 ///
1493 /// # Examples
1494 ///
1495 /// ```
1496 /// use bytecode_lang::{ModuleBuilder, ValType};
1497 ///
1498 /// let mut m = ModuleBuilder::new();
1499 /// let a = m.func_type(&[ValType::Dyn], &[ValType::Dyn]);
1500 /// assert_eq!(m.func_type(&[ValType::Dyn], &[ValType::Dyn]), a);
1501 /// ```
1502 pub fn func_type(&mut self, params: &[ValType], results: &[ValType]) -> TypeId {
1503 self.add_type(TypeDef::Func(FuncType {
1504 params: params.to_vec(),
1505 results: results.to_vec(),
1506 }))
1507 }
1508
1509 /// Adds a constant and returns its id; equal constants share one id.
1510 /// An aggregate may only refer to constants that already exist
1511 /// (otherwise [`ConstForwardRef`](BuildError::ConstForwardRef)).
1512 ///
1513 /// # Examples
1514 ///
1515 /// ```
1516 /// use bytecode_lang::{Const, ModuleBuilder};
1517 ///
1518 /// let mut m = ModuleBuilder::new();
1519 /// let one = m.constant(Const::Int(1));
1520 /// let two = m.constant(Const::Int(2));
1521 /// let list = m.constant(Const::Array(vec![one, two, one]));
1522 /// assert_eq!(m.constant(Const::Int(1)), one);
1523 /// assert_eq!(m.finish().unwrap().consts().len(), 3);
1524 /// # let _ = list;
1525 /// ```
1526 pub fn constant(&mut self, c: Const) -> ConstId {
1527 if let Some(&id) = self.const_index.get(&c) {
1528 return id;
1529 }
1530 let len = self.consts.len();
1531 if let Some(bad) = c.children().find(|child| child.index() >= len) {
1532 self.fail(BuildError::ConstForwardRef(bad));
1533 return ConstId(u32::MAX);
1534 }
1535 let Some(id) = next_id(len) else {
1536 self.fail(BuildError::ModuleFull("constant"));
1537 return ConstId(u32::MAX);
1538 };
1539 let _previous = self.const_index.insert(c.clone(), ConstId(id));
1540 self.consts.push(c);
1541 ConstId(id)
1542 }
1543
1544 /// Adds a host-function import.
1545 ///
1546 /// # Examples
1547 ///
1548 /// ```
1549 /// use bytecode_lang::{ImportId, ModuleBuilder, ValType};
1550 ///
1551 /// let mut m = ModuleBuilder::new();
1552 /// let sig = m.func_type(&[ValType::Str], &[]);
1553 /// assert_eq!(m.import("ls.io", "print", sig), ImportId(0));
1554 /// ```
1555 pub fn import(&mut self, module: &str, name: &str, sig: TypeId) -> ImportId {
1556 let (module, name) = (self.string(module), self.string(name));
1557 let Some(id) = next_id(self.imports.len()) else {
1558 self.fail(BuildError::ModuleFull("import"));
1559 return ImportId(u32::MAX);
1560 };
1561 self.imports.push(Import {
1562 module,
1563 name,
1564 sig,
1565 params: None,
1566 });
1567 ImportId(id)
1568 }
1569
1570 /// Adds a host-function import with a dynamic-call signature, so it can
1571 /// be a first-class value taking named, spread, extra, missing, and
1572 /// by-reference arguments through `dcall`/`dcall_shape` (PHP's
1573 /// `printf(...$args)`, `array_push(&$a, ...$v)`). `sig` must already be
1574 /// a function type of this builder; the list is checked against it as
1575 /// [`FunctionBuilder::set_params`] checks a function's.
1576 ///
1577 /// # Examples
1578 ///
1579 /// ```
1580 /// use bytecode_lang::{ModuleBuilder, Param, ParamKind, ParamList, ValType};
1581 ///
1582 /// // printf(string $format, mixed ...$values): int
1583 /// let mut m = ModuleBuilder::new();
1584 /// let format = m.string("format");
1585 /// let sig = m.func_type(&[ValType::Dyn, ValType::Dyn], &[ValType::Dyn]);
1586 /// let list = ParamList::new(vec![Param::normal(format), Param::new(ParamKind::RestMap, None)]);
1587 /// let printf = m.import_with_params("php.std", "printf", sig, list);
1588 /// let module = m.finish().unwrap();
1589 /// assert!(module.import(printf).unwrap().params.is_some());
1590 /// ```
1591 pub fn import_with_params(
1592 &mut self,
1593 module: &str,
1594 name: &str,
1595 sig: TypeId,
1596 params: ParamList,
1597 ) -> ImportId {
1598 let id = self.import(module, name, sig);
1599 let checked = match self.types.get(sig.index()) {
1600 Some(Some(TypeDef::Func(ft))) => {
1601 params.validate().and_then(|()| params.fits(&ft.params))
1602 }
1603 _ => Err(ParamError::Signature { index: 0 }),
1604 };
1605 if let Err(error) = checked {
1606 self.fail(BuildError::InvalidParams {
1607 owner: Callee::Import(id),
1608 error,
1609 });
1610 }
1611 if let Some(import) = self.imports.get_mut(id.index()) {
1612 import.params = Some(params);
1613 }
1614 id
1615 }
1616
1617 /// Adds a global.
1618 ///
1619 /// # Examples
1620 ///
1621 /// ```
1622 /// use bytecode_lang::{Const, GlobalId, ModuleBuilder, ValType};
1623 ///
1624 /// let mut m = ModuleBuilder::new();
1625 /// let zero = m.constant(Const::Int(0));
1626 /// assert_eq!(m.global("hits", ValType::I64, true, Some(zero)), GlobalId(0));
1627 /// ```
1628 pub fn global(
1629 &mut self,
1630 name: &str,
1631 ty: ValType,
1632 mutable: bool,
1633 init: Option<ConstId>,
1634 ) -> GlobalId {
1635 let name = self.string(name);
1636 let Some(id) = next_id(self.globals.len()) else {
1637 self.fail(BuildError::ModuleFull("global"));
1638 return GlobalId(u32::MAX);
1639 };
1640 self.globals.push(Global {
1641 name,
1642 ty,
1643 mutable,
1644 init,
1645 });
1646 GlobalId(id)
1647 }
1648
1649 /// Declares a function and returns its builder. The function's id is
1650 /// fixed now ([`FunctionBuilder::id`]), so other functions can call it
1651 /// before it is added.
1652 ///
1653 /// # Examples
1654 ///
1655 /// ```
1656 /// use bytecode_lang::{FuncId, ModuleBuilder, ValType};
1657 ///
1658 /// let mut m = ModuleBuilder::new();
1659 /// let even = m.function("even", &[ValType::I64], &[ValType::Bool]);
1660 /// let odd = m.function("odd", &[ValType::I64], &[ValType::Bool]);
1661 /// assert_eq!((even.id(), odd.id()), (FuncId(0), FuncId(1)));
1662 /// ```
1663 pub fn function(
1664 &mut self,
1665 name: &str,
1666 params: &[ValType],
1667 results: &[ValType],
1668 ) -> FunctionBuilder {
1669 let name = self.string(name);
1670 let sig = self.func_type(params, results);
1671 let id = match next_id(self.functions.len()) {
1672 Some(id) => {
1673 self.functions.push(None);
1674 self.declared.push((name, sig));
1675 FuncId(id)
1676 }
1677 None => {
1678 self.fail(BuildError::ModuleFull("function"));
1679 FuncId(u32::MAX)
1680 }
1681 };
1682 FunctionBuilder::new(id, name, sig, params)
1683 }
1684
1685 /// Resolves the function's labels and places it in its slot.
1686 ///
1687 /// # Errors
1688 ///
1689 /// The first error the function builder recorded, an unbound label, a
1690 /// branch target past the end, an inverted range, or
1691 /// [`ForeignFunction`](BuildError::ForeignFunction) if the builder was
1692 /// not declared by this module builder or was already added.
1693 ///
1694 /// # Examples
1695 ///
1696 /// ```
1697 /// use bytecode_lang::{BuildError, Inst, ModuleBuilder, Target};
1698 ///
1699 /// let mut m = ModuleBuilder::new();
1700 /// let mut f = m.function("f", &[], &[]);
1701 /// f.emit(Inst::Jmp { target: Target(5) }); // raw target past the end
1702 /// assert_eq!(
1703 /// m.add_function(f),
1704 /// Err(BuildError::TargetOutOfRange { func: bytecode_lang::FuncId(0), target: 5 }),
1705 /// );
1706 /// ```
1707 pub fn add_function(&mut self, f: FunctionBuilder) -> Result<FuncId, BuildError> {
1708 let id = f.id;
1709 let declared = self.declared.get(id.index()) == Some(&(f.name, f.sig));
1710 let empty = matches!(self.functions.get(id.index()), Some(None));
1711 if !declared || !empty {
1712 return Err(BuildError::ForeignFunction(id));
1713 }
1714 let (id, function) = f.finish()?;
1715 if let Some(slot) = self.functions.get_mut(id.index()) {
1716 *slot = Some(function);
1717 }
1718 Ok(id)
1719 }
1720
1721 /// Exports `item` under `name`.
1722 ///
1723 /// # Examples
1724 ///
1725 /// ```
1726 /// use bytecode_lang::{ExportItem, ModuleBuilder, ValType};
1727 ///
1728 /// let mut m = ModuleBuilder::new();
1729 /// let g = m.global("version", ValType::I32, false, None);
1730 /// m.export("version", ExportItem::Global(g));
1731 /// assert_eq!(m.finish().unwrap().exports().len(), 1);
1732 /// ```
1733 pub fn export(&mut self, name: &str, item: ExportItem) {
1734 let name = self.string(name);
1735 if next_id(self.exports.len()).is_none() {
1736 self.fail(BuildError::ModuleFull("export"));
1737 return;
1738 }
1739 self.exports.push(Export { name, item });
1740 }
1741
1742 /// Binds a dynamic-operation hook, replacing any earlier binding.
1743 ///
1744 /// # Examples
1745 ///
1746 /// ```
1747 /// use bytecode_lang::{Callee, Hook, ModuleBuilder, ValType};
1748 ///
1749 /// let mut m = ModuleBuilder::new();
1750 /// let sig = m.func_type(&[ValType::Dyn, ValType::Dyn], &[ValType::Dyn]);
1751 /// let loose_eq = m.import("mox.rt", "loose_eq", sig);
1752 /// m.hook(Hook::Eq, Callee::Import(loose_eq));
1753 /// let module = m.finish().unwrap();
1754 /// assert_eq!(module.hook(Hook::Eq), Some(Callee::Import(loose_eq)));
1755 /// ```
1756 pub fn hook(&mut self, hook: Hook, callee: Callee) {
1757 let _previous = self.hooks.insert(hook, callee);
1758 }
1759
1760 /// Names the module (the name the loader links imports against).
1761 ///
1762 /// # Examples
1763 ///
1764 /// ```
1765 /// use bytecode_lang::ModuleBuilder;
1766 ///
1767 /// let mut m = ModuleBuilder::new();
1768 /// m.set_name("app");
1769 /// assert!(m.finish().unwrap().name().is_some());
1770 /// ```
1771 pub fn set_name(&mut self, name: &str) {
1772 self.name = Some(self.string(name));
1773 }
1774
1775 /// Sets the initializer the loader runs after binding imports.
1776 ///
1777 /// # Examples
1778 ///
1779 /// ```
1780 /// use bytecode_lang::{FuncId, ModuleBuilder};
1781 ///
1782 /// let mut m = ModuleBuilder::new();
1783 /// let mut f = m.function("init", &[], &[]);
1784 /// f.ret_void();
1785 /// let init = m.add_function(f).unwrap();
1786 /// m.set_start(init);
1787 /// assert_eq!(m.finish().unwrap().start(), Some(FuncId(0)));
1788 /// ```
1789 pub fn set_start(&mut self, func: FuncId) {
1790 self.start = Some(func);
1791 }
1792
1793 /// Produces the module.
1794 ///
1795 /// # Errors
1796 ///
1797 /// The first error recorded while building, a declared function never
1798 /// added ([`UndefinedFunction`](BuildError::UndefinedFunction)), a
1799 /// reserved type never defined ([`UndefinedType`](BuildError::UndefinedType)),
1800 /// or a section too large to encode ([`TooLarge`](BuildError::TooLarge)).
1801 ///
1802 /// # Examples
1803 ///
1804 /// ```
1805 /// use bytecode_lang::{BuildError, FuncId, ModuleBuilder};
1806 ///
1807 /// let mut m = ModuleBuilder::new();
1808 /// let _forgotten = m.function("f", &[], &[]);
1809 /// assert_eq!(m.finish().unwrap_err(), BuildError::UndefinedFunction(FuncId(0)));
1810 /// ```
1811 pub fn finish(self) -> Result<Module, BuildError> {
1812 if let Some(error) = self.error {
1813 return Err(error);
1814 }
1815 let mut functions = Vec::with_capacity(self.functions.len());
1816 for (i, f) in self.functions.into_iter().enumerate() {
1817 let id = FuncId(u32::try_from(i).unwrap_or(u32::MAX));
1818 functions.push(f.ok_or(BuildError::UndefinedFunction(id))?);
1819 }
1820 let mut types = Vec::with_capacity(self.types.len());
1821 for (i, t) in self.types.into_iter().enumerate() {
1822 let id = TypeId(u32::try_from(i).unwrap_or(u32::MAX));
1823 types.push(t.ok_or(BuildError::UndefinedType(id))?);
1824 }
1825 let module = Module {
1826 strings: self.strings,
1827 types,
1828 consts: self.consts,
1829 imports: self.imports,
1830 globals: self.globals,
1831 functions,
1832 exports: self.exports,
1833 hooks: self
1834 .hooks
1835 .into_iter()
1836 .map(|(hook, callee)| HookBinding { hook, callee })
1837 .collect(),
1838 name: self.name,
1839 start: self.start,
1840 };
1841 for (section, size) in (1u32..).zip(section_sizes(&module)) {
1842 if size > u64::from(u32::MAX) {
1843 return Err(BuildError::TooLarge { section });
1844 }
1845 }
1846 Ok(module)
1847 }
1848}