#![doc(html_root_url = "https://docs.rs/rucc-cost/0.10.4")]
pub mod cost;
pub mod cycles;
pub mod heuristics;
pub mod table;
pub mod tune;
pub mod x86_64;
pub use cost::{Complexity, Cost};
pub use cycles::{Bytes, Cycles};
pub use table::{AddrMode, Builder, CostTable, Width};
pub use tune::{TuneFlag, Tuning};
use rucc_target::Arch;
use rucc_tuple::TargetTuple;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Goal {
Speed,
Size,
}
impl Goal {
#[must_use]
pub const fn for_size(size: bool) -> Self {
if size { Self::Size } else { Self::Speed }
}
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Speed => "speed",
Self::Size => "size",
}
}
}
impl std::fmt::Display for Goal {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum RegClass {
Integer,
Float,
}
impl RegClass {
pub const ALL: [Self; 2] = [Self::Integer, Self::Float];
pub const COUNT: usize = Self::ALL.len();
#[must_use]
pub const fn index(self) -> usize {
self as usize
}
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Integer => "integer",
Self::Float => "float",
}
}
}
impl std::fmt::Display for RegClass {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
pub trait TargetCosts: Send + Sync {
fn table(&self, goal: Goal) -> &CostTable;
fn tune(&self, flag: TuneFlag) -> bool;
fn allocatable(&self, class: RegClass) -> u32;
fn name(&self) -> &'static str;
fn branch_cost(&self, goal: Goal, predictable: bool) -> Cycles {
if goal == Goal::Speed && predictable {
return heuristics::BRANCH_COST_PREDICTABLE;
}
self.table(goal).branch_cost
}
}
#[must_use]
pub fn for_arch(arch: Arch) -> Option<&'static dyn TargetCosts> {
match arch {
Arch::X86_64 => Some(x86_64::COSTS),
_ => None,
}
}
#[must_use]
pub fn for_tuple(tuple: TargetTuple) -> Option<&'static dyn TargetCosts> {
let arch = match tuple.arch() {
rucc_tuple::Arch::X86_64 => Arch::X86_64,
rucc_tuple::Arch::Aarch64 => Arch::Aarch64,
rucc_tuple::Arch::Riscv64 => Arch::Riscv64,
_ => return None,
};
for_arch(arch)
}
#[cfg(test)]
mod tests {
use super::{Goal, TuneFlag, for_arch};
use rucc_target::Arch;
#[test]
fn the_only_backend_has_costs() {
let costs = for_arch(Arch::X86_64).expect("x86-64 is the back end rucc has");
assert_eq!(costs.name(), "x86-64");
assert!(!costs.table(Goal::Speed).add.is_infinite());
}
#[test]
fn a_target_with_no_back_end_has_no_costs_rather_than_made_up_ones() {
assert!(for_arch(Arch::Aarch64).is_none());
}
#[test]
fn a_tuning_flag_does_not_depend_on_the_goal() {
let costs = for_arch(Arch::X86_64).unwrap();
for flag in TuneFlag::ALL {
let _: bool = costs.tune(flag);
}
}
}