/* * Copyright (c) 2021, Ali Mohammad Pur * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include namespace Wasm { class Configuration { public: explicit Configuration(Store& store) : m_store(store) { } void set_frame(Frame frame, bool is_tailcall = false) { auto continuation = frame.expression().instructions().size() - 1; if (auto size = frame.expression().compiled_instructions.dispatches.size(); size > 0) continuation = size - 1; Label label(frame.arity(), continuation, m_value_stack.size()); frame.label_index() = m_label_stack.size(); if (auto hint = frame.expression().stack_usage_hint(); hint.has_value()) m_value_stack.ensure_capacity(*hint + m_value_stack.size()); if (!is_tailcall) { if (auto hint = frame.expression().frame_usage_hint(); hint.has_value()) m_label_stack.ensure_capacity(*hint + m_label_stack.size()); } m_frame_stack.append(move(frame)); m_label_stack.append(label); m_locals_base = m_frame_stack.unchecked_last().locals().data(); } ALWAYS_INLINE auto& frame() const { return m_frame_stack.unchecked_last(); } ALWAYS_INLINE auto& frame() { return m_frame_stack.unchecked_last(); } ALWAYS_INLINE auto& ip() const { return m_ip; } ALWAYS_INLINE auto& ip() { return m_ip; } ALWAYS_INLINE auto& depth() const { return m_depth; } ALWAYS_INLINE auto& depth() { return m_depth; } ALWAYS_INLINE auto& value_stack() const { return m_value_stack; } ALWAYS_INLINE auto& value_stack() { return m_value_stack; } ALWAYS_INLINE auto& label_stack() const { return m_label_stack; } ALWAYS_INLINE auto& label_stack() { return m_label_stack; } ALWAYS_INLINE auto& store() const { return m_store; } ALWAYS_INLINE auto& store() { return m_store; } ALWAYS_INLINE Value const* raw_locals() const { return m_locals_base; } ALWAYS_INLINE Value const& local(LocalIndex index) const { return m_locals_base[index.value()]; } ALWAYS_INLINE Value& local(LocalIndex index) { return m_locals_base[index.value()]; } struct CallFrameHandle { explicit CallFrameHandle(Configuration& configuration) : configuration(configuration) { configuration.depth()++; } ~CallFrameHandle() { configuration.unwind({}, *this); } Configuration& configuration; }; void unwind(Badge, CallFrameHandle const&) { unwind_impl(); } ErrorOr, Trap> prepare_call(FunctionAddress, Vector& arguments, bool is_tailcall = false); Result call(Interpreter&, FunctionAddress, Vector arguments); Result execute(Interpreter&); void enable_instruction_count_limit() { m_should_limit_instruction_count = true; } bool should_limit_instruction_count() const { return m_should_limit_instruction_count; } void dump_stack(); ALWAYS_INLINE FLATTEN void push_to_destination(Value value, Dispatch::RegisterOrStack destination) { if (destination == Dispatch::RegisterOrStack::Stack) { value_stack().unchecked_append(value); return; } regs.data()[to_underlying(destination)] = value; } ALWAYS_INLINE FLATTEN Value& source_value(u8 index, Dispatch::RegisterOrStack const* sources) { // Note: The last source in a dispatch *must* be equal to the destination for this to be valid. auto const source = sources[index]; if (source == Dispatch::RegisterOrStack::Stack) return value_stack().unsafe_last(); return regs.data()[to_underlying(source)]; } ALWAYS_INLINE FLATTEN Value take_source(u8 index, Dispatch::RegisterOrStack const* sources) { auto const source = sources[index]; if (source == Dispatch::RegisterOrStack::Stack) return value_stack().unsafe_take_last(); return regs.data()[to_underlying(source)]; } Array regs = { Value(0), Value(0), Value(0), Value(0), Value(0), Value(0), Value(0), Value(0), }; private: void unwind_impl(); Store& m_store; Vector m_value_stack; Vector m_label_stack; DoublyLinkedList m_frame_stack; size_t m_depth { 0 }; u64 m_ip { 0 }; bool m_should_limit_instruction_count { false }; Value* m_locals_base { nullptr }; }; }