use entity::EntityRef;
use std::fmt;
pub type RegUnit = u16;
pub type RegUnitMask = [u32; 3];
pub type RegClassMask = u32;
pub const MAX_TRACKED_TOPRCS: usize = 4;
pub struct RegBank {
pub name: &'static str,
pub first_unit: RegUnit,
pub units: RegUnit,
pub names: &'static [&'static str],
pub prefix: &'static str,
pub first_toprc: usize,
pub num_toprcs: usize,
pub pressure_tracking: bool,
}
impl RegBank {
fn contains(&self, regunit: RegUnit) -> bool {
regunit >= self.first_unit && regunit - self.first_unit < self.units
}
fn parse_regunit(&self, name: &str) -> Option<RegUnit> {
match self.names.iter().position(|&x| x == name) {
Some(offset) => {
Some(offset as RegUnit)
}
None => {
if name.starts_with(self.prefix) {
name[self.prefix.len()..].parse().ok()
} else {
None
}
}
}.and_then(|offset| {
if offset < self.units {
Some(offset + self.first_unit)
} else {
None
}
})
}
fn write_regunit(&self, f: &mut fmt::Formatter, regunit: RegUnit) -> fmt::Result {
let offset = regunit - self.first_unit;
assert!(offset < self.units);
if (offset as usize) < self.names.len() {
write!(f, "%{}", self.names[offset as usize])
} else {
write!(f, "%{}{}", self.prefix, offset)
}
}
}
pub type RegClass = &'static RegClassData;
pub struct RegClassData {
pub name: &'static str,
pub index: u8,
pub width: u8,
pub bank: u8,
pub toprc: u8,
pub first: RegUnit,
pub subclasses: RegClassMask,
pub mask: RegUnitMask,
pub info: &'static RegInfo,
}
impl RegClassData {
pub fn intersect_index(&self, other: RegClass) -> Option<RegClassIndex> {
let mask = self.subclasses & other.subclasses;
if mask == 0 {
None
} else {
Some(RegClassIndex(mask.trailing_zeros() as u8))
}
}
pub fn intersect(&self, other: RegClass) -> Option<RegClass> {
self.intersect_index(other).map(|rci| self.info.rc(rci))
}
pub fn has_subclass<RCI: Into<RegClassIndex>>(&self, other: RCI) -> bool {
self.subclasses & (1 << other.into().0) != 0
}
pub fn toprc(&self) -> RegClass {
self.info.rc(RegClassIndex(self.toprc))
}
pub fn unit(&self, offset: usize) -> RegUnit {
let uoffset = offset * usize::from(self.width);
self.first + uoffset as RegUnit
}
pub fn contains(&self, regunit: RegUnit) -> bool {
self.mask[(regunit / 32) as usize] & (1u32 << (regunit % 32)) != 0
}
}
impl fmt::Display for RegClassData {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(self.name)
}
}
impl fmt::Debug for RegClassData {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(self.name)
}
}
impl PartialEq for RegClassData {
fn eq(&self, other: &Self) -> bool {
self.index == other.index
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct RegClassIndex(u8);
impl EntityRef for RegClassIndex {
fn new(idx: usize) -> Self {
RegClassIndex(idx as u8)
}
fn index(self) -> usize {
usize::from(self.0)
}
}
impl From<RegClass> for RegClassIndex {
fn from(rc: RegClass) -> Self {
RegClassIndex(rc.index)
}
}
impl fmt::Display for RegClassIndex {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "rci{}", self.0)
}
}
pub fn regs_overlap(rc1: RegClass, reg1: RegUnit, rc2: RegClass, reg2: RegUnit) -> bool {
let end1 = reg1 + RegUnit::from(rc1.width);
let end2 = reg2 + RegUnit::from(rc2.width);
!(end1 <= reg2 || end2 <= reg1)
}
#[derive(Clone)]
pub struct RegInfo {
pub banks: &'static [RegBank],
pub classes: &'static [RegClass],
}
impl RegInfo {
pub fn bank_containing_regunit(&self, regunit: RegUnit) -> Option<&RegBank> {
self.banks.iter().find(|b| b.contains(regunit))
}
pub fn parse_regunit(&self, name: &str) -> Option<RegUnit> {
self.banks
.iter()
.filter_map(|b| b.parse_regunit(name))
.next()
}
pub fn display_regunit(&self, regunit: RegUnit) -> DisplayRegUnit {
DisplayRegUnit {
regunit,
reginfo: self,
}
}
pub fn rc(&self, idx: RegClassIndex) -> RegClass {
self.classes[idx.index()]
}
pub fn toprc(&self, idx: RegClassIndex) -> RegClass {
self.classes[self.rc(idx).toprc as usize]
}
}
pub struct DisplayRegUnit<'a> {
regunit: RegUnit,
reginfo: &'a RegInfo,
}
impl<'a> fmt::Display for DisplayRegUnit<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.reginfo.bank_containing_regunit(self.regunit) {
Some(b) => b.write_regunit(f, self.regunit),
None => write!(f, "%INVALID{}", self.regunit),
}
}
}