2021-04-30 17:38:51 -03:00
|
|
|
/*
|
|
|
|
|
* Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenityos.org>
|
|
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
|
|
|
*/
|
|
|
|
|
|
2023-01-25 16:19:05 -03:00
|
|
|
#include <AK/MemoryStream.h>
|
2021-04-30 17:38:51 -03:00
|
|
|
#include <LibWasm/AbstractMachine/Configuration.h>
|
|
|
|
|
#include <LibWasm/AbstractMachine/Interpreter.h>
|
2021-06-03 20:00:09 -03:00
|
|
|
#include <LibWasm/Printer/Printer.h>
|
2021-04-30 17:38:51 -03:00
|
|
|
|
|
|
|
|
namespace Wasm {
|
|
|
|
|
|
2025-09-22 06:38:44 -03:00
|
|
|
void Configuration::unwind_impl()
|
2021-05-23 17:58:02 -03:00
|
|
|
{
|
2026-04-04 14:26:41 -03:00
|
|
|
if (m_compiled_direct_call_depth > 0) {
|
|
|
|
|
m_compiled_direct_call_depth--;
|
|
|
|
|
m_depth--;
|
|
|
|
|
return;
|
|
|
|
|
}
|
2025-12-03 23:06:50 -03:00
|
|
|
auto last_frame = m_frame_stack.take_last();
|
2021-05-23 17:58:02 -03:00
|
|
|
m_depth--;
|
2026-04-04 14:26:41 -03:00
|
|
|
|
|
|
|
|
m_locals_base = m_frame_stack.is_empty() ? nullptr : m_frame_stack.last().locals_data();
|
|
|
|
|
if (m_frame_stack.is_empty()) {
|
|
|
|
|
m_default_memory = nullptr;
|
|
|
|
|
m_default_memory_base = nullptr;
|
|
|
|
|
} else {
|
|
|
|
|
auto const& memories = m_frame_stack.last().module().memories();
|
|
|
|
|
m_default_memory = memories.is_empty() ? nullptr : m_store.unsafe_get(memories[0]);
|
|
|
|
|
m_default_memory_base = m_default_memory ? m_default_memory->data().data() : nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!last_frame.owns_locals()) {
|
|
|
|
|
// Non-owning frame: just restore the caller's runtime state.
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Owning frame: full cleanup.
|
|
|
|
|
release_arguments_allocation(last_frame.owned_locals(), m_locals_base != nullptr);
|
2021-05-23 17:58:02 -03:00
|
|
|
}
|
|
|
|
|
|
2025-12-03 23:06:50 -03:00
|
|
|
Result Configuration::call(Interpreter& interpreter, FunctionAddress address, Vector<Value, ArgumentsStaticSize>& arguments)
|
2025-09-22 06:38:44 -03:00
|
|
|
{
|
|
|
|
|
if (auto fn = TRY(prepare_call(address, arguments)); fn.has_value())
|
2025-11-07 07:29:20 -03:00
|
|
|
return fn->function()(*this, arguments.span());
|
2025-09-22 06:38:44 -03:00
|
|
|
m_ip = 0;
|
|
|
|
|
return execute(interpreter);
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-03 23:06:50 -03:00
|
|
|
ErrorOr<Optional<HostFunction&>, Trap> Configuration::prepare_call(FunctionAddress address, Vector<Value, ArgumentsStaticSize>& arguments, bool is_tailcall)
|
2021-04-30 17:38:51 -03:00
|
|
|
{
|
|
|
|
|
auto* function = m_store.get(address);
|
|
|
|
|
if (!function)
|
2025-04-22 04:48:26 -03:00
|
|
|
return Trap::from_string("Attempt to call nonexistent function by address");
|
2025-09-22 06:38:44 -03:00
|
|
|
|
2021-04-30 17:38:51 -03:00
|
|
|
if (auto* wasm_function = function->get_pointer<WasmFunction>()) {
|
2026-04-04 14:26:41 -03:00
|
|
|
TRY(prepare_wasm_call(*wasm_function, arguments, is_tailcall));
|
2025-09-22 06:38:44 -03:00
|
|
|
return OptionalNone {};
|
2021-04-30 17:38:51 -03:00
|
|
|
}
|
|
|
|
|
|
2025-09-22 06:38:44 -03:00
|
|
|
return function->get<HostFunction>();
|
2021-04-30 17:38:51 -03:00
|
|
|
}
|
|
|
|
|
|
2026-06-02 11:59:39 -03:00
|
|
|
ErrorOr<void, Trap> Configuration::prepare_wasm_call(WasmFunction const& wasm_function, Vector<Value, ArgumentsStaticSize>& arguments, bool is_tailcall)
|
|
|
|
|
{
|
2026-06-04 11:53:04 -03:00
|
|
|
// Tier-0 by default: don't block the call waiting for native compilation. Non-Web embedders
|
|
|
|
|
// compile synchronously at instantiate time (so the JIT is already live here); the Web path
|
|
|
|
|
// compiles in the background and the interpreter picks up the native entry on a later call
|
|
|
|
|
// once it's published. Either way, execution falls back to the interpreter until then.
|
2026-06-02 11:59:39 -03:00
|
|
|
if (is_tailcall)
|
|
|
|
|
unwind_impl();
|
|
|
|
|
|
|
|
|
|
arguments.ensure_capacity(arguments.size() + wasm_function.code().func().total_local_count());
|
|
|
|
|
for (auto const& local : wasm_function.code().func().locals()) {
|
|
|
|
|
for (size_t i = 0; i < local.n(); ++i)
|
|
|
|
|
arguments.unchecked_append(Value(local.type()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
set_frame(
|
|
|
|
|
is_tailcall ? IsTailcall::Yes : IsTailcall::No,
|
|
|
|
|
wasm_function.module(),
|
|
|
|
|
move(arguments),
|
|
|
|
|
wasm_function.code().func().body(),
|
|
|
|
|
wasm_function.type().results().size());
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-23 18:34:58 -03:00
|
|
|
Result Configuration::execute(Interpreter& interpreter)
|
2021-04-30 17:38:51 -03:00
|
|
|
{
|
|
|
|
|
interpreter.interpret(*this);
|
2021-05-07 02:29:18 -03:00
|
|
|
if (interpreter.did_trap())
|
2025-04-22 04:48:26 -03:00
|
|
|
return interpreter.trap();
|
2021-04-30 17:38:51 -03:00
|
|
|
|
2025-06-06 09:20:07 -03:00
|
|
|
Vector<Value> results { value_stack().span().slice_from_end(frame().arity()) };
|
|
|
|
|
value_stack().shrink(value_stack().size() - results.size(), true);
|
|
|
|
|
results.reverse();
|
2024-08-03 00:51:40 -03:00
|
|
|
|
2026-01-19 08:54:57 -03:00
|
|
|
// If we reached here from a tailcall -> return, we might not have a label to pop (because the return already popped it)
|
|
|
|
|
if (!label_stack().is_empty())
|
|
|
|
|
label_stack().take_last();
|
|
|
|
|
|
2021-05-23 13:51:17 -03:00
|
|
|
return Result { move(results) };
|
2021-04-30 17:38:51 -03:00
|
|
|
}
|
|
|
|
|
|
2026-04-04 14:26:41 -03:00
|
|
|
ErrorOr<void, Trap> Configuration::execute_for_compiled_call(Interpreter& interpreter, Value* single_result)
|
|
|
|
|
{
|
|
|
|
|
interpreter.interpret(*this);
|
|
|
|
|
if (interpreter.did_trap())
|
|
|
|
|
return interpreter.trap();
|
|
|
|
|
|
|
|
|
|
VERIFY(frame().arity() <= 1);
|
|
|
|
|
if (frame().arity() == 1) {
|
|
|
|
|
auto result = value_stack().unsafe_take_last();
|
|
|
|
|
if (single_result)
|
|
|
|
|
*single_result = result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!label_stack().is_empty())
|
|
|
|
|
label_stack().take_last();
|
|
|
|
|
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-30 19:49:01 -03:00
|
|
|
void Configuration::dump_stack()
|
|
|
|
|
{
|
2022-04-01 14:58:27 -03:00
|
|
|
auto print_value = []<typename... Ts>(CheckedFormatString<Ts...> format, Ts... vs) {
|
2023-01-25 16:19:05 -03:00
|
|
|
AllocatingMemoryStream memory_stream;
|
2023-01-21 06:29:21 -03:00
|
|
|
Printer { memory_stream }.print(vs...);
|
2023-01-09 08:06:13 -03:00
|
|
|
auto buffer = ByteBuffer::create_uninitialized(memory_stream.used_buffer_size()).release_value_but_fixme_should_propagate_errors();
|
2023-03-01 11:27:35 -03:00
|
|
|
memory_stream.read_until_filled(buffer).release_value_but_fixme_should_propagate_errors();
|
2021-09-10 18:43:11 -03:00
|
|
|
dbgln(format.view(), StringView(buffer).trim_whitespace());
|
2021-06-03 20:00:09 -03:00
|
|
|
};
|
2024-08-03 00:51:40 -03:00
|
|
|
for (auto const& value : value_stack()) {
|
|
|
|
|
print_value(" {}", value);
|
2021-04-30 19:49:01 -03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-30 17:38:51 -03:00
|
|
|
}
|