From c5df55a8a2091a34afb604930bc9ce9c02a8f036 Mon Sep 17 00:00:00 2001 From: Ali Mohammad Pur Date: Mon, 24 May 2021 02:04:58 +0430 Subject: [PATCH] LibWasm: Make Interpreter a virtual interface This allows multiply different kinds of interpreters to be used by the runtime; currently a BytecodeInterpreter and a DebuggerBytecodeInterpreter is provided. --- .../AbstractMachine/AbstractMachine.cpp | 21 +++--- .../LibWasm/AbstractMachine/AbstractMachine.h | 4 +- .../LibWasm/AbstractMachine/Configuration.cpp | 10 +-- .../LibWasm/AbstractMachine/Configuration.h | 7 +- .../LibWasm/AbstractMachine/Interpreter.cpp | 68 ++++++++++--------- .../LibWasm/AbstractMachine/Interpreter.h | 29 ++++++-- Userland/Utilities/wasm.cpp | 13 ++-- 7 files changed, 86 insertions(+), 66 deletions(-) diff --git a/Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.cpp b/Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.cpp index 210d73e487..2daca7ab14 100644 --- a/Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.cpp +++ b/Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.cpp @@ -4,6 +4,7 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include "Interpreter.h" #include #include #include @@ -105,18 +106,18 @@ InstantiationResult AbstractMachine::instantiate(const Module& module, Vector([&](auto& global_section) { for (auto& entry : global_section.entries()) { Configuration config { m_store }; - config.pre_interpret_hook = &pre_interpret_hook; - config.post_interpret_hook = &post_interpret_hook; config.set_frame(Frame { auxiliary_instance, Vector {}, entry.expression(), 1, }); - auto result = config.execute(); + auto result = config.execute(interpreter); // What if this traps? if (result.is_trap()) instantiation_result = InstantiationError { "Global value construction trapped" }; @@ -140,15 +141,13 @@ InstantiationResult AbstractMachine::instantiate(const Module& module, Vector {}, data.offset, 1, }); - auto result = config.execute(); + auto result = config.execute(interpreter); size_t offset = 0; result.values().first().value().visit( [&](const auto& value) { offset = value; }, @@ -284,11 +283,15 @@ Optional AbstractMachine::allocate_all(const Module& module, } Result AbstractMachine::invoke(FunctionAddress address, Vector arguments) +{ + BytecodeInterpreter interpreter; + return invoke(interpreter, address, move(arguments)); +} + +Result AbstractMachine::invoke(Interpreter& interpreter, FunctionAddress address, Vector arguments) { Configuration configuration { m_store }; - configuration.pre_interpret_hook = &pre_interpret_hook; - configuration.post_interpret_hook = &post_interpret_hook; - return configuration.call(address, move(arguments)); + return configuration.call(interpreter, address, move(arguments)); } void Linker::link(const ModuleInstance& instance) diff --git a/Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.h b/Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.h index 0b786c457c..8e5a4052c4 100644 --- a/Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.h +++ b/Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.h @@ -441,13 +441,11 @@ public: // Load and instantiate a module, and link it into this interpreter. InstantiationResult instantiate(const Module&, Vector); Result invoke(FunctionAddress, Vector); + Result invoke(Interpreter&, FunctionAddress, Vector); auto& store() const { return m_store; } auto& store() { return m_store; } - Function pre_interpret_hook; - Function post_interpret_hook; - private: Optional allocate_all(const Module&, ModuleInstance&, Vector&, Vector& global_values); Store m_store; diff --git a/Userland/Libraries/LibWasm/AbstractMachine/Configuration.cpp b/Userland/Libraries/LibWasm/AbstractMachine/Configuration.cpp index eb085d7476..4a27fcbd49 100644 --- a/Userland/Libraries/LibWasm/AbstractMachine/Configuration.cpp +++ b/Userland/Libraries/LibWasm/AbstractMachine/Configuration.cpp @@ -32,7 +32,7 @@ void Configuration::unwind(Badge, const CallFrameHandle& frame_ VERIFY(m_stack.size() == frame_handle.stack_size); } -Result Configuration::call(FunctionAddress address, Vector arguments) +Result Configuration::call(Interpreter& interpreter, FunctionAddress address, Vector arguments) { auto* function = m_store.get(address); if (!function) @@ -52,7 +52,7 @@ Result Configuration::call(FunctionAddress address, Vector arguments) wasm_function->type().results().size(), }); m_ip = 0; - return execute(); + return execute(interpreter); } // It better be a host function, else something is really wrong. @@ -60,12 +60,8 @@ Result Configuration::call(FunctionAddress address, Vector arguments) return host_function.function()(*this, arguments); } -Result Configuration::execute() +Result Configuration::execute(Interpreter& interpreter) { - Interpreter interpreter; - interpreter.pre_interpret_hook = pre_interpret_hook; - interpreter.post_interpret_hook = post_interpret_hook; - interpreter.interpret(*this); if (interpreter.did_trap()) return Trap {}; diff --git a/Userland/Libraries/LibWasm/AbstractMachine/Configuration.h b/Userland/Libraries/LibWasm/AbstractMachine/Configuration.h index 60a1b53364..166cbb761f 100644 --- a/Userland/Libraries/LibWasm/AbstractMachine/Configuration.h +++ b/Userland/Libraries/LibWasm/AbstractMachine/Configuration.h @@ -58,14 +58,11 @@ public: }; void unwind(Badge, const CallFrameHandle&); - Result call(FunctionAddress, Vector arguments); - Result execute(); + Result call(Interpreter&, FunctionAddress, Vector arguments); + Result execute(Interpreter&); void dump_stack(); - Function* pre_interpret_hook { nullptr }; - Function* post_interpret_hook { nullptr }; - private: Store& m_store; size_t m_current_frame_index { 0 }; diff --git a/Userland/Libraries/LibWasm/AbstractMachine/Interpreter.cpp b/Userland/Libraries/LibWasm/AbstractMachine/Interpreter.cpp index 0312056efc..45942f4dc0 100644 --- a/Userland/Libraries/LibWasm/AbstractMachine/Interpreter.cpp +++ b/Userland/Libraries/LibWasm/AbstractMachine/Interpreter.cpp @@ -29,7 +29,7 @@ namespace Wasm { } \ } while (false) -void Interpreter::interpret(Configuration& configuration) +void BytecodeInterpreter::interpret(Configuration& configuration) { auto& instructions = configuration.frame().expression().instructions(); auto max_ip_value = InstructionPointer { instructions.size() }; @@ -46,7 +46,7 @@ void Interpreter::interpret(Configuration& configuration) } } -void Interpreter::branch_to_label(Configuration& configuration, LabelIndex index) +void BytecodeInterpreter::branch_to_label(Configuration& configuration, LabelIndex index) { dbgln_if(WASM_TRACE_DEBUG, "Branch to label with index {}...", index.value()); auto label = configuration.nth_label(index.value()); @@ -71,7 +71,7 @@ void Interpreter::branch_to_label(Configuration& configuration, LabelIndex index configuration.ip() = label->continuation(); } -ReadonlyBytes Interpreter::load_from_memory(Configuration& configuration, const Instruction& instruction, size_t size) +ReadonlyBytes BytecodeInterpreter::load_from_memory(Configuration& configuration, const Instruction& instruction, size_t size) { auto& address = configuration.frame().module().memories().first(); auto memory = configuration.store().get(address); @@ -95,7 +95,7 @@ ReadonlyBytes Interpreter::load_from_memory(Configuration& configuration, const return memory->data().bytes().slice(instance_address, size); } -void Interpreter::store_to_memory(Configuration& configuration, const Instruction& instruction, ReadonlyBytes data) +void BytecodeInterpreter::store_to_memory(Configuration& configuration, const Instruction& instruction, ReadonlyBytes data) { auto& address = configuration.frame().module().memories().first(); auto memory = configuration.store().get(address); @@ -113,7 +113,7 @@ void Interpreter::store_to_memory(Configuration& configuration, const Instructio data.copy_to(memory->data().bytes().slice(instance_address, data.size())); } -void Interpreter::call_address(Configuration& configuration, FunctionAddress address) +void BytecodeInterpreter::call_address(Configuration& configuration, FunctionAddress address) { auto instance = configuration.store().get(address); TRAP_IF_NOT(instance); @@ -129,7 +129,7 @@ void Interpreter::call_address(Configuration& configuration, FunctionAddress add Result result { Trap {} }; { Configuration::CallFrameHandle handle { configuration }; - result = configuration.call(address, move(args)); + result = configuration.call(*this, address, move(args)); } if (result.is_trap()) { @@ -215,7 +215,7 @@ void Interpreter::call_address(Configuration& configuration, FunctionAddress add } while (false) template -T Interpreter::read_value(ReadonlyBytes data) +T BytecodeInterpreter::read_value(ReadonlyBytes data) { T value; InputMemoryStream stream { data }; @@ -226,7 +226,7 @@ T Interpreter::read_value(ReadonlyBytes data) } template<> -float Interpreter::read_value(ReadonlyBytes data) +float BytecodeInterpreter::read_value(ReadonlyBytes data) { InputMemoryStream stream { data }; LittleEndian raw_value; @@ -237,7 +237,7 @@ float Interpreter::read_value(ReadonlyBytes data) } template<> -double Interpreter::read_value(ReadonlyBytes data) +double BytecodeInterpreter::read_value(ReadonlyBytes data) { InputMemoryStream stream { data }; LittleEndian raw_value; @@ -282,7 +282,7 @@ struct ConvertToRaw { }; template -MakeSigned Interpreter::checked_signed_truncate(V value) +MakeSigned BytecodeInterpreter::checked_signed_truncate(V value) { if (isnan(value) || isinf(value)) { // "undefined", let's just trap. m_do_trap = true; @@ -305,7 +305,7 @@ MakeSigned Interpreter::checked_signed_truncate(V value) } template -MakeUnsigned Interpreter::checked_unsigned_truncate(V value) +MakeUnsigned BytecodeInterpreter::checked_unsigned_truncate(V value) { if (isnan(value) || isinf(value)) { // "undefined", let's just trap. m_do_trap = true; @@ -326,7 +326,7 @@ MakeUnsigned Interpreter::checked_unsigned_truncate(V value) return true; } -Vector Interpreter::pop_values(Configuration& configuration, size_t count) +Vector BytecodeInterpreter::pop_values(Configuration& configuration, size_t count) { Vector results; for (size_t i = 0; i < count; ++i) { @@ -339,28 +339,10 @@ Vector Interpreter::pop_values(Configuration& configuration, size_t count return results; } -void Interpreter::interpret(Configuration& configuration, InstructionPointer& ip, const Instruction& instruction) +void BytecodeInterpreter::interpret(Configuration& configuration, InstructionPointer& ip, const Instruction& instruction) { dbgln_if(WASM_TRACE_DEBUG, "Executing instruction {} at ip {}", instruction_name(instruction.opcode()), ip.value()); - if (pre_interpret_hook && *pre_interpret_hook) { - auto result = pre_interpret_hook->operator()(configuration, ip, instruction); - if (!result) { - m_do_trap = true; - return; - } - } - - ScopeGuard guard { [&] { - if (post_interpret_hook && *post_interpret_hook) { - auto result = post_interpret_hook->operator()(configuration, ip, instruction, *this); - if (!result) { - m_do_trap = true; - return; - } - } - } }; - switch (instruction.opcode().value()) { case Instructions::unreachable.value(): m_do_trap = true; @@ -912,4 +894,28 @@ void Interpreter::interpret(Configuration& configuration, InstructionPointer& ip return; } } + +void DebuggerBytecodeInterpreter::interpret(Configuration& configuration, InstructionPointer& ip, const Instruction& instruction) +{ + if (pre_interpret_hook) { + auto result = pre_interpret_hook(configuration, ip, instruction); + if (!result) { + m_do_trap = true; + return; + } + } + + ScopeGuard guard { [&] { + if (post_interpret_hook) { + auto result = post_interpret_hook(configuration, ip, instruction, *this); + if (!result) { + m_do_trap = true; + return; + } + } + } }; + + BytecodeInterpreter::interpret(configuration, ip, instruction); +} + } diff --git a/Userland/Libraries/LibWasm/AbstractMachine/Interpreter.h b/Userland/Libraries/LibWasm/AbstractMachine/Interpreter.h index 5b4f98ab87..cbe37862a8 100644 --- a/Userland/Libraries/LibWasm/AbstractMachine/Interpreter.h +++ b/Userland/Libraries/LibWasm/AbstractMachine/Interpreter.h @@ -11,15 +11,20 @@ namespace Wasm { struct Interpreter { - void interpret(Configuration&); - bool did_trap() const { return m_do_trap; } - void clear_trap() { m_do_trap = false; } + virtual ~Interpreter() = default; + virtual void interpret(Configuration&) = 0; + virtual bool did_trap() const = 0; + virtual void clear_trap() = 0; +}; - Function* pre_interpret_hook { nullptr }; - Function* post_interpret_hook { nullptr }; +struct BytecodeInterpreter : public Interpreter { + virtual void interpret(Configuration&) override; + virtual ~BytecodeInterpreter() override = default; + virtual bool did_trap() const override { return m_do_trap; } + virtual void clear_trap() override { m_do_trap = false; } -private: - void interpret(Configuration&, InstructionPointer&, const Instruction&); +protected: + virtual void interpret(Configuration&, InstructionPointer&, const Instruction&); void branch_to_label(Configuration&, LabelIndex); ReadonlyBytes load_from_memory(Configuration&, const Instruction&, size_t); void store_to_memory(Configuration&, const Instruction&, ReadonlyBytes data); @@ -44,4 +49,14 @@ private: bool m_do_trap { false }; }; +struct DebuggerBytecodeInterpreter : public BytecodeInterpreter { + virtual ~DebuggerBytecodeInterpreter() override = default; + + Function pre_interpret_hook; + Function post_interpret_hook; + +private: + virtual void interpret(Configuration&, InstructionPointer&, const Instruction&) override; +}; + } diff --git a/Userland/Utilities/wasm.cpp b/Userland/Utilities/wasm.cpp index ed0199671a..5ca2d8e79b 100644 --- a/Userland/Utilities/wasm.cpp +++ b/Userland/Utilities/wasm.cpp @@ -20,6 +20,7 @@ static auto g_stdout = Core::OutputFileStream::standard_error(); static Wasm::Printer g_printer { g_stdout }; static bool g_continue { false }; static void (*old_signal)(int); +static Wasm::DebuggerBytecodeInterpreter g_interpreter; static void print_buffer(ReadonlyBytes buffer, int split) { @@ -190,7 +191,11 @@ static bool pre_interpret_hook(Wasm::Configuration& config, Wasm::InstructionPoi for (auto& param : type.parameters()) values.append(Wasm::Value { param, values_to_push.take_last() }); - auto result = config.call(*address, move(values)); + Wasm::Result result { Wasm::Trap {} }; + { + Wasm::Configuration::CallFrameHandle handle { config }; + result = config.call(g_interpreter, *address, move(values)); + } if (result.is_trap()) warnln("Execution trapped!"); if (!result.values().is_empty()) @@ -328,8 +333,8 @@ int main(int argc, char* argv[]) Core::EventLoop main_loop; if (debug) { g_line_editor = Line::Editor::construct(); - machine.pre_interpret_hook = pre_interpret_hook; - machine.post_interpret_hook = post_interpret_hook; + g_interpreter.pre_interpret_hook = pre_interpret_hook; + g_interpreter.post_interpret_hook = post_interpret_hook; } // First, resolve the linked modules NonnullOwnPtrVector linked_instances; @@ -433,7 +438,7 @@ int main(int argc, char* argv[]) outln(); } - auto result = machine.invoke(run_address.value(), move(values)); + auto result = machine.invoke(g_interpreter, run_address.value(), move(values)); if (debug) { Wasm::Configuration config { machine.store() };