use super::super::{
ExecutableTransaction,
Interpreter,
internal::inc_pc,
is_wrapping,
};
use crate::{
constraints::reg_key::*,
error::SimpleResult,
};
use fuel_asm::{
PanicReason,
RegId,
};
use fuel_types::Word;
impl<M, S, Tx, Ecal, V> Interpreter<M, S, Tx, Ecal, V>
where
Tx: ExecutableTransaction,
{
pub(crate) fn alu_muldiv(
&mut self,
ra: RegId,
lhs: Word,
rhs: Word,
divider: Word,
) -> SimpleResult<()> {
let (
SystemRegisters {
flag,
mut of,
mut err,
pc,
..
},
mut w,
) = split_registers(&mut self.registers);
let dest = &mut w[ra.try_into()?];
let (result, overflow) = muldiv(lhs, rhs, divider);
if overflow != 0 && !is_wrapping(flag.into()) {
return Err(PanicReason::ArithmeticOverflow.into())
}
*of = overflow;
*err = 0;
*dest = result;
inc_pc(pc);
Ok(())
}
}
#[allow(clippy::cast_possible_truncation)]
pub(crate) fn muldiv(lhs: u64, rhs: u64, divider: u64) -> (u64, u64) {
let lhs = lhs as u128;
let rhs = rhs as u128;
let divider = divider as u128;
let intermediate = lhs
.checked_mul(rhs)
.expect("Cannot overflow as we have enough bits");
if let Some(result) = intermediate.checked_div(divider) {
(result as u64, (result >> 64) as u64)
} else {
((intermediate >> 64) as u64, 0)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[rstest::rstest]
#[case(0, 0, 0, 0, 0)]
#[case(0, 0, 1, 0, 0)]
#[case(0, 5, 1, 0, 0)]
#[case(9, 9, 1, 81, 0)]
#[case(9, 9, 2, 40, 0)]
#[case(9, 9, 3, 27, 0)]
#[case(9, 9, 4, 20, 0)]
#[case(u64::MAX, 5, 10, u64::MAX / 2, 0)]
#[case(u64::MAX, 2, 6, u64::MAX / 3, 0)]
#[case(u64::MAX, u64::MAX, u64::MAX, u64::MAX, 0)]
#[case(u64::MAX, 4, 2, 0xfffffffffffffffe, 1)]
#[case(u64::MAX, 3, 2, 0x7ffffffffffffffe, 1)]
fn fused_muldiv(
#[case] lhs: u64,
#[case] rhs: u64,
#[case] divisor: u64,
#[case] expected: u64,
#[case] expected_overflow: u64,
) {
let (result, overflow) = muldiv(lhs, rhs, divisor);
assert_eq!(result, expected);
assert_eq!(overflow, expected_overflow);
}
}