use crate::uarch::bpred::direction::{BranchClass, DirectionPredictor, Jump, Retired};
const TABLE_BITS: usize = 12;
const TABLE_SIZE: usize = 1 << TABLE_BITS;
#[derive(Debug)]
pub struct GSharePredictor {
ghr: u64,
pht: Vec<u8>,
}
#[derive(Clone, Copy, Debug)]
pub struct GShareHistory {
ghr: u64,
}
impl Default for GSharePredictor {
fn default() -> Self {
Self::new()
}
}
impl GSharePredictor {
pub fn new() -> Self {
Self { ghr: 0, pht: vec![1; TABLE_SIZE] }
}
#[cfg(test)]
pub const fn history(&self) -> u64 {
self.ghr
}
const fn index(pc: u64, ghr: u64) -> usize {
let pc_part = (pc >> 2) & ((TABLE_SIZE as u64) - 1);
let ghr_part = ghr & ((TABLE_SIZE as u64) - 1);
(pc_part ^ ghr_part) as usize
}
const fn shifted(ghr: u64, taken: bool) -> u64 {
((ghr << 1) | taken as u64) & ((TABLE_SIZE as u64) - 1)
}
}
impl DirectionPredictor for GSharePredictor {
type History = GShareHistory;
fn lookup(&self, pc: u64, _target: u64) -> (bool, GShareHistory) {
let taken = self.pht[Self::index(pc, self.ghr)] >= 2;
(taken, GShareHistory { ghr: self.ghr })
}
fn unconditional(&self, _pc: u64, _jump: Jump) -> GShareHistory {
GShareHistory { ghr: self.ghr }
}
fn update_histories(&mut self, _pc: u64, taken: bool, _history: &GShareHistory) {
self.ghr = Self::shifted(self.ghr, taken);
}
fn squash(&mut self, history: &GShareHistory) {
self.ghr = history.ghr;
}
fn correct(&mut self, _pc: u64, taken: bool, history: &GShareHistory) {
self.ghr = Self::shifted(history.ghr, taken);
}
fn commit(&mut self, pc: u64, retired: Retired, history: &GShareHistory) {
if retired.class != BranchClass::Conditional {
return;
}
let counter = &mut self.pht[Self::index(pc, history.ghr)];
if retired.taken && *counter < 3 {
*counter += 1;
} else if !retired.taken && *counter > 0 {
*counter -= 1;
}
}
}