use super::map::RenameMap;
use crate::uarch::pipeline::rob::RobTag;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct CheckpointId(pub u8);
#[derive(Clone, Debug)]
pub struct Checkpoint {
pub branch_tag: RobTag,
pub rename_map: RenameMap,
}
#[derive(Debug)]
pub struct ReservedCheckpoint<'a> {
table: &'a mut CheckpointTable,
index: usize,
}
impl ReservedCheckpoint<'_> {
pub fn fill(self, branch_tag: RobTag, rename_map: &RenameMap) -> CheckpointId {
self.table.slots[self.index] =
Some(Checkpoint { branch_tag, rename_map: rename_map.clone() });
self.table.count += 1;
CheckpointId(self.index as u8)
}
}
#[derive(Debug)]
pub struct CheckpointTable {
slots: Vec<Option<Checkpoint>>,
count: usize,
}
impl CheckpointTable {
pub fn new(capacity: usize) -> Self {
let mut slots = Vec::with_capacity(capacity);
slots.resize_with(capacity, || None);
Self { slots, count: 0 }
}
#[inline]
pub const fn capacity(&self) -> usize {
self.slots.len()
}
#[cfg(test)]
#[inline]
pub const fn is_full(&self) -> bool {
self.count == self.slots.len()
}
#[cfg(test)]
#[inline]
pub const fn available(&self) -> usize {
self.slots.len() - self.count
}
pub fn reserve(&mut self) -> Option<ReservedCheckpoint<'_>> {
let index = self.slots.iter().position(Option::is_none)?;
Some(ReservedCheckpoint { table: self, index })
}
#[cfg(test)]
pub fn allocate(&mut self, branch_tag: RobTag, rename_map: &RenameMap) -> Option<CheckpointId> {
Some(self.reserve()?.fill(branch_tag, rename_map))
}
pub fn find_by_tag(&self, tag: RobTag) -> Option<&Checkpoint> {
self.slots.iter().filter_map(|s| s.as_ref()).find(|c| c.branch_tag == tag)
}
pub fn free(&mut self, id: CheckpointId) {
let idx = id.0 as usize;
if idx < self.slots.len() && self.slots[idx].is_some() {
self.slots[idx] = None;
self.count -= 1;
}
}
pub fn flush_after(&mut self, keep_tag: RobTag) {
for slot in &mut self.slots {
if let &mut Some(ref ckpt) = slot
&& ckpt.branch_tag.is_newer_than(keep_tag)
{
*slot = None;
self.count -= 1;
}
}
}
pub fn flush_all(&mut self) {
self.slots.fill(None);
self.count = 0;
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used, unused_results)]
mod tests {
use super::*;
use crate::isa::reg::RegIdx;
use crate::uarch::pipeline::rename::prf::PhysReg;
fn make_rename_map(marker: u16) -> RenameMap {
let mut rm = RenameMap::new();
rm.set(RegIdx::new(1), false, PhysReg(marker));
rm
}
#[test]
fn test_allocate_and_find() {
let mut table = CheckpointTable::new(4);
assert_eq!(table.available(), 4);
assert!(!table.is_full());
let rm = make_rename_map(100);
let tag = RobTag(10);
let id = table.allocate(tag, &rm).unwrap();
assert_eq!(table.available(), 3);
let ckpt = table.find_by_tag(tag).unwrap();
assert_eq!(ckpt.branch_tag, tag);
assert_eq!(ckpt.rename_map.get(RegIdx::new(1), false), PhysReg(100));
table.free(id);
assert_eq!(table.available(), 4);
assert!(table.find_by_tag(tag).is_none());
}
#[test]
fn test_full_table() {
let mut table = CheckpointTable::new(2);
let rm = make_rename_map(1);
table.allocate(RobTag(1), &rm).unwrap();
table.allocate(RobTag(2), &rm).unwrap();
assert!(table.is_full());
assert!(table.allocate(RobTag(3), &rm).is_none());
}
#[test]
fn test_flush_after() {
let mut table = CheckpointTable::new(4);
let rm = make_rename_map(1);
table.allocate(RobTag(1), &rm).unwrap();
table.allocate(RobTag(2), &rm).unwrap();
table.allocate(RobTag(3), &rm).unwrap();
table.allocate(RobTag(4), &rm).unwrap();
assert!(table.is_full());
table.flush_after(RobTag(2));
assert_eq!(table.available(), 2);
assert!(table.find_by_tag(RobTag(1)).is_some());
assert!(table.find_by_tag(RobTag(2)).is_some());
assert!(table.find_by_tag(RobTag(3)).is_none());
assert!(table.find_by_tag(RobTag(4)).is_none());
}
#[test]
fn test_flush_all() {
let mut table = CheckpointTable::new(4);
let rm = make_rename_map(1);
table.allocate(RobTag(1), &rm).unwrap();
table.allocate(RobTag(2), &rm).unwrap();
table.flush_all();
assert_eq!(table.available(), 4);
assert!(table.find_by_tag(RobTag(1)).is_none());
}
#[test]
fn test_zero_capacity() {
let mut table = CheckpointTable::new(0);
assert!(table.is_full());
assert_eq!(table.available(), 0);
let rm = make_rename_map(1);
assert!(table.allocate(RobTag(1), &rm).is_none());
table.flush_after(RobTag(1));
table.flush_all();
}
}