use crate::ir::Opcode;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) struct Cost(u32);
impl core::fmt::Debug for Cost {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
if *self == Cost::infinity() {
write!(f, "Cost::Infinite")
} else {
f.debug_tuple("Cost::Finite").field(&self.cost()).finish()
}
}
}
impl Cost {
pub(crate) fn infinity() -> Cost {
Cost(u32::MAX)
}
pub(crate) fn zero() -> Cost {
Cost(0)
}
fn new(cost: u32) -> Cost {
Cost(cost)
}
fn cost(&self) -> u32 {
self.0
}
fn of_opcode(op: Opcode) -> Cost {
match op {
Opcode::Iconst | Opcode::F32const | Opcode::F64const => Cost::new(1),
Opcode::Uextend
| Opcode::Sextend
| Opcode::Ireduce
| Opcode::Iconcat
| Opcode::Isplit => Cost::new(1),
Opcode::Iadd
| Opcode::Isub
| Opcode::Band
| Opcode::Bor
| Opcode::Bxor
| Opcode::Bnot
| Opcode::Ishl
| Opcode::Ushr
| Opcode::Sshr => Cost::new(3),
Opcode::Imul => Cost::new(10),
_ => {
let mut c = Cost::new(4);
if op.can_trap() || op.other_side_effects() {
c = c + Cost::new(10);
}
if op.can_load() {
c = c + Cost::new(20);
}
if op.can_store() {
c = c + Cost::new(50);
}
c
}
}
}
pub(crate) fn of_pure_op(op: Opcode, operand_costs: impl IntoIterator<Item = Self>) -> Self {
let c = Self::of_opcode(op) + operand_costs.into_iter().sum();
Cost::new(c.cost())
}
pub(crate) fn of_skeleton_op(op: Opcode, arity: usize) -> Self {
Cost::of_opcode(op) + Cost::new(u32::try_from(arity).unwrap())
}
}
impl core::iter::Sum<Cost> for Cost {
fn sum<I: Iterator<Item = Cost>>(iter: I) -> Self {
iter.fold(Self::zero(), |a, b| a + b)
}
}
impl core::default::Default for Cost {
fn default() -> Cost {
Cost::zero()
}
}
impl core::ops::Add<Cost> for Cost {
type Output = Cost;
fn add(self, other: Cost) -> Cost {
Cost::new(self.cost().saturating_add(other.cost()))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn add_cost() {
let a = Cost::new(5);
let b = Cost::new(37);
assert_eq!(a + b, Cost::new(42));
assert_eq!(b + a, Cost::new(42));
}
#[test]
fn add_infinity() {
let a = Cost::new(5);
let b = Cost::infinity();
assert_eq!(a + b, Cost::infinity());
assert_eq!(b + a, Cost::infinity());
}
#[test]
fn op_cost_saturates_to_infinity() {
let a = Cost::new(u32::MAX - 10);
let b = Cost::new(11);
assert_eq!(a + b, Cost::infinity());
assert_eq!(b + a, Cost::infinity());
}
}