use crate::{
engine::translator::func::{
encoder::{BytePos, Pos},
utils::Reset,
},
ir::{BranchOffset, Op},
};
use alloc::vec::Vec;
use core::{
error::Error as CoreError,
fmt::{self, Display},
slice::Iter as SliceIter,
};
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct LabelRef(usize);
impl From<usize> for LabelRef {
#[inline]
fn from(value: usize) -> Self {
Self(value)
}
}
impl From<LabelRef> for usize {
#[inline]
fn from(value: LabelRef) -> Self {
value.0
}
}
#[derive(Debug, Default)]
pub struct LabelRegistry {
labels: RegisteredLabels,
users: Vec<LabelUser>,
}
#[derive(Debug, Default)]
pub struct RegisteredLabels {
labels: Vec<Label>,
}
#[derive(Debug, Copy, Clone)]
pub enum Label {
Pinned(Pos<Op>),
Unpinned,
}
impl Reset for RegisteredLabels {
fn reset(&mut self) {
self.labels.clear();
}
}
impl RegisteredLabels {
#[inline]
pub fn push(&mut self) -> LabelRef {
let index = self.labels.len();
self.labels.push(Label::Unpinned);
LabelRef::from(index)
}
#[inline]
fn get(&self, lref: LabelRef) -> Label {
self.labels[usize::from(lref)]
}
#[inline]
fn get_mut(&mut self, lref: LabelRef) -> &mut Label {
&mut self.labels[usize::from(lref)]
}
}
#[derive(Debug, Copy, Clone)]
struct LabelUser {
dst: LabelRef,
src: BytePos,
pos: Pos<BranchOffset>,
}
#[derive(Debug, Copy, Clone)]
enum LabelError {
AlreadyPinned { label: LabelRef, target: Pos<Op> },
}
impl LabelError {
#[cold]
#[inline]
fn already_pinned(label: LabelRef, target: Pos<Op>) -> Self {
Self::AlreadyPinned { label, target }
}
}
impl CoreError for LabelError {}
impl Display for LabelError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::AlreadyPinned { label, target } => {
write!(
f,
"trying to pin already pinned label {label:?} (pinned to {target:?})"
)
}
}
}
}
impl Reset for LabelRegistry {
fn reset(&mut self) {
self.labels.reset();
self.users.clear();
}
}
impl LabelRegistry {
pub fn new_label(&mut self) -> LabelRef {
self.labels.push()
}
pub fn new_user(&mut self, dst: LabelRef, src: BytePos, pos: Pos<BranchOffset>) {
self.users.push(LabelUser { dst, src, pos })
}
pub fn get_label(&self, lref: LabelRef) -> Label {
self.labels.get(lref)
}
pub fn is_pinned(&self, lref: LabelRef) -> bool {
matches!(self.get_label(lref), Label::Pinned(_))
}
#[track_caller]
pub fn pin_label(&mut self, label: LabelRef, target: Pos<Op>) {
if let Err(error) = self.pin_label_or_err(label, target) {
panic!("failed to pin label: {error}")
}
}
fn pin_label_or_err(&mut self, label: LabelRef, target: Pos<Op>) -> Result<(), LabelError> {
let cell = self.labels.get_mut(label);
if let Label::Pinned(pinned) = cell {
return Err(LabelError::already_pinned(label, *pinned));
}
debug_assert!(matches!(cell, Label::Unpinned));
*cell = Label::Pinned(target);
Ok(())
}
pub fn pin_label_if_unpinned(&mut self, label: LabelRef, target: Pos<Op>) {
let cell = self.labels.get_mut(label);
if matches!(cell, Label::Unpinned) {
*cell = Label::Pinned(target)
}
}
pub fn resolved_users(&self) -> ResolvedUserIter<'_> {
ResolvedUserIter {
users: self.users.iter(),
labels: &self.labels,
}
}
}
#[derive(Debug)]
pub struct ResolvedUserIter<'a> {
users: SliceIter<'a, LabelUser>,
labels: &'a RegisteredLabels,
}
#[derive(Debug, Copy, Clone)]
pub struct ResolvedLabelUser {
pub src: BytePos,
pub dst: Pos<Op>,
pub pos: Pos<BranchOffset>,
}
impl Iterator for ResolvedUserIter<'_> {
type Item = ResolvedLabelUser;
fn next(&mut self) -> Option<Self::Item> {
let next = self.users.next()?;
let src = next.src;
let pos = next.pos;
let Label::Pinned(dst) = self.labels.get(next.dst) else {
panic!("encountered unexpected unpinned label: {:?}", next.dst)
};
Some(ResolvedLabelUser { src, dst, pos })
}
}