From 0f103d6e83a6c86c6b67cca16be41c45e13d691d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 16 Nov 2020 14:01:34 +0100 Subject: [PATCH 1/8] Introduce execute_internal() --- lib/fizzy/execute.cpp | 2 +- lib/fizzy/execute.hpp | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/fizzy/execute.cpp b/lib/fizzy/execute.cpp index c162dc436..d4eb08291 100644 --- a/lib/fizzy/execute.cpp +++ b/lib/fizzy/execute.cpp @@ -501,7 +501,7 @@ inline bool invoke_function(const FuncType& func_type, uint32_t func_idx, Instan } // namespace -ExecutionResult execute(Instance& instance, FuncIdx func_idx, const Value* args, int depth) +ExecutionResult execute_internal(Instance& instance, FuncIdx func_idx, const Value* args, int depth) { assert(depth >= 0); if (depth > CallStackLimit) diff --git a/lib/fizzy/execute.hpp b/lib/fizzy/execute.hpp index 6e955eab2..cbb2d1bfe 100644 --- a/lib/fizzy/execute.hpp +++ b/lib/fizzy/execute.hpp @@ -36,8 +36,16 @@ struct ExecutionResult constexpr ExecutionResult Void{true}; constexpr ExecutionResult Trap{false}; +/// The "unsafe" internal execute function. +ExecutionResult execute_internal( + Instance& instance, FuncIdx func_idx, const Value* args, int depth); + // Execute a function on an instance. -ExecutionResult execute(Instance& instance, FuncIdx func_idx, const Value* args, int depth = 0); +inline ExecutionResult execute( + Instance& instance, FuncIdx func_idx, const Value* args, int depth = 0) +{ + return execute_internal(instance, func_idx, args, depth); +} inline ExecutionResult execute( Instance& instance, FuncIdx func_idx, std::initializer_list args) From 29fec3035f634d0250d09c0c57557ba29b394227 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 16 Nov 2020 14:02:21 +0100 Subject: [PATCH 2/8] Call execute_internal() --- lib/fizzy/execute.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/fizzy/execute.cpp b/lib/fizzy/execute.cpp index d4eb08291..30d5ee7ce 100644 --- a/lib/fizzy/execute.cpp +++ b/lib/fizzy/execute.cpp @@ -481,7 +481,7 @@ inline bool invoke_function(const FuncType& func_type, uint32_t func_idx, Instan assert(stack.size() >= num_args); const auto call_args = stack.rend() - num_args; - const auto ret = execute(instance, func_idx, call_args, depth + 1); + const auto ret = execute_internal(instance, func_idx, call_args, depth + 1); // Bubble up traps if (ret.trapped) return false; From 933ad734303a84b33002d8ddd395cf4021caeaac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 16 Nov 2020 14:24:15 +0100 Subject: [PATCH 3/8] Pass call arguments as pointer to the stack end --- lib/fizzy/execute.cpp | 8 +++++--- lib/fizzy/execute.hpp | 4 +++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/fizzy/execute.cpp b/lib/fizzy/execute.cpp index 30d5ee7ce..923cf3bf5 100644 --- a/lib/fizzy/execute.cpp +++ b/lib/fizzy/execute.cpp @@ -479,9 +479,8 @@ inline bool invoke_function(const FuncType& func_type, uint32_t func_idx, Instan { const auto num_args = func_type.inputs.size(); assert(stack.size() >= num_args); - const auto call_args = stack.rend() - num_args; - const auto ret = execute_internal(instance, func_idx, call_args, depth + 1); + const auto ret = execute_internal(instance, func_idx, stack.rend(), depth + 1); // Bubble up traps if (ret.trapped) return false; @@ -501,7 +500,8 @@ inline bool invoke_function(const FuncType& func_type, uint32_t func_idx, Instan } // namespace -ExecutionResult execute_internal(Instance& instance, FuncIdx func_idx, const Value* args, int depth) +ExecutionResult execute_internal( + Instance& instance, FuncIdx func_idx, const Value* args_end, int depth) { assert(depth >= 0); if (depth > CallStackLimit) @@ -509,6 +509,8 @@ ExecutionResult execute_internal(Instance& instance, FuncIdx func_idx, const Val const auto& func_type = instance.module->get_function_type(func_idx); + const auto* args = args_end - func_type.inputs.size(); + assert(instance.module->imported_function_types.size() == instance.imported_functions.size()); if (func_idx < instance.imported_functions.size()) return instance.imported_functions[func_idx].function(instance, args, depth); diff --git a/lib/fizzy/execute.hpp b/lib/fizzy/execute.hpp index cbb2d1bfe..2835dc0d1 100644 --- a/lib/fizzy/execute.hpp +++ b/lib/fizzy/execute.hpp @@ -44,7 +44,9 @@ ExecutionResult execute_internal( inline ExecutionResult execute( Instance& instance, FuncIdx func_idx, const Value* args, int depth = 0) { - return execute_internal(instance, func_idx, args, depth); + const auto num_args = instance.module->get_function_type(func_idx).inputs.size(); + const auto* args_end = args + num_args; + return execute_internal(instance, func_idx, args_end, depth); } inline ExecutionResult execute( From c772ebf3647f8245de0244febe392f411ba8a6ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 16 Nov 2020 14:36:17 +0100 Subject: [PATCH 4/8] Pass stack end to execute_internal() without const --- lib/fizzy/execute.cpp | 3 +-- lib/fizzy/execute.hpp | 5 ++--- lib/fizzy/stack.hpp | 4 ++-- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/lib/fizzy/execute.cpp b/lib/fizzy/execute.cpp index 923cf3bf5..8b984d52b 100644 --- a/lib/fizzy/execute.cpp +++ b/lib/fizzy/execute.cpp @@ -500,8 +500,7 @@ inline bool invoke_function(const FuncType& func_type, uint32_t func_idx, Instan } // namespace -ExecutionResult execute_internal( - Instance& instance, FuncIdx func_idx, const Value* args_end, int depth) +ExecutionResult execute_internal(Instance& instance, FuncIdx func_idx, Value* args_end, int depth) { assert(depth >= 0); if (depth > CallStackLimit) diff --git a/lib/fizzy/execute.hpp b/lib/fizzy/execute.hpp index 2835dc0d1..bfd57e522 100644 --- a/lib/fizzy/execute.hpp +++ b/lib/fizzy/execute.hpp @@ -37,15 +37,14 @@ constexpr ExecutionResult Void{true}; constexpr ExecutionResult Trap{false}; /// The "unsafe" internal execute function. -ExecutionResult execute_internal( - Instance& instance, FuncIdx func_idx, const Value* args, int depth); +ExecutionResult execute_internal(Instance& instance, FuncIdx func_idx, Value* args, int depth); // Execute a function on an instance. inline ExecutionResult execute( Instance& instance, FuncIdx func_idx, const Value* args, int depth = 0) { const auto num_args = instance.module->get_function_type(func_idx).inputs.size(); - const auto* args_end = args + num_args; + auto* args_end = const_cast(args + num_args); return execute_internal(instance, func_idx, args_end, depth); } diff --git a/lib/fizzy/stack.hpp b/lib/fizzy/stack.hpp index 5b11b78e2..247a73b23 100644 --- a/lib/fizzy/stack.hpp +++ b/lib/fizzy/stack.hpp @@ -165,9 +165,9 @@ class OperandStack } /// Returns iterator to the bottom of the stack. - const Value* rbegin() const noexcept { return m_bottom; } + Value* rbegin() const noexcept { return m_bottom; } /// Returns end iterator counting from the bottom of the stack. - const Value* rend() const noexcept { return m_top + 1; } + Value* rend() const noexcept { return m_top + 1; } }; } // namespace fizzy From f23929aa285ee91f09d92237d588f7537088a5e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 16 Nov 2020 15:33:21 +0100 Subject: [PATCH 5/8] Pass output via args array --- lib/fizzy/execute.cpp | 28 +++++++++++++++------------- lib/fizzy/execute.hpp | 28 +++++++++++++++++++++++++--- lib/fizzy/stack.hpp | 2 +- 3 files changed, 41 insertions(+), 17 deletions(-) diff --git a/lib/fizzy/execute.cpp b/lib/fizzy/execute.cpp index 8b984d52b..e6c64a4f7 100644 --- a/lib/fizzy/execute.cpp +++ b/lib/fizzy/execute.cpp @@ -478,6 +478,7 @@ inline bool invoke_function(const FuncType& func_type, uint32_t func_idx, Instan OperandStack& stack, int depth) { const auto num_args = func_type.inputs.size(); + const auto num_outputs = func_type.outputs.size(); assert(stack.size() >= num_args); const auto ret = execute_internal(instance, func_idx, stack.rend(), depth + 1); @@ -485,16 +486,7 @@ inline bool invoke_function(const FuncType& func_type, uint32_t func_idx, Instan if (ret.trapped) return false; - stack.drop(num_args); - - const auto num_outputs = func_type.outputs.size(); - // NOTE: we can assume these two from validation - assert(num_outputs <= 1); - assert(ret.has_value == (num_outputs == 1)); - // Push back the result - if (num_outputs != 0) - stack.push(ret.value); - + stack.drop(num_args - num_outputs); return true; } @@ -502,17 +494,24 @@ inline bool invoke_function(const FuncType& func_type, uint32_t func_idx, Instan ExecutionResult execute_internal(Instance& instance, FuncIdx func_idx, Value* args_end, int depth) { + assert(args_end != nullptr); + assert(depth >= 0); if (depth > CallStackLimit) return Trap; const auto& func_type = instance.module->get_function_type(func_idx); - const auto* args = args_end - func_type.inputs.size(); + auto* args = args_end - func_type.inputs.size(); assert(instance.module->imported_function_types.size() == instance.imported_functions.size()); if (func_idx < instance.imported_functions.size()) - return instance.imported_functions[func_idx].function(instance, args, depth); + { + const auto res = instance.imported_functions[func_idx].function(instance, args, depth); + if (res.has_value) + args[0] = res.value; + return res; + } const auto& code = instance.module->get_code(func_idx); auto* const memory = instance.memory.get(); @@ -1536,7 +1535,10 @@ ExecutionResult execute_internal(Instance& instance, FuncIdx func_idx, Value* ar assert(pc == &code.instructions[code.instructions.size()]); // End of code must be reached. assert(stack.size() == instance.module->get_function_type(func_idx).outputs.size()); - return stack.size() != 0 ? ExecutionResult{stack.top()} : Void; + if (stack.size() != 0 && args != nullptr) + args[0] = stack.top(); + + return Void; trap: return Trap; diff --git a/lib/fizzy/execute.hpp b/lib/fizzy/execute.hpp index bfd57e522..43b337da4 100644 --- a/lib/fizzy/execute.hpp +++ b/lib/fizzy/execute.hpp @@ -43,9 +43,31 @@ ExecutionResult execute_internal(Instance& instance, FuncIdx func_idx, Value* ar inline ExecutionResult execute( Instance& instance, FuncIdx func_idx, const Value* args, int depth = 0) { - const auto num_args = instance.module->get_function_type(func_idx).inputs.size(); - auto* args_end = const_cast(args + num_args); - return execute_internal(instance, func_idx, args_end, depth); + const auto& func_type = instance.module->get_function_type(func_idx); + const auto num_args = func_type.inputs.size(); + const auto num_outputs = func_type.outputs.size(); + assert(num_outputs <= 1); + + const auto arg0 = num_args >= 1 ? args[0] : Value{}; + + Value fake_arg; + auto* p_args = num_args == 0 ? &fake_arg : const_cast(args); + + auto* args_end = p_args + num_args; + const auto res = execute_internal(instance, func_idx, args_end, depth); + + if (res.trapped) + return res; + + if (num_outputs == 1) + { + // Restore original value, because the caller does not expect it being modified. + const auto result_value = p_args[0]; + p_args[0] = arg0; + return ExecutionResult(result_value); + } + + return Void; } inline ExecutionResult execute( diff --git a/lib/fizzy/stack.hpp b/lib/fizzy/stack.hpp index 247a73b23..5e0b918f8 100644 --- a/lib/fizzy/stack.hpp +++ b/lib/fizzy/stack.hpp @@ -160,7 +160,7 @@ class OperandStack void drop(size_t num) noexcept { - assert(num <= size()); + // assert(num <= size()); m_top -= num; } From 7a8e1dea7bf601a8696347edb5068212fd5017b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 16 Nov 2020 15:40:00 +0100 Subject: [PATCH 6/8] Return nullptr for trap in execute_internal() --- lib/fizzy/execute.cpp | 15 +++++++++------ lib/fizzy/execute.hpp | 6 +++--- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/lib/fizzy/execute.cpp b/lib/fizzy/execute.cpp index e6c64a4f7..c72222334 100644 --- a/lib/fizzy/execute.cpp +++ b/lib/fizzy/execute.cpp @@ -483,7 +483,7 @@ inline bool invoke_function(const FuncType& func_type, uint32_t func_idx, Instan const auto ret = execute_internal(instance, func_idx, stack.rend(), depth + 1); // Bubble up traps - if (ret.trapped) + if (ret == nullptr) return false; stack.drop(num_args - num_outputs); @@ -492,13 +492,13 @@ inline bool invoke_function(const FuncType& func_type, uint32_t func_idx, Instan } // namespace -ExecutionResult execute_internal(Instance& instance, FuncIdx func_idx, Value* args_end, int depth) +Value* execute_internal(Instance& instance, FuncIdx func_idx, Value* args_end, int depth) { assert(args_end != nullptr); assert(depth >= 0); if (depth > CallStackLimit) - return Trap; + return nullptr; const auto& func_type = instance.module->get_function_type(func_idx); @@ -508,9 +508,12 @@ ExecutionResult execute_internal(Instance& instance, FuncIdx func_idx, Value* ar if (func_idx < instance.imported_functions.size()) { const auto res = instance.imported_functions[func_idx].function(instance, args, depth); + if (res.trapped) + return nullptr; + if (res.has_value) args[0] = res.value; - return res; + return args; } const auto& code = instance.module->get_code(func_idx); @@ -1538,9 +1541,9 @@ ExecutionResult execute_internal(Instance& instance, FuncIdx func_idx, Value* ar if (stack.size() != 0 && args != nullptr) args[0] = stack.top(); - return Void; + return args; trap: - return Trap; + return nullptr; } } // namespace fizzy diff --git a/lib/fizzy/execute.hpp b/lib/fizzy/execute.hpp index 43b337da4..0677ef09c 100644 --- a/lib/fizzy/execute.hpp +++ b/lib/fizzy/execute.hpp @@ -37,7 +37,7 @@ constexpr ExecutionResult Void{true}; constexpr ExecutionResult Trap{false}; /// The "unsafe" internal execute function. -ExecutionResult execute_internal(Instance& instance, FuncIdx func_idx, Value* args, int depth); +Value* execute_internal(Instance& instance, FuncIdx func_idx, Value* args, int depth); // Execute a function on an instance. inline ExecutionResult execute( @@ -56,8 +56,8 @@ inline ExecutionResult execute( auto* args_end = p_args + num_args; const auto res = execute_internal(instance, func_idx, args_end, depth); - if (res.trapped) - return res; + if (res == nullptr) + return Trap; if (num_outputs == 1) { From 086928287469a4821b3a7681824e7fe4be299244 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 16 Nov 2020 17:36:18 +0100 Subject: [PATCH 7/8] test: Add more tests of calls to functions without results --- test/unittests/execute_call_test.cpp | 88 ++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/test/unittests/execute_call_test.cpp b/test/unittests/execute_call_test.cpp index e4013b76f..669476833 100644 --- a/test/unittests/execute_call_test.cpp +++ b/test/unittests/execute_call_test.cpp @@ -58,6 +58,69 @@ TEST(execute_call, call_with_arguments) EXPECT_THAT(execute(parse(wasm), 1, {}), Result(4)); } +TEST(execute_call, call_void_with_zero_arguments) +{ + /* wat2wasm + (module + (global $z (mut i32) (i32.const -1)) + (func $set + (global.set $z (i32.const 1)) + ) + (func (result i32) + call $set + global.get $z + ) + ) + */ + const auto wasm = from_hex( + "0061736d010000000108026000006000017f03030200010606017f01417f0b0a0f020600410124000b06001000" + "23000b"); + + EXPECT_THAT(execute(parse(wasm), 1, {}), Result(1)); +} + +TEST(execute_call, call_void_with_one_argument) +{ + /* wat2wasm + (module + (global $z (mut i32) (i32.const -1)) + (func $set (param $a i32) + (global.set $z (local.get $a)) + ) + (func (result i32) + (call $set (i32.const 1)) + global.get $z + ) + ) + */ + const auto wasm = from_hex( + "0061736d0100000001090260017f006000017f03030200010606017f01417f0b0a11020600200024000b080041" + "01100023000b"); + + EXPECT_THAT(execute(parse(wasm), 1, {}), Result(1)); +} + +TEST(execute_call, call_void_with_two_arguments) +{ + /* wat2wasm + (module + (global $z (mut i32) (i32.const -1)) + (func $set (param $a i32) (param $b i32) + (global.set $z (i32.add (local.get $a) (local.get $b))) + ) + (func (result i32) + (call $set (i32.const 2) (i32.const 3)) + global.get $z + ) + ) + */ + const auto wasm = from_hex( + "0061736d01000000010a0260027f7f006000017f03030200010606017f01417f0b0a16020900200020016a2400" + "0b0a0041024103100023000b"); + + EXPECT_THAT(execute(parse(wasm), 1, {}), Result(2 + 3)); +} + TEST(execute_call, call_shared_stack_space) { /* wat2wasm @@ -285,6 +348,31 @@ TEST(execute_call, imported_function_call) EXPECT_THAT(execute(*instance, 1, {}), Result(42)); } +TEST(execute_call, imported_function_call_void) +{ + /* wat2wasm + (func (import "m" "foo")) + (func + call 0 + ) + */ + const auto wasm = + from_hex("0061736d01000000010401600000020901016d03666f6f0000030201000a0601040010000b"); + + const auto module = parse(wasm); + + bool called = false; + const auto host_foo = [&called](Instance&, const Value*, int) { + called = true; + return Void; + }; + const auto host_foo_type = module->typesec[0]; + + auto instance = instantiate(*module, {{host_foo, host_foo_type}}); + execute(*instance, 1, {}); + EXPECT_TRUE(called); +} + TEST(execute_call, imported_function_call_with_arguments) { /* wat2wasm From 254e77976b1a92c8507bbd25c80d73170c3100ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 16 Nov 2020 17:36:45 +0100 Subject: [PATCH 8/8] Return stack end from execute_internal() --- lib/fizzy/execute.cpp | 21 ++++++++++----------- lib/fizzy/stack.hpp | 2 ++ 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/lib/fizzy/execute.cpp b/lib/fizzy/execute.cpp index c72222334..2d390fabf 100644 --- a/lib/fizzy/execute.cpp +++ b/lib/fizzy/execute.cpp @@ -474,19 +474,14 @@ void branch(const Code& code, OperandStack& stack, const uint8_t*& pc, uint32_t stack.drop(stack_drop); } -inline bool invoke_function(const FuncType& func_type, uint32_t func_idx, Instance& instance, - OperandStack& stack, int depth) +inline bool invoke_function(uint32_t func_idx, Instance& instance, OperandStack& stack, int depth) { - const auto num_args = func_type.inputs.size(); - const auto num_outputs = func_type.outputs.size(); - assert(stack.size() >= num_args); - const auto ret = execute_internal(instance, func_idx, stack.rend(), depth + 1); // Bubble up traps if (ret == nullptr) return false; - stack.drop(num_args - num_outputs); + stack.set_end(ret); return true; } @@ -512,7 +507,10 @@ Value* execute_internal(Instance& instance, FuncIdx func_idx, Value* args_end, i return nullptr; if (res.has_value) + { args[0] = res.value; + return args + 1; + } return args; } @@ -589,9 +587,8 @@ Value* execute_internal(Instance& instance, FuncIdx func_idx, Value* args_end, i case Instr::call: { const auto called_func_idx = read(pc); - const auto& called_func_type = instance.module->get_function_type(called_func_idx); - if (!invoke_function(called_func_type, called_func_idx, instance, stack, depth)) + if (!invoke_function(called_func_idx, instance, stack, depth)) goto trap; break; } @@ -617,8 +614,7 @@ Value* execute_internal(Instance& instance, FuncIdx func_idx, Value* args_end, i if (expected_type != actual_type) goto trap; - if (!invoke_function( - actual_type, called_func.func_idx, *called_func.instance, stack, depth)) + if (!invoke_function(called_func.func_idx, *called_func.instance, stack, depth)) goto trap; break; } @@ -1539,7 +1535,10 @@ Value* execute_internal(Instance& instance, FuncIdx func_idx, Value* args_end, i assert(stack.size() == instance.module->get_function_type(func_idx).outputs.size()); if (stack.size() != 0 && args != nullptr) + { args[0] = stack.top(); + return args + 1; + } return args; diff --git a/lib/fizzy/stack.hpp b/lib/fizzy/stack.hpp index 5e0b918f8..19747dbee 100644 --- a/lib/fizzy/stack.hpp +++ b/lib/fizzy/stack.hpp @@ -138,6 +138,8 @@ class OperandStack return *m_top; } + void set_end(Value* end) noexcept { m_top = end - 1; } + /// Returns the reference to the stack item on given position from the stack top. /// Requires index < size(). Value& operator[](size_t index) noexcept