use crate::{compiler::error::CompilationError, BranchOffset, InstrLoc, LabelRef};
use alloc::vec::Vec;
use core::{
fmt::{self, Display},
slice::Iter as SliceIter,
};
#[derive(Debug, Copy, Clone)]
pub enum Label {
Pinned(InstrLoc),
Unpinned,
}
#[derive(Debug, Default)]
pub struct LabelRegistry {
labels: Vec<Label>,
users: Vec<LabelUser>,
}
#[derive(Debug)]
pub struct LabelUser {
label: LabelRef,
user: InstrLoc,
}
impl LabelUser {
pub fn new(label: LabelRef, user: InstrLoc) -> Self {
Self { label, user }
}
}
#[derive(Debug, Copy, Clone)]
pub enum LabelError {
AlreadyPinned {
label: LabelRef,
pinned_to: InstrLoc,
},
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 an 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);
index as LabelRef
}
#[inline]
fn get_label(&self, label: LabelRef) -> &Label {
&self.labels[label as usize]
}
#[inline]
fn get_label_mut(&mut self, label: LabelRef) -> &mut Label {
&mut self.labels[label as usize]
}
pub fn pin_label(&mut self, label: LabelRef, instr: InstrLoc) -> 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: InstrLoc) {
if let unpinned @ Label::Unpinned = self.get_label_mut(label) {
*unpinned = Label::Pinned(instr)
}
}
pub fn try_resolve_label(
&mut self,
label: LabelRef,
user: InstrLoc,
) -> Result<BranchOffset, CompilationError> {
let offset = match *self.get_label(label) {
Label::Pinned(target) => BranchOffset::from_src_to_dst(user, target)
.ok_or(CompilationError::BranchOffsetOutOfBounds)?,
Label::Unpinned => {
self.users.push(LabelUser::new(label, user));
BranchOffset::uninit()
}
};
Ok(offset)
}
fn resolve_label(&self, label: LabelRef) -> Result<InstrLoc, 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 = (InstrLoc, Result<BranchOffset, CompilationError>);
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::from_src_to_dst(src, dst)
.ok_or(CompilationError::BranchOffsetOutOfBounds);
Some((src, offset))
}
}