use super::Prefetcher;
use crate::common::VirtAddr;
const MAX_CONFIDENCE: u8 = 3;
#[derive(Clone, Copy, Debug)]
pub struct StrideTracker {
last_addr: u64,
stride: i64,
confidence: u8,
}
impl StrideTracker {
pub const fn starting_at(addr: u64) -> Self {
Self { last_addr: addr, stride: 0, confidence: 0 }
}
pub const fn stride(&self) -> i64 {
self.stride
}
pub fn train(&mut self, addr: u64) -> Option<i64> {
let stride = (addr as i64).wrapping_sub(self.last_addr as i64);
self.last_addr = addr;
if stride == self.stride {
if self.confidence < MAX_CONFIDENCE {
self.confidence += 1;
return None;
}
return (stride != 0).then_some(stride);
}
if self.confidence > 0 {
self.confidence -= 1;
} else {
self.stride = stride;
}
None
}
}
pub const fn line_along(addr: u64, stride: i64, k: i64, line_bytes: u64) -> u64 {
let line = line_bytes as i64;
let step = if stride.abs() < line { line * stride.signum() } else { stride };
(addr as i64).wrapping_add(step.wrapping_mul(k)) as u64 & !(line_bytes - 1)
}
pub const fn pc_index(pc: VirtAddr, table_mask: usize) -> usize {
(pc.val() >> 1) as usize & table_mask
}
#[derive(Clone, Copy, Debug)]
struct StrideEntry {
pc: VirtAddr,
tracker: StrideTracker,
}
#[derive(Debug)]
pub struct StridePrefetcher {
table: Vec<Option<StrideEntry>>,
line_bytes: u64,
table_mask: usize,
degree: usize,
}
impl StridePrefetcher {
pub fn new(line_bytes: usize, table_size: usize, degree: usize) -> Self {
let safe_size =
if table_size > 0 && table_size.is_power_of_two() { table_size } else { 64 };
Self {
table: vec![None; safe_size],
line_bytes: line_bytes as u64,
table_mask: safe_size - 1,
degree: if degree == 0 { 1 } else { degree },
}
}
}
impl Prefetcher for StridePrefetcher {
fn observe(&mut self, addr: u64, pc: Option<VirtAddr>, _hit: bool) -> Vec<u64> {
let Some(pc) = pc else { return Vec::new() };
let slot = &mut self.table[pc_index(pc, self.table_mask)];
if slot.is_none_or(|entry| entry.pc != pc) {
*slot = Some(StrideEntry { pc, tracker: StrideTracker::starting_at(addr) });
return Vec::new();
}
let Some(entry) = slot.as_mut() else { return Vec::new() };
let Some(stride) = entry.tracker.train(addr) else { return Vec::new() };
(1..=self.degree as i64).map(|k| line_along(addr, stride, k, self.line_bytes)).collect()
}
}