mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-12-04 05:20:30 +00:00
LibWasm: Start implementing a naive bytecode interpreter
As the parser now flattens out the instructions and inserts synthetic nesting/structured instructions where needed, we can treat the whole thing as a simple parsed bytecode stream. This currently knows how to execute the following instructions: - unreachable - nop - local.get - local.set - {i,f}{32,64}.const - block - loop - if/else - branch / branch_if - i32_add - i32_and/or/xor - i32_ne This also extends the 'wasm' utility to optionally execute the first function in the module with optionally user-supplied arguments.
This commit is contained in:
parent
faa34a0a8b
commit
056be42c0b
Notes:
sideshowbarker
2024-07-18 17:54:36 +09:00
Author: https://github.com/alimpfard Commit: https://github.com/SerenityOS/serenity/commit/056be42c0be Pull-request: https://github.com/SerenityOS/serenity/pull/7097 Reviewed-by: https://github.com/Dexesttp
10 changed files with 513 additions and 30 deletions
|
@ -422,3 +422,6 @@
|
|||
#cmakedefine01 WSSCREEN_DEBUG
|
||||
#endif
|
||||
|
||||
#ifndef WASM_TRACE_DEBUG
|
||||
#cmakedefine01 WASM_TRACE_DEBUG
|
||||
#endif
|
||||
|
|
|
@ -179,6 +179,7 @@ set(LINE_EDITOR_DEBUG ON)
|
|||
set(LANGUAGE_SERVER_DEBUG ON)
|
||||
set(GL_DEBUG ON)
|
||||
set(WASM_BINPARSER_DEBUG ON)
|
||||
set(WASM_TRACE_DEBUG ON)
|
||||
set(PDF_DEBUG ON)
|
||||
set(SOLITAIRE_DEBUG ON)
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ public:
|
|||
}
|
||||
|
||||
template<typename T>
|
||||
requires(sizeof(T) <= sizeof(u64)) explicit Value(ValueType type, T raw_value)
|
||||
requires(sizeof(T) == sizeof(u64)) explicit Value(ValueType type, T raw_value)
|
||||
: m_value(0)
|
||||
, m_type(type)
|
||||
{
|
||||
|
@ -89,6 +89,33 @@ public:
|
|||
{
|
||||
}
|
||||
|
||||
Value& operator=(Value&& value)
|
||||
{
|
||||
m_value = move(value.m_value);
|
||||
m_type = move(value.m_type);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Optional<T> to()
|
||||
{
|
||||
Optional<T> result;
|
||||
m_value.visit(
|
||||
[&](auto value) {
|
||||
if constexpr (!IsSame<T, FunctionAddress> && !IsSame<T, ExternAddress>)
|
||||
result = value;
|
||||
},
|
||||
[&](const FunctionAddress& address) {
|
||||
if constexpr (IsSame<T, FunctionAddress>)
|
||||
result = address;
|
||||
},
|
||||
[&](const ExternAddress& address) {
|
||||
if constexpr (IsSame<T, ExternAddress>)
|
||||
result = address;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
auto& type() const { return m_type; }
|
||||
auto& value() const { return m_value; }
|
||||
|
||||
|
@ -317,14 +344,17 @@ private:
|
|||
|
||||
class Label {
|
||||
public:
|
||||
explicit Label(InstructionPointer continuation)
|
||||
: m_continuation(continuation)
|
||||
explicit Label(size_t arity, InstructionPointer continuation)
|
||||
: m_arity(arity)
|
||||
, m_continuation(continuation)
|
||||
{
|
||||
}
|
||||
|
||||
auto continuation() const { return m_continuation; }
|
||||
auto arity() const { return m_arity; }
|
||||
|
||||
private:
|
||||
size_t m_arity { 0 };
|
||||
InstructionPointer m_continuation;
|
||||
};
|
||||
|
||||
|
@ -342,6 +372,7 @@ public:
|
|||
|
||||
auto& module() const { return m_module; }
|
||||
auto& locals() const { return m_locals; }
|
||||
auto& locals() { return m_locals; }
|
||||
auto& expression() const { return m_expression; }
|
||||
auto arity() const { return m_arity; }
|
||||
|
||||
|
|
|
@ -74,4 +74,33 @@ Result Configuration::execute()
|
|||
return Result { move(results_moved) };
|
||||
}
|
||||
|
||||
void Configuration::dump_stack()
|
||||
{
|
||||
for (const auto& entry : stack().entries()) {
|
||||
entry.visit(
|
||||
[](const NonnullOwnPtr<Value>& v) {
|
||||
v->value().visit([]<typename T>(const T& v) {
|
||||
if constexpr (IsIntegral<T> || IsFloatingPoint<T>)
|
||||
dbgln(" {}", v);
|
||||
else
|
||||
dbgln(" *{}", v.value());
|
||||
});
|
||||
},
|
||||
[](const NonnullOwnPtr<Frame>& f) {
|
||||
dbgln(" frame({})", f->arity());
|
||||
for (auto& local : f->locals()) {
|
||||
local.value().visit([]<typename T>(const T& v) {
|
||||
if constexpr (IsIntegral<T> || IsFloatingPoint<T>)
|
||||
dbgln(" {}", v);
|
||||
else
|
||||
dbgln(" *{}", v.value());
|
||||
});
|
||||
}
|
||||
},
|
||||
[](const NonnullOwnPtr<Label>& l) {
|
||||
dbgln(" label({}) -> {}", l->arity(), l->continuation());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ public:
|
|||
{
|
||||
m_current_frame = frame.ptr();
|
||||
m_stack.push(move(frame));
|
||||
m_stack.push(make<Label>(m_current_frame->expression().instructions().size() - 1));
|
||||
m_stack.push(make<Label>(m_current_frame->arity(), m_current_frame->expression().instructions().size() - 1));
|
||||
}
|
||||
auto& frame() const { return m_current_frame; }
|
||||
auto& frame() { return m_current_frame; }
|
||||
|
@ -34,10 +34,14 @@ public:
|
|||
auto& depth() { return m_depth; }
|
||||
auto& stack() const { return m_stack; }
|
||||
auto& stack() { return m_stack; }
|
||||
auto& store() const { return m_store; }
|
||||
auto& store() { return m_store; }
|
||||
|
||||
Result call(FunctionAddress, Vector<Value> arguments);
|
||||
Result execute();
|
||||
|
||||
void dump_stack();
|
||||
|
||||
private:
|
||||
Store& m_store;
|
||||
Frame* m_current_frame { nullptr };
|
||||
|
|
|
@ -4,20 +4,403 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Debug.h>
|
||||
#include <LibWasm/AbstractMachine/AbstractMachine.h>
|
||||
#include <LibWasm/AbstractMachine/Configuration.h>
|
||||
#include <LibWasm/AbstractMachine/Interpreter.h>
|
||||
#include <LibWasm/Opcode.h>
|
||||
#include <LibWasm/Printer/Printer.h>
|
||||
|
||||
namespace Wasm {
|
||||
|
||||
void Interpreter::interpret(Configuration& configuration)
|
||||
{
|
||||
// FIXME: Interpret stuff
|
||||
dbgln("FIXME: Interpret stuff!");
|
||||
// Push some dummy values
|
||||
for (size_t i = 0; i < configuration.frame()->arity(); ++i)
|
||||
configuration.stack().push(make<Value>(0));
|
||||
auto& instructions = configuration.frame()->expression().instructions();
|
||||
auto max_ip_value = InstructionPointer { instructions.size() };
|
||||
auto& current_ip_value = configuration.ip();
|
||||
|
||||
while (current_ip_value < max_ip_value) {
|
||||
auto& instruction = instructions[current_ip_value.value()];
|
||||
interpret(configuration, current_ip_value, instruction);
|
||||
++current_ip_value;
|
||||
}
|
||||
}
|
||||
|
||||
void Interpreter::branch_to_label(Configuration& configuration, LabelIndex index)
|
||||
{
|
||||
auto label = configuration.nth_label(index.value());
|
||||
VERIFY(label.has_value());
|
||||
NonnullOwnPtrVector<Value> results;
|
||||
// Pop results in order
|
||||
for (size_t i = 0; i < label->arity(); ++i)
|
||||
results.append(move(configuration.stack().pop().get<NonnullOwnPtr<Value>>()));
|
||||
|
||||
size_t drop_count = index.value() + 1;
|
||||
if (label->continuation() < configuration.ip())
|
||||
--drop_count;
|
||||
|
||||
for (; !configuration.stack().is_empty();) {
|
||||
auto entry = configuration.stack().pop();
|
||||
if (entry.has<NonnullOwnPtr<Label>>()) {
|
||||
if (drop_count-- == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Push results in reverse
|
||||
for (size_t i = results.size(); i > 0; --i)
|
||||
configuration.stack().push(move(static_cast<Vector<NonnullOwnPtr<Value>>&>(results)[i - 1]));
|
||||
|
||||
configuration.ip() = label->continuation() + 1;
|
||||
}
|
||||
|
||||
ReadonlyBytes Interpreter::load_from_memory(Configuration& configuration, const Instruction& instruction, size_t size)
|
||||
{
|
||||
auto& address = configuration.frame()->module().memories().first();
|
||||
auto memory = configuration.store().get(address);
|
||||
VERIFY(memory);
|
||||
auto& arg = instruction.arguments().get<Instruction::MemoryArgument>();
|
||||
auto base = configuration.stack().pop().get<NonnullOwnPtr<Value>>()->to<i32>();
|
||||
VERIFY(base.has_value());
|
||||
auto instance_address = base.value() + static_cast<i64>(arg.offset);
|
||||
if (instance_address < 0 || static_cast<u64>(instance_address + size) > memory->size()) {
|
||||
dbgln("LibWasm: Memory access out of bounds (expected 0 > {} and {} > {})", instance_address, instance_address + size, memory->size());
|
||||
return {};
|
||||
}
|
||||
return memory->data().bytes().slice(instance_address, size);
|
||||
}
|
||||
|
||||
void Interpreter::interpret(Configuration& configuration, InstructionPointer& ip, const Instruction& instruction)
|
||||
{
|
||||
dbgln_if(WASM_TRACE_DEBUG, "Executing instruction {} at ip {}", instruction_name(instruction.opcode()), ip.value());
|
||||
if constexpr (WASM_TRACE_DEBUG)
|
||||
configuration.dump_stack();
|
||||
switch (instruction.opcode().value()) {
|
||||
case Instructions::unreachable.value():
|
||||
VERIFY_NOT_REACHED(); // FIXME: This is definitely not right :)
|
||||
case Instructions::nop.value():
|
||||
return;
|
||||
case Instructions::local_get.value():
|
||||
configuration.stack().push(make<Value>(configuration.frame()->locals()[instruction.arguments().get<LocalIndex>().value()]));
|
||||
return;
|
||||
case Instructions::local_set.value(): {
|
||||
auto entry = configuration.stack().pop();
|
||||
configuration.frame()->locals()[instruction.arguments().get<LocalIndex>().value()] = move(*entry.get<NonnullOwnPtr<Value>>());
|
||||
return;
|
||||
}
|
||||
case Instructions::i32_const.value():
|
||||
configuration.stack().push(make<Value>(ValueType { ValueType::I32 }, static_cast<i64>(instruction.arguments().get<i32>())));
|
||||
return;
|
||||
case Instructions::i64_const.value():
|
||||
configuration.stack().push(make<Value>(ValueType { ValueType::I64 }, instruction.arguments().get<i64>()));
|
||||
return;
|
||||
case Instructions::f32_const.value():
|
||||
configuration.stack().push(make<Value>(ValueType { ValueType::F32 }, static_cast<double>(instruction.arguments().get<float>())));
|
||||
return;
|
||||
case Instructions::f64_const.value():
|
||||
configuration.stack().push(make<Value>(ValueType { ValueType::F64 }, instruction.arguments().get<double>()));
|
||||
return;
|
||||
case Instructions::block.value(): {
|
||||
size_t arity = 0;
|
||||
auto& args = instruction.arguments().get<Instruction::StructuredInstructionArgs>();
|
||||
if (args.block_type.kind() != BlockType::Empty)
|
||||
arity = 1;
|
||||
configuration.stack().push(make<Label>(arity, args.end_ip));
|
||||
return;
|
||||
}
|
||||
case Instructions::loop.value(): {
|
||||
size_t arity = 0;
|
||||
auto& args = instruction.arguments().get<Instruction::StructuredInstructionArgs>();
|
||||
if (args.block_type.kind() != BlockType::Empty)
|
||||
arity = 1;
|
||||
configuration.stack().push(make<Label>(arity, ip.value() + 1));
|
||||
return;
|
||||
}
|
||||
case Instructions::if_.value(): {
|
||||
size_t arity = 0;
|
||||
auto& args = instruction.arguments().get<Instruction::StructuredInstructionArgs>();
|
||||
if (args.block_type.kind() != BlockType::Empty)
|
||||
arity = 1;
|
||||
|
||||
auto entry = configuration.stack().pop();
|
||||
auto value = entry.get<NonnullOwnPtr<Value>>()->to<i32>();
|
||||
VERIFY(value.has_value());
|
||||
configuration.stack().push(make<Label>(arity, args.end_ip));
|
||||
if (value.value() == 0) {
|
||||
if (args.else_ip.has_value()) {
|
||||
configuration.ip() = args.else_ip.value();
|
||||
} else {
|
||||
configuration.ip() = args.end_ip;
|
||||
configuration.stack().pop();
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
case Instructions::structured_end.value():
|
||||
return;
|
||||
case Instructions::structured_else.value(): {
|
||||
auto label = configuration.nth_label(0);
|
||||
VERIFY(label.has_value());
|
||||
NonnullOwnPtrVector<Value> results;
|
||||
// Pop results in order
|
||||
for (size_t i = 0; i < label->arity(); ++i)
|
||||
results.append(move(configuration.stack().pop().get<NonnullOwnPtr<Value>>()));
|
||||
|
||||
// drop all locals
|
||||
for (; !configuration.stack().is_empty();) {
|
||||
auto entry = configuration.stack().pop();
|
||||
if (entry.has<NonnullOwnPtr<Label>>())
|
||||
break;
|
||||
}
|
||||
|
||||
// Push results in reverse
|
||||
for (size_t i = 1; i < results.size() + 1; ++i)
|
||||
configuration.stack().push(move(static_cast<Vector<NonnullOwnPtr<Value>>&>(results)[results.size() - i]));
|
||||
|
||||
if (instruction.opcode() == Instructions::structured_end)
|
||||
return;
|
||||
|
||||
// Jump to the end label
|
||||
configuration.ip() = label->continuation();
|
||||
return;
|
||||
}
|
||||
|
||||
case Instructions::br.value():
|
||||
return branch_to_label(configuration, instruction.arguments().get<LabelIndex>());
|
||||
case Instructions::br_if.value(): {
|
||||
if (configuration.stack().pop().get<NonnullOwnPtr<Value>>()->to<i32>().value_or(0) == 0)
|
||||
return;
|
||||
return branch_to_label(configuration, instruction.arguments().get<LabelIndex>());
|
||||
}
|
||||
case Instructions::br_table.value():
|
||||
case Instructions::call.value():
|
||||
case Instructions::call_indirect.value():
|
||||
case Instructions::i32_load.value():
|
||||
case Instructions::i64_load.value():
|
||||
case Instructions::f32_load.value():
|
||||
case Instructions::f64_load.value():
|
||||
case Instructions::i32_load8_s.value():
|
||||
goto unimplemented;
|
||||
case Instructions::i32_load8_u.value(): {
|
||||
auto slice = load_from_memory(configuration, instruction, 1);
|
||||
VERIFY(slice.size() == 1);
|
||||
configuration.stack().push(make<Value>(static_cast<i32>(slice[0])));
|
||||
return;
|
||||
}
|
||||
case Instructions::i32_load16_s.value():
|
||||
case Instructions::i32_load16_u.value():
|
||||
case Instructions::i64_load8_s.value():
|
||||
case Instructions::i64_load8_u.value():
|
||||
case Instructions::i64_load16_s.value():
|
||||
case Instructions::i64_load16_u.value():
|
||||
case Instructions::i64_load32_s.value():
|
||||
case Instructions::i64_load32_u.value():
|
||||
case Instructions::i32_store.value():
|
||||
case Instructions::i64_store.value():
|
||||
case Instructions::f32_store.value():
|
||||
case Instructions::f64_store.value():
|
||||
case Instructions::i32_store8.value():
|
||||
case Instructions::i32_store16.value():
|
||||
case Instructions::i64_store8.value():
|
||||
case Instructions::i64_store16.value():
|
||||
case Instructions::i64_store32.value():
|
||||
case Instructions::local_tee.value():
|
||||
case Instructions::global_get.value():
|
||||
case Instructions::global_set.value():
|
||||
case Instructions::memory_size.value():
|
||||
case Instructions::memory_grow.value():
|
||||
case Instructions::table_get.value():
|
||||
case Instructions::table_set.value():
|
||||
case Instructions::select_typed.value():
|
||||
case Instructions::ref_null.value():
|
||||
case Instructions::ref_func.value():
|
||||
case Instructions::ref_is_null.value():
|
||||
case Instructions::return_.value():
|
||||
case Instructions::drop.value():
|
||||
case Instructions::select.value():
|
||||
case Instructions::i32_eqz.value():
|
||||
case Instructions::i32_eq.value():
|
||||
goto unimplemented;
|
||||
case Instructions::i32_ne.value(): {
|
||||
auto lhs = configuration.stack().pop().get<NonnullOwnPtr<Value>>()->to<i32>();
|
||||
auto rhs = configuration.stack().pop().get<NonnullOwnPtr<Value>>()->to<i32>();
|
||||
VERIFY(lhs.has_value());
|
||||
VERIFY(rhs.has_value());
|
||||
configuration.stack().push(make<Value>(lhs.value() != rhs.value()));
|
||||
return;
|
||||
}
|
||||
case Instructions::i32_lts.value():
|
||||
case Instructions::i32_ltu.value():
|
||||
case Instructions::i32_gts.value():
|
||||
case Instructions::i32_gtu.value():
|
||||
case Instructions::i32_les.value():
|
||||
case Instructions::i32_leu.value():
|
||||
case Instructions::i32_ges.value():
|
||||
case Instructions::i32_geu.value():
|
||||
case Instructions::i64_eqz.value():
|
||||
case Instructions::i64_eq.value():
|
||||
case Instructions::i64_ne.value():
|
||||
case Instructions::i64_lts.value():
|
||||
case Instructions::i64_ltu.value():
|
||||
case Instructions::i64_gts.value():
|
||||
case Instructions::i64_gtu.value():
|
||||
case Instructions::i64_les.value():
|
||||
case Instructions::i64_leu.value():
|
||||
case Instructions::i64_ges.value():
|
||||
case Instructions::i64_geu.value():
|
||||
case Instructions::f32_eq.value():
|
||||
case Instructions::f32_ne.value():
|
||||
case Instructions::f32_lt.value():
|
||||
case Instructions::f32_gt.value():
|
||||
case Instructions::f32_le.value():
|
||||
case Instructions::f32_ge.value():
|
||||
case Instructions::f64_eq.value():
|
||||
case Instructions::f64_ne.value():
|
||||
case Instructions::f64_lt.value():
|
||||
case Instructions::f64_gt.value():
|
||||
case Instructions::f64_le.value():
|
||||
case Instructions::f64_ge.value():
|
||||
case Instructions::i32_clz.value():
|
||||
case Instructions::i32_ctz.value():
|
||||
case Instructions::i32_popcnt.value():
|
||||
goto unimplemented;
|
||||
case Instructions::i32_add.value(): {
|
||||
auto lhs = configuration.stack().pop().get<NonnullOwnPtr<Value>>()->to<i32>();
|
||||
auto rhs = configuration.stack().pop().get<NonnullOwnPtr<Value>>()->to<i32>();
|
||||
VERIFY(lhs.has_value());
|
||||
VERIFY(rhs.has_value());
|
||||
configuration.stack().push(make<Value>(lhs.value() + rhs.value()));
|
||||
return;
|
||||
}
|
||||
case Instructions::i32_sub.value():
|
||||
case Instructions::i32_mul.value():
|
||||
case Instructions::i32_divs.value():
|
||||
case Instructions::i32_divu.value():
|
||||
case Instructions::i32_rems.value():
|
||||
case Instructions::i32_remu.value():
|
||||
goto unimplemented;
|
||||
case Instructions::i32_and.value(): {
|
||||
auto lhs = configuration.stack().pop().get<NonnullOwnPtr<Value>>()->to<i32>();
|
||||
auto rhs = configuration.stack().pop().get<NonnullOwnPtr<Value>>()->to<i32>();
|
||||
VERIFY(lhs.has_value());
|
||||
VERIFY(rhs.has_value());
|
||||
configuration.stack().push(make<Value>(lhs.value() & rhs.value()));
|
||||
return;
|
||||
}
|
||||
case Instructions::i32_or.value(): {
|
||||
auto lhs = configuration.stack().pop().get<NonnullOwnPtr<Value>>()->to<i32>();
|
||||
auto rhs = configuration.stack().pop().get<NonnullOwnPtr<Value>>()->to<i32>();
|
||||
VERIFY(lhs.has_value());
|
||||
VERIFY(rhs.has_value());
|
||||
configuration.stack().push(make<Value>(lhs.value() | rhs.value()));
|
||||
return;
|
||||
}
|
||||
case Instructions::i32_xor.value(): {
|
||||
auto lhs = configuration.stack().pop().get<NonnullOwnPtr<Value>>()->to<i32>();
|
||||
auto rhs = configuration.stack().pop().get<NonnullOwnPtr<Value>>()->to<i32>();
|
||||
VERIFY(lhs.has_value());
|
||||
VERIFY(rhs.has_value());
|
||||
configuration.stack().push(make<Value>(lhs.value() ^ rhs.value()));
|
||||
return;
|
||||
}
|
||||
case Instructions::i32_shl.value():
|
||||
case Instructions::i32_shrs.value():
|
||||
case Instructions::i32_shru.value():
|
||||
case Instructions::i32_rotl.value():
|
||||
case Instructions::i32_rotr.value():
|
||||
case Instructions::i64_clz.value():
|
||||
case Instructions::i64_ctz.value():
|
||||
case Instructions::i64_popcnt.value():
|
||||
case Instructions::i64_add.value():
|
||||
case Instructions::i64_sub.value():
|
||||
case Instructions::i64_mul.value():
|
||||
case Instructions::i64_divs.value():
|
||||
case Instructions::i64_divu.value():
|
||||
case Instructions::i64_rems.value():
|
||||
case Instructions::i64_remu.value():
|
||||
case Instructions::i64_and.value():
|
||||
case Instructions::i64_or.value():
|
||||
case Instructions::i64_xor.value():
|
||||
case Instructions::i64_shl.value():
|
||||
case Instructions::i64_shrs.value():
|
||||
case Instructions::i64_shru.value():
|
||||
case Instructions::i64_rotl.value():
|
||||
case Instructions::i64_rotr.value():
|
||||
case Instructions::f32_abs.value():
|
||||
case Instructions::f32_neg.value():
|
||||
case Instructions::f32_ceil.value():
|
||||
case Instructions::f32_floor.value():
|
||||
case Instructions::f32_trunc.value():
|
||||
case Instructions::f32_nearest.value():
|
||||
case Instructions::f32_sqrt.value():
|
||||
case Instructions::f32_add.value():
|
||||
case Instructions::f32_sub.value():
|
||||
case Instructions::f32_mul.value():
|
||||
case Instructions::f32_div.value():
|
||||
case Instructions::f32_min.value():
|
||||
case Instructions::f32_max.value():
|
||||
case Instructions::f32_copysign.value():
|
||||
case Instructions::f64_abs.value():
|
||||
case Instructions::f64_neg.value():
|
||||
case Instructions::f64_ceil.value():
|
||||
case Instructions::f64_floor.value():
|
||||
case Instructions::f64_trunc.value():
|
||||
case Instructions::f64_nearest.value():
|
||||
case Instructions::f64_sqrt.value():
|
||||
case Instructions::f64_add.value():
|
||||
case Instructions::f64_sub.value():
|
||||
case Instructions::f64_mul.value():
|
||||
case Instructions::f64_div.value():
|
||||
case Instructions::f64_min.value():
|
||||
case Instructions::f64_max.value():
|
||||
case Instructions::f64_copysign.value():
|
||||
case Instructions::i32_wrap_i64.value():
|
||||
case Instructions::i32_trunc_sf32.value():
|
||||
case Instructions::i32_trunc_uf32.value():
|
||||
case Instructions::i32_trunc_sf64.value():
|
||||
case Instructions::i32_trunc_uf64.value():
|
||||
case Instructions::i64_extend_si32.value():
|
||||
case Instructions::i64_extend_ui32.value():
|
||||
case Instructions::i64_trunc_sf32.value():
|
||||
case Instructions::i64_trunc_uf32.value():
|
||||
case Instructions::i64_trunc_sf64.value():
|
||||
case Instructions::i64_trunc_uf64.value():
|
||||
case Instructions::f32_convert_si32.value():
|
||||
case Instructions::f32_convert_ui32.value():
|
||||
case Instructions::f32_convert_si64.value():
|
||||
case Instructions::f32_convert_ui64.value():
|
||||
case Instructions::f32_demote_f64.value():
|
||||
case Instructions::f64_convert_si32.value():
|
||||
case Instructions::f64_convert_ui32.value():
|
||||
case Instructions::f64_convert_si64.value():
|
||||
case Instructions::f64_convert_ui64.value():
|
||||
case Instructions::f64_promote_f32.value():
|
||||
case Instructions::i32_reinterpret_f32.value():
|
||||
case Instructions::i64_reinterpret_f64.value():
|
||||
case Instructions::f32_reinterpret_i32.value():
|
||||
case Instructions::f64_reinterpret_i64.value():
|
||||
case Instructions::i32_trunc_sat_f32_s.value():
|
||||
case Instructions::i32_trunc_sat_f32_u.value():
|
||||
case Instructions::i32_trunc_sat_f64_s.value():
|
||||
case Instructions::i32_trunc_sat_f64_u.value():
|
||||
case Instructions::i64_trunc_sat_f32_s.value():
|
||||
case Instructions::i64_trunc_sat_f32_u.value():
|
||||
case Instructions::i64_trunc_sat_f64_s.value():
|
||||
case Instructions::i64_trunc_sat_f64_u.value():
|
||||
case Instructions::memory_init.value():
|
||||
case Instructions::data_drop.value():
|
||||
case Instructions::memory_copy.value():
|
||||
case Instructions::memory_fill.value():
|
||||
case Instructions::table_init.value():
|
||||
case Instructions::elem_drop.value():
|
||||
case Instructions::table_copy.value():
|
||||
case Instructions::table_grow.value():
|
||||
case Instructions::table_size.value():
|
||||
case Instructions::table_fill.value():
|
||||
default:
|
||||
unimplemented:;
|
||||
dbgln("Instruction '{}' not implemented", instruction_name(instruction.opcode()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,11 @@ namespace Wasm {
|
|||
|
||||
struct Interpreter {
|
||||
void interpret(Configuration&);
|
||||
|
||||
private:
|
||||
void interpret(Configuration&, InstructionPointer&, const Instruction&);
|
||||
void branch_to_label(Configuration&, LabelIndex);
|
||||
ReadonlyBytes load_from_memory(Configuration&, const Instruction&, size_t);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ struct Names {
|
|||
static HashMap<OpCode, String> instruction_names;
|
||||
};
|
||||
|
||||
static String instruction_name(const OpCode& opcode)
|
||||
String instruction_name(const OpCode& opcode)
|
||||
{
|
||||
return Names::instruction_names.get(opcode).value_or("<unknown>");
|
||||
}
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
|
||||
namespace Wasm {
|
||||
|
||||
String instruction_name(const OpCode& opcode);
|
||||
|
||||
struct Printer {
|
||||
explicit Printer(OutputStream& stream, size_t initial_indent = 0)
|
||||
: m_stream(stream)
|
||||
|
|
|
@ -16,16 +16,31 @@ int main(int argc, char* argv[])
|
|||
const char* filename = nullptr;
|
||||
bool print = false;
|
||||
bool attempt_instantiate = false;
|
||||
bool attemp_execute = false;
|
||||
String exported_function_to_execute;
|
||||
Vector<u64> values_to_push;
|
||||
|
||||
Core::ArgsParser parser;
|
||||
parser.add_positional_argument(filename, "File name to parse", "file");
|
||||
parser.add_option(print, "Print the parsed module", "print", 'p');
|
||||
parser.add_option(attempt_instantiate, "Attempt to instantiate the module", "instantiate", 'i');
|
||||
parser.add_option(attemp_execute, "Attempt to execute a function from the module (implies -i)", "execute", 'e');
|
||||
parser.add_option(exported_function_to_execute, "Attempt to execute the named exported function from the module (implies -i)", "execute", 'e', "name");
|
||||
parser.add_option(Core::ArgsParser::Option {
|
||||
.requires_argument = true,
|
||||
.help_string = "Supply arguments to the function (default=0) (expects u64, casts to required type)",
|
||||
.long_name = "arg",
|
||||
.short_name = 0,
|
||||
.value_name = "u64",
|
||||
.accept_value = [&](const char* str) -> bool {
|
||||
if (auto v = StringView { str }.to_uint<u64>(); v.has_value()) {
|
||||
values_to_push.append(v.value());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
});
|
||||
parser.parse(argc, argv);
|
||||
|
||||
if (attemp_execute)
|
||||
if (!exported_function_to_execute.is_empty())
|
||||
attempt_instantiate = true;
|
||||
|
||||
auto result = Core::File::open(filename, Core::OpenMode::ReadOnly);
|
||||
|
@ -80,30 +95,40 @@ int main(int argc, char* argv[])
|
|||
}
|
||||
}
|
||||
|
||||
if (attemp_execute) {
|
||||
if (!exported_function_to_execute.is_empty()) {
|
||||
Optional<Wasm::FunctionAddress> run_address;
|
||||
Vector<Wasm::Value> values;
|
||||
// Pick a function that takes no args :P
|
||||
for (auto& address : machine.module_instance().functions()) {
|
||||
auto fn = machine.store().get(address);
|
||||
if (!fn)
|
||||
continue;
|
||||
if (auto ptr = fn->get_pointer<Wasm::WasmFunction>()) {
|
||||
const Wasm::FunctionType& ty = ptr->type();
|
||||
for (auto& param : ty.parameters()) {
|
||||
values.append(Wasm::Value { param, 0ull });
|
||||
}
|
||||
run_address = address;
|
||||
break;
|
||||
for (auto& entry : machine.module_instance().exports()) {
|
||||
if (entry.name() == exported_function_to_execute) {
|
||||
if (auto addr = entry.value().get_pointer<Wasm::FunctionAddress>())
|
||||
run_address = *addr;
|
||||
}
|
||||
}
|
||||
if (!run_address.has_value()) {
|
||||
warnln("No nullary function, sorry :(");
|
||||
warnln("No such exported function, sorry :(");
|
||||
return 1;
|
||||
}
|
||||
outln("Executing ");
|
||||
print_func(*run_address);
|
||||
outln();
|
||||
|
||||
auto instance = machine.store().get(*run_address);
|
||||
VERIFY(instance);
|
||||
|
||||
if (instance->has<Wasm::HostFunction>()) {
|
||||
warnln("Exported function is a host function, cannot run that yet");
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (auto& param : instance->get<Wasm::WasmFunction>().type().parameters()) {
|
||||
if (values_to_push.is_empty())
|
||||
values.append(Wasm::Value { param, 0ull });
|
||||
else
|
||||
values.append(Wasm::Value { param, values_to_push.take_last() });
|
||||
}
|
||||
|
||||
if (print) {
|
||||
outln("Executing ");
|
||||
print_func(*run_address);
|
||||
outln();
|
||||
}
|
||||
|
||||
auto result = machine.invoke(run_address.value(), move(values));
|
||||
if (!result.values().is_empty())
|
||||
|
|
Loading…
Reference in a new issue