use super::labels::{LabelRef, LabelRegistry};
use crate::engine::{
bytecode::{BranchOffset, Instruction},
Engine,
FuncBody,
};
use alloc::vec::Vec;
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct Instr(u32);
impl Instr {
pub fn from_usize(value: usize) -> Self {
let value = value.try_into().unwrap_or_else(|error| {
panic!("invalid index {value} for instruction reference: {error}")
});
Self(value)
}
pub fn into_usize(self) -> usize {
self.0 as usize
}
pub fn from_u32(value: u32) -> Self {
Self(value)
}
pub fn into_u32(self) -> u32 {
self.0
}
}
#[derive(Debug, Copy, Clone)]
pub struct RelativeDepth(u32);
impl RelativeDepth {
pub fn into_u32(self) -> u32 {
self.0
}
pub fn from_u32(relative_depth: u32) -> Self {
Self(relative_depth)
}
}
#[derive(Debug, Default)]
pub struct InstructionsBuilder {
insts: Vec<Instruction>,
labels: LabelRegistry,
}
impl InstructionsBuilder {
pub fn reset(&mut self) {
self.insts.clear();
self.labels.reset();
}
pub fn current_pc(&self) -> Instr {
Instr::from_usize(self.insts.len())
}
pub fn new_label(&mut self) -> LabelRef {
self.labels.new_label()
}
pub fn pin_label_if_unpinned(&mut self, label: LabelRef) {
self.labels.try_pin_label(label, self.current_pc())
}
pub fn pin_label(&mut self, label: LabelRef) {
self.labels
.pin_label(label, self.current_pc())
.unwrap_or_else(|err| panic!("failed to pin label: {err}"));
}
pub fn push_inst(&mut self, inst: Instruction) -> Instr {
let idx = self.current_pc();
self.insts.push(inst);
idx
}
pub fn try_resolve_label(&mut self, label: LabelRef) -> BranchOffset {
let user = self.current_pc();
self.try_resolve_label_for(label, user)
}
pub fn try_resolve_label_for(&mut self, label: LabelRef, instr: Instr) -> BranchOffset {
self.labels.try_resolve_label(label, instr)
}
#[must_use]
pub fn finish(
&mut self,
engine: &Engine,
len_locals: usize,
max_stack_height: usize,
) -> FuncBody {
self.update_branch_offsets();
engine.alloc_func_body(len_locals, max_stack_height, self.insts.drain(..))
}
fn update_branch_offsets(&mut self) {
for (user, offset) in self.labels.resolved_users() {
self.insts[user.into_usize()].update_branch_offset(offset);
}
}
}
impl Instruction {
pub fn update_branch_offset(&mut self, offset: BranchOffset) {
match self {
Instruction::Br(params)
| Instruction::BrIfEqz(params)
| Instruction::BrIfNez(params) => params.init(offset),
_ => panic!("tried to update branch offset of a non-branch instruction: {self:?}"),
}
}
}