use super::Instr;
use crate::engine::bytecode::BranchOffset;
use alloc::vec::Vec;
use core::{
fmt::{self, Display},
slice::Iter as SliceIter,
};
#[derive(Debug, Copy, Clone)]
pub enum Label {
Pinned(Instr),
Unpinned,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct LabelRef(u32);
impl LabelRef {
#[inline]
fn into_usize(self) -> usize {
self.0 as usize
}
}
#[derive(Debug, Default)]
pub struct LabelRegistry {
labels: Vec<Label>,
users: Vec<LabelUser>,
}
#[derive(Debug)]
pub struct LabelUser {
label: LabelRef,
user: Instr,
}
impl LabelUser {
pub fn new(label: LabelRef, user: Instr) -> Self {
Self { label, user }
}
}
#[derive(Debug, Copy, Clone)]
pub enum LabelError {
AlreadyPinned { label: LabelRef, pinned_to: Instr },
Unpinned { label: LabelRef },
}
impl Display for LabelError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
LabelError::AlreadyPinned { label, pinned_to } => {
write!(
f,
"trying to pin already pinned label {label:?} (pinned to {pinned_to:?})"
)
}
LabelError::Unpinned { label } => {
write!(f, "trying to resolve unpinned label: {label:?}")
}
}
}
}
impl LabelRegistry {
pub fn reset(&mut self) {
self.labels.clear();
self.users.clear();
}
pub fn new_label(&mut self) -> LabelRef {
let index: u32 = self
.labels
.len()
.try_into()
.unwrap_or_else(|err| panic!("cannot have more than u32::MAX label refs: {err}"));
self.labels.push(Label::Unpinned);
LabelRef(index)
}
#[inline]
fn get_label(&self, label: LabelRef) -> &Label {
&self.labels[label.into_usize()]
}
#[inline]
fn get_label_mut(&mut self, label: LabelRef) -> &mut Label {
&mut self.labels[label.into_usize()]
}
pub fn pin_label(&mut self, label: LabelRef, instr: Instr) -> Result<(), LabelError> {
match self.get_label_mut(label) {
Label::Pinned(pinned) => Err(LabelError::AlreadyPinned {
label,
pinned_to: *pinned,
}),
unpinned @ Label::Unpinned => {
*unpinned = Label::Pinned(instr);
Ok(())
}
}
}
pub fn try_pin_label(&mut self, label: LabelRef, instr: Instr) {
if let unpinned @ Label::Unpinned = self.get_label_mut(label) {
*unpinned = Label::Pinned(instr)
}
}
pub fn try_resolve_label(&mut self, label: LabelRef, user: Instr) -> BranchOffset {
match *self.get_label(label) {
Label::Pinned(target) => BranchOffset::init(user, target),
Label::Unpinned => {
self.users.push(LabelUser::new(label, user));
BranchOffset::uninit()
}
}
}
fn resolve_label(&self, label: LabelRef) -> Result<Instr, LabelError> {
match self.get_label(label) {
Label::Pinned(instr) => Ok(*instr),
Label::Unpinned => Err(LabelError::Unpinned { label }),
}
}
pub fn resolved_users(&self) -> ResolvedUserIter {
ResolvedUserIter {
users: self.users.iter(),
registry: self,
}
}
}
#[derive(Debug)]
pub struct ResolvedUserIter<'a> {
users: SliceIter<'a, LabelUser>,
registry: &'a LabelRegistry,
}
impl<'a> Iterator for ResolvedUserIter<'a> {
type Item = (Instr, BranchOffset);
fn next(&mut self) -> Option<Self::Item> {
let next = self.users.next()?;
let src = next.user;
let dst = self
.registry
.resolve_label(next.label)
.unwrap_or_else(|err| panic!("failed to resolve user: {err}"));
let offset = BranchOffset::init(src, dst);
Some((src, offset))
}
}