use crate::{engine::translator::utils::Instr, ir::BranchOffset, Error};
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 core::error::Error for LabelError {}
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,
) -> Result<BranchOffset, Error> {
let offset = match *self.get_label(label) {
Label::Pinned(target) => {
BranchOffset::from_src_to_dst(u32::from(user), u32::from(target))?
}
Label::Unpinned => {
self.users.push(LabelUser::new(label, user));
BranchOffset::uninit()
}
};
Ok(offset)
}
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 Iterator for ResolvedUserIter<'_> {
type Item = (Instr, Result<BranchOffset, Error>);
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(u32::from(src), u32::from(dst)).map_err(Into::into);
Some((src, offset))
}
}