From d2f43452f5f97e215c3f450c1151a54fb641ba9a Mon Sep 17 00:00:00 2001 From: Brendo Costa Date: Thu, 17 Oct 2024 12:22:36 -0300 Subject: [PATCH] Reworked execution of branches --- src/gwr/execution/machine.gleam | 326 +++++++++--------- src/gwr/execution/stack.gleam | 13 +- test/assets/rust/misc/fibonacci.rs | 29 ++ test/assets/rust/misc/fibonacci.wasm | Bin 0 -> 197 bytes test/assets/{ => wat}/control/block.wasm | Bin test/assets/{ => wat}/control/block.wat | 0 test/assets/{ => wat}/control/call.wasm | Bin test/assets/{ => wat}/control/call.wat | 0 test/assets/{ => wat}/control/if_else.wasm | Bin test/assets/{ => wat}/control/if_else.wat | 0 test/assets/{ => wat}/control/loop.wasm | Bin test/assets/{ => wat}/control/loop.wat | 0 test/assets/{ => wat}/control/recursion.wasm | Bin test/assets/{ => wat}/control/recursion.wat | 0 .../{control => wat/misc}/fibonacci.wasm | Bin .../{control => wat/misc}/fibonacci.wat | 2 +- test/assets/{ => wat/misc}/sum.wasm | Bin test/assets/{ => wat/misc}/sum.wat | 3 +- test/gwr/execution/execution_test.gleam | 40 ++- 19 files changed, 228 insertions(+), 185 deletions(-) create mode 100644 test/assets/rust/misc/fibonacci.rs create mode 100755 test/assets/rust/misc/fibonacci.wasm rename test/assets/{ => wat}/control/block.wasm (100%) rename test/assets/{ => wat}/control/block.wat (100%) rename test/assets/{ => wat}/control/call.wasm (100%) rename test/assets/{ => wat}/control/call.wat (100%) rename test/assets/{ => wat}/control/if_else.wasm (100%) rename test/assets/{ => wat}/control/if_else.wat (100%) rename test/assets/{ => wat}/control/loop.wasm (100%) rename test/assets/{ => wat}/control/loop.wat (100%) rename test/assets/{ => wat}/control/recursion.wasm (100%) rename test/assets/{ => wat}/control/recursion.wat (100%) rename test/assets/{control => wat/misc}/fibonacci.wasm (100%) rename test/assets/{control => wat/misc}/fibonacci.wat (90%) rename test/assets/{ => wat/misc}/sum.wasm (100%) rename test/assets/{ => wat/misc}/sum.wat (72%) diff --git a/src/gwr/execution/machine.gleam b/src/gwr/execution/machine.gleam index fe390c3..256d3e2 100644 --- a/src/gwr/execution/machine.gleam +++ b/src/gwr/execution/machine.gleam @@ -28,6 +28,12 @@ pub type MachineState MachineState(store: store.Store, stack: stack.Stack) } +pub type Jump +{ + Branch(target: List(instruction.Instruction)) + Return +} + pub fn initialize(from module: module.Module) -> Result(Machine, String) { @@ -112,113 +118,6 @@ pub fn update_references(from store: store.Store, with new_module_instance: runt store.Store(..store, functions: updated_functions) } -pub fn execute(state: MachineState, instructions: List(instruction.Instruction)) -> Result(MachineState, String) -{ - use state <- result.try( - list.fold( - from: Ok(state), - over: instructions, - with: fn (current_state, instruction) - { - use current_state <- result.try(current_state) - case instruction - { - instruction.End -> Ok(current_state) - instruction.Block(block_type:, instructions:) -> block(current_state, block_type, instructions) - instruction.If(block_type:, instructions: if_instructions, else_: else_) -> - { - // 1. Assert: due to validation, a value of value type {\mathsf{i32}} is on the top of the stack. - // 2. Pop the value {\mathsf{i32}}.{\mathsf{const}}~c from the stack. - case stack.pop_as(from: current_state.stack, with: stack.to_value) - { - Ok(#(stack, runtime.Integer32(c))) -> - { - let state = MachineState(..current_state, stack: stack) - case c != 0 - { - // 3. If c is non-zero, then: - // a. Execute the block instruction {\mathsf{block}}~{\mathit{blocktype}}~{\mathit{instr}}_1^\ast~{\mathsf{end}}. - True -> block(state, block_type, if_instructions) - // 4. Else: - // a. Execute the block instruction {\mathsf{block}}~{\mathit{blocktype}}~{\mathit{instr}}_2^\ast~{\mathsf{end}}. - False -> case else_ - { - option.Some(instruction.Else(else_instructions)) -> block(state, block_type, else_instructions) - option.None -> Ok(state) - anything_else -> Error("gwr/execution/machine.execute: (If/Else) illegal instruction in the Else's field " <> string.inspect(anything_else)) - } - } - } - anything_else -> Error("gwr/execution/machine.execute: (If/Else) expected the If's continuation flag but got " <> string.inspect(anything_else)) - } - } - instruction.Loop(block_type:, instructions:) -> loop(current_state, block_type, instructions) - instruction.Br(index:) -> br(current_state, index) - instruction.BrIf(index:) -> br_if(current_state, index) - instruction.Return -> return(current_state) - instruction.Call(index:) -> call(current_state, index) - - instruction.I32Const(value) -> integer_const(current_state, types.Integer32, value) - instruction.I64Const(value) -> integer_const(current_state, types.Integer64, value) - instruction.F32Const(value) -> float_const(current_state, types.Float32, value) - instruction.F64Const(value) -> float_const(current_state, types.Float64, value) - instruction.I32Eqz -> integer_eqz(current_state, types.Integer32) - instruction.I32Eq -> integer_eq(current_state, types.Integer32) - instruction.I32Ne -> integer_ne(current_state, types.Integer32) - instruction.I32LtS -> integer_lt_s(current_state, types.Integer32) - instruction.I32LtU -> integer_lt_u(current_state, types.Integer32) - instruction.I32GtS -> integer_gt_s(current_state, types.Integer32) - instruction.I32GtU -> integer_gt_u(current_state, types.Integer32) - instruction.I32LeS -> integer_le_s(current_state, types.Integer32) - instruction.I32LeU -> integer_le_u(current_state, types.Integer32) - instruction.I32GeS -> integer_ge_s(current_state, types.Integer32) - instruction.I32GeU -> integer_ge_u(current_state, types.Integer32) - instruction.I64Eqz -> integer_eqz(current_state, types.Integer64) - instruction.I64Eq -> integer_eq(current_state, types.Integer64) - instruction.I64Ne -> integer_ne(current_state, types.Integer64) - instruction.I64LtS -> integer_lt_s(current_state, types.Integer64) - instruction.I64LtU -> integer_lt_u(current_state, types.Integer64) - instruction.I64GtS -> integer_gt_s(current_state, types.Integer64) - instruction.I64GtU -> integer_gt_u(current_state, types.Integer64) - instruction.I64LeS -> integer_le_s(current_state, types.Integer64) - instruction.I64LeU -> integer_le_u(current_state, types.Integer64) - instruction.I64GeS -> integer_ge_s(current_state, types.Integer64) - instruction.I64GeU -> integer_ge_u(current_state, types.Integer64) - instruction.F32Eq -> float_eq(current_state, types.Float32) - instruction.F32Ne -> float_ne(current_state, types.Float32) - instruction.F32Lt -> float_lt(current_state, types.Float32) - instruction.F32Gt -> float_gt(current_state, types.Float32) - instruction.F32Le -> float_le(current_state, types.Float32) - instruction.F32Ge -> float_ge(current_state, types.Float32) - instruction.F64Eq -> float_eq(current_state, types.Float64) - instruction.F64Ne -> float_ne(current_state, types.Float64) - instruction.F64Lt -> float_lt(current_state, types.Float64) - instruction.F64Gt -> float_gt(current_state, types.Float64) - instruction.F64Le -> float_le(current_state, types.Float64) - instruction.F64Ge -> float_ge(current_state, types.Float64) - - instruction.I32Clz -> integer_clz(current_state, types.Integer32) - instruction.I64Clz -> integer_clz(current_state, types.Integer64) - instruction.I32Ctz -> integer_ctz(current_state, types.Integer32) - instruction.I64Ctz -> integer_ctz(current_state, types.Integer64) - instruction.I32Popcnt -> integer_popcnt(current_state, types.Integer32) - instruction.I64Popcnt -> integer_popcnt(current_state, types.Integer64) - - instruction.LocalGet(index) -> local_get(current_state, index) - instruction.LocalSet(index) -> local_set(current_state, index) - instruction.I32Add -> integer_add(current_state, types.Integer32) - instruction.I64Add -> integer_add(current_state, types.Integer64) - instruction.I32Sub -> integer_sub(current_state, types.Integer32) - instruction.I64Sub -> integer_sub(current_state, types.Integer64) - unknown -> Error("gwr/execution/machine.execute: unknown instruction \"" <> string.inspect(unknown) <> "\"") - } - } - ) - ) - - Ok(state) -} - fn get_default_value_for_type(type_: types.ValueType) -> runtime.Value { case type_ @@ -233,7 +132,7 @@ fn get_default_value_for_type(type_: types.ValueType) -> runtime.Value } } -pub fn return(state: MachineState) -> Result(MachineState, String) +pub fn return(state: MachineState) -> Result(#(MachineState, option.Option(Jump)), String) { // 1. Let F be the current frame. use frame <- result.try(result.replace_error(stack.get_current_frame(from: state.stack), "gwr/execution/machine.return: couldn't get the current frame")) @@ -256,7 +155,7 @@ pub fn return(state: MachineState) -> Result(MachineState, String) // 9. Push {\mathit{val}}^n to the stack. let stack = stack.push(to: stack, push: results |> list.reverse) // 10. Jump to the instruction after the original call that pushed the frame. - Ok(MachineState(..state, stack: stack)) + Ok(#(MachineState(..state, stack: stack), option.Some(Return))) } pub fn call(state: MachineState, index: index.FunctionIndex) -> Result(MachineState, String) @@ -309,6 +208,19 @@ pub fn invoke(state: MachineState, address: runtime.Address) -> Result(MachineSt } } +pub fn unwind_stack(state: MachineState) -> Result(MachineState, String) +{ + case stack.get_current_label(from: state.stack) + { + Ok(label) -> + { + use state <- result.try(exit_with_label(state, label)) + unwind_stack(state) + } + Error(_) -> Ok(state) + } +} + pub fn execute_with_frame(state: MachineState, frame: runtime.Frame, instructions: List(instruction.Instruction)) -> Result(MachineState, String) { // 9. Push the activation of F with arity m to the stack. @@ -316,21 +228,14 @@ pub fn execute_with_frame(state: MachineState, frame: runtime.Frame, instruction // 10. Let L be the label whose arity is m and whose continuation is the end of the function. let label = runtime.Label(arity: frame.arity, continuation: []) // 11. Enter the instruction sequence {\mathit{instr}}^\ast with label L. - use state <- result.try(execute_with_label(MachineState(..state, stack: stack), label, instructions, [])) - + use #(state, jump) <- result.try(enter_with_label(MachineState(..state, stack: stack), label, instructions, [])) // Returning from a function - - case stack.peek(from: state.stack) + case jump { - option.Some(stack.ValueEntry(value)) -> - { - // @NOTE: this block handles returns by jumps (i.e., return) - let #(stack, _) = stack.pop(from: state.stack) - let stack = stack.push(to: stack, push: [stack.ValueEntry(value)]) - Ok(MachineState(..state, stack: stack)) - } + option.Some(Return) -> Ok(state) _ -> { + use state <- result.try(unwind_stack(state)) // 1. Let F be the current frame. use frame <- result.try(result.replace_error(stack.get_current_frame(from: state.stack), "gwr/execution/machine.execute_with_frame: couldn't get the current frame")) // 2. Let n be the arity of the activation of F. @@ -352,7 +257,7 @@ pub fn execute_with_frame(state: MachineState, frame: runtime.Frame, instruction } } -pub fn br(state: MachineState, index: index.LabelIndex) +pub fn br(state: MachineState, index: index.LabelIndex) -> Result(#(MachineState, option.Option(Jump)), String) { // 1. Assert: due to validation, the stack contains at least l+1 labels. let all_labels = stack.pop_all(from: state.stack).1 |> list.filter(stack.is_label) @@ -392,12 +297,12 @@ pub fn br(state: MachineState, index: index.LabelIndex) ) // 7. Push the values {\mathit{val}}^n to the stack. - let stack = stack.push(to: stack, push: [stack.Jump(values |> list.reverse)]) + let stack = stack.push(to: stack, push: values |> list.reverse) // 8. Jump to the continuation of L. - execute(MachineState(..state, stack: stack), label.continuation) + Ok(#(MachineState(..state, stack: stack), option.Some(Branch(target: label.continuation)))) } -pub fn br_if(state: MachineState, index: index.LabelIndex) +pub fn br_if(state: MachineState, index: index.LabelIndex) -> Result(#(MachineState, option.Option(Jump)), String) { // 1. Assert: due to validation, a value of value type {\mathsf{i32}} is on the top of the stack. // 2. Pop the value {\mathsf{i32}}.{\mathsf{const}}~c from the stack. @@ -413,14 +318,14 @@ pub fn br_if(state: MachineState, index: index.LabelIndex) True -> br(state, index) // 4. Else: // b. Do nothing. - False -> Ok(state) + False -> Ok(#(state, option.None)) } } _ -> Error("gwr/execution/machine.br_if: expected the top of the stack to contain an i32 value") } } -pub fn loop(state: MachineState, block_type: instruction.BlockType, instructions: List(instruction.Instruction)) -> Result(MachineState, String) +pub fn loop(state: MachineState, block_type: instruction.BlockType, instructions: List(instruction.Instruction)) -> Result(#(MachineState, option.Option(Jump)), String) { // 1. Let F be the current frame. use frame <- result.try(result.replace_error(stack.get_current_frame(from: state.stack), "gwr/execution/machine.loop: couldn't get the current frame")) @@ -428,7 +333,6 @@ pub fn loop(state: MachineState, block_type: instruction.BlockType, instructions // 3. Let [t_1^m] {\rightarrow} [t_2^n] be the function type {\mathrm{expand}}_F({\mathit{blocktype}}). use function_type <- result.try(expand_block_type(frame.framestate, block_type)) let m = list.length(function_type.parameters) - // let n = list.length(function_type.results) // 4. Let L be the label whose arity is m and whose continuation is the start of the loop. let label = runtime.Label(arity: m, continuation: [instruction.Loop(block_type: block_type, instructions: instructions)]) // 5. Assert: due to validation, there are at least m values on the top of the stack. @@ -437,10 +341,10 @@ pub fn loop(state: MachineState, block_type: instruction.BlockType, instructions // 6. Pop the values {\mathit{val}}^m from the stack. let #(stack, values) = stack.pop_repeat(from: state.stack, up_to: m) // 7. Enter the block {\mathit{val}}^m~{\mathit{instr}}^\ast with label L. - execute_with_label(MachineState(..state, stack: stack), label, instructions, values |> list.reverse) + enter_with_label(MachineState(..state, stack: stack), label, instructions, values |> list.reverse) } -pub fn block(state: MachineState, block_type: instruction.BlockType, instructions: List(instruction.Instruction)) -> Result(MachineState, String) +pub fn block(state: MachineState, block_type: instruction.BlockType, instructions: List(instruction.Instruction)) -> Result(#(MachineState, option.Option(Jump)), String) { // 1. Let F be the current frame. use frame <- result.try(result.replace_error(stack.get_current_frame(from: state.stack), "gwr/execution/machine.block: couldn't get the current frame")) @@ -457,51 +361,149 @@ pub fn block(state: MachineState, block_type: instruction.BlockType, instruction // 6. Pop the values {\mathit{val}}^m from the stack. let #(stack, values) = stack.pop_repeat(from: state.stack, up_to: m) // 7. Enter the block {\mathit{val}}^m~{\mathit{instr}}^\ast with label L. - execute_with_label(MachineState(..state, stack: stack), label, instructions, values |> list.reverse) + enter_with_label(MachineState(..state, stack: stack), label, instructions, values |> list.reverse) } -pub fn execute_with_label(state: MachineState, label: runtime.Label, instructions: List(instruction.Instruction), parameters: List(stack.StackEntry)) -> Result(MachineState, String) +pub fn enter_with_label(state: MachineState, label: runtime.Label, instructions: List(instruction.Instruction), parameters: List(stack.StackEntry)) -> Result(#(MachineState, option.Option(Jump)), String) { - // Entering {\mathit{instr}}^\ast with label L - // 1. Push L to the stack. - let state = MachineState(..state, stack: stack.push(state.stack, [stack.LabelEntry(label)] |> list.append(parameters) )) - // 2. Jump to the start of the instruction sequence {\mathit{instr}}^\ast. - use state <- result.try(execute(state, instructions)) - - // Exiting {\mathit{instr}}^\ast with label L + let state = MachineState(..state, stack: stack.push(to: state.stack, push: [stack.LabelEntry(label)] |> list.append(parameters))) + evaluate_expression(state, instructions) +} - case stack.peek(state.stack) - { - option.Some(stack.ValueEntry(_)) -> - { - // @NOTE: this block handles exits by jumps (i.e., return) - Ok(state) - } - option.Some(stack.Jump(values)) -> +pub fn exit_with_label(state: MachineState, label: runtime.Label) -> Result(MachineState, String) +{ + // 1. Pop all values {\mathit{val}}^\ast from the top of the stack. + let #(stack, values) = stack.pop_while(from: state.stack, with: stack.is_value) + // 2. Assert: due to validation, the label L is now on the top of the stack. + // 3. Pop the label from the stack. + use stack <- result.try( + case stack.pop(stack) { - // @NOTE: this block handles jumps by br / br_if instructions - Ok(MachineState(..state, stack: stack.push(to: state.stack, push: values))) + #(stack, option.Some(stack.LabelEntry(some_label))) if some_label == label -> Ok(stack) + #(_, anything_else) -> Error("gwr/execution/machine.exit_with_label: expected the label " <> string.inspect(label) <> " pushed to the stack before execution but got " <> string.inspect(anything_else)) } + ) + // 4. Push {\mathit{val}}^\ast back to the stack. + let state = MachineState(..state, stack: stack.push(to: stack, push: values |> list.reverse)) + // 5. Jump to the position after the {\mathsf{end}} of the structured control instruction associated with the label L. + Ok(state) +} + +pub fn evaluate_expression(state: MachineState, instructions: List(instruction.Instruction)) -> Result(#(MachineState, option.Option(Jump)), String) +{ + case instructions + { + [] -> Ok(#(state, option.None)) _ -> { - // 1. Pop all values {\mathit{val}}^\ast from the top of the stack. - let #(stack, values) = stack.pop_while(from: state.stack, with: stack.is_value) - // 2. Assert: due to validation, the label L is now on the top of the stack. - // 3. Pop the label from the stack. - - use stack <- result.try( - case stack.pop(stack) + use instruction <- result.try(result.replace_error(list.first(instructions), "gwr/execution/machine.evaluate_expression: couldn't get the current instruction")) + use #(state, jump) <- result.try( + case instruction { - #(stack, option.Some(stack.LabelEntry(some_label))) if some_label == label -> Ok(stack) - #(_, anything_else) -> Error("gwr/execution/machine.execute_with_label: expected the label " <> string.inspect(label) <> " pushed to the stack before execution but got " <> string.inspect(anything_else)) + instruction.Block(block_type:, instructions:) -> block(state, block_type, instructions) + instruction.If(block_type:, instructions:, else_: else_) -> if_else(state, block_type, instructions, else_) + instruction.Loop(block_type:, instructions:) -> loop(state, block_type, instructions) + instruction.Br(index:) -> br(state, index) + instruction.BrIf(index:) -> br_if(state, index) + instruction.Return -> return(state) + _ -> result.map( + case instruction { + instruction.End -> Ok(state) + instruction.Call(index:) -> call(state, index) + + instruction.I32Const(value) -> integer_const(state, types.Integer32, value) + instruction.I64Const(value) -> integer_const(state, types.Integer64, value) + instruction.F32Const(value) -> float_const(state, types.Float32, value) + instruction.F64Const(value) -> float_const(state, types.Float64, value) + instruction.I32Eqz -> integer_eqz(state, types.Integer32) + instruction.I32Eq -> integer_eq(state, types.Integer32) + instruction.I32Ne -> integer_ne(state, types.Integer32) + instruction.I32LtS -> integer_lt_s(state, types.Integer32) + instruction.I32LtU -> integer_lt_u(state, types.Integer32) + instruction.I32GtS -> integer_gt_s(state, types.Integer32) + instruction.I32GtU -> integer_gt_u(state, types.Integer32) + instruction.I32LeS -> integer_le_s(state, types.Integer32) + instruction.I32LeU -> integer_le_u(state, types.Integer32) + instruction.I32GeS -> integer_ge_s(state, types.Integer32) + instruction.I32GeU -> integer_ge_u(state, types.Integer32) + instruction.I64Eqz -> integer_eqz(state, types.Integer64) + instruction.I64Eq -> integer_eq(state, types.Integer64) + instruction.I64Ne -> integer_ne(state, types.Integer64) + instruction.I64LtS -> integer_lt_s(state, types.Integer64) + instruction.I64LtU -> integer_lt_u(state, types.Integer64) + instruction.I64GtS -> integer_gt_s(state, types.Integer64) + instruction.I64GtU -> integer_gt_u(state, types.Integer64) + instruction.I64LeS -> integer_le_s(state, types.Integer64) + instruction.I64LeU -> integer_le_u(state, types.Integer64) + instruction.I64GeS -> integer_ge_s(state, types.Integer64) + instruction.I64GeU -> integer_ge_u(state, types.Integer64) + instruction.F32Eq -> float_eq(state, types.Float32) + instruction.F32Ne -> float_ne(state, types.Float32) + instruction.F32Lt -> float_lt(state, types.Float32) + instruction.F32Gt -> float_gt(state, types.Float32) + instruction.F32Le -> float_le(state, types.Float32) + instruction.F32Ge -> float_ge(state, types.Float32) + instruction.F64Eq -> float_eq(state, types.Float64) + instruction.F64Ne -> float_ne(state, types.Float64) + instruction.F64Lt -> float_lt(state, types.Float64) + instruction.F64Gt -> float_gt(state, types.Float64) + instruction.F64Le -> float_le(state, types.Float64) + instruction.F64Ge -> float_ge(state, types.Float64) + + instruction.I32Clz -> integer_clz(state, types.Integer32) + instruction.I64Clz -> integer_clz(state, types.Integer64) + instruction.I32Ctz -> integer_ctz(state, types.Integer32) + instruction.I64Ctz -> integer_ctz(state, types.Integer64) + instruction.I32Popcnt -> integer_popcnt(state, types.Integer32) + instruction.I64Popcnt -> integer_popcnt(state, types.Integer64) + + instruction.LocalGet(index) -> local_get(state, index) + instruction.LocalSet(index) -> local_set(state, index) + instruction.I32Add -> integer_add(state, types.Integer32) + instruction.I64Add -> integer_add(state, types.Integer64) + instruction.I32Sub -> integer_sub(state, types.Integer32) + instruction.I64Sub -> integer_sub(state, types.Integer64) + unknown -> Error("gwr/execution/machine.evaluate_expression: attempt to execute an unknown or unimplemented instruction \"" <> string.inspect(unknown) <> "\"") + }, + fn (state) { #(state, option.None) } + ) } ) + case jump + { + option.Some(Return) -> Ok(#(state, jump)) + option.Some(Branch(target: instructions)) -> evaluate_expression(state, instructions) + option.None -> evaluate_expression(state, instructions |> list.drop(1)) + } + } + } +} - // 4. Push {\mathit{val}}^\ast back to the stack. - let state = MachineState(..state, stack: stack.push(to: stack, push: values |> list.reverse)) - // 5. Jump to the position after the {\mathsf{end}} of the structured control instruction associated with the label L. - Ok(state) - } +pub fn if_else(state: MachineState, block_type: instruction.BlockType, if_instructions: List(instruction.Instruction), else_: option.Option(instruction.Instruction)) -> Result(#(MachineState, option.Option(Jump)), String) +{ + // 1. Assert: due to validation, a value of value type {\mathsf{i32}} is on the top of the stack. + // 2. Pop the value {\mathsf{i32}}.{\mathsf{const}}~c from the stack. + case stack.pop_as(from: state.stack, with: stack.to_value) + { + Ok(#(stack, runtime.Integer32(c))) -> + { + let state = MachineState(..state, stack: stack) + case c != 0 + { + // 3. If c is non-zero, then: + // a. Execute the block instruction {\mathsf{block}}~{\mathit{blocktype}}~{\mathit{instr}}_1^\ast~{\mathsf{end}}. + True -> block(state, block_type, if_instructions) + // 4. Else: + // a. Execute the block instruction {\mathsf{block}}~{\mathit{blocktype}}~{\mathit{instr}}_2^\ast~{\mathsf{end}}. + False -> case else_ + { + option.Some(instruction.Else(else_instructions)) -> block(state, block_type, else_instructions) + option.None -> Ok(#(state, option.None)) + anything_else -> Error("gwr/execution/machine.if_else: illegal instruction in the Else's field " <> string.inspect(anything_else)) + } + } + } + anything_else -> Error("gwr/execution/machine.if_else: expected the If's continuation flag but got " <> string.inspect(anything_else)) } } diff --git a/src/gwr/execution/stack.gleam b/src/gwr/execution/stack.gleam index b254d61..c37e5c9 100644 --- a/src/gwr/execution/stack.gleam +++ b/src/gwr/execution/stack.gleam @@ -17,10 +17,6 @@ pub type StackEntry ValueEntry(runtime.Value) LabelEntry(runtime.Label) ActivationEntry(runtime.Frame) - // @NOTE: the "Jump" entry is not part of the spec and - // is intended for signaling down stack that a jump has - // occurred. - Jump(List(StackEntry)) } pub fn create() -> Stack @@ -195,6 +191,15 @@ pub fn get_current_frame(from stack: Stack) -> Result(runtime.Frame, Nil) } } +pub fn get_current_label(from stack: Stack) -> Result(runtime.Label, Nil) +{ + pop_while(from: stack, with: fn (entry) { !is_activation_frame(entry) }).1 + |> list.filter(is_label) + |> list.map(to_label) + |> result.values + |> list.first +} + pub fn replace_current_frame(from stack: Stack, with new_frame: runtime.Frame) -> Result(Stack, Nil) { let #(stack, upper_entries) = pop_while(from: stack, with: fn (entry) { !is_activation_frame(entry) }) diff --git a/test/assets/rust/misc/fibonacci.rs b/test/assets/rust/misc/fibonacci.rs new file mode 100644 index 0000000..ce2da40 --- /dev/null +++ b/test/assets/rust/misc/fibonacci.rs @@ -0,0 +1,29 @@ +// rustc --crate-type cdylib --target wasm32-unknown-unknown -C debuginfo=none -C panic=abort -C strip=symbols -C opt-level=3 ./test/assets/rust/misc/fibonacci.rs -o ./test/assets/rust/misc/fibonacci.wasm + +#![no_std] + +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! +{ + loop {} +} + +#[no_mangle] +pub extern fn fibonacci(value: i32) -> i32 +{ + if value <= 0 + { + return 0; + } + else + { + if value == 1 + { + return 1; + } + else + { + return fibonacci(value - 1) + fibonacci(value - 2); + } + } +} \ No newline at end of file diff --git a/test/assets/rust/misc/fibonacci.wasm b/test/assets/rust/misc/fibonacci.wasm new file mode 100755 index 0000000000000000000000000000000000000000..3c61d61e1dd8a96cb7cb75e9fea815c5ae0335af GIT binary patch literal 197 zcmY+)K@Ng25Czab9mPmPWb2L|!5h%Ei3czywLp!K07l%nu^i2#Il6N;f9Ah|pLzm7 z37siYWJOpqAy2j($w)|a*#2Vd7MkUH1+hj(FUn6z`(;BT(- zQ(%>z=tvw@S-{dEgT+S68aqQ4MsX=ZQd6Ni5wb6l-$OpjKx(7pR(rz|q9{0nwqMa7 DcbF(a literal 0 HcmV?d00001 diff --git a/test/assets/control/block.wasm b/test/assets/wat/control/block.wasm similarity index 100% rename from test/assets/control/block.wasm rename to test/assets/wat/control/block.wasm diff --git a/test/assets/control/block.wat b/test/assets/wat/control/block.wat similarity index 100% rename from test/assets/control/block.wat rename to test/assets/wat/control/block.wat diff --git a/test/assets/control/call.wasm b/test/assets/wat/control/call.wasm similarity index 100% rename from test/assets/control/call.wasm rename to test/assets/wat/control/call.wasm diff --git a/test/assets/control/call.wat b/test/assets/wat/control/call.wat similarity index 100% rename from test/assets/control/call.wat rename to test/assets/wat/control/call.wat diff --git a/test/assets/control/if_else.wasm b/test/assets/wat/control/if_else.wasm similarity index 100% rename from test/assets/control/if_else.wasm rename to test/assets/wat/control/if_else.wasm diff --git a/test/assets/control/if_else.wat b/test/assets/wat/control/if_else.wat similarity index 100% rename from test/assets/control/if_else.wat rename to test/assets/wat/control/if_else.wat diff --git a/test/assets/control/loop.wasm b/test/assets/wat/control/loop.wasm similarity index 100% rename from test/assets/control/loop.wasm rename to test/assets/wat/control/loop.wasm diff --git a/test/assets/control/loop.wat b/test/assets/wat/control/loop.wat similarity index 100% rename from test/assets/control/loop.wat rename to test/assets/wat/control/loop.wat diff --git a/test/assets/control/recursion.wasm b/test/assets/wat/control/recursion.wasm similarity index 100% rename from test/assets/control/recursion.wasm rename to test/assets/wat/control/recursion.wasm diff --git a/test/assets/control/recursion.wat b/test/assets/wat/control/recursion.wat similarity index 100% rename from test/assets/control/recursion.wat rename to test/assets/wat/control/recursion.wat diff --git a/test/assets/control/fibonacci.wasm b/test/assets/wat/misc/fibonacci.wasm similarity index 100% rename from test/assets/control/fibonacci.wasm rename to test/assets/wat/misc/fibonacci.wasm diff --git a/test/assets/control/fibonacci.wat b/test/assets/wat/misc/fibonacci.wat similarity index 90% rename from test/assets/control/fibonacci.wat rename to test/assets/wat/misc/fibonacci.wat index a40b57a..f63e483 100644 --- a/test/assets/control/fibonacci.wat +++ b/test/assets/wat/misc/fibonacci.wat @@ -1,4 +1,4 @@ -;; wat2wasm ./test/assets/control/fibonacci.wat -o ./test/assets/control/fibonacci.wasm +;; wat2wasm ./test/assets/wat/misc/fibonacci.wat -o ./test/assets/wat/misc/fibonacci.wasm (module (func $fibonacci (export "fibonacci") (param $value i32) (result i32) (if (result i32) diff --git a/test/assets/sum.wasm b/test/assets/wat/misc/sum.wasm similarity index 100% rename from test/assets/sum.wasm rename to test/assets/wat/misc/sum.wasm diff --git a/test/assets/sum.wat b/test/assets/wat/misc/sum.wat similarity index 72% rename from test/assets/sum.wat rename to test/assets/wat/misc/sum.wat index 10b4e7e..53896d6 100644 --- a/test/assets/sum.wat +++ b/test/assets/wat/misc/sum.wat @@ -1,5 +1,4 @@ -;; sum.wat - +;; wat2wasm ./test/assets/wat/misc/sum.wat -o ./test/assets/wat/misc/sum.wasm (module (type $t0 (func (param i32 i32) (result i32))) (func $sum (export "sum") (type $t0) (param $p0 i32) (param $p1 i32) (result i32) diff --git a/test/gwr/execution/execution_test.gleam b/test/gwr/execution/execution_test.gleam index 2524515..12c744f 100644 --- a/test/gwr/execution/execution_test.gleam +++ b/test/gwr/execution/execution_test.gleam @@ -10,17 +10,9 @@ pub fn main() gleeunit.main() } -pub fn sum_test() -{ - let module_data = simplifile.read_bits(from: "./test/assets/sum.wasm") |> should.be_ok - let instance = gwr.create(from: module_data) |> should.be_ok - let #(_, result) = gwr.call(instance, "sum", [runtime.Integer32(4), runtime.Integer32(2)]) |> should.be_ok - result |> should.equal([runtime.Integer32(6)]) -} - pub fn block_test() { - let module_data = simplifile.read_bits(from: "./test/assets/control/block.wasm") |> should.be_ok + let module_data = simplifile.read_bits(from: "./test/assets/wat/control/block.wasm") |> should.be_ok let instance = gwr.create(from: module_data) |> should.be_ok let #(_, result) = gwr.call(instance, "block_test", [runtime.Integer32(2), runtime.Integer32(3)]) |> should.be_ok result |> should.equal([runtime.Integer32(7)]) @@ -28,7 +20,7 @@ pub fn block_test() pub fn if_else___if_scope___test() { - let module_data = simplifile.read_bits(from: "./test/assets/control/if_else.wasm") |> should.be_ok + let module_data = simplifile.read_bits(from: "./test/assets/wat/control/if_else.wasm") |> should.be_ok let instance = gwr.create(from: module_data) |> should.be_ok let #(_, result) = gwr.call(instance, "if_else_test", [runtime.Integer32(0)]) |> should.be_ok result |> should.equal([runtime.Integer32(110)]) @@ -36,7 +28,7 @@ pub fn if_else___if_scope___test() pub fn if_else___else_scope___test() { - let module_data = simplifile.read_bits(from: "./test/assets/control/if_else.wasm") |> should.be_ok + let module_data = simplifile.read_bits(from: "./test/assets/wat/control/if_else.wasm") |> should.be_ok let instance = gwr.create(from: module_data) |> should.be_ok let #(_, result) = gwr.call(instance, "if_else_test", [runtime.Integer32(1)]) |> should.be_ok result |> should.equal([runtime.Integer32(120)]) @@ -44,7 +36,7 @@ pub fn if_else___else_scope___test() pub fn loop___test() { - let module_data = simplifile.read_bits(from: "./test/assets/control/loop.wasm") |> should.be_ok + let module_data = simplifile.read_bits(from: "./test/assets/wat/control/loop.wasm") |> should.be_ok let instance = gwr.create(from: module_data) |> should.be_ok let #(_, result) = gwr.call(instance, "loop_test", []) |> should.be_ok result |> should.equal([runtime.Integer32(10)]) @@ -52,7 +44,7 @@ pub fn loop___test() pub fn call_test() { - let module_data = simplifile.read_bits(from: "./test/assets/control/call.wasm") |> should.be_ok + let module_data = simplifile.read_bits(from: "./test/assets/wat/control/call.wasm") |> should.be_ok let instance = gwr.create(from: module_data) |> should.be_ok let #(_, result) = gwr.call(instance, "call_test", [runtime.Integer32(5), runtime.Integer32(2)]) |> should.be_ok result |> should.equal([runtime.Integer32(7)]) @@ -60,15 +52,31 @@ pub fn call_test() pub fn recursion_test() { - let module_data = simplifile.read_bits(from: "./test/assets/control/recursion.wasm") |> should.be_ok + let module_data = simplifile.read_bits(from: "./test/assets/wat/control/recursion.wasm") |> should.be_ok let instance = gwr.create(from: module_data) |> should.be_ok let #(_, result) = gwr.call(instance, "recursion_test", [runtime.Integer32(3), runtime.Integer32(0)]) |> should.be_ok result |> should.equal([runtime.Integer32(6)]) } -pub fn fibonacci_test() +pub fn wat_misc_fibonacci_test() +{ + let module_data = simplifile.read_bits(from: "./test/assets/wat/misc/fibonacci.wasm") |> should.be_ok + let instance = gwr.create(from: module_data) |> should.be_ok + let #(_, result) = gwr.call(instance, "fibonacci", [runtime.Integer32(12)]) |> should.be_ok + result |> should.equal([runtime.Integer32(144)]) +} + +pub fn wat_misc_sum_test() +{ + let module_data = simplifile.read_bits(from: "./test/assets/wat/misc/sum.wasm") |> should.be_ok + let instance = gwr.create(from: module_data) |> should.be_ok + let #(_, result) = gwr.call(instance, "sum", [runtime.Integer32(4), runtime.Integer32(2)]) |> should.be_ok + result |> should.equal([runtime.Integer32(6)]) +} + +pub fn rust_misc_fibonacci_test() { - let module_data = simplifile.read_bits(from: "./test/assets/control/fibonacci.wasm") |> should.be_ok + let module_data = simplifile.read_bits(from: "./test/assets/rust/misc/fibonacci.wasm") |> should.be_ok let instance = gwr.create(from: module_data) |> should.be_ok let #(_, result) = gwr.call(instance, "fibonacci", [runtime.Integer32(12)]) |> should.be_ok result |> should.equal([runtime.Integer32(144)])