use crate::isa::op::AluOp;
use crate::isa::fp::{FpFlags, RoundingMode};
pub(super) const I32_MAX_P1_F64: f64 = (i32::MAX as f64) + 1.0;
pub(super) const I32_MIN_F64: f64 = i32::MIN as f64;
pub(super) const U32_MAX_P1_F64: f64 = (u32::MAX as f64) + 1.0;
pub(super) const I64_MAX_P1_F64: f64 = 9223372036854775808.0; pub(super) const I64_MIN_F64: f64 = i64::MIN as f64;
pub(super) const U64_MAX_P1_F64: f64 = 18446744073709551616.0;
#[inline]
pub(super) const fn f64_to_i32_rv(v: f64) -> i32 {
if v.is_nan() {
i32::MAX
} else {
v as i32 }
}
#[inline]
pub(super) const fn f64_to_u32_rv(v: f64) -> u32 {
if v.is_nan() { u32::MAX } else { v as u32 }
}
#[inline]
pub(super) const fn f64_to_i64_rv(v: f64) -> i64 {
if v.is_nan() { i64::MAX } else { v as i64 }
}
#[inline]
pub(super) const fn f64_to_u64_rv(v: f64) -> u64 {
if v.is_nan() { u64::MAX } else { v as u64 }
}
pub(super) fn fp_to_int_convert(op: AluOp, val: f64, rm: RoundingMode) -> (u64, FpFlags) {
let mut flags = FpFlags::NONE;
if val.is_nan() {
flags = flags | FpFlags::NV;
let result = match op {
AluOp::FCvtWS => i32::MAX as i64 as u64,
AluOp::FCvtWUS => u32::MAX as i32 as i64 as u64,
AluOp::FCvtLS => i64::MAX as u64,
AluOp::FCvtLUS => u64::MAX,
_ => 0,
};
return (result, flags);
}
if val.is_infinite() {
flags = flags | FpFlags::NV;
let result = match (op, val > 0.0) {
(AluOp::FCvtWS, true) => i32::MAX as i64 as u64,
(AluOp::FCvtWS, false) => i32::MIN as i64 as u64,
(AluOp::FCvtWUS, true) => u32::MAX as i32 as i64 as u64,
(AluOp::FCvtLS, true) => i64::MAX as u64,
(AluOp::FCvtLS, false) => i64::MIN as u64,
(AluOp::FCvtLUS, true) => u64::MAX,
_ => 0,
};
return (result, flags);
}
let rounded = round_to_integer(val, rm);
let inexact = val != rounded;
let (overflow, result) = match op {
AluOp::FCvtWS => {
if (I32_MIN_F64..I32_MAX_P1_F64).contains(&rounded) {
(false, rounded as i32 as i64 as u64)
} else {
(true, if rounded > 0.0 { i32::MAX } else { i32::MIN } as i64 as u64)
}
}
AluOp::FCvtWUS => {
if (0.0..U32_MAX_P1_F64).contains(&rounded) {
(false, rounded as u32 as i32 as i64 as u64)
} else {
(true, if rounded > 0.0 { u32::MAX as i32 as i64 as u64 } else { 0 })
}
}
AluOp::FCvtLS => {
if (I64_MIN_F64..I64_MAX_P1_F64).contains(&rounded) {
(false, rounded as i64 as u64)
} else {
(true, if rounded > 0.0 { i64::MAX } else { i64::MIN } as u64)
}
}
AluOp::FCvtLUS => {
if rounded < 0.0 {
(true, 0u64)
} else if rounded >= U64_MAX_P1_F64 {
(true, u64::MAX)
} else {
(false, rounded as u64)
}
}
_ => (false, 0),
};
if overflow {
flags = flags | FpFlags::NV;
} else if inexact {
flags = flags | FpFlags::NX;
}
(result, flags)
}
pub(super) const fn round_to_integer(val: f64, rm: RoundingMode) -> f64 {
match rm {
RoundingMode::Rne => {
val.round_ties_even()
}
RoundingMode::Rtz => val.trunc(),
RoundingMode::Rdn => val.floor(),
RoundingMode::Rup => val.ceil(),
RoundingMode::Rmm => {
val.round()
}
}
}