use crate::isa::op::AluOp;
use super::convert::{f64_to_i32_rv, f64_to_i64_rv, f64_to_u32_rv, f64_to_u64_rv};
use super::nan_handling::{
box_f32, box_f32_canon, canonicalize_f64_bits, fmax_f32, fmax_f64, fmin_f32, fmin_f64,
unbox_f32,
};
pub(super) const fn classify_f32(sign: u32, exp: u32, frac: u32) -> u32 {
if exp == 0xFF && frac != 0 {
if frac & 0x0040_0000 != 0 {
1 << 9 } else {
1 << 8 }
} else if exp == 0xFF && frac == 0 {
if sign != 0 { 1 << 0 } else { 1 << 7 } } else if exp == 0 && frac == 0 {
if sign != 0 { 1 << 3 } else { 1 << 4 } } else if exp == 0 {
if sign != 0 { 1 << 2 } else { 1 << 5 } } else if sign != 0 {
1 << 1
} else {
1 << 6
} }
pub(super) const fn classify_f64(sign: u64, exp: u64, frac: u64) -> u64 {
if exp == 0x7FF && frac != 0 {
if frac & 0x0008_0000_0000_0000 != 0 {
1 << 9 } else {
1 << 8 }
} else if exp == 0x7FF && frac == 0 {
if sign != 0 { 1 << 0 } else { 1 << 7 } } else if exp == 0 && frac == 0 {
if sign != 0 { 1 << 3 } else { 1 << 4 } } else if exp == 0 {
if sign != 0 { 1 << 2 } else { 1 << 5 } } else if sign != 0 {
1 << 1
} else {
1 << 6
} }
pub(super) const F32_SIGN_BIT: u32 = 0x8000_0000;
pub(super) const F64_SIGN_BIT: u64 = 0x8000_0000_0000_0000;
pub(super) fn execute_f32(op: AluOp, a: u64, b: u64, c: u64) -> u64 {
let fa = unbox_f32(a);
let fb = unbox_f32(b);
let fc = unbox_f32(c);
match op {
AluOp::FAdd => box_f32_canon(fa + fb),
AluOp::FSub => box_f32_canon(fa - fb),
AluOp::FMul => box_f32_canon(fa * fb),
AluOp::FDiv => box_f32_canon(fa / fb),
AluOp::FSqrt => box_f32_canon(fa.sqrt()),
AluOp::FMin => box_f32(fmin_f32(fa, fb)),
AluOp::FMax => box_f32(fmax_f32(fa, fb)),
AluOp::FMAdd => box_f32_canon(fa.mul_add(fb, fc)),
AluOp::FMSub => box_f32_canon(fa.mul_add(fb, -fc)),
AluOp::FNMAdd => box_f32_canon((-fa).mul_add(fb, -fc)),
AluOp::FNMSub => box_f32_canon((-fa).mul_add(fb, fc)),
AluOp::FSgnJ => {
box_f32(f32::from_bits((fa.to_bits() & !F32_SIGN_BIT) | (fb.to_bits() & F32_SIGN_BIT)))
}
AluOp::FSgnJN => {
box_f32(f32::from_bits((fa.to_bits() & !F32_SIGN_BIT) | (!fb.to_bits() & F32_SIGN_BIT)))
}
AluOp::FSgnJX => box_f32(f32::from_bits(fa.to_bits() ^ (fb.to_bits() & F32_SIGN_BIT))),
AluOp::FEq => (fa == fb) as u64,
AluOp::FLt => (fa < fb) as u64,
AluOp::FLe => (fa <= fb) as u64,
AluOp::FClass => {
let bits = fa.to_bits();
let sign = (bits >> 31) & 1;
let exp = (bits >> 23) & 0xFF;
let frac = bits & 0x007F_FFFF;
classify_f32(sign, exp, frac) as u64
}
AluOp::FCvtWS => f64_to_i32_rv(fa as f64) as i64 as u64,
AluOp::FCvtWUS => f64_to_u32_rv(fa as f64) as i32 as i64 as u64,
AluOp::FCvtLS => f64_to_i64_rv(fa as f64) as u64,
AluOp::FCvtLUS => f64_to_u64_rv(fa as f64),
AluOp::FCvtSD => box_f32_canon(fa),
AluOp::FCvtSW => ((a as i32) as f64).to_bits(),
AluOp::FCvtSWU => ((a as u32) as f64).to_bits(),
AluOp::FCvtSL => ((a as i64) as f64).to_bits(),
AluOp::FCvtSLU => (a as f64).to_bits(),
AluOp::FCvtDS => (unbox_f32(a) as f64).to_bits(),
AluOp::FMvToF => box_f32(f32::from_bits(a as u32)),
AluOp::FMvToX => (a as i32) as u64,
_ => 0,
}
}
pub(super) fn execute_f64(op: AluOp, a: u64, b: u64, c: u64) -> u64 {
let fa = f64::from_bits(a);
let fb = f64::from_bits(b);
let fc = f64::from_bits(c);
match op {
AluOp::FAdd => canonicalize_f64_bits(fa + fb),
AluOp::FSub => canonicalize_f64_bits(fa - fb),
AluOp::FMul => canonicalize_f64_bits(fa * fb),
AluOp::FDiv => canonicalize_f64_bits(fa / fb),
AluOp::FSqrt => canonicalize_f64_bits(fa.sqrt()),
AluOp::FMin => fmin_f64(fa, fb).to_bits(),
AluOp::FMax => fmax_f64(fa, fb).to_bits(),
AluOp::FMAdd => canonicalize_f64_bits(fa.mul_add(fb, fc)),
AluOp::FMSub => canonicalize_f64_bits(fa.mul_add(fb, -fc)),
AluOp::FNMAdd => canonicalize_f64_bits((-fa).mul_add(fb, -fc)),
AluOp::FNMSub => canonicalize_f64_bits((-fa).mul_add(fb, fc)),
AluOp::FSgnJ => {
f64::from_bits((fa.to_bits() & !F64_SIGN_BIT) | (fb.to_bits() & F64_SIGN_BIT)).to_bits()
}
AluOp::FSgnJN => {
f64::from_bits((fa.to_bits() & !F64_SIGN_BIT) | (!fb.to_bits() & F64_SIGN_BIT))
.to_bits()
}
AluOp::FSgnJX => f64::from_bits(fa.to_bits() ^ (fb.to_bits() & F64_SIGN_BIT)).to_bits(),
AluOp::FEq => (fa == fb) as u64,
AluOp::FLt => (fa < fb) as u64,
AluOp::FLe => (fa <= fb) as u64,
AluOp::FClass => {
let bits = fa.to_bits();
let sign = (bits >> 63) & 1;
let exp = (bits >> 52) & 0x7FF;
let frac = bits & 0x000F_FFFF_FFFF_FFFF;
classify_f64(sign, exp, frac)
}
AluOp::FCvtWS => f64_to_i32_rv(fa) as i64 as u64,
AluOp::FCvtWUS => f64_to_u32_rv(fa) as i32 as i64 as u64,
AluOp::FCvtLS => f64_to_i64_rv(fa) as u64,
AluOp::FCvtLUS => f64_to_u64_rv(fa),
AluOp::FCvtSD => box_f32_canon(fa as f32),
AluOp::FCvtSW => ((a as i32) as f64).to_bits(),
AluOp::FCvtSWU => ((a as u32) as f64).to_bits(),
AluOp::FCvtSL => ((a as i64) as f64).to_bits(),
AluOp::FCvtSLU => (a as f64).to_bits(),
AluOp::FMvToF | AluOp::FMvToX => a,
_ => 0,
}
}