#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum BranchKind {
#[default]
Conditional,
Jump {
call: bool,
},
Indirect {
returns: bool,
call: bool,
},
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct BtbHit {
pub target: u64,
pub kind: BranchKind,
}
#[derive(Clone, Copy, Debug, Default)]
struct BtbEntry {
tag: u64,
target: u64,
kind: BranchKind,
valid: bool,
}
#[derive(Debug)]
pub struct Btb {
table: Vec<BtbEntry>,
num_sets: usize,
ways: usize,
replace_ptr: Vec<u8>,
}
impl Btb {
pub fn new(size: usize, ways: usize) -> Self {
let ways = ways.max(1);
let num_sets = (size / ways).max(1);
debug_assert!(num_sets.is_power_of_two(), "BTB num_sets must be a power of 2");
Self {
table: vec![BtbEntry::default(); num_sets * ways],
num_sets,
ways,
replace_ptr: vec![0; num_sets],
}
}
#[inline]
const fn set_index(&self, pc: u64) -> usize {
((pc >> 2) as usize) & (self.num_sets - 1)
}
pub fn lookup(&self, pc: u64) -> Option<BtbHit> {
let set = self.set_index(pc);
let base = set * self.ways;
self.table[base..base + self.ways]
.iter()
.find(|e| e.valid && e.tag == pc)
.map(|e| BtbHit { target: e.target, kind: e.kind })
}
pub fn invalidate(&mut self, pc: u64) {
let set = self.set_index(pc);
let base = set * self.ways;
for e in &mut self.table[base..base + self.ways] {
if e.valid && e.tag == pc {
e.valid = false;
}
}
}
pub fn update(&mut self, pc: u64, target: u64, kind: BranchKind) {
let set = self.set_index(pc);
let base = set * self.ways;
let entry = BtbEntry { tag: pc, target, kind, valid: true };
for w in 0..self.ways {
let e = &mut self.table[base + w];
if e.valid && e.tag == pc {
*e = entry;
return;
}
}
for w in 0..self.ways {
let e = &mut self.table[base + w];
if !e.valid {
*e = entry;
return;
}
}
let victim = self.replace_ptr[set] as usize % self.ways;
self.replace_ptr[set] = ((victim + 1) % self.ways) as u8;
self.table[base + victim] = entry;
}
}