use rucc_cost::{CostTable, Cycles, Goal, RegClass, TargetCosts, TuneFlag};
use rucc_ir::Module;
use rucc_session::OptLevel;
#[derive(Clone, Copy)]
pub struct Machine {
costs: Option<&'static dyn TargetCosts>,
goal: Goal,
}
impl Machine {
#[must_use]
pub fn of(module: &Module, level: OptLevel) -> Self {
Self::with(rucc_cost::for_tuple(module.tuple), Goal::for_size(level.is_size()))
}
#[must_use]
pub const fn with(costs: Option<&'static dyn TargetCosts>, goal: Goal) -> Self {
Self { costs, goal }
}
#[must_use]
pub const fn unknown() -> Self {
Self { costs: None, goal: Goal::Speed }
}
#[must_use]
pub fn costs(self) -> Option<&'static dyn TargetCosts> {
self.costs
}
#[must_use]
pub const fn goal(self) -> Goal {
self.goal
}
#[must_use]
pub fn table(self) -> Option<&'static CostTable> {
self.costs.map(|costs| costs.table(self.goal))
}
#[must_use]
pub fn tune(self, flag: TuneFlag) -> bool {
self.costs.is_some_and(|costs| costs.tune(flag))
}
#[must_use]
pub fn allocatable(self, class: RegClass) -> Option<u32> {
self.costs.map(|costs| costs.allocatable(class))
}
#[must_use]
pub fn branch_cost(self, predictable: bool) -> Option<Cycles> {
self.costs.map(|costs| costs.branch_cost(self.goal, predictable))
}
#[must_use]
pub fn name(self) -> &'static str {
self.costs.map_or("unknown", TargetCosts::name)
}
}
impl std::fmt::Debug for Machine {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Machine({}, {})", self.name(), self.goal)
}
}
#[cfg(test)]
pub(crate) mod fixtures {
use super::Machine;
use crate::analysis::Analyses;
use rucc_cost::Goal;
pub(crate) fn analyses() -> Analyses {
Analyses::new(machine())
}
pub(crate) fn machine() -> Machine {
Machine::with(rucc_cost::for_arch(rucc_target::Arch::X86_64), Goal::Speed)
}
}
#[cfg(test)]
mod tests {
use super::Machine;
use rucc_cost::{Goal, RegClass};
#[test]
fn a_machine_nobody_has_a_table_for_answers_nothing_rather_than_a_number() {
let machine = Machine::unknown();
assert!(machine.costs().is_none());
assert!(machine.table().is_none());
assert!(machine.allocatable(RegClass::Integer).is_none());
assert!(machine.branch_cost(false).is_none());
assert_eq!(machine.name(), "unknown");
}
#[test]
fn the_two_banks_of_x86_64_are_not_the_same_size() {
let machine = Machine::with(rucc_cost::for_arch(rucc_target::Arch::X86_64), Goal::Speed);
assert_eq!(machine.allocatable(RegClass::Integer), Some(12));
assert_eq!(machine.allocatable(RegClass::Float), Some(14));
}
}