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; use std::f64::consts::{PI, FRAC_PI_2}; #[inline] pub(crate) fn f2u(f: f64) -> u64 { unsafe { std::mem::transmute(f) } } #[inline] pub(crate) fn u2f(f: u64) -> f64 { unsafe { std::mem::transmute(f) } } #[inline] pub(crate) fn i2u(f: i64) -> u64 { unsafe { std::mem::transmute(f) } } #[inline] pub(crate) 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 + 1) } }; 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); trace!("fti {} -> {}", val, res); state.update_status(res); state.write(dst, res)?; } ArithOp::IntToFloat { dst, a } => { state.clear_status(); let i = u2i(state.read(a)?); let res: f64 = i as f64; trace!("itf {} -> {}", i, res); state.update_status_float(res); state.write(dst, f2u(res))?; } 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)?; } ArithOp::Abs { dst, a } => { state.clear_status(); let x = u2i(state.read(a)?); let res = i2u(x.abs()); state.update_status(res); state.write(dst, res)?; } ArithOp::Sgn { dst, a } => { state.clear_status(); let x = u2i(state.read(a)?); let res = i2u(if x >= 0 { 0 } else { -1 }); state.update_status(res); state.write(dst, res)?; } ArithOp::Pow { dst, a, pow } => { state.clear_status(); let x = state.read(a)?; let p = state.read(pow)?; if p > u32::MAX as u64 { state.set_flag(Flag::Invalid, true); } else { let res = x.pow(p as u32); state.update_status(res); state.write(dst, res)?; } } ArithOp::FloatPow { dst, a, pow } => { state.clear_status(); let x = u2f(state.read(a)?); let p = u2f(state.read(pow)?); let res = x.powf(p); state.update_status_float(res); state.write(dst, f2u(res))?; } ArithOp::FloatRoot { dst, a, root } => { state.clear_status(); let x = u2f(state.read(a)?); let p = u2f(state.read(root)?); if p == 0f64 { state.set_flag(Flag::Invalid, true); } else { let res = x.powf(1f64 / p); state.update_status_float(res); state.write(dst, f2u(res))?; } } ArithOp::FloatHyp { dst, a, b } => { state.clear_status(); let a = u2f(state.read(a)?); let b = u2f(state.read(b)?); let res = a.hypot(b); state.update_status_float(res); state.write(dst, f2u(res))?; } ArithOp::FloatAbs { dst, a } => { state.clear_status(); let a = u2f(state.read(a)?); let res = a.abs(); state.update_status_float(res); state.write(dst, f2u(res))?; } ArithOp::FloatSgn { dst, a } => { state.clear_status(); let a = u2f(state.read(a)?); let res = a.signum(); state.update_status_float(res); state.write(dst, f2u(res))?; } /* Simple trig */ ArithOp::FloatSin { dst, a } => { state.clear_status(); let a = u2f(state.read(a)?); let res = a.sin(); state.update_status_float(res); state.write(dst, f2u(res))?; } ArithOp::FloatAsin { dst, a } => { state.clear_status(); let a = u2f(state.read(a)?); let res = a.asin(); state.update_status_float(res); state.write(dst, f2u(res))?; } ArithOp::FloatCos { dst, a } => { state.clear_status(); let a = u2f(state.read(a)?); let res = a.cos(); state.update_status_float(res); state.write(dst, f2u(res))?; } ArithOp::FloatAcos { dst, a } => { state.clear_status(); let a = u2f(state.read(a)?); let res = a.acos(); state.update_status_float(res); state.write(dst, f2u(res))?; } ArithOp::FloatTan { dst, a } => { state.clear_status(); let a = u2f(state.read(a)?); let res = a.tan(); state.update_status_float(res); state.write(dst, f2u(res))?; } ArithOp::FloatAtan { dst, a } => { state.clear_status(); let a = u2f(state.read(a)?); let res = a.atan(); state.update_status_float(res); state.write(dst, f2u(res))?; } ArithOp::FloatAtan2 { dst, y, x } => { state.clear_status(); let a = u2f(state.read(y)?); let b = u2f(state.read(x)?); let res = a.atan2(b); state.update_status_float(res); state.write(dst, f2u(res))?; } ArithOp::FloatCot { dst, a } => { state.clear_status(); let a = u2f(state.read(a)?); let tan = a.tan(); if tan == 0.0 { state.set_flag(Flag::Invalid, true); } else { let res = 1f64 / tan; state.update_status_float(res); state.write(dst, f2u(res))?; } } ArithOp::FloatAcot { dst, a } => { state.clear_status(); let x = u2f(state.read(a)?); // TODO verify let res = if x > 1.0 { (1.0/x).atan() } else if x < -1.0 { PI + (1.0/x).atan() } else { FRAC_PI_2 - x.atan() }; state.update_status_float(res); state.write(dst, f2u(res))?; } /* Hyperbolic trig */ ArithOp::FloatHypSin { dst, a } => { state.clear_status(); let a = u2f(state.read(a)?); let res = a.sinh(); state.update_status_float(res); state.write(dst, f2u(res))?; } ArithOp::FloatHypAsin { dst, a } => { state.clear_status(); let a = u2f(state.read(a)?); let res = a.asinh(); state.update_status_float(res); state.write(dst, f2u(res))?; } ArithOp::FloatHypCos { dst, a } => { state.clear_status(); let a = u2f(state.read(a)?); let res = a.cosh(); state.update_status_float(res); state.write(dst, f2u(res))?; } ArithOp::FloatHypAcos { dst, a } => { state.clear_status(); let a = u2f(state.read(a)?); if a > 1.0 { let res = a.acos(); state.update_status_float(res); state.write(dst, f2u(res))?; } } ArithOp::FloatHypTan { dst, a } => { state.clear_status(); let a = u2f(state.read(a)?); let res = a.tanh(); state.update_status_float(res); state.write(dst, f2u(res))?; } ArithOp::FloatHypAtan { dst, a } => { state.clear_status(); let a = u2f(state.read(a)?); if a > -1.0 && a < 1.0 { let res = a.atanh(); state.update_status_float(res); state.write(dst, f2u(res))?; } else { state.set_flag(Flag::Invalid, true); } } ArithOp::FloatHypCot { dst, a } => { state.clear_status(); let a = u2f(state.read(a)?); let tan = a.tanh(); if tan == 0.0 { state.set_flag(Flag::Invalid, true); } else { let res = 1f64 / tan; state.update_status_float(res); state.write(dst, f2u(res))?; } } ArithOp::FloatHypAcot { dst, a } => { state.clear_status(); let x = u2f(state.read(a)?); // TODO verify if x > 1.0 || x < -1.0 { let res = (1.0/x).atanh(); state.update_status_float(res); state.write(dst, f2u(res))?; } else { state.set_flag(Flag::Invalid, true); }; } } 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("ftst"), A(a)]), ArithOp::FloatCompare { a, b } => sexp::list(&[A("fcmp"), A(a), A(b)]), ArithOp::FloatRangeTest { val, a: start, b: end } => sexp::list(&[A("frcmp"), 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("frng"), A(dst)]) } else if min.is_imm_equal(0) { sexp::list(&[A("frng"), A(dst), A(max)]) } else { sexp::list(&[A("frng"), A(dst), A(min), A(max)]) } } ArithOp::FloatAdd { dst, a, b } => to_sexp_2_or_3("fadd", dst, a, b), ArithOp::FloatSub { dst, a, b } => to_sexp_2_or_3("fsub", dst, a, b), ArithOp::FloatMul { dst, a, b } => to_sexp_2_or_3("fmul", dst, a, b), ArithOp::FloatDiv { dst, rem, a, div } => { if rem.is_discard() { to_sexp_2_or_3("fdiv", dst, a, div) } else { if &dst.as_rd() == a { sexp::list(&[A("fdivr"), A(dst), A(rem), A(div)]) } else { sexp::list(&[A("fdivr"), A(dst), A(rem), A(a), A(div)]) } } } ArithOp::FloatMod { dst, a, div } => to_sexp_2_or_3("fmod", 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), ArithOp::Abs { dst, a } => to_sexp_1_or_2("abs", dst, a), ArithOp::Sgn { dst, a } => to_sexp_1_or_2("sgn", dst, a), ArithOp::Pow { dst, a, pow } => to_sexp_2_or_3("pow", dst, a, pow), ArithOp::FloatPow { dst, a, pow } => to_sexp_2_or_3("fpow", dst, a, pow), ArithOp::FloatRoot { dst, a, root } => to_sexp_2_or_3("froot", dst, a, root), ArithOp::FloatHyp { dst, a, b } => to_sexp_2_or_3("fhyp", dst, a, b), ArithOp::FloatAbs { dst, a } => to_sexp_1_or_2("fabs", dst, a), ArithOp::FloatSgn { dst, a } => to_sexp_1_or_2("fsgn", dst, a), ArithOp::FloatSin { dst, a } => to_sexp_1_or_2("fsin", dst, a), ArithOp::FloatAsin { dst, a } => to_sexp_1_or_2("fasin", dst, a), ArithOp::FloatCos { dst, a } => to_sexp_1_or_2("fcos", dst, a), ArithOp::FloatAcos { dst, a } => to_sexp_1_or_2("facos", dst, a), ArithOp::FloatTan { dst, a } => to_sexp_1_or_2("ftan", dst, a), ArithOp::FloatAtan { dst, a } => to_sexp_1_or_2("fatan", dst, a), ArithOp::FloatAtan2 { dst, x, y } => to_sexp_2_or_3("fatan2", dst, x, y), ArithOp::FloatCot { dst, a } => to_sexp_1_or_2("fcot", dst, a), ArithOp::FloatAcot { dst, a } => to_sexp_1_or_2("facot", dst, a), ArithOp::FloatHypSin { dst, a } => to_sexp_1_or_2("fsinh", dst, a), ArithOp::FloatHypAsin { dst, a } => to_sexp_1_or_2("fasinh", dst, a), ArithOp::FloatHypCos { dst, a } => to_sexp_1_or_2("fcosh", dst, a), ArithOp::FloatHypAcos { dst, a } => to_sexp_1_or_2("facosh", dst, a), ArithOp::FloatHypTan { dst, a } => to_sexp_1_or_2("ftanh", dst, a), ArithOp::FloatHypAtan { dst, a } => to_sexp_1_or_2("fatanh", dst, a), ArithOp::FloatHypCot { dst, a } => to_sexp_1_or_2("fcoth", dst, a), ArithOp::FloatHypAcot { dst, a } => to_sexp_1_or_2("facoth", dst, a), } } fn is_compile_time_evaluable(&self) -> bool { match self { ArithOp::Test { .. } | ArithOp::Compare { .. } | ArithOp::RangeTest { .. } | ArithOp::FloatTest { .. } | ArithOp::FloatCompare { .. } | ArithOp::FloatRangeTest { .. } => false, // rng is allowed... makes little sense, but it also has no side effects _ => true, // a bold claim... } } } 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)]) } }