diff --git a/src/execution/interpreter_loop.rs b/src/execution/interpreter_loop.rs index 3eae9c59e..5a19e2a4c 100644 --- a/src/execution/interpreter_loop.rs +++ b/src/execution/interpreter_loop.rs @@ -118,7 +118,7 @@ pub(super) fn run( wasm.read_var_u32().unwrap_validated(); do_sidetable_control_transfer(&mut wasm, stack, &mut stp, current_sidetable); } - BLOCK => { + BLOCK | LOOP => { BlockType::read_unvalidated(&mut wasm); } RETURN => { diff --git a/src/validation/code.rs b/src/validation/code.rs index 1146b1a93..585d6d221 100644 --- a/src/validation/code.rs +++ b/src/validation/code.rs @@ -219,7 +219,12 @@ fn read_instructions( stack.assert_push_ctrl(label_info, block_ty)?; } LOOP => { - todo!("implement loop"); + let block_ty = BlockType::read(wasm)?.as_func_type(fn_types)?; + let label_info = LabelInfo::Loop { + ip: wasm.pc, + stp: sidetable.len(), + }; + stack.assert_push_ctrl(label_info, block_ty)?; } IF => { todo!("implement if"); @@ -259,9 +264,7 @@ fn read_instructions( LabelInfo::If { .. } => { todo!("implement if"); } - LabelInfo::Loop { .. } => { - todo!("implement loop"); - } + LabelInfo::Loop { .. } => (), LabelInfo::Func { stps_to_backpatch } => { // same as blocks, except jump just before the end instr, not after it // the last end instruction will handle the return to callee during execution diff --git a/tests/structured_control_flow/loop.rs b/tests/structured_control_flow/loop.rs index 8b1378917..87817671d 100644 --- a/tests/structured_control_flow/loop.rs +++ b/tests/structured_control_flow/loop.rs @@ -1 +1,70 @@ +use wasm::{validate, RuntimeInstance}; +const FIBONACCI_WITH_LOOP_AND_BR_IF: &str = r#" +(module + (func $fibonacci (param $n i32) (result i32) + (local $prev i32) + (local $curr i32) + (local $counter i32) + + i32.const 0 + local.set $prev + i32.const 1 + local.set $curr + + local.get $n + i32.const 1 + i32.add + local.set $counter + + block $exit + loop $loop + local.get $counter + i32.const 1 + i32.le_s + br_if $exit + + local.get $curr + local.get $curr + local.get $prev + i32.add + local.set $curr + local.set $prev + + local.get $counter + i32.const 1 + i32.sub + local.set $counter + + br $loop + + drop + drop + drop + + end $loop + end $exit + + local.get $curr + ) + + (export "fibonacci" (func $fibonacci)) +)"#; + +#[test_log::test] +fn fibonacci_with_loop_and_br_if() { + let wasm_bytes = wat::parse_str(FIBONACCI_WITH_LOOP_AND_BR_IF).unwrap(); + + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + let fibonacci_fn = instance.get_function_by_index(0, 0).unwrap(); + + assert_eq!(1, instance.invoke(&fibonacci_fn, -5).unwrap()); + assert_eq!(1, instance.invoke(&fibonacci_fn, 0).unwrap()); + assert_eq!(1, instance.invoke(&fibonacci_fn, 1).unwrap()); + assert_eq!(2, instance.invoke(&fibonacci_fn, 2).unwrap()); + assert_eq!(3, instance.invoke(&fibonacci_fn, 3).unwrap()); + assert_eq!(5, instance.invoke(&fibonacci_fn, 4).unwrap()); + assert_eq!(8, instance.invoke(&fibonacci_fn, 5).unwrap()); +}