diff --git a/lib/fizzy/execute.cpp b/lib/fizzy/execute.cpp index 7f3e085ac..1ea4fb6e2 100644 --- a/lib/fizzy/execute.cpp +++ b/lib/fizzy/execute.cpp @@ -271,10 +271,10 @@ bool invoke_function( { const auto num_args = func_type.inputs.size(); assert(stack.size() >= num_args); - std::vector call_args{stack.rend() - num_args, stack.rend()}; + span call_args{stack.rend() - num_args, num_args}; stack.drop(num_args); - const auto ret = func(instance, std::move(call_args), depth + 1); + const auto ret = func(instance, call_args, depth + 1); // Bubble up traps if (ret.trapped) return false; @@ -293,8 +293,8 @@ bool invoke_function( inline bool invoke_function(const FuncType& func_type, uint32_t func_idx, Instance& instance, OperandStack& stack, int depth) { - const auto func = [func_idx](Instance& _instance, std::vector args, int _depth) { - return execute(_instance, func_idx, std::move(args), _depth); + const auto func = [func_idx](Instance& _instance, span args, int _depth) { + return execute(_instance, func_idx, args, _depth); }; return invoke_function(func_type, func, instance, stack, depth); } @@ -549,10 +549,9 @@ std::unique_ptr instantiate(Module module, auto it_table = instance->table->begin() + elementsec_offsets[i]; for (const auto idx : instance->module.elementsec[i].init) { - auto func = [idx, &instance_ref = *instance]( - fizzy::Instance&, std::vector args, int depth) { - return execute(instance_ref, idx, std::move(args), depth); - }; + auto func = [idx, &instance_ref = *instance](fizzy::Instance&, + span args, + int depth) { return execute(instance_ref, idx, args, depth); }; *it_table++ = ExternalFunction{std::move(func), instance->module.get_function_type(idx)}; @@ -584,10 +583,8 @@ std::unique_ptr instantiate(Module module, // Wrap the function with the lambda capturing shared instance auto& table_function = (*it_table)->function; table_function = [shared_instance, func = std::move(table_function)]( - fizzy::Instance& _instance, std::vector args, - int depth) { - return func(_instance, std::move(args), depth); - }; + fizzy::Instance& _instance, span args, + int depth) { return func(_instance, args, depth); }; ++it_table; } } @@ -599,15 +596,14 @@ std::unique_ptr instantiate(Module module, return instance; } -execution_result execute( - Instance& instance, FuncIdx func_idx, std::vector args, int depth) +execution_result execute(Instance& instance, FuncIdx func_idx, span args, int depth) { assert(depth >= 0); if (depth > CallStackLimit) return {true, {}}; if (func_idx < instance.imported_functions.size()) - return instance.imported_functions[func_idx].function(instance, std::move(args), depth); + return instance.imported_functions[func_idx].function(instance, args, depth); const auto code_idx = func_idx - instance.imported_functions.size(); assert(code_idx < instance.module.codesec.size()); @@ -615,8 +611,8 @@ execution_result execute( const auto& code = instance.module.codesec[code_idx]; auto* const memory = instance.memory.get(); - std::vector locals = std::move(args); - locals.resize(locals.size() + code.local_count); + std::vector locals(args.size() + code.local_count, 0); + std::copy_n(args.begin(), args.size(), locals.begin()); OperandStack stack(static_cast(code.max_stack_height)); @@ -1523,8 +1519,8 @@ std::optional find_exported_function(Instance& instance, std:: return std::nullopt; const auto idx = *opt_index; - auto func = [idx, &instance](fizzy::Instance&, std::vector args, int depth) { - return execute(instance, idx, std::move(args), depth); + auto func = [idx, &instance](fizzy::Instance&, span args, int depth) { + return execute(instance, idx, args, depth); }; return ExternalFunction{std::move(func), instance.module.get_function_type(idx)}; diff --git a/lib/fizzy/execute.hpp b/lib/fizzy/execute.hpp index 8dcbacddd..76c14a26e 100644 --- a/lib/fizzy/execute.hpp +++ b/lib/fizzy/execute.hpp @@ -6,6 +6,7 @@ #include "exceptions.hpp" #include "module.hpp" +#include "span.hpp" #include "types.hpp" #include #include @@ -27,7 +28,7 @@ struct Instance; struct ExternalFunction { - std::function, int depth)> function; + std::function, int depth)> function; FuncType type; }; @@ -95,7 +96,13 @@ std::unique_ptr instantiate(Module module, // Execute a function on an instance. execution_result execute( - Instance& instance, FuncIdx func_idx, std::vector args, int depth = 0); + Instance& instance, FuncIdx func_idx, span args, int depth = 0); + +inline execution_result execute( + Instance& instance, FuncIdx func_idx, std::initializer_list args) +{ + return execute(instance, func_idx, span{args}); +} // Function that should be used by instantiate as imports, identified by module and function name. @@ -105,7 +112,7 @@ struct ImportedFunction std::string name; std::vector inputs; std::optional output; - std::function, int depth)> function; + std::function, int depth)> function; }; // Create vector of ExternalFunctions ready to be passed to instantiate. diff --git a/lib/fizzy/span.hpp b/lib/fizzy/span.hpp index e3b4899c6..d14e6428d 100644 --- a/lib/fizzy/span.hpp +++ b/lib/fizzy/span.hpp @@ -52,6 +52,7 @@ class span constexpr T& operator[](std::size_t index) const noexcept { return m_begin[index]; } + constexpr T* data() const noexcept { return m_begin; } [[nodiscard]] constexpr std::size_t size() const noexcept { return m_size; } constexpr iterator begin() const noexcept { return m_begin; } diff --git a/test/bench_internal/CMakeLists.txt b/test/bench_internal/CMakeLists.txt index 4174d9b6c..9f3535367 100644 --- a/test/bench_internal/CMakeLists.txt +++ b/test/bench_internal/CMakeLists.txt @@ -6,6 +6,7 @@ add_executable(fizzy-bench-internal) target_sources(fizzy-bench-internal PRIVATE bench_internal.cpp + execute_benchmarks.cpp experimental.cpp parser_benchmarks.cpp parser_noinline.cpp diff --git a/test/bench_internal/execute_benchmarks.cpp b/test/bench_internal/execute_benchmarks.cpp new file mode 100644 index 000000000..81eda67ad --- /dev/null +++ b/test/bench_internal/execute_benchmarks.cpp @@ -0,0 +1,80 @@ +// Fizzy: A fast WebAssembly interpreter +// Copyright 2020 The Fizzy Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include "span.hpp" +#include +#include +#include + +namespace +{ +[[gnu::noinline]] auto init_locals_1(fizzy::span args, uint32_t local_count) +{ + std::vector locals; + locals.reserve(args.size() + local_count); + std::copy_n(args.begin(), args.size(), std::back_inserter(locals)); + locals.resize(locals.size() + local_count); + return locals; +} + +[[gnu::noinline]] auto init_locals_2(fizzy::span args, uint32_t local_count) +{ + std::vector locals(args.size() + local_count); + std::copy_n(args.begin(), args.size(), locals.begin()); + return locals; +} + +[[gnu::noinline]] auto init_locals_3(fizzy::span args, uint32_t local_count) +{ + std::vector locals(args.size() + local_count); + __builtin_memcpy(locals.data(), args.data(), args.size()); + return locals; +} + +[[gnu::noinline]] auto init_locals_4(fizzy::span args, uint32_t local_count) +{ + auto locals = std::make_unique(args.size() + local_count); + std::copy_n(args.begin(), args.size(), &locals[0]); + std::fill_n(&locals[args.size()], local_count, 0); + return locals; +} + +[[gnu::noinline]] auto init_locals_5(fizzy::span args, uint32_t local_count) +{ + auto locals = std::make_unique(args.size() + local_count); + __builtin_memcpy(locals.get(), args.data(), args.size()); + __builtin_memset(locals.get() + args.size(), 0, local_count); + return locals; +} +} // namespace + +template , uint32_t)> +static void init_locals(benchmark::State& state) +{ + const auto num_args = static_cast(state.range(0)); + const auto num_locals = static_cast(state.range(1)); + + const std::vector args(num_args, 0xa49); + benchmark::ClobberMemory(); + + for ([[maybe_unused]] auto _ : state) + { + const auto locals = Fn(args, num_locals); + benchmark::DoNotOptimize(locals); + } +} +#define ARGS \ + Args({0, 0}) \ + ->Args({2, 4}) \ + ->Args({2, 38}) \ + ->Args({3, 4}) \ + ->Args({3, 8}) \ + ->Args({3, 13}) \ + ->Args({5, 30}) \ + ->Args({10, 100}) +BENCHMARK_TEMPLATE(init_locals, std::vector, init_locals_1)->ARGS; +BENCHMARK_TEMPLATE(init_locals, std::vector, init_locals_2)->ARGS; +BENCHMARK_TEMPLATE(init_locals, std::vector, init_locals_3)->ARGS; +BENCHMARK_TEMPLATE(init_locals, std::unique_ptr, init_locals_4)->ARGS; +BENCHMARK_TEMPLATE(init_locals, std::unique_ptr, init_locals_5)->ARGS; diff --git a/test/spectests/spectests.cpp b/test/spectests/spectests.cpp index e5f4fe759..03d3eaa88 100644 --- a/test/spectests/spectests.cpp +++ b/test/spectests/spectests.cpp @@ -441,7 +441,7 @@ class test_runner try { - return fizzy::execute(*instance, *func_idx, std::move(args)); + return fizzy::execute(*instance, *func_idx, args); } catch (fizzy::unsupported_feature const& ex) { diff --git a/test/unittests/api_test.cpp b/test/unittests/api_test.cpp index e1f955dbe..ee375c776 100644 --- a/test/unittests/api_test.cpp +++ b/test/unittests/api_test.cpp @@ -16,12 +16,12 @@ namespace { auto function_returning_value(uint64_t value) noexcept { - return [value](Instance&, std::vector, int) { + return [value](Instance&, span, int) { return execution_result{false, {value}}; }; } -execution_result function_returning_void(Instance&, std::vector, int) noexcept +execution_result function_returning_void(Instance&, span, int) noexcept { return {}; } @@ -222,7 +222,7 @@ TEST(api, find_exported_function) "0061736d010000000105016000017f021001087370656374657374036261720000040401700000050401010102" "0606017f0041000b07170403666f6f000001670300037461620100036d656d0200"); - auto bar = [](Instance&, std::vector, int) { return execution_result{false, {42}}; }; + auto bar = [](Instance&, span, int) { return execution_result{false, {42}}; }; const auto bar_type = FuncType{{}, {ValType::i32}}; auto instance_reexported_function = diff --git a/test/unittests/execute_call_test.cpp b/test/unittests/execute_call_test.cpp index d26b63c96..00f189485 100644 --- a/test/unittests/execute_call_test.cpp +++ b/test/unittests/execute_call_test.cpp @@ -147,11 +147,11 @@ TEST(execute_call, call_indirect_imported_table) const Module module = parse(bin); - auto f1 = [](Instance&, std::vector, int) { return execution_result{false, {1}}; }; - auto f2 = [](Instance&, std::vector, int) { return execution_result{false, {2}}; }; - auto f3 = [](Instance&, std::vector, int) { return execution_result{false, {3}}; }; - auto f4 = [](Instance&, std::vector, int) { return execution_result{false, {4}}; }; - auto f5 = [](Instance&, std::vector, int) { return execution_result{true, {}}; }; + auto f1 = [](Instance&, span, int) { return execution_result{false, {1}}; }; + auto f2 = [](Instance&, span, int) { return execution_result{false, {2}}; }; + auto f3 = [](Instance&, span, int) { return execution_result{false, {3}}; }; + auto f4 = [](Instance&, span, int) { return execution_result{false, {4}}; }; + auto f5 = [](Instance&, span, int) { return execution_result{true, {}}; }; auto out_i32 = FuncType{{}, {ValType::i32}}; auto out_i64 = FuncType{{}, {ValType::i64}}; @@ -218,7 +218,7 @@ TEST(execute_call, imported_function_call) const auto module = parse(wasm); - constexpr auto host_foo = [](Instance&, std::vector, int) -> execution_result { + constexpr auto host_foo = [](Instance&, span, int) -> execution_result { return {false, {42}}; }; const auto host_foo_type = module.typesec[0]; @@ -245,7 +245,7 @@ TEST(execute_call, imported_function_call_with_arguments) const auto module = parse(wasm); - auto host_foo = [](Instance&, std::vector args, int) -> execution_result { + auto host_foo = [](Instance&, span args, int) -> execution_result { return {false, {args[0] * 2}}; }; const auto host_foo_type = module.typesec[0]; @@ -289,10 +289,10 @@ TEST(execute_call, imported_functions_call_indirect) ASSERT_EQ(module.importsec.size(), 2); ASSERT_EQ(module.codesec.size(), 2); - constexpr auto sqr = [](Instance&, std::vector args, int) -> execution_result { + constexpr auto sqr = [](Instance&, span args, int) -> execution_result { return {false, {args[0] * args[0]}}; }; - constexpr auto isqrt = [](Instance&, std::vector args, int) -> execution_result { + constexpr auto isqrt = [](Instance&, span args, int) -> execution_result { return {false, {(11 + args[0] / 11) / 2}}; }; @@ -337,10 +337,8 @@ TEST(execute_call, imported_function_from_another_module) const auto func_idx = fizzy::find_exported_function(module1, "sub"); ASSERT_TRUE(func_idx.has_value()); - auto sub = [&instance1, func_idx]( - Instance&, std::vector args, int) -> execution_result { - return fizzy::execute(*instance1, *func_idx, std::move(args)); - }; + auto sub = [&instance1, func_idx](Instance&, span args, + int) -> execution_result { return fizzy::execute(*instance1, *func_idx, args); }; auto instance2 = instantiate(module2, {{sub, module1.typesec[0]}}); @@ -516,7 +514,7 @@ TEST(execute_call, call_imported_infinite_recursion) "0061736d010000000105016000017f020b01036d6f6403666f6f0000030201000a0601040010000b"); const auto module = parse(wasm); - auto host_foo = [](Instance& instance, std::vector, int depth) -> execution_result { + auto host_foo = [](Instance& instance, span, int depth) -> execution_result { return execute(instance, 0, {}, depth + 1); }; const auto host_foo_type = module.typesec[0]; diff --git a/test/unittests/execute_control_test.cpp b/test/unittests/execute_control_test.cpp index 52ee4dcf9..be82f29e4 100644 --- a/test/unittests/execute_control_test.cpp +++ b/test/unittests/execute_control_test.cpp @@ -664,7 +664,7 @@ TEST(execute_control, br_1_out_of_function_and_imported_function) "0061736d010000000108026000006000017f02150108696d706f727465640866756e6374696f6e000003020101" "0a0d010b00034041010c010b41000b"); - constexpr auto fake_imported_function = [](Instance&, std::vector, + constexpr auto fake_imported_function = [](Instance&, span, int) noexcept -> execution_result { return {}; }; const auto module = parse(bin); diff --git a/test/unittests/execute_test.cpp b/test/unittests/execute_test.cpp index 29fdbeffb..c326b305b 100644 --- a/test/unittests/execute_test.cpp +++ b/test/unittests/execute_test.cpp @@ -601,7 +601,7 @@ TEST(execute, imported_function) const auto module = parse(wasm); ASSERT_EQ(module.typesec.size(), 1); - constexpr auto host_foo = [](Instance&, std::vector args, int) -> execution_result { + constexpr auto host_foo = [](Instance&, span args, int) -> execution_result { return {false, {args[0] + args[1]}}; }; @@ -621,10 +621,10 @@ TEST(execute, imported_two_functions) const auto module = parse(wasm); ASSERT_EQ(module.typesec.size(), 1); - constexpr auto host_foo1 = [](Instance&, std::vector args, int) -> execution_result { + constexpr auto host_foo1 = [](Instance&, span args, int) -> execution_result { return {false, {args[0] + args[1]}}; }; - constexpr auto host_foo2 = [](Instance&, std::vector args, int) -> execution_result { + constexpr auto host_foo2 = [](Instance&, span args, int) -> execution_result { return {false, {args[0] * args[1]}}; }; @@ -648,10 +648,10 @@ TEST(execute, imported_functions_and_regular_one) "0061736d0100000001070160027f7f017f021702036d6f6404666f6f310000036d6f6404666f6f320000030201" "000a0901070041aa80a8010b"); - constexpr auto host_foo1 = [](Instance&, std::vector args, int) -> execution_result { + constexpr auto host_foo1 = [](Instance&, span args, int) -> execution_result { return {false, {args[0] + args[1]}}; }; - constexpr auto host_foo2 = [](Instance&, std::vector args, int) -> execution_result { + constexpr auto host_foo2 = [](Instance&, span args, int) -> execution_result { return {false, {args[0] * args[0]}}; }; @@ -663,7 +663,7 @@ TEST(execute, imported_functions_and_regular_one) EXPECT_THAT(execute(*instance, 1, {20}), Result(400)); // check correct number of arguments is passed to host - constexpr auto count_args = [](Instance&, std::vector args, int) -> execution_result { + constexpr auto count_args = [](Instance&, span args, int) -> execution_result { return {false, {args.size()}}; }; @@ -688,10 +688,10 @@ TEST(execute, imported_two_functions_different_type) "0061736d01000000010c0260027f7f017f60017e017e021702036d6f6404666f6f310000036d6f6404666f6f32" "0001030201010a0901070042aa80a8010b"); - constexpr auto host_foo1 = [](Instance&, std::vector args, int) -> execution_result { + constexpr auto host_foo1 = [](Instance&, span args, int) -> execution_result { return {false, {args[0] + args[1]}}; }; - constexpr auto host_foo2 = [](Instance&, std::vector args, int) -> execution_result { + constexpr auto host_foo2 = [](Instance&, span args, int) -> execution_result { return {false, {args[0] * args[0]}}; }; @@ -712,7 +712,7 @@ TEST(execute, imported_function_traps) */ const auto wasm = from_hex("0061736d0100000001070160027f7f017f020b01036d6f6403666f6f0000"); - constexpr auto host_foo = [](Instance&, std::vector, int) -> execution_result { + constexpr auto host_foo = [](Instance&, span, int) -> execution_result { return {true, {}}; }; @@ -1003,9 +1003,11 @@ TEST(execute, reuse_args) "0061736d01000000010b0260027e7e017e6000017e03030200010a19020e002000200180210120002001820b08" "004217420510000b"); + auto instance = instantiate(parse(wasm)); + const std::vector args{20, 3}; const auto expected = args[0] % (args[0] / args[1]); - EXPECT_THAT(execute(parse(wasm), 0, args), Result(expected)); + EXPECT_THAT(execute(*instance, 0, args), Result(expected)); EXPECT_THAT(args, ElementsAre(20, 3)); EXPECT_THAT(execute(parse(wasm), 1, {}), Result(23 % (23 / 5))); diff --git a/test/unittests/instantiate_test.cpp b/test/unittests/instantiate_test.cpp index bea9f4068..c3488a6de 100644 --- a/test/unittests/instantiate_test.cpp +++ b/test/unittests/instantiate_test.cpp @@ -30,7 +30,7 @@ TEST(instantiate, imported_functions) const auto bin = from_hex("0061736d0100000001060160017f017f020b01036d6f6403666f6f0000"); const auto module = parse(bin); - auto host_foo = [](Instance&, std::vector, int) -> execution_result { + auto host_foo = [](Instance&, span, int) -> execution_result { return {true, {}}; }; auto instance = instantiate(module, {{host_foo, module.typesec[0]}}); @@ -53,10 +53,10 @@ TEST(instantiate, imported_functions_multiple) "0061736d0100000001090260017f017f600000021702036d6f6404666f6f310000036d6f6404666f6f320001"); const auto module = parse(bin); - auto host_foo1 = [](Instance&, std::vector, int) -> execution_result { + auto host_foo1 = [](Instance&, span, int) -> execution_result { return {true, {0}}; }; - auto host_foo2 = [](Instance&, std::vector, int) -> execution_result { + auto host_foo2 = [](Instance&, span, int) -> execution_result { return {true, {}}; }; auto instance = @@ -93,7 +93,7 @@ TEST(instantiate, imported_function_wrong_type) const auto bin = from_hex("0061736d0100000001060160017f017f020b01036d6f6403666f6f0000"); const auto module = parse(bin); - auto host_foo = [](Instance&, std::vector, int) -> execution_result { + auto host_foo = [](Instance&, span, int) -> execution_result { return {true, {}}; }; const auto host_foo_type = FuncType{{}, {}}; @@ -574,7 +574,7 @@ TEST(instantiate, element_section_fills_imported_table) "0061736d010000000105016000017f020b01016d037461620170000403050400000000090f020041010b020001" "0041020b0202030a1504040041010b040041020b040041030b040041040b"); - auto f0 = [](Instance&, std::vector, int) { return execution_result{false, {0}}; }; + auto f0 = [](Instance&, span, int) { return execution_result{false, {0}}; }; table_elements table(4); table[0] = ExternalFunction{f0, FuncType{{}, {ValType::i32}}}; @@ -602,7 +602,7 @@ TEST(instantiate, element_section_out_of_bounds_doesnt_change_imported_table) "0b0200000a0601040041010b"); Module module = parse(bin); - auto f0 = [](Instance&, std::vector, int) { return execution_result{false, {0}}; }; + auto f0 = [](Instance&, span, int) { return execution_result{false, {0}}; }; table_elements table(3); table[0] = ExternalFunction{f0, FuncType{{}, {ValType::i32}}}; diff --git a/test/unittests/span_test.cpp b/test/unittests/span_test.cpp index 40f71d0c4..b5e3f17ea 100644 --- a/test/unittests/span_test.cpp +++ b/test/unittests/span_test.cpp @@ -19,6 +19,7 @@ TEST(span, vector) EXPECT_EQ(s[0], 2); EXPECT_EQ(s[1], 3); EXPECT_EQ(s[2], 4); + EXPECT_EQ(*s.data(), 2); EXPECT_EQ(*s.begin(), 2); EXPECT_EQ(*(s.end() - 1), 4); @@ -89,6 +90,7 @@ TEST(span, iterator) span slice{str.data() + 2, 3}; auto it = slice.begin(); + EXPECT_EQ(&*it, slice.data()); EXPECT_EQ(*it, 'a'); ++it; EXPECT_EQ(*it, 'b'); diff --git a/test/utils/execute_helpers.hpp b/test/utils/execute_helpers.hpp index 3e885bcd6..a057230f0 100644 --- a/test/utils/execute_helpers.hpp +++ b/test/utils/execute_helpers.hpp @@ -5,12 +5,14 @@ #pragma once #include "execute.hpp" +#include namespace fizzy::test { -inline execution_result execute(const Module& module, FuncIdx func_idx, std::vector args) +inline execution_result execute( + const Module& module, FuncIdx func_idx, std::initializer_list args) { auto instance = instantiate(module); - return execute(*instance, func_idx, std::move(args)); + return execute(*instance, func_idx, args); } } // namespace fizzy::test \ No newline at end of file diff --git a/test/utils/fizzy_engine.cpp b/test/utils/fizzy_engine.cpp index b47b14ed4..21563331b 100644 --- a/test/utils/fizzy_engine.cpp +++ b/test/utils/fizzy_engine.cpp @@ -53,7 +53,8 @@ FuncType translate_signature(std::string_view signature) return func_type; } -fizzy::execution_result env_adler32(fizzy::Instance& instance, std::vector args, int) +fizzy::execution_result env_adler32( + fizzy::Instance& instance, fizzy::span args, int) { assert(instance.memory != nullptr); const auto ret = fizzy::test::adler32(bytes_view{*instance.memory}.substr(args[0], args[1]));