use std::ops::Rem; use num_traits::PrimInt; use crsn::asm::data::{Rd, Wr}; use crsn::module::{EvalRes, OpTrait}; use crsn::runtime::fault::Fault; use crsn::runtime::run_thread::{state::RunState, ThreadInfo}; use crsn::sexp; use crsn::sexp::Sexp; use crsn::utils::A; use crate::defs::{ArithOp, FloatToIntMode}; use crsn::asm::instr::cond::Flag; use rand::Rng; #[inline] fn f2u(f: f64) -> u64 { unsafe { std::mem::transmute(f) } } #[inline] fn u2f(f: u64) -> f64 { unsafe { std::mem::transmute(f) } } #[inline] fn i2u(f: i64) -> u64 { unsafe { std::mem::transmute(f) } } #[inline] fn u2i(f: u64) -> i64 { unsafe { std::mem::transmute(f) } } impl OpTrait for ArithOp { fn execute(&self, _ti: &ThreadInfo, state: &mut RunState) -> Result { let eres = EvalRes::default(); match self { // Integers ArithOp::Test { a } => { state.clear_status(); let res = state.read(a)?; state.update_status(res); } ArithOp::Compare { a, b } => { state.clear_status(); let x = state.read(a)?; let y = state.read(b)?; state.set_flag(Flag::Equal, x == y); state.set_flag(Flag::Lower, x < y); state.set_flag(Flag::Greater, x > y); // Test flags are set when both arguments have the property if x == y { state.update_status(x); } } ArithOp::RangeTest { val, a, b } => { state.clear_status(); let val = state.read(val)?; let a = state.read(a)?; let b = state.read(b)?; state.set_flag(Flag::Equal, val >= a && val <= b); state.set_flag(Flag::Lower, val < a); state.set_flag(Flag::Greater, val > b); state.update_status(val); } ArithOp::Rng { dst, min, max } => { state.clear_status(); let min = state.read(min)?; let mut max = state.read(max)?; let val = if min == max { min } else if min > max { state.set_flag(Flag::Invalid, true); min } else { let offset = min; max -= min; offset + if max == u64::MAX { rand::thread_rng().gen() } else { rand::thread_rng().gen_range(0, max) } }; state.write(dst, val)?; state.update_status(val); } ArithOp::Add { dst, a, b } => { state.clear_status(); let x = state.read(a)?; let y = state.read(b)?; let (res, ov) = if let Some(v) = x.checked_add(y) { (v, false) } else { (x.wrapping_add(y), true) }; state.update_status(res); state.set_flag(Flag::Overflow, ov); state.write(dst, res)?; } ArithOp::Sub { dst, a, b } => { state.clear_status(); let x = state.read(a)?; let y = state.read(b)?; let (res, ov) = if let Some(v) = x.checked_sub(y) { (v, false) } else { (x.wrapping_sub(y), true) }; state.update_status(res); state.set_flag(Flag::Overflow, ov); state.write(dst, res)?; } ArithOp::Mul { dst, a, b } => { state.clear_status(); let x = state.read(a)?; let y = state.read(b)?; let res = if let Some(v) = x.checked_mul(y) { v } else { state.set_flag(Flag::Overflow, true); x.wrapping_mul(y) }; state.update_status(res); state.write(dst, res)?; } ArithOp::Div { dst, rem, a, div } => { state.clear_status(); let x = state.read(a)?; let d = state.read(div)?; if d == 0 { state.set_flag(Flag::Invalid, true); } else { let (res, remainder, ov) = if let Some(v) = x.checked_div(d) { (v, x.rem(d), false) } else { (x.wrapping_div(d), x.wrapping_rem(d), true) }; state.update_status(res); state.set_flag(Flag::Overflow, ov); state.write(dst, res)?; state.write(rem, remainder)?; } } ArithOp::Mod { dst, a, div } => { state.clear_status(); let x = state.read(a)?; let d = state.read(div)?; if d == 0 { state.set_flag(Flag::Invalid, true); } else { let (remainder, ov) = if let Some(v) = x.checked_rem(d) { (v, false) } else { (x.wrapping_rem(d), true) }; state.update_status(remainder); state.set_flag(Flag::Overflow, ov); state.write(dst, remainder)?; } } // Floating point ArithOp::FloatToInt { dst, a, mode } => { state.clear_status(); let val : f64 = u2f(state.read(a)?); let res = i2u(match mode { FloatToIntMode::Floor => val.floor(), FloatToIntMode::Ceil => val.ceil(), FloatToIntMode::Round => val.round(), } as i64); state.update_status(res); state.write(dst, res)?; } ArithOp::IntToFloat { dst, a } => { state.clear_status(); let val : f64 = u2i(state.read(a)?) as f64; state.update_status_float(val); state.write(dst, f2u(val))?; } ArithOp::FloatTest { a } => { state.clear_status(); let res = u2f(state.read(a)?); state.update_status_float(res); } ArithOp::FloatCompare { a, b } => { state.clear_status(); let x = u2f(state.read(a)?); let y = u2f(state.read(b)?); state.set_flag(Flag::Equal, x == y); state.set_flag(Flag::Lower, x < y); state.set_flag(Flag::Greater, x > y); // Test flags are set when both arguments have the property if x == y { state.update_status_float(x); } } ArithOp::FloatRangeTest { val, a, b } => { state.clear_status(); let val = u2f(state.read(val)?); let a = u2f(state.read(a)?); let b = u2f(state.read(b)?); state.set_flag(Flag::Equal, val >= a && val <= b); state.set_flag(Flag::Lower, val < a); state.set_flag(Flag::Greater, val > b); state.update_status_float(val); } ArithOp::FloatRng { dst, min, max } => { state.clear_status(); let min = u2f(state.read(min)?); let max = u2f(state.read(max)?); let val = if min == max { min } else if min > max { state.set_flag(Flag::Invalid, true); min } else { rand::thread_rng() .gen_range(min, max) }; state.write(dst, f2u(val))?; state.update_status_float(val); } ArithOp::FloatAdd { dst, a, b } => { state.clear_status(); let x = u2f(state.read(a)?); let y = u2f(state.read(b)?); let res = x + y; state.update_status_float(res); state.write(dst, f2u(res))?; } ArithOp::FloatSub { dst, a, b } => { state.clear_status(); let x = u2f(state.read(a)?); let y = u2f(state.read(b)?); let res = x - y; state.update_status_float(res); state.write(dst, f2u(res))?; } ArithOp::FloatMul { dst, a, b } => { state.clear_status(); let x = u2f(state.read(a)?); let y = u2f(state.read(b)?); let res = x * y; state.update_status_float(res); state.write(dst, f2u(res))?; } ArithOp::FloatDiv { dst, rem, a, div } => { state.clear_status(); let x = u2f(state.read(a)?); let d = u2f(state.read(div)?); if d == 0.0 { state.set_flag(Flag::Invalid, true); } else { let res = x / d; let remainder = x % d; state.update_status_float(res); state.write(dst, f2u(res))?; state.write(rem, f2u(remainder))?; } } ArithOp::FloatMod { dst, a, div } => { state.clear_status(); let x = u2f(state.read(a)?); let d = u2f(state.read(div)?); if d == 0.0 { state.set_flag(Flag::Invalid, true); } else { let rem = x % d; state.update_status_float(rem); state.write(dst, f2u(rem))?; } } // Bitwise ArithOp::And { dst, a, b } => { state.clear_status(); let x = state.read(a)?; let y = state.read(b)?; let res = x & y; state.update_status(res); state.write(dst, res)?; } ArithOp::Or { dst, a, b } => { state.clear_status(); let x = state.read(a)?; let y = state.read(b)?; let res = x | y; state.update_status(res); state.write(dst, res)?; } ArithOp::Xor { dst, a, b } => { state.clear_status(); let x = state.read(a)?; let y = state.read(b)?; let res = x ^ y; state.update_status(res); state.write(dst, res)?; } ArithOp::Cpl { dst, a } => { state.clear_status(); let x = state.read(a)?; let res = !x; state.update_status(res); state.write(dst, res)?; } ArithOp::Rol { dst, a, n } => { state.clear_status(); let x = state.read(a)?; let y = state.read(n)?; if y > u32::MAX as u64 { state.set_flag(Flag::Invalid, true); } else { let res = x.rotate_left(y as u32); state.update_status(res); state.write(dst, res)?; } } ArithOp::Ror { dst, a, n } => { state.clear_status(); let x = state.read(a)?; let y = state.read(n)?; if y > u32::MAX as u64 { state.set_flag(Flag::Invalid, true); } else { let res = x.rotate_right(y as u32); state.update_status(res); state.write(dst, res)?; } } ArithOp::Lsl { dst, a, n } => { state.clear_status(); let x = state.read(a)?; let y = state.read(n)?; let res = x << y; state.update_status(res); state.write(dst, res)?; } ArithOp::Lsr { dst, a, n } => { state.clear_status(); let x = state.read(a)?; let y = state.read(n)?; let res = x >> y; state.update_status(res); state.write(dst, res)?; } ArithOp::Asr { dst, a, n } => { state.clear_status(); let x = state.read(a)?; let y = state.read(n)?; if y > u32::MAX as u64 { state.set_flag(Flag::Invalid, true); } else { let res = x.signed_shr(y as u32); state.update_status(res); state.write(dst, res)?; } } ArithOp::Sw32 { dst, src } => { let val = state.read(src)?; let res = ((val & 0xFFFF_FFFF) << 32) | ((val & !0xFFFF_FFFF) >> 32); state.write(dst, res)?; } ArithOp::Sw16 { dst, src } => { let val = state.read(src)?; let res = ((val & 0x0000_FFFF_0000_FFFF) << 16) | ((val & 0xFFFF_0000_FFFF_0000) >> 16); state.write(dst, res)?; } ArithOp::Sw8 { dst, src } => { let val = state.read(src)?; let res = ((val & 0x00FF_00FF_00FF_00FF) << 8) | ((val & 0xFF00_FF00_FF00_FF00) >> 8); state.write(dst, res)?; } ArithOp::Rev { dst, src } => { let val = state.read(src)?; let res = val.swap_bytes(); state.write(dst, res)?; } ArithOp::Rbit { dst, src } => { let val = state.read(src)?; let res = val.reverse_bits(); state.write(dst, res)?; } ArithOp::Clz { dst, src, mask: slice } => { state.clear_status(); let mut val = (state.read(src)? >> slice.src_pos) << (64 - slice.width); if !slice.is_full() { val |= (1 << slice.width) - 1; } let res = val.leading_zeros() as u64; state.update_status(res); state.write(dst, res)?; } ArithOp::Clo { dst, src, mask: slice } => { state.clear_status(); let mut val = (state.read(src)? >> slice.src_pos) << (64 - slice.width); if !slice.is_full() { val &= ((1 << slice.width) - 1) << slice.src_pos; } let res = val.leading_ones() as u64; state.update_status(res); state.write(dst, res)?; } ArithOp::SignExtend { dst, src, mask: slice } => { state.clear_status(); let val = state.read(src)?; let res = if 0 != (val & (1 << (slice.width - 1))) { val | (((1 << (64 - slice.width)) - 1) << slice.width) } else { val & ((1 << slice.width) - 1) }; state.update_status(res); state.write(dst, res)?; } _ => unimplemented!() // TODO implement float trig etc } Ok(eres) } fn to_sexp(&self) -> Sexp { match self { ArithOp::Test { a } => sexp::list(&[A("tst"), A(a)]), ArithOp::Compare { a, b } => sexp::list(&[A("cmp"), A(a), A(b)]), ArithOp::RangeTest { val, a: start, b: end } => sexp::list(&[A("rcmp"), A(val), A(start), A(end)]), ArithOp::Rng { dst, min, max } => { if min.is_imm_equal(0) && max.is_imm_equal(u64::MAX) { sexp::list(&[A("rng"), A(dst)]) } else if min.is_imm_equal(0) { sexp::list(&[A("rng"), A(dst), A(max)]) } else { sexp::list(&[A("rng"), A(dst), A(min), A(max)]) } } ArithOp::Add { dst, a, b } => to_sexp_2_or_3("add", dst, a, b), ArithOp::Sub { dst, a, b } => to_sexp_2_or_3("sub", dst, a, b), ArithOp::Mul { dst, a, b } => to_sexp_2_or_3("mul", dst, a, b), ArithOp::Div { dst, rem, a, div } => { if rem.is_discard() { to_sexp_2_or_3("div", dst, a, div) } else { if &dst.as_rd() == a { sexp::list(&[A("divr"), A(dst), A(rem), A(div)]) } else { sexp::list(&[A("divr"), A(dst), A(rem), A(a), A(div)]) } } } ArithOp::Mod { dst, a, div } => to_sexp_2_or_3("mod", dst, a, div), // TODO render as float ArithOp::FloatTest { a } => sexp::list(&[A("tstf"), A(a)]), ArithOp::FloatCompare { a, b } => sexp::list(&[A("cmpf"), A(a), A(b)]), ArithOp::FloatRangeTest { val, a: start, b: end } => sexp::list(&[A("rcmpf"), A(val), A(start), A(end)]), ArithOp::FloatRng { dst, min, max } => { if min.is_imm_equal(0f64.to_bits()) && max.is_imm_equal(f64::MAX.to_bits()) { sexp::list(&[A("rngf"), A(dst)]) } else if min.is_imm_equal(0) { sexp::list(&[A("rngf"), A(dst), A(max)]) } else { sexp::list(&[A("rngf"), A(dst), A(min), A(max)]) } } ArithOp::FloatAdd { dst, a, b } => to_sexp_2_or_3("addf", dst, a, b), ArithOp::FloatSub { dst, a, b } => to_sexp_2_or_3("subf", dst, a, b), ArithOp::FloatMul { dst, a, b } => to_sexp_2_or_3("mulf", dst, a, b), ArithOp::FloatDiv { dst, rem, a, div } => { if rem.is_discard() { to_sexp_2_or_3("divf", dst, a, div) } else { if &dst.as_rd() == a { sexp::list(&[A("divrf"), A(dst), A(rem), A(div)]) } else { sexp::list(&[A("divrf"), A(dst), A(rem), A(a), A(div)]) } } } ArithOp::FloatMod { dst, a, div } => to_sexp_2_or_3("modf", dst, a, div), ArithOp::IntToFloat { dst, a } => to_sexp_1_or_2("itf", dst, a), ArithOp::FloatToInt { dst, a, mode: FloatToIntMode::Floor } => to_sexp_1_or_2("ftif", dst, a), ArithOp::FloatToInt { dst, a, mode: FloatToIntMode::Round } => to_sexp_1_or_2("ftir", dst, a), ArithOp::FloatToInt { dst, a, mode: FloatToIntMode::Ceil } => to_sexp_1_or_2("ftic", dst, a), ArithOp::And { dst, a, b } => to_sexp_2_or_3("and", dst, a, b), ArithOp::Or { dst, a, b } => to_sexp_2_or_3("or", dst, a, b), ArithOp::Xor { dst, a, b } => to_sexp_2_or_3("xor", dst, a, b), ArithOp::Cpl { dst, a } => to_sexp_1_or_2("cpl", dst, a), ArithOp::Rol { dst, a, n } => to_sexp_2_or_3("rol", dst, a, n), ArithOp::Ror { dst, a, n } => to_sexp_2_or_3("ror", dst, a, n), ArithOp::Lsl { dst, a, n } => to_sexp_2_or_3("lsl", dst, a, n), ArithOp::Lsr { dst, a, n } => to_sexp_2_or_3("lsr", dst, a, n), ArithOp::Asr { dst, a, n } => to_sexp_2_or_3("asr", dst, a, n), ArithOp::Sw32 { dst, src } => to_sexp_1_or_2("sw32", dst, src), ArithOp::Sw16 { dst, src } => to_sexp_1_or_2("sw16", dst, src), ArithOp::Sw8 { dst, src } => to_sexp_1_or_2("sw8", dst, src), ArithOp::Rev { dst, src } => to_sexp_1_or_2("rev", dst, src), ArithOp::Rbit { dst, src } => to_sexp_1_or_2("rbit", dst, src), ArithOp::Clz { dst, src, mask: slice } => to_sexp_1_or_2(&format!("clz{}", slice), dst, src), ArithOp::Clo { dst, src, mask: slice } => to_sexp_1_or_2(&format!("clo{}", slice), dst, src), ArithOp::SignExtend { dst, src, mask: slice } => to_sexp_1_or_2(&format!("se{}", slice), dst, src), _ => unimplemented!() // FIXME } } } fn to_sexp_2_or_3(name: &str, dst: &Wr, a: &Rd, b: &Rd) -> Sexp { if &dst.as_rd() == a { sexp::list(&[A(name), A(dst), A(b)]) } else { sexp::list(&[A(name), A(dst), A(a), A(b)]) } } fn to_sexp_1_or_2(name: &str, dst: &Wr, src: &Rd) -> Sexp { if &dst.as_rd() == src { sexp::list(&[A(name), A(dst)]) } else { sexp::list(&[A(name), A(dst), A(src)]) } }