From 999d9694f304b61cd07756d3582831db3dcef553 Mon Sep 17 00:00:00 2001 From: nerodesu017 <46645625+nerodesu017@users.noreply.github.com> Date: Mon, 1 Jul 2024 15:53:53 +0300 Subject: [PATCH] feat + test: i32.div_u operation --- src/execution/mod.rs | 19 +++++++++++ src/validation/code.rs | 7 ++++ tests/arithmetic/division.rs | 62 ++++++++++++++++++++++++++++++++++++ 3 files changed, 88 insertions(+) diff --git a/src/execution/mod.rs b/src/execution/mod.rs index a2951faa..608ae29c 100644 --- a/src/execution/mod.rs +++ b/src/execution/mod.rs @@ -239,6 +239,25 @@ impl<'b> RuntimeInstance<'b> { trace!("Instruction: i32.div_s [{divisor} {dividend}] -> [{res}]"); stack.push_value(res.into()); } + // i32.div_u: [i32 i32] -> [i32] + 0x6E => { + let dividend: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); + let divisor: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); + + let dividend = dividend as u32; + let divisor = divisor as u32; + + if dividend == 0 { + return Err(crate::Error::RuntimeError( + crate::core::error::RuntimeError::DivideBy0, + )); + } + + let res = (divisor / dividend) as i32; + + trace!("Instruction: i32.div_u [{divisor} {dividend}] -> [{res}]"); + stack.push_value(res.into()); + } other => { trace!("Unknown instruction {other:#x}, skipping.."); } diff --git a/src/validation/code.rs b/src/validation/code.rs index 387a499d..7dcf5c3d 100644 --- a/src/validation/code.rs +++ b/src/validation/code.rs @@ -183,6 +183,13 @@ fn read_instructions( value_stack.push_back(ValType::NumType(NumType::I32)); } + // i32.div_u: [i32 i32] -> [i32] + 0x6E => { + assert_pop_value_stack(value_stack, ValType::NumType(NumType::I32))?; + assert_pop_value_stack(value_stack, ValType::NumType(NumType::I32))?; + + value_stack.push_back(ValType::NumType(NumType::I32)); + } // i32.const: [] -> [i32] 0x41 => { let _num = wasm.read_var_i32()?; diff --git a/tests/arithmetic/division.rs b/tests/arithmetic/division.rs index 1a2e899a..d5ece68a 100644 --- a/tests/arithmetic/division.rs +++ b/tests/arithmetic/division.rs @@ -80,3 +80,65 @@ pub fn division_signed_panic_result_unrepresentable() { wasm::Error::RuntimeError(wasm::RuntimeError::UnrepresentableResult) ); } + + +/// A simple function to test unsigned division +#[test_log::test] +pub fn division_unsigned_simple() { + use wasm::{validate, RuntimeInstance}; + + let wat = r#" + (module + (func (export "unsigned_division") (param $divisor i32) (param $dividend i32) (result i32) + local.get $divisor + local.get $dividend + i32.div_u) + ) + "#; + + let wasm_bytes = wat::parse_str(wat).unwrap(); + + let validation_info = validate(&wasm_bytes).expect("validation failed"); + + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!(10, instance.invoke_func(0, (20, 2)).unwrap()); + assert_eq!(9_001, instance.invoke_func(0, (81_018_001, 9_001)).unwrap()); + assert_eq!(0, instance.invoke_func(0, (i32::MIN, -1)).unwrap()); + + + assert_eq!(0, instance.invoke_func(0, (i32::MIN, -1)).unwrap()); + assert_eq!(-20, instance.invoke_func(0, (-20, 1)).unwrap()); + assert_eq!(2147483638, instance.invoke_func(0, (-20, 2)).unwrap()); + assert_eq!(1431655758, instance.invoke_func(0, (-20, 3)).unwrap()); + assert_eq!(1073741819, instance.invoke_func(0, (-20, 4)).unwrap()); + +} + +/// A simple function to test unsigned division's RuntimeError when dividing by 0 +#[test_log::test] +pub fn division_unsigned_panic_dividend_0() { + use wasm::{validate, RuntimeInstance}; + + let wat = r#" + (module + (func (export "unsigned_division") (param $divisor i32) (param $dividend i32) (result i32) + local.get $divisor + local.get $dividend + i32.div_u) + ) + "#; + + let wasm_bytes = wat::parse_str(wat).unwrap(); + + let validation_info = validate(&wasm_bytes).expect("validation failed"); + + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + let result = instance.invoke_func::<(i32, i32), i32>(0, (222, 0)); + + assert_eq!( + result.unwrap_err(), + wasm::Error::RuntimeError(wasm::RuntimeError::DivideBy0) + ); +}