LibWasm: Split parameters from locals
This commit is contained in:
parent
ac979648bd
commit
b89ecfc6bc
15 changed files with 153 additions and 53 deletions
|
|
@ -293,7 +293,8 @@ InstantiationResult AbstractMachine::instantiate(Module const& module, Vector<Ex
|
|||
config.enable_instruction_count_limit();
|
||||
config.set_frame(Frame {
|
||||
auxiliary_instance,
|
||||
Vector<Value> {},
|
||||
{},
|
||||
{},
|
||||
entry.expression(),
|
||||
1,
|
||||
});
|
||||
|
|
@ -315,7 +316,8 @@ InstantiationResult AbstractMachine::instantiate(Module const& module, Vector<Ex
|
|||
config.enable_instruction_count_limit();
|
||||
config.set_frame(Frame {
|
||||
main_module_instance,
|
||||
Vector<Value> {},
|
||||
{},
|
||||
{},
|
||||
entry,
|
||||
entry.instructions().size() - 1,
|
||||
});
|
||||
|
|
@ -350,7 +352,8 @@ InstantiationResult AbstractMachine::instantiate(Module const& module, Vector<Ex
|
|||
config.enable_instruction_count_limit();
|
||||
config.set_frame(Frame {
|
||||
main_module_instance,
|
||||
Vector<Value> {},
|
||||
{},
|
||||
{},
|
||||
active_ptr->expression,
|
||||
1,
|
||||
});
|
||||
|
|
@ -385,7 +388,8 @@ InstantiationResult AbstractMachine::instantiate(Module const& module, Vector<Ex
|
|||
config.enable_instruction_count_limit();
|
||||
config.set_frame(Frame {
|
||||
main_module_instance,
|
||||
Vector<Value> {},
|
||||
{},
|
||||
{},
|
||||
data.offset,
|
||||
1,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -315,10 +315,7 @@ private:
|
|||
class ModuleInstance {
|
||||
public:
|
||||
explicit ModuleInstance(
|
||||
Vector<FunctionType> types, Vector<FunctionAddress> function_addresses, Vector<TableAddress> table_addresses,
|
||||
Vector<MemoryAddress> memory_addresses, Vector<GlobalAddress> global_addresses, Vector<DataAddress> data_addresses,
|
||||
Vector<TagAddress> tag_addresses, Vector<TagType> tag_types,
|
||||
Vector<ExportInstance> exports)
|
||||
Vector<FunctionType> types, Vector<FunctionAddress> function_addresses, Vector<TableAddress> table_addresses, Vector<MemoryAddress> memory_addresses, Vector<GlobalAddress> global_addresses, Vector<DataAddress> data_addresses, Vector<TagAddress> tag_addresses, Vector<TagType> tag_types, Vector<ExportInstance> exports)
|
||||
: m_types(move(types))
|
||||
, m_tag_types(move(tag_types))
|
||||
, m_functions(move(function_addresses))
|
||||
|
|
@ -392,7 +389,7 @@ private:
|
|||
|
||||
class HostFunction {
|
||||
public:
|
||||
explicit HostFunction(AK::Function<Result(Configuration&, Vector<Value>&)> function, FunctionType const& type, ByteString name)
|
||||
explicit HostFunction(AK::Function<Result(Configuration&, Span<Value>)> function, FunctionType const& type, ByteString name)
|
||||
: m_function(move(function))
|
||||
, m_type(type)
|
||||
, m_name(move(name))
|
||||
|
|
@ -404,7 +401,7 @@ public:
|
|||
auto& name() const { return m_name; }
|
||||
|
||||
private:
|
||||
AK::Function<Result(Configuration&, Vector<Value>&)> m_function;
|
||||
AK::Function<Result(Configuration&, Span<Value>)> m_function;
|
||||
FunctionType m_type;
|
||||
ByteString m_name;
|
||||
};
|
||||
|
|
@ -672,8 +669,9 @@ private:
|
|||
|
||||
class Frame {
|
||||
public:
|
||||
explicit Frame(ModuleInstance const& module, Vector<Value> locals, Expression const& expression, size_t arity)
|
||||
explicit Frame(ModuleInstance const& module, Vector<Value, 8> arguments, Vector<Value, 8> locals, Expression const& expression, size_t arity)
|
||||
: m_module(module)
|
||||
, m_arguments(move(arguments))
|
||||
, m_locals(move(locals))
|
||||
, m_expression(expression)
|
||||
, m_arity(arity)
|
||||
|
|
@ -683,13 +681,23 @@ public:
|
|||
auto& module() const { return m_module; }
|
||||
auto& locals() const { return m_locals; }
|
||||
auto& locals() { return m_locals; }
|
||||
auto& arguments() const { return m_arguments; }
|
||||
auto& arguments() { return m_arguments; }
|
||||
auto& expression() const { return m_expression; }
|
||||
auto arity() const { return m_arity; }
|
||||
auto label_index() const { return m_label_index; }
|
||||
auto& label_index() { return m_label_index; }
|
||||
|
||||
Value& local_or_argument(LocalIndex index)
|
||||
{
|
||||
if (index.value() & LocalArgumentMarker)
|
||||
return m_arguments[index.value() & ~LocalArgumentMarker];
|
||||
return m_locals[index.value()];
|
||||
}
|
||||
|
||||
private:
|
||||
ModuleInstance const& m_module;
|
||||
Vector<Value, 8> m_arguments;
|
||||
Vector<Value, 8> m_locals;
|
||||
Expression const& m_expression;
|
||||
size_t m_arity { 0 };
|
||||
|
|
|
|||
|
|
@ -137,9 +137,9 @@ struct Continue {
|
|||
static Outcome operator()(BytecodeInterpreter& interpreter, Configuration& configuration, Instruction const*, SourcesAndDestination addresses, u64 current_ip_value, Dispatch const* cc)
|
||||
{
|
||||
current_ip_value++;
|
||||
addresses.sources_and_destination = cc[current_ip_value].sources_and_destination;
|
||||
auto const instruction = cc[current_ip_value].instruction;
|
||||
auto const handler = bit_cast<Outcome (*)(HANDLER_PARAMS(DECOMPOSE_PARAMS_TYPE_ONLY))>(cc[current_ip_value].handler_ptr);
|
||||
addresses.sources_and_destination = cc[current_ip_value].sources_and_destination;
|
||||
TAILCALL return handler(interpreter, configuration, instruction, addresses, current_ip_value, cc);
|
||||
}
|
||||
};
|
||||
|
|
@ -1027,6 +1027,12 @@ HANDLE_INSTRUCTION(local_get)
|
|||
TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY));
|
||||
}
|
||||
|
||||
HANDLE_INSTRUCTION(synthetic_argument_get)
|
||||
{
|
||||
configuration.push_to_destination(configuration.argument(instruction->local_index()), addresses.destination);
|
||||
TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY));
|
||||
}
|
||||
|
||||
HANDLE_INSTRUCTION(i32_const)
|
||||
{
|
||||
configuration.push_to_destination(Value(instruction->arguments().unsafe_get<i32>()), addresses.destination);
|
||||
|
|
@ -1035,39 +1041,39 @@ HANDLE_INSTRUCTION(i32_const)
|
|||
|
||||
HANDLE_INSTRUCTION(synthetic_i32_add2local)
|
||||
{
|
||||
configuration.push_to_destination(Value(static_cast<i32>(Operators::Add {}(configuration.local(instruction->local_index()).to<u32>(), configuration.local(instruction->arguments().get<LocalIndex>()).to<u32>()))), addresses.destination);
|
||||
configuration.push_to_destination(Value(static_cast<i32>(Operators::Add {}(configuration.local_or_argument(instruction->local_index()).to<u32>(), configuration.local_or_argument(instruction->arguments().get<LocalIndex>()).to<u32>()))), addresses.destination);
|
||||
TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY));
|
||||
}
|
||||
|
||||
HANDLE_INSTRUCTION(synthetic_i32_addconstlocal)
|
||||
{
|
||||
configuration.push_to_destination(Value(static_cast<i32>(Operators::Add {}(configuration.local(instruction->local_index()).to<u32>(), instruction->arguments().unsafe_get<i32>()))), addresses.destination);
|
||||
configuration.push_to_destination(Value(static_cast<i32>(Operators::Add {}(configuration.local_or_argument(instruction->local_index()).to<u32>(), instruction->arguments().unsafe_get<i32>()))), addresses.destination);
|
||||
TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY));
|
||||
}
|
||||
|
||||
HANDLE_INSTRUCTION(synthetic_i32_andconstlocal)
|
||||
{
|
||||
configuration.push_to_destination(Value(Operators::BitAnd {}(configuration.local(instruction->local_index()).to<i32>(), instruction->arguments().unsafe_get<i32>())), addresses.destination);
|
||||
configuration.push_to_destination(Value(Operators::BitAnd {}(configuration.local_or_argument(instruction->local_index()).to<i32>(), instruction->arguments().unsafe_get<i32>())), addresses.destination);
|
||||
TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY));
|
||||
}
|
||||
|
||||
HANDLE_INSTRUCTION(synthetic_i32_storelocal)
|
||||
{
|
||||
if (interpreter.store_value(configuration, *instruction, ConvertToRaw<i32> {}(configuration.local(instruction->local_index()).to<i32>()), 0, addresses))
|
||||
if (interpreter.store_value(configuration, *instruction, ConvertToRaw<i32> {}(configuration.local_or_argument(instruction->local_index()).to<i32>()), 0, addresses))
|
||||
return Outcome::Return;
|
||||
TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY));
|
||||
}
|
||||
|
||||
HANDLE_INSTRUCTION(synthetic_i64_storelocal)
|
||||
{
|
||||
if (interpreter.store_value(configuration, *instruction, ConvertToRaw<i64> {}(configuration.local(instruction->local_index()).to<i64>()), 0, addresses))
|
||||
if (interpreter.store_value(configuration, *instruction, ConvertToRaw<i64> {}(configuration.local_or_argument(instruction->local_index()).to<i64>()), 0, addresses))
|
||||
return Outcome::Return;
|
||||
TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY));
|
||||
}
|
||||
|
||||
HANDLE_INSTRUCTION(synthetic_local_seti32_const)
|
||||
{
|
||||
configuration.local(instruction->local_index()) = Value(instruction->arguments().unsafe_get<i32>());
|
||||
configuration.local_or_argument(instruction->local_index()) = Value(instruction->arguments().unsafe_get<i32>());
|
||||
TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY));
|
||||
}
|
||||
|
||||
|
|
@ -1181,7 +1187,7 @@ HANDLE_INSTRUCTION(nop)
|
|||
HANDLE_INSTRUCTION(local_set)
|
||||
{
|
||||
// bounds checked by verifier.
|
||||
configuration.local(instruction->local_index()) = configuration.take_source(0, addresses.sources);
|
||||
configuration.local_or_argument(instruction->local_index()) = configuration.take_source(0, addresses.sources);
|
||||
TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY));
|
||||
}
|
||||
|
||||
|
|
@ -1577,7 +1583,7 @@ HANDLE_INSTRUCTION(local_tee)
|
|||
auto value = configuration.source_value(0, addresses.sources); // bounds checked by verifier.
|
||||
auto local_index = instruction->local_index();
|
||||
dbgln_if(WASM_TRACE_DEBUG, "stack:peek -> locals({})", local_index.value());
|
||||
configuration.frame().locals()[local_index.value()] = value;
|
||||
configuration.frame().local_or_argument(local_index) = value;
|
||||
TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY));
|
||||
}
|
||||
|
||||
|
|
@ -4044,7 +4050,7 @@ Outcome BytecodeInterpreter::call_address(Configuration& configuration, Function
|
|||
if (source == CallAddressSource::IndirectCall || source == CallAddressSource::IndirectTailCall) {
|
||||
TRAP_IF_NOT(type->parameters().size() <= configuration.value_stack().size());
|
||||
}
|
||||
Vector<Value> args;
|
||||
Vector<Value, 8> args;
|
||||
if (!type->parameters().is_empty()) {
|
||||
args.ensure_capacity(type->parameters().size());
|
||||
auto span = configuration.value_stack().span().slice_from_end(type->parameters().size());
|
||||
|
|
@ -4478,6 +4484,21 @@ CompiledInstructions try_compile_instructions(Expression const& expression, Span
|
|||
|
||||
result.dispatches.remove_all(nops_to_remove, [](auto const& it) { return it.key(); });
|
||||
|
||||
// Rewrite local.get of arguments to argument.get to keep local.get for locals only.
|
||||
for (size_t i = 0; i < result.dispatches.size(); ++i) {
|
||||
auto& dispatch = result.dispatches[i];
|
||||
if (dispatch.instruction->opcode() == Instructions::local_get) {
|
||||
auto local_index = dispatch.instruction->local_index();
|
||||
if (local_index.value() & LocalArgumentMarker) {
|
||||
result.extra_instruction_storage.append(Instruction(
|
||||
Instructions::synthetic_argument_get,
|
||||
local_index));
|
||||
result.dispatches[i].instruction = &result.extra_instruction_storage.unsafe_last();
|
||||
result.dispatches[i].instruction_opcode = result.dispatches[i].instruction->opcode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate registers for instructions, meeting the following constraints:
|
||||
// - Any instruction that produces polymorphic stack, or requires its inputs on the stack must sink all active values to the stack.
|
||||
// - All instructions must have the same location for their last input and their destination value (if any).
|
||||
|
|
|
|||
|
|
@ -16,17 +16,18 @@ void Configuration::unwind_impl()
|
|||
m_frame_stack.take_last();
|
||||
m_depth--;
|
||||
m_locals_base = m_frame_stack.is_empty() ? nullptr : m_frame_stack.unchecked_last().locals().data();
|
||||
m_arguments_base = m_frame_stack.is_empty() ? nullptr : m_frame_stack.unchecked_last().arguments().data();
|
||||
}
|
||||
|
||||
Result Configuration::call(Interpreter& interpreter, FunctionAddress address, Vector<Value> arguments)
|
||||
Result Configuration::call(Interpreter& interpreter, FunctionAddress address, Vector<Value, 8> arguments)
|
||||
{
|
||||
if (auto fn = TRY(prepare_call(address, arguments)); fn.has_value())
|
||||
return fn->function()(*this, arguments);
|
||||
return fn->function()(*this, arguments.span());
|
||||
m_ip = 0;
|
||||
return execute(interpreter);
|
||||
}
|
||||
|
||||
ErrorOr<Optional<HostFunction&>, Trap> Configuration::prepare_call(FunctionAddress address, Vector<Value>& arguments, bool is_tailcall)
|
||||
ErrorOr<Optional<HostFunction&>, Trap> Configuration::prepare_call(FunctionAddress address, Vector<Value, 8>& arguments, bool is_tailcall)
|
||||
{
|
||||
auto* function = m_store.get(address);
|
||||
if (!function)
|
||||
|
|
@ -35,15 +36,16 @@ ErrorOr<Optional<HostFunction&>, Trap> Configuration::prepare_call(FunctionAddre
|
|||
if (auto* wasm_function = function->get_pointer<WasmFunction>()) {
|
||||
if (is_tailcall)
|
||||
unwind_impl(); // Unwind the current frame, the "return" in the tail-called function will unwind the frame we're gonna push now.
|
||||
Vector<Value> locals = move(arguments);
|
||||
locals.ensure_capacity(locals.size() + wasm_function->code().func().locals().size());
|
||||
Vector<Value, 8> locals;
|
||||
locals.ensure_capacity(wasm_function->code().func().total_local_count());
|
||||
for (auto& local : wasm_function->code().func().locals()) {
|
||||
for (size_t i = 0; i < local.n(); ++i)
|
||||
locals.append(Value(local.type()));
|
||||
locals.unchecked_append(Value(local.type()));
|
||||
}
|
||||
|
||||
set_frame(Frame {
|
||||
wasm_function->module(),
|
||||
move(arguments),
|
||||
move(locals),
|
||||
wasm_function->code().func().body(),
|
||||
wasm_function->type().results().size(),
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ public:
|
|||
m_frame_stack.append(move(frame));
|
||||
m_label_stack.append(label);
|
||||
m_locals_base = m_frame_stack.unchecked_last().locals().data();
|
||||
m_arguments_base = m_frame_stack.unchecked_last().arguments().data();
|
||||
}
|
||||
ALWAYS_INLINE auto& frame() const { return m_frame_stack.unchecked_last(); }
|
||||
ALWAYS_INLINE auto& frame() { return m_frame_stack.unchecked_last(); }
|
||||
|
|
@ -49,7 +50,15 @@ public:
|
|||
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& local_or_argument(LocalIndex index)
|
||||
{
|
||||
if (index.value() & LocalArgumentMarker)
|
||||
return m_arguments_base[index.value() & ~LocalArgumentMarker];
|
||||
return m_locals_base[index.value()];
|
||||
}
|
||||
ALWAYS_INLINE Value const& argument(LocalIndex index) const { return m_arguments_base[index.value() & ~LocalArgumentMarker]; }
|
||||
ALWAYS_INLINE Value& argument(LocalIndex index) { return m_arguments_base[index.value() & ~LocalArgumentMarker]; }
|
||||
|
||||
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()]; }
|
||||
|
||||
|
|
@ -69,8 +78,8 @@ public:
|
|||
};
|
||||
|
||||
void unwind(Badge<CallFrameHandle>, CallFrameHandle const&) { unwind_impl(); }
|
||||
ErrorOr<Optional<HostFunction&>, Trap> prepare_call(FunctionAddress, Vector<Value>& arguments, bool is_tailcall = false);
|
||||
Result call(Interpreter&, FunctionAddress, Vector<Value> arguments);
|
||||
ErrorOr<Optional<HostFunction&>, Trap> prepare_call(FunctionAddress, Vector<Value, 8>& arguments, bool is_tailcall = false);
|
||||
Result call(Interpreter&, FunctionAddress, Vector<Value, 8> arguments);
|
||||
Result execute(Interpreter&);
|
||||
|
||||
void enable_instruction_count_limit() { m_should_limit_instruction_count = true; }
|
||||
|
|
@ -126,6 +135,7 @@ private:
|
|||
u64 m_ip { 0 };
|
||||
bool m_should_limit_instruction_count { false };
|
||||
Value* m_locals_base { nullptr };
|
||||
Value* m_arguments_base { nullptr };
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -261,6 +261,7 @@ ErrorOr<void, ValidationError> Validator::validate(CodeSection const& section)
|
|||
auto function_validator = fork();
|
||||
function_validator.m_context.locals = {};
|
||||
function_validator.m_context.locals.extend(function_type.parameters());
|
||||
function_validator.m_context.current_function_parameter_count = function_type.parameters().size();
|
||||
for (auto& local : function.locals()) {
|
||||
for (size_t i = 0; i < local.n(); ++i)
|
||||
function_validator.m_context.locals.append(local.type());
|
||||
|
|
@ -1362,8 +1363,7 @@ VALIDATE_INSTRUCTION(select_typed)
|
|||
// https://webassembly.github.io/spec/core/bikeshed/#variable-instructions%E2%91%A2
|
||||
VALIDATE_INSTRUCTION(local_get)
|
||||
{
|
||||
auto index = instruction.local_index();
|
||||
TRY(validate(index));
|
||||
auto index = TRY(validate(instruction.local_index()));
|
||||
|
||||
stack.append(m_context.locals[index.value()]);
|
||||
return {};
|
||||
|
|
@ -1371,8 +1371,7 @@ VALIDATE_INSTRUCTION(local_get)
|
|||
|
||||
VALIDATE_INSTRUCTION(local_set)
|
||||
{
|
||||
auto index = instruction.local_index();
|
||||
TRY(validate(index));
|
||||
auto index = TRY(validate(instruction.local_index()));
|
||||
|
||||
auto& value_type = m_context.locals[index.value()];
|
||||
TRY(stack.take(value_type));
|
||||
|
|
@ -1382,8 +1381,7 @@ VALIDATE_INSTRUCTION(local_set)
|
|||
|
||||
VALIDATE_INSTRUCTION(local_tee)
|
||||
{
|
||||
auto index = instruction.local_index();
|
||||
TRY(validate(index));
|
||||
auto index = TRY(validate(instruction.local_index()));
|
||||
|
||||
auto& value_type = m_context.locals[index.value()];
|
||||
TRY(stack.take(value_type));
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ struct Context {
|
|||
Optional<u32> data_count;
|
||||
RefPtr<RefRBTree> references { make_ref_counted<RefRBTree>() };
|
||||
size_t imported_function_count { 0 };
|
||||
size_t current_function_parameter_count { 0 };
|
||||
};
|
||||
|
||||
struct ValidationError : public Error {
|
||||
|
|
@ -124,10 +125,15 @@ public:
|
|||
return Errors::invalid("LabelIndex"sv);
|
||||
}
|
||||
|
||||
ErrorOr<void, ValidationError> validate(LocalIndex index) const
|
||||
ErrorOr<LocalIndex, ValidationError> validate(LocalIndex index) const
|
||||
{
|
||||
if (index.value() & LocalArgumentMarker)
|
||||
index = index.value() & ~LocalArgumentMarker;
|
||||
else
|
||||
index = index.value() + m_context.current_function_parameter_count;
|
||||
|
||||
if (index.value() < m_context.locals.size())
|
||||
return {};
|
||||
return index;
|
||||
return Errors::invalid("LocalIndex"sv);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -499,7 +499,8 @@ namespace Instructions {
|
|||
M(synthetic_call_21, 0xfe0000000000000bull, 2, 1) \
|
||||
M(synthetic_call_30, 0xfe0000000000000cull, 3, 0) \
|
||||
M(synthetic_call_31, 0xfe0000000000000dull, 3, 1) \
|
||||
M(synthetic_end_expression, 0xfe0000000000000eull, 0, 0)
|
||||
M(synthetic_end_expression, 0xfe0000000000000eull, 0, 0) \
|
||||
M(synthetic_argument_get, 0xfe0000000000000full, 0, 1)
|
||||
|
||||
#define ENUMERATE_WASM_OPCODES(M) \
|
||||
ENUMERATE_SINGLE_BYTE_WASM_OPCODES(M) \
|
||||
|
|
@ -510,7 +511,7 @@ ENUMERATE_WASM_OPCODES(M)
|
|||
#undef M
|
||||
|
||||
static constexpr inline OpCode SyntheticInstructionBase = 0xfe00000000000000ull;
|
||||
static constexpr inline size_t SyntheticInstructionCount = 15;
|
||||
static constexpr inline size_t SyntheticInstructionCount = 16;
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include <AK/ConstrainedStream.h>
|
||||
#include <AK/Debug.h>
|
||||
#include <AK/Endian.h>
|
||||
#include <AK/Enumerate.h>
|
||||
#include <AK/LEB128.h>
|
||||
#include <AK/MemoryStream.h>
|
||||
#include <AK/ScopeLogger.h>
|
||||
|
|
@ -1472,9 +1473,31 @@ ParseResult<NonnullRefPtr<Module>> Module::parse(Stream& stream)
|
|||
return ParseError::SectionSizeMismatch;
|
||||
}
|
||||
|
||||
module_ptr->preprocess();
|
||||
|
||||
return module_ptr;
|
||||
}
|
||||
|
||||
void Module::preprocess()
|
||||
{
|
||||
for (auto const [i, type_id] : enumerate(function_section().types())) {
|
||||
if (type_id.value() >= type_section().types().size() || i >= code_section().functions().size()) {
|
||||
dbgln("WASM Module preprocessing: skipping function {} with invalid type id {}", i, type_id.value());
|
||||
continue;
|
||||
}
|
||||
|
||||
auto& function = code_section().functions()[i];
|
||||
auto const parameter_count = type_section().types()[type_id.value()].parameters().size();
|
||||
for (auto& instruction : function.func().body().instructions()) {
|
||||
auto& mutable_instruction = const_cast<Instruction&>(instruction);
|
||||
if (instruction.local_index() < parameter_count)
|
||||
mutable_instruction.set_local_index({}, instruction.local_index().value() | LocalArgumentMarker);
|
||||
else
|
||||
mutable_instruction.set_local_index({}, instruction.local_index().value() - parameter_count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ByteString parse_error_to_byte_string(ParseError error)
|
||||
{
|
||||
switch (error) {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/GenericShorthands.h>
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/TemporaryChange.h>
|
||||
#include <LibWasm/AbstractMachine/AbstractMachine.h>
|
||||
|
|
@ -473,8 +474,12 @@ void Printer::print(Wasm::Instruction const& instruction)
|
|||
print_indent();
|
||||
print("({}", instruction_name(instruction.opcode()));
|
||||
if (instruction.arguments().has<u8>()) {
|
||||
if (instruction.opcode() == Instructions::local_get || instruction.opcode() == Instructions::local_set || instruction.opcode() == Instructions::local_tee)
|
||||
print(" (local index {})", instruction.local_index());
|
||||
if (first_is_one_of(instruction.opcode(), Instructions::local_get, Instructions::local_set, Instructions::local_tee, Instructions::synthetic_argument_get)) {
|
||||
if (instruction.local_index().value() & LocalArgumentMarker)
|
||||
print(" (argument index {})", instruction.local_index().value() & ~LocalArgumentMarker);
|
||||
else
|
||||
print(" (local index {})", instruction.local_index().value());
|
||||
}
|
||||
print(")\n");
|
||||
} else {
|
||||
print(" ");
|
||||
|
|
@ -485,7 +490,12 @@ void Printer::print(Wasm::Instruction const& instruction)
|
|||
[&](FunctionIndex const& index) { print("(function index {})", index.value()); },
|
||||
[&](GlobalIndex const& index) { print("(global index {})", index.value()); },
|
||||
[&](LabelIndex const& index) { print("(label index {})", index.value()); },
|
||||
[&](LocalIndex const& index) { print("(local index {})", index.value()); },
|
||||
[&](LocalIndex const& index) {
|
||||
if (index.value() & LocalArgumentMarker)
|
||||
print("(argument index {})", index.value() & ~LocalArgumentMarker);
|
||||
else
|
||||
print("(local index {})", index.value());
|
||||
},
|
||||
[&](TableIndex const& index) { print("(table index {})", index.value()); },
|
||||
[&](Instruction::IndirectCallArgs const& args) { print("(indirect (type index {}) (table index {}))", args.type.value(), args.table.value()); },
|
||||
[&](Instruction::MemoryArgument const& args) { print("(memory index {} (align {}) (offset {}))", args.memory_index.value(), args.align, args.offset); },
|
||||
|
|
@ -530,8 +540,12 @@ void Printer::print(Wasm::Instruction const& instruction)
|
|||
[&](Vector<ValueType> const&) { print("(types...)"); },
|
||||
[&](auto const& value) { print("(const {})", value); });
|
||||
|
||||
if (instruction.local_index().value())
|
||||
print(" (local index {})", instruction.local_index().value());
|
||||
if (first_is_one_of(instruction.opcode(), Instructions::local_get, Instructions::local_set, Instructions::local_tee, Instructions::synthetic_argument_get, Instructions::synthetic_local_seti32_const, Instructions::synthetic_i32_storelocal)) {
|
||||
if (instruction.local_index().value() & LocalArgumentMarker)
|
||||
print(" (argument index {})", instruction.local_index().value() & ~LocalArgumentMarker);
|
||||
else
|
||||
print(" (local index {})", instruction.local_index().value());
|
||||
}
|
||||
|
||||
print(")\n");
|
||||
}
|
||||
|
|
@ -1259,5 +1273,6 @@ HashMap<Wasm::OpCode, ByteString> Wasm::Names::instruction_names {
|
|||
{ Instructions::synthetic_call_30, "synthetic:call.30" },
|
||||
{ Instructions::synthetic_call_31, "synthetic:call.31" },
|
||||
{ Instructions::synthetic_end_expression, "synthetic:expression.end" },
|
||||
{ Instructions::synthetic_argument_get, "synthetic:argument.get" },
|
||||
};
|
||||
HashMap<ByteString, Wasm::OpCode> Wasm::Names::instructions_by_name;
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@
|
|||
|
||||
namespace Wasm {
|
||||
|
||||
class Module;
|
||||
|
||||
template<size_t M>
|
||||
using NativeIntegralType = Conditional<M == 8, u8, Conditional<M == 16, u16, Conditional<M == 32, u32, Conditional<M == 64, u64, void>>>>;
|
||||
|
||||
|
|
@ -79,6 +81,8 @@ AK_TYPEDEF_DISTINCT_ORDERED_ID(u32, LabelIndex);
|
|||
AK_TYPEDEF_DISTINCT_ORDERED_ID(u32, DataIndex);
|
||||
AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(u32, InstructionPointer, Arithmetic, Comparison, Flags, Increment);
|
||||
|
||||
constexpr static inline auto LocalArgumentMarker = static_cast<LocalIndex::Type>(1) << (sizeof(LocalIndex::Type) * 8 - 1);
|
||||
|
||||
ParseError with_eof_check(Stream const& stream, ParseError error_if_not_eof);
|
||||
|
||||
template<typename T>
|
||||
|
|
@ -564,6 +568,8 @@ public:
|
|||
|
||||
LocalIndex local_index() const { return m_local_index; }
|
||||
|
||||
void set_local_index(Badge<Module>, LocalIndex index) { m_local_index = index; }
|
||||
|
||||
private:
|
||||
OpCode m_opcode { 0 };
|
||||
LocalIndex m_local_index;
|
||||
|
|
@ -1064,6 +1070,8 @@ public:
|
|||
: m_locals(move(locals))
|
||||
, m_body(move(body))
|
||||
{
|
||||
for (auto const& local : m_locals)
|
||||
m_total_local_count += local.n();
|
||||
}
|
||||
|
||||
auto& locals() const { return m_locals; }
|
||||
|
|
@ -1071,9 +1079,12 @@ public:
|
|||
|
||||
static ParseResult<Func> parse(ConstrainedStream& stream, size_t size_hint);
|
||||
|
||||
auto total_local_count() const { return m_total_local_count; }
|
||||
|
||||
private:
|
||||
Vector<Locals> m_locals;
|
||||
Expression m_body;
|
||||
size_t m_total_local_count { 0 };
|
||||
};
|
||||
class Code {
|
||||
public:
|
||||
|
|
@ -1236,6 +1247,7 @@ public:
|
|||
|
||||
private:
|
||||
void set_validation_status(ValidationStatus status) { m_validation_status = status; }
|
||||
void preprocess();
|
||||
|
||||
Vector<CustomSection> m_custom_sections;
|
||||
TypeSection m_type_section;
|
||||
|
|
|
|||
|
|
@ -994,7 +994,7 @@ struct InvocationOf<impl> {
|
|||
return_ty.append(ValueType(ValueType::I32));
|
||||
|
||||
return HostFunction(
|
||||
[&self, function_name](Configuration& configuration, Vector<Value>& arguments) -> Wasm::Result {
|
||||
[&self, function_name](Configuration& configuration, Span<Value> arguments) -> Wasm::Result {
|
||||
Tuple args = [&]<typename... Ts, auto... Is>(IndexSequence<Is...>) {
|
||||
return Tuple { ABI::deserialize(ABI::to_compatible_value<Ts>(arguments[Is]))... };
|
||||
}.template operator()<Args...>(MakeIndexSequence<sizeof...(Args)>());
|
||||
|
|
@ -1018,9 +1018,9 @@ struct InvocationOf<impl> {
|
|||
// Return values are passed as pointers, after the arguments
|
||||
if constexpr (requires { &R::serialize_into; }) {
|
||||
constexpr auto ResultCount = []<auto N>(void (R::*)(Array<Bytes, N>) const) { return N; }(&R::serialize_into);
|
||||
ABI::serialize(*value.result(), address_spans<ResultCount>(arguments.span().slice(sizeof...(Args)), configuration));
|
||||
ABI::serialize(*value.result(), address_spans<ResultCount>(arguments.slice(sizeof...(Args)), configuration));
|
||||
} else {
|
||||
ABI::serialize(*value.result(), address_spans<1>(arguments.span().slice(sizeof...(Args)), configuration));
|
||||
ABI::serialize(*value.result(), address_spans<1>(arguments.slice(sizeof...(Args)), configuration));
|
||||
}
|
||||
}
|
||||
// Return value is errno, we have nothing to return.
|
||||
|
|
|
|||
|
|
@ -239,7 +239,7 @@ JS::ThrowCompletionOr<NonnullOwnPtr<Wasm::ModuleInstance>> instantiate_module(JS
|
|||
|
||||
// 3.2. If o is not an Object, throw a TypeError exception.
|
||||
if (!value.is_object())
|
||||
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObject, value);
|
||||
return vm.throw_completion<JS::TypeError>(JS::ErrorType::IsNotAEvaluatedFrom, value.to_string_without_side_effects(), "Object"_string, MUST(String::formatted("[wasm import object][\"{}\"]", import_name.module)));
|
||||
auto const& object = value.as_object();
|
||||
|
||||
// 3.3. Let v be ? Get(o, componentName).
|
||||
|
|
@ -268,7 +268,7 @@ JS::ThrowCompletionOr<NonnullOwnPtr<Wasm::ModuleInstance>> instantiate_module(JS
|
|||
// 3.4.3.1. Create a host function from v and functype, and let funcaddr be the result.
|
||||
cache.add_imported_object(function);
|
||||
Wasm::HostFunction host_function {
|
||||
[&](auto&, auto& arguments) -> Wasm::Result {
|
||||
[&](auto&, auto arguments) -> Wasm::Result {
|
||||
GC::RootVector<JS::Value> argument_values { vm.heap() };
|
||||
size_t index = 0;
|
||||
for (auto& entry : arguments) {
|
||||
|
|
|
|||
|
|
@ -140,7 +140,7 @@ private:
|
|||
static Optional<Wasm::FunctionAddress> alloc_noop_function(Wasm::FunctionType type)
|
||||
{
|
||||
return m_machine.store().allocate(Wasm::HostFunction {
|
||||
[](auto&, auto&) -> Wasm::Result {
|
||||
[](auto&, auto) -> Wasm::Result {
|
||||
// Noop, this just needs to exist.
|
||||
return Wasm::Result { Vector<Wasm::Value> {} };
|
||||
},
|
||||
|
|
|
|||
|
|
@ -441,7 +441,7 @@ ErrorOr<int> ladybird_main(Main::Arguments arguments)
|
|||
|
||||
Wasm::FunctionType function_type = { move(params), move(results) };
|
||||
auto host_function = Wasm::HostFunction {
|
||||
[&vm, &function, formal_params, returns, name](Wasm::Configuration&, Vector<Wasm::Value>& args) mutable -> Wasm::Result {
|
||||
[&vm, &function, formal_params, returns, name](Wasm::Configuration&, Span<Wasm::Value> args) mutable -> Wasm::Result {
|
||||
Vector<JS::Value> js_args;
|
||||
js_args.ensure_capacity(args.size());
|
||||
for (size_t i = 0; i < formal_params.size(); ++i) {
|
||||
|
|
@ -671,7 +671,7 @@ ErrorOr<int> ladybird_main(Main::Arguments arguments)
|
|||
continue;
|
||||
auto type = parse_result->type_section().types()[entry.type.get<Wasm::TypeIndex>().value()];
|
||||
auto address = machine.store().allocate(Wasm::HostFunction(
|
||||
[name = entry.name, type = type](auto&, auto& arguments) -> Wasm::Result {
|
||||
[name = entry.name, type = type](auto&, auto arguments) -> Wasm::Result {
|
||||
StringBuilder argument_builder;
|
||||
bool first = true;
|
||||
size_t index = 0;
|
||||
|
|
|
|||
Loading…
Reference in a new issue