From eb3855cc3b899353c2f24ee4a8883afe11dff7fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 9 Apr 2020 11:14:07 +0200 Subject: [PATCH 1/2] Optimize operand stack of small size --- lib/fizzy/stack.hpp | 44 +++++++++++++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/lib/fizzy/stack.hpp b/lib/fizzy/stack.hpp index c74d10b98..fdf1e6078 100644 --- a/lib/fizzy/stack.hpp +++ b/lib/fizzy/stack.hpp @@ -58,6 +58,9 @@ class Stack : public std::vector class OperandStack { + /// The size of the pre-allocated internal storage: 128 bytes. + static constexpr auto small_storage_size = 128 / sizeof(uint64_t); + /// The pointer to the top item, or below the stack bottom if stack is empty. /// /// This pointer always alias m_storage, but it is kept as the first field @@ -65,25 +68,44 @@ class OperandStack /// in the constructor after the m_storage. uint64_t* m_top; - /// The storage for items. - std::unique_ptr m_storage; + /// The bottom of the stack. Set in the constructor and never modified. + /// + /// TODO: This pointer is rarely used and may be removed. + /// The value can be recomputed as (m_large_storage ? m_large_storage : m_small_storage). + /// Moreover, methods using it may be replaced by ones not needing bottom of the stack. + uint64_t* m_bottom; + + /// The pre-allocated internal storage. + uint64_t m_small_storage[small_storage_size]; + + /// The unbounded storage for items. + std::unique_ptr m_large_storage; public: - /// Default constructor. Sets the top item pointer to below the stack bottom. + /// Default constructor. + /// + /// Based on @p max_stack_height decides if to use small pre-allocated storage or allocate + /// large storage. + /// Sets the top item pointer to below the stack bottom. explicit OperandStack(size_t max_stack_height) - : m_storage{std::make_unique(max_stack_height)} { - m_top = m_storage.get() - 1; + if (max_stack_height <= small_storage_size) + { + m_bottom = &m_small_storage[0]; + } + else + { + m_large_storage = std::make_unique(max_stack_height); + m_bottom = &m_large_storage[0]; + } + m_top = m_bottom - 1; } OperandStack(const OperandStack&) = delete; OperandStack& operator=(const OperandStack&) = delete; /// The current number of items on the stack (aka stack height). - [[nodiscard]] size_t size() const noexcept - { - return static_cast(m_top + 1 - m_storage.get()); - } + [[nodiscard]] size_t size() noexcept { return static_cast(m_top + 1 - m_bottom); } /// Returns the reference to the top item. /// Requires non-empty stack. @@ -121,11 +143,11 @@ class OperandStack { assert(new_size <= size()); // For new_size == 0, the m_top will point below the storage. - m_top = m_storage.get() + new_size - 1; + m_top = m_bottom + new_size - 1; } /// Returns iterator to the bottom of the stack. - [[nodiscard]] const uint64_t* rbegin() const noexcept { return m_storage.get(); } + [[nodiscard]] const uint64_t* rbegin() const noexcept { return m_bottom; } /// Returns end iterator counting from the bottom of the stack. [[nodiscard]] const uint64_t* rend() const noexcept { return m_top + 1; } From 6780f697244b35a81126f2d95bc87d0cb0861734 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 3 Jun 2020 19:21:37 +0200 Subject: [PATCH 2/2] test: Add example of using large stack space --- test/unittests/execute_test.cpp | 48 ++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/test/unittests/execute_test.cpp b/test/unittests/execute_test.cpp index 5f078a715..7f20b6aca 100644 --- a/test/unittests/execute_test.cpp +++ b/test/unittests/execute_test.cpp @@ -1007,4 +1007,50 @@ TEST(execute, reuse_args) EXPECT_THAT(args, ElementsAre(20, 3)); EXPECT_THAT(execute(parse(wasm), 1, {}), Result(23 % (23 / 5))); -} \ No newline at end of file +} + +TEST(execute, stack_abuse) +{ + /* wat2wasm + (func (param i32) (result i32) + local.get 0 + i32.const 1 + i32.const 2 + i32.const 3 + i32.const 4 + i32.const 5 + i32.const 6 + i32.const 7 + i32.const 8 + i32.const 9 + i32.const 10 + i32.const 11 + i32.const 12 + i32.const 13 + i32.const 14 + i32.const 15 + i32.const 16 + i32.add + i32.add + i32.add + i32.add + i32.add + i32.add + i32.add + i32.add + i32.add + i32.add + i32.add + i32.add + i32.add + i32.add + i32.add + i32.add + ) + */ + const auto wasm = from_hex( + "0061736d0100000001060160017f017f030201000a360134002000410141024103410441054106410741084109" + "410a410b410c410d410e410f41106a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a0b"); + + EXPECT_THAT(execute(parse(wasm), 0, {1000}), Result(1136)); +}