mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-07-29 12:19:54 +00:00
LibWasm: Split main interpreter stack into three
Instead of one stack to hold frames, labels, and values, there is now three separate stacks. This speeds up fib(30) from 580ms to 480ms.
This commit is contained in:
parent
15510fb42e
commit
a3b077c641
Notes:
github-actions[bot]
2024-08-06 23:11:27 +00:00
Author: https://github.com/dzfrias
Commit: a3b077c641
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/960
Reviewed-by: https://github.com/alimpfard
4 changed files with 159 additions and 256 deletions
|
@ -563,17 +563,20 @@ private:
|
||||||
|
|
||||||
class Label {
|
class Label {
|
||||||
public:
|
public:
|
||||||
explicit Label(size_t arity, InstructionPointer continuation)
|
explicit Label(size_t arity, InstructionPointer continuation, size_t stack_height)
|
||||||
: m_arity(arity)
|
: m_arity(arity)
|
||||||
|
, m_stack_height(stack_height)
|
||||||
, m_continuation(continuation)
|
, m_continuation(continuation)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
auto continuation() const { return m_continuation; }
|
auto continuation() const { return m_continuation; }
|
||||||
auto arity() const { return m_arity; }
|
auto arity() const { return m_arity; }
|
||||||
|
auto stack_height() const { return m_stack_height; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
size_t m_arity { 0 };
|
size_t m_arity { 0 };
|
||||||
|
size_t m_stack_height { 0 };
|
||||||
InstructionPointer m_continuation { 0 };
|
InstructionPointer m_continuation { 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -592,31 +595,15 @@ public:
|
||||||
auto& locals() { return m_locals; }
|
auto& locals() { return m_locals; }
|
||||||
auto& expression() const { return m_expression; }
|
auto& expression() const { return m_expression; }
|
||||||
auto arity() const { return m_arity; }
|
auto arity() const { return m_arity; }
|
||||||
|
auto label_index() const { return m_label_index; }
|
||||||
|
auto& label_index() { return m_label_index; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ModuleInstance const& m_module;
|
ModuleInstance const& m_module;
|
||||||
Vector<Value> m_locals;
|
Vector<Value> m_locals;
|
||||||
Expression const& m_expression;
|
Expression const& m_expression;
|
||||||
size_t m_arity { 0 };
|
size_t m_arity { 0 };
|
||||||
};
|
size_t m_label_index { 0 };
|
||||||
|
|
||||||
class Stack {
|
|
||||||
public:
|
|
||||||
using EntryType = Variant<Value, Label, Frame>;
|
|
||||||
Stack() = default;
|
|
||||||
|
|
||||||
[[nodiscard]] ALWAYS_INLINE bool is_empty() const { return m_data.is_empty(); }
|
|
||||||
ALWAYS_INLINE void push(EntryType entry) { m_data.append(move(entry)); }
|
|
||||||
ALWAYS_INLINE auto pop() { return m_data.take_last(); }
|
|
||||||
ALWAYS_INLINE auto& peek() const { return m_data.last(); }
|
|
||||||
ALWAYS_INLINE auto& peek() { return m_data.last(); }
|
|
||||||
|
|
||||||
ALWAYS_INLINE auto size() const { return m_data.size(); }
|
|
||||||
ALWAYS_INLINE auto& entries() const { return m_data; }
|
|
||||||
ALWAYS_INLINE auto& entries() { return m_data; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
Vector<EntryType, 1024> m_data;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
using InstantiationResult = AK::ErrorOr<NonnullOwnPtr<ModuleInstance>, InstantiationError>;
|
using InstantiationResult = AK::ErrorOr<NonnullOwnPtr<ModuleInstance>, InstantiationError>;
|
||||||
|
|
|
@ -59,24 +59,19 @@ void BytecodeInterpreter::interpret(Configuration& configuration)
|
||||||
void BytecodeInterpreter::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());
|
dbgln_if(WASM_TRACE_DEBUG, "Branch to label with index {}...", index.value());
|
||||||
auto label = configuration.nth_label(index.value());
|
for (size_t i = 0; i < index.value(); ++i)
|
||||||
dbgln_if(WASM_TRACE_DEBUG, "...which is actually IP {}, and has {} result(s)", label->continuation().value(), label->arity());
|
configuration.label_stack().take_last();
|
||||||
auto results = pop_values(configuration, label->arity());
|
auto label = configuration.label_stack().last();
|
||||||
|
dbgln_if(WASM_TRACE_DEBUG, "...which is actually IP {}, and has {} result(s)", label.continuation().value(), label.arity());
|
||||||
|
auto results = pop_values(configuration, label.arity());
|
||||||
|
|
||||||
size_t drop_count = index.value() + 1;
|
while (configuration.value_stack().size() != label.stack_height())
|
||||||
for (; !configuration.stack().is_empty();) {
|
configuration.value_stack().take_last();
|
||||||
auto& entry = configuration.stack().peek();
|
|
||||||
if (entry.has<Label>()) {
|
|
||||||
if (--drop_count == 0)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
configuration.stack().pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto& result : results.in_reverse())
|
for (auto& result : results.in_reverse())
|
||||||
configuration.stack().push(move(result));
|
configuration.value_stack().append(result);
|
||||||
|
|
||||||
configuration.ip() = label->continuation();
|
configuration.ip() = label.continuation();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ReadType, typename PushType>
|
template<typename ReadType, typename PushType>
|
||||||
|
@ -85,8 +80,8 @@ void BytecodeInterpreter::load_and_push(Configuration& configuration, Instructio
|
||||||
auto& arg = instruction.arguments().get<Instruction::MemoryArgument>();
|
auto& arg = instruction.arguments().get<Instruction::MemoryArgument>();
|
||||||
auto& address = configuration.frame().module().memories()[arg.memory_index.value()];
|
auto& address = configuration.frame().module().memories()[arg.memory_index.value()];
|
||||||
auto memory = configuration.store().get(address);
|
auto memory = configuration.store().get(address);
|
||||||
auto& entry = configuration.stack().peek();
|
auto& entry = configuration.value_stack().last();
|
||||||
auto base = *entry.get<Value>().to<i32>();
|
auto base = *entry.to<i32>();
|
||||||
u64 instance_address = static_cast<u64>(bit_cast<u32>(base)) + arg.offset;
|
u64 instance_address = static_cast<u64>(bit_cast<u32>(base)) + arg.offset;
|
||||||
if (instance_address + sizeof(ReadType) > memory->size()) {
|
if (instance_address + sizeof(ReadType) > memory->size()) {
|
||||||
m_trap = Trap { "Memory access out of bounds" };
|
m_trap = Trap { "Memory access out of bounds" };
|
||||||
|
@ -95,7 +90,7 @@ void BytecodeInterpreter::load_and_push(Configuration& configuration, Instructio
|
||||||
}
|
}
|
||||||
dbgln_if(WASM_TRACE_DEBUG, "load({} : {}) -> stack", instance_address, sizeof(ReadType));
|
dbgln_if(WASM_TRACE_DEBUG, "load({} : {}) -> stack", instance_address, sizeof(ReadType));
|
||||||
auto slice = memory->data().bytes().slice(instance_address, sizeof(ReadType));
|
auto slice = memory->data().bytes().slice(instance_address, sizeof(ReadType));
|
||||||
configuration.stack().peek() = Value(static_cast<PushType>(read_value<ReadType>(slice)));
|
entry = Value(static_cast<PushType>(read_value<ReadType>(slice)));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename TDst, typename TSrc>
|
template<typename TDst, typename TSrc>
|
||||||
|
@ -110,8 +105,8 @@ void BytecodeInterpreter::load_and_push_mxn(Configuration& configuration, Instru
|
||||||
auto& arg = instruction.arguments().get<Instruction::MemoryArgument>();
|
auto& arg = instruction.arguments().get<Instruction::MemoryArgument>();
|
||||||
auto& address = configuration.frame().module().memories()[arg.memory_index.value()];
|
auto& address = configuration.frame().module().memories()[arg.memory_index.value()];
|
||||||
auto memory = configuration.store().get(address);
|
auto memory = configuration.store().get(address);
|
||||||
auto& entry = configuration.stack().peek();
|
auto& entry = configuration.value_stack().last();
|
||||||
auto base = *entry.get<Value>().to<i32>();
|
auto base = *entry.to<i32>();
|
||||||
u64 instance_address = static_cast<u64>(bit_cast<u32>(base)) + arg.offset;
|
u64 instance_address = static_cast<u64>(bit_cast<u32>(base)) + arg.offset;
|
||||||
if (instance_address + M * N / 8 > memory->size()) {
|
if (instance_address + M * N / 8 > memory->size()) {
|
||||||
m_trap = Trap { "Memory access out of bounds" };
|
m_trap = Trap { "Memory access out of bounds" };
|
||||||
|
@ -129,7 +124,7 @@ void BytecodeInterpreter::load_and_push_mxn(Configuration& configuration, Instru
|
||||||
else
|
else
|
||||||
ByteReader::load(slice.data(), bytes);
|
ByteReader::load(slice.data(), bytes);
|
||||||
|
|
||||||
configuration.stack().peek() = Value(bit_cast<u128>(convert_vector<V128>(bytes)));
|
entry = Value(bit_cast<u128>(convert_vector<V128>(bytes)));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<size_t N>
|
template<size_t N>
|
||||||
|
@ -138,8 +133,8 @@ void BytecodeInterpreter::load_and_push_lane_n(Configuration& configuration, Ins
|
||||||
auto memarg_and_lane = instruction.arguments().get<Instruction::MemoryAndLaneArgument>();
|
auto memarg_and_lane = instruction.arguments().get<Instruction::MemoryAndLaneArgument>();
|
||||||
auto& address = configuration.frame().module().memories()[memarg_and_lane.memory.memory_index.value()];
|
auto& address = configuration.frame().module().memories()[memarg_and_lane.memory.memory_index.value()];
|
||||||
auto memory = configuration.store().get(address);
|
auto memory = configuration.store().get(address);
|
||||||
auto vector = *configuration.stack().pop().get<Value>().to<u128>();
|
auto vector = *configuration.value_stack().take_last().to<u128>();
|
||||||
auto base = *configuration.stack().pop().get<Value>().to<u32>();
|
auto base = *configuration.value_stack().take_last().to<u32>();
|
||||||
u64 instance_address = static_cast<u64>(bit_cast<u32>(base)) + memarg_and_lane.memory.offset;
|
u64 instance_address = static_cast<u64>(bit_cast<u32>(base)) + memarg_and_lane.memory.offset;
|
||||||
if (instance_address + N / 8 > memory->size()) {
|
if (instance_address + N / 8 > memory->size()) {
|
||||||
m_trap = Trap { "Memory access out of bounds" };
|
m_trap = Trap { "Memory access out of bounds" };
|
||||||
|
@ -148,7 +143,7 @@ void BytecodeInterpreter::load_and_push_lane_n(Configuration& configuration, Ins
|
||||||
auto slice = memory->data().bytes().slice(instance_address, N / 8);
|
auto slice = memory->data().bytes().slice(instance_address, N / 8);
|
||||||
auto dst = bit_cast<u8*>(&vector) + memarg_and_lane.lane * N / 8;
|
auto dst = bit_cast<u8*>(&vector) + memarg_and_lane.lane * N / 8;
|
||||||
memcpy(dst, slice.data(), N / 8);
|
memcpy(dst, slice.data(), N / 8);
|
||||||
configuration.stack().push(Value(vector));
|
configuration.value_stack().append(Value(vector));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<size_t N>
|
template<size_t N>
|
||||||
|
@ -157,7 +152,7 @@ void BytecodeInterpreter::load_and_push_zero_n(Configuration& configuration, Ins
|
||||||
auto memarg_and_lane = instruction.arguments().get<Instruction::MemoryArgument>();
|
auto memarg_and_lane = instruction.arguments().get<Instruction::MemoryArgument>();
|
||||||
auto& address = configuration.frame().module().memories()[memarg_and_lane.memory_index.value()];
|
auto& address = configuration.frame().module().memories()[memarg_and_lane.memory_index.value()];
|
||||||
auto memory = configuration.store().get(address);
|
auto memory = configuration.store().get(address);
|
||||||
auto base = *configuration.stack().pop().get<Value>().to<u32>();
|
auto base = *configuration.value_stack().take_last().to<u32>();
|
||||||
u64 instance_address = static_cast<u64>(bit_cast<u32>(base)) + memarg_and_lane.offset;
|
u64 instance_address = static_cast<u64>(bit_cast<u32>(base)) + memarg_and_lane.offset;
|
||||||
if (instance_address + N / 8 > memory->size()) {
|
if (instance_address + N / 8 > memory->size()) {
|
||||||
m_trap = Trap { "Memory access out of bounds" };
|
m_trap = Trap { "Memory access out of bounds" };
|
||||||
|
@ -166,7 +161,7 @@ void BytecodeInterpreter::load_and_push_zero_n(Configuration& configuration, Ins
|
||||||
auto slice = memory->data().bytes().slice(instance_address, N / 8);
|
auto slice = memory->data().bytes().slice(instance_address, N / 8);
|
||||||
u128 vector = 0;
|
u128 vector = 0;
|
||||||
memcpy(&vector, slice.data(), N / 8);
|
memcpy(&vector, slice.data(), N / 8);
|
||||||
configuration.stack().push(Value(vector));
|
configuration.value_stack().append(Value(vector));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<size_t M>
|
template<size_t M>
|
||||||
|
@ -175,8 +170,8 @@ void BytecodeInterpreter::load_and_push_m_splat(Configuration& configuration, In
|
||||||
auto& arg = instruction.arguments().get<Instruction::MemoryArgument>();
|
auto& arg = instruction.arguments().get<Instruction::MemoryArgument>();
|
||||||
auto& address = configuration.frame().module().memories()[arg.memory_index.value()];
|
auto& address = configuration.frame().module().memories()[arg.memory_index.value()];
|
||||||
auto memory = configuration.store().get(address);
|
auto memory = configuration.store().get(address);
|
||||||
auto& entry = configuration.stack().peek();
|
auto& entry = configuration.value_stack().last();
|
||||||
auto base = *entry.get<Value>().to<i32>();
|
auto base = *entry.to<i32>();
|
||||||
u64 instance_address = static_cast<u64>(bit_cast<u32>(base)) + arg.offset;
|
u64 instance_address = static_cast<u64>(bit_cast<u32>(base)) + arg.offset;
|
||||||
if (instance_address + M / 8 > memory->size()) {
|
if (instance_address + M / 8 > memory->size()) {
|
||||||
m_trap = Trap { "Memory access out of bounds" };
|
m_trap = Trap { "Memory access out of bounds" };
|
||||||
|
@ -193,7 +188,7 @@ template<size_t M, template<size_t> typename NativeType>
|
||||||
void BytecodeInterpreter::set_top_m_splat(Wasm::Configuration& configuration, NativeType<M> value)
|
void BytecodeInterpreter::set_top_m_splat(Wasm::Configuration& configuration, NativeType<M> value)
|
||||||
{
|
{
|
||||||
auto push = [&](auto result) {
|
auto push = [&](auto result) {
|
||||||
configuration.stack().peek() = Value(bit_cast<u128>(result));
|
configuration.value_stack().last() = Value(bit_cast<u128>(result));
|
||||||
};
|
};
|
||||||
|
|
||||||
if constexpr (IsFloatingPoint<NativeType<32>>) {
|
if constexpr (IsFloatingPoint<NativeType<32>>) {
|
||||||
|
@ -222,8 +217,8 @@ void BytecodeInterpreter::pop_and_push_m_splat(Wasm::Configuration& configuratio
|
||||||
{
|
{
|
||||||
using PopT = Conditional<M <= 32, NativeType<32>, NativeType<64>>;
|
using PopT = Conditional<M <= 32, NativeType<32>, NativeType<64>>;
|
||||||
using ReadT = NativeType<M>;
|
using ReadT = NativeType<M>;
|
||||||
auto entry = configuration.stack().peek();
|
auto entry = configuration.value_stack().last();
|
||||||
auto value = static_cast<ReadT>(*entry.get<Value>().to<PopT>());
|
auto value = static_cast<ReadT>(*entry.to<PopT>());
|
||||||
dbgln_if(WASM_TRACE_DEBUG, "stack({}) -> splat({})", value, M);
|
dbgln_if(WASM_TRACE_DEBUG, "stack({}) -> splat({})", value, M);
|
||||||
set_top_m_splat<M, NativeType>(configuration, value);
|
set_top_m_splat<M, NativeType>(configuration, value);
|
||||||
}
|
}
|
||||||
|
@ -231,7 +226,7 @@ void BytecodeInterpreter::pop_and_push_m_splat(Wasm::Configuration& configuratio
|
||||||
template<typename M, template<typename> typename SetSign, typename VectorType>
|
template<typename M, template<typename> typename SetSign, typename VectorType>
|
||||||
VectorType BytecodeInterpreter::pop_vector(Configuration& configuration)
|
VectorType BytecodeInterpreter::pop_vector(Configuration& configuration)
|
||||||
{
|
{
|
||||||
return bit_cast<VectorType>(configuration.stack().pop().get<Value>().value().get<u128>());
|
return bit_cast<VectorType>(*configuration.value_stack().take_last().to<u128>());
|
||||||
}
|
}
|
||||||
|
|
||||||
void BytecodeInterpreter::call_address(Configuration& configuration, FunctionAddress address)
|
void BytecodeInterpreter::call_address(Configuration& configuration, FunctionAddress address)
|
||||||
|
@ -241,22 +236,20 @@ void BytecodeInterpreter::call_address(Configuration& configuration, FunctionAdd
|
||||||
auto instance = configuration.store().get(address);
|
auto instance = configuration.store().get(address);
|
||||||
FunctionType const* type { nullptr };
|
FunctionType const* type { nullptr };
|
||||||
instance->visit([&](auto const& function) { type = &function.type(); });
|
instance->visit([&](auto const& function) { type = &function.type(); });
|
||||||
TRAP_IF_NOT(configuration.stack().entries().size() > type->parameters().size());
|
|
||||||
Vector<Value> args;
|
Vector<Value> args;
|
||||||
args.ensure_capacity(type->parameters().size());
|
args.ensure_capacity(type->parameters().size());
|
||||||
auto span = configuration.stack().entries().span().slice_from_end(type->parameters().size());
|
auto span = configuration.value_stack().span().slice_from_end(type->parameters().size());
|
||||||
for (auto& entry : span) {
|
for (auto& value : span)
|
||||||
auto* call_argument = entry.get_pointer<Value>();
|
args.unchecked_append(value);
|
||||||
TRAP_IF_NOT(call_argument);
|
|
||||||
args.unchecked_append(move(*call_argument));
|
|
||||||
}
|
|
||||||
|
|
||||||
configuration.stack().entries().remove(configuration.stack().size() - span.size(), span.size());
|
configuration.value_stack().remove(configuration.value_stack().size() - span.size(), span.size());
|
||||||
|
|
||||||
Result result { Trap { ""sv } };
|
Result result { Trap { ""sv } };
|
||||||
{
|
if (instance->has<WasmFunction>()) {
|
||||||
CallFrameHandle handle { *this, configuration };
|
CallFrameHandle handle { *this, configuration };
|
||||||
result = configuration.call(*this, address, move(args));
|
result = configuration.call(*this, address, move(args));
|
||||||
|
} else {
|
||||||
|
result = configuration.call(*this, address, move(args));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.is_trap()) {
|
if (result.is_trap()) {
|
||||||
|
@ -269,17 +262,17 @@ void BytecodeInterpreter::call_address(Configuration& configuration, FunctionAdd
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
configuration.stack().entries().ensure_capacity(configuration.stack().size() + result.values().size());
|
configuration.value_stack().ensure_capacity(configuration.value_stack().size() + result.values().size());
|
||||||
for (auto& entry : result.values().in_reverse())
|
for (auto& entry : result.values().in_reverse())
|
||||||
configuration.stack().entries().unchecked_append(move(entry));
|
configuration.value_stack().unchecked_append(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename PopTypeLHS, typename PushType, typename Operator, typename PopTypeRHS, typename... Args>
|
template<typename PopTypeLHS, typename PushType, typename Operator, typename PopTypeRHS, typename... Args>
|
||||||
void BytecodeInterpreter::binary_numeric_operation(Configuration& configuration, Args&&... args)
|
void BytecodeInterpreter::binary_numeric_operation(Configuration& configuration, Args&&... args)
|
||||||
{
|
{
|
||||||
auto rhs = configuration.stack().pop().get<Value>().to<PopTypeRHS>();
|
auto rhs = configuration.value_stack().take_last().to<PopTypeRHS>();
|
||||||
auto& lhs_entry = configuration.stack().peek();
|
auto& lhs_entry = configuration.value_stack().last();
|
||||||
auto lhs = lhs_entry.get<Value>().to<PopTypeLHS>();
|
auto lhs = lhs_entry.to<PopTypeLHS>();
|
||||||
PushType result;
|
PushType result;
|
||||||
auto call_result = Operator { forward<Args>(args)... }(lhs.value(), rhs.value());
|
auto call_result = Operator { forward<Args>(args)... }(lhs.value(), rhs.value());
|
||||||
if constexpr (IsSpecializationOf<decltype(call_result), AK::ErrorOr>) {
|
if constexpr (IsSpecializationOf<decltype(call_result), AK::ErrorOr>) {
|
||||||
|
@ -298,8 +291,8 @@ void BytecodeInterpreter::binary_numeric_operation(Configuration& configuration,
|
||||||
template<typename PopType, typename PushType, typename Operator, typename... Args>
|
template<typename PopType, typename PushType, typename Operator, typename... Args>
|
||||||
void BytecodeInterpreter::unary_operation(Configuration& configuration, Args&&... args)
|
void BytecodeInterpreter::unary_operation(Configuration& configuration, Args&&... args)
|
||||||
{
|
{
|
||||||
auto& entry = configuration.stack().peek();
|
auto& entry = configuration.value_stack().last();
|
||||||
auto value = entry.get<Value>().to<PopType>();
|
auto value = entry.to<PopType>();
|
||||||
auto call_result = Operator { forward<Args>(args)... }(*value);
|
auto call_result = Operator { forward<Args>(args)... }(*value);
|
||||||
PushType result;
|
PushType result;
|
||||||
if constexpr (IsSpecializationOf<decltype(call_result), AK::ErrorOr>) {
|
if constexpr (IsSpecializationOf<decltype(call_result), AK::ErrorOr>) {
|
||||||
|
@ -349,11 +342,10 @@ template<typename PopT, typename StoreT>
|
||||||
void BytecodeInterpreter::pop_and_store(Configuration& configuration, Instruction const& instruction)
|
void BytecodeInterpreter::pop_and_store(Configuration& configuration, Instruction const& instruction)
|
||||||
{
|
{
|
||||||
auto& memarg = instruction.arguments().get<Instruction::MemoryArgument>();
|
auto& memarg = instruction.arguments().get<Instruction::MemoryArgument>();
|
||||||
auto entry = configuration.stack().pop();
|
auto entry = configuration.value_stack().take_last();
|
||||||
auto value = ConvertToRaw<StoreT> {}(*entry.get<Value>().to<PopT>());
|
auto value = ConvertToRaw<StoreT> {}(*entry.to<PopT>());
|
||||||
dbgln_if(WASM_TRACE_DEBUG, "stack({}) -> temporary({}b)", value, sizeof(StoreT));
|
dbgln_if(WASM_TRACE_DEBUG, "stack({}) -> temporary({}b)", value, sizeof(StoreT));
|
||||||
auto base_entry = configuration.stack().pop();
|
auto base = configuration.value_stack().take_last().to<i32>();
|
||||||
auto base = base_entry.get<Value>().to<i32>();
|
|
||||||
store_to_memory(configuration, memarg, { &value, sizeof(StoreT) }, *base);
|
store_to_memory(configuration, memarg, { &value, sizeof(StoreT) }, *base);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -361,9 +353,9 @@ template<size_t N>
|
||||||
void BytecodeInterpreter::pop_and_store_lane_n(Configuration& configuration, Instruction const& instruction)
|
void BytecodeInterpreter::pop_and_store_lane_n(Configuration& configuration, Instruction const& instruction)
|
||||||
{
|
{
|
||||||
auto& memarg_and_lane = instruction.arguments().get<Instruction::MemoryAndLaneArgument>();
|
auto& memarg_and_lane = instruction.arguments().get<Instruction::MemoryAndLaneArgument>();
|
||||||
auto vector = *configuration.stack().pop().get<Value>().to<u128>();
|
auto vector = *configuration.value_stack().take_last().to<u128>();
|
||||||
auto src = bit_cast<u8*>(&vector) + memarg_and_lane.lane * N / 8;
|
auto src = bit_cast<u8*>(&vector) + memarg_and_lane.lane * N / 8;
|
||||||
auto base = *configuration.stack().pop().get<Value>().to<u32>();
|
auto base = *configuration.value_stack().take_last().to<u32>();
|
||||||
store_to_memory(configuration, memarg_and_lane.memory, { src, N / 8 }, base);
|
store_to_memory(configuration, memarg_and_lane.memory, { src, N / 8 }, base);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -423,7 +415,7 @@ Vector<Value> BytecodeInterpreter::pop_values(Configuration& configuration, size
|
||||||
results.resize(count);
|
results.resize(count);
|
||||||
|
|
||||||
for (size_t i = 0; i < count; ++i)
|
for (size_t i = 0; i < count; ++i)
|
||||||
results[i] = configuration.stack().pop().get<Value>();
|
results[i] = configuration.value_stack().take_last();
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
@ -439,28 +431,28 @@ void BytecodeInterpreter::interpret(Configuration& configuration, InstructionPoi
|
||||||
case Instructions::nop.value():
|
case Instructions::nop.value():
|
||||||
return;
|
return;
|
||||||
case Instructions::local_get.value():
|
case Instructions::local_get.value():
|
||||||
configuration.stack().push(Value(configuration.frame().locals()[instruction.arguments().get<LocalIndex>().value()]));
|
configuration.value_stack().append(Value(configuration.frame().locals()[instruction.arguments().get<LocalIndex>().value()]));
|
||||||
return;
|
return;
|
||||||
case Instructions::local_set.value(): {
|
case Instructions::local_set.value(): {
|
||||||
auto entry = configuration.stack().pop();
|
auto value = configuration.value_stack().take_last();
|
||||||
configuration.frame().locals()[instruction.arguments().get<LocalIndex>().value()] = move(entry.get<Value>());
|
configuration.frame().locals()[instruction.arguments().get<LocalIndex>().value()] = value;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case Instructions::i32_const.value():
|
case Instructions::i32_const.value():
|
||||||
configuration.stack().push(Value(ValueType { ValueType::I32 }, static_cast<i64>(instruction.arguments().get<i32>())));
|
configuration.value_stack().append(Value(ValueType { ValueType::I32 }, static_cast<i64>(instruction.arguments().get<i32>())));
|
||||||
return;
|
return;
|
||||||
case Instructions::i64_const.value():
|
case Instructions::i64_const.value():
|
||||||
configuration.stack().push(Value(ValueType { ValueType::I64 }, instruction.arguments().get<i64>()));
|
configuration.value_stack().append(Value(ValueType { ValueType::I64 }, instruction.arguments().get<i64>()));
|
||||||
return;
|
return;
|
||||||
case Instructions::f32_const.value():
|
case Instructions::f32_const.value():
|
||||||
configuration.stack().push(Value(Value::AnyValueType(instruction.arguments().get<float>())));
|
configuration.value_stack().append(Value(Value::AnyValueType(instruction.arguments().get<float>())));
|
||||||
return;
|
return;
|
||||||
case Instructions::f64_const.value():
|
case Instructions::f64_const.value():
|
||||||
configuration.stack().push(Value(Value::AnyValueType(instruction.arguments().get<double>())));
|
configuration.value_stack().append(Value(Value::AnyValueType(instruction.arguments().get<double>())));
|
||||||
return;
|
return;
|
||||||
case Instructions::block.value(): {
|
case Instructions::block.value(): {
|
||||||
size_t arity = 0;
|
size_t arity = 0;
|
||||||
size_t parameter_count = 0;
|
size_t param_arity = 0;
|
||||||
auto& args = instruction.arguments().get<Instruction::StructuredInstructionArgs>();
|
auto& args = instruction.arguments().get<Instruction::StructuredInstructionArgs>();
|
||||||
switch (args.block_type.kind()) {
|
switch (args.block_type.kind()) {
|
||||||
case BlockType::Empty:
|
case BlockType::Empty:
|
||||||
|
@ -471,11 +463,11 @@ void BytecodeInterpreter::interpret(Configuration& configuration, InstructionPoi
|
||||||
case BlockType::Index: {
|
case BlockType::Index: {
|
||||||
auto& type = configuration.frame().module().types()[args.block_type.type_index().value()];
|
auto& type = configuration.frame().module().types()[args.block_type.type_index().value()];
|
||||||
arity = type.results().size();
|
arity = type.results().size();
|
||||||
parameter_count = type.parameters().size();
|
param_arity = type.parameters().size();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
configuration.stack().entries().insert(configuration.stack().size() - parameter_count, Label(arity, args.end_ip));
|
configuration.label_stack().append(Label(arity, args.end_ip, configuration.value_stack().size() - param_arity));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case Instructions::loop.value(): {
|
case Instructions::loop.value(): {
|
||||||
|
@ -485,12 +477,12 @@ void BytecodeInterpreter::interpret(Configuration& configuration, InstructionPoi
|
||||||
auto& type = configuration.frame().module().types()[args.block_type.type_index().value()];
|
auto& type = configuration.frame().module().types()[args.block_type.type_index().value()];
|
||||||
arity = type.parameters().size();
|
arity = type.parameters().size();
|
||||||
}
|
}
|
||||||
configuration.stack().entries().insert(configuration.stack().size() - arity, Label(arity, ip.value() + 1));
|
configuration.label_stack().append(Label(arity, ip.value() + 1, configuration.value_stack().size() - arity));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case Instructions::if_.value(): {
|
case Instructions::if_.value(): {
|
||||||
size_t arity = 0;
|
size_t arity = 0;
|
||||||
size_t parameter_count = 0;
|
size_t param_arity = 0;
|
||||||
auto& args = instruction.arguments().get<Instruction::StructuredInstructionArgs>();
|
auto& args = instruction.arguments().get<Instruction::StructuredInstructionArgs>();
|
||||||
switch (args.block_type.kind()) {
|
switch (args.block_type.kind()) {
|
||||||
case BlockType::Empty:
|
case BlockType::Empty:
|
||||||
|
@ -501,78 +493,55 @@ void BytecodeInterpreter::interpret(Configuration& configuration, InstructionPoi
|
||||||
case BlockType::Index: {
|
case BlockType::Index: {
|
||||||
auto& type = configuration.frame().module().types()[args.block_type.type_index().value()];
|
auto& type = configuration.frame().module().types()[args.block_type.type_index().value()];
|
||||||
arity = type.results().size();
|
arity = type.results().size();
|
||||||
parameter_count = type.parameters().size();
|
param_arity = type.parameters().size();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto entry = configuration.stack().pop();
|
auto value = configuration.value_stack().take_last().to<i32>();
|
||||||
auto value = entry.get<Value>().to<i32>();
|
auto end_label = Label(arity, args.end_ip.value(), configuration.value_stack().size() - param_arity);
|
||||||
auto end_label = Label(arity, args.end_ip.value());
|
if (value == 0) {
|
||||||
if (value.value() == 0) {
|
|
||||||
if (args.else_ip.has_value()) {
|
if (args.else_ip.has_value()) {
|
||||||
configuration.ip() = args.else_ip.value();
|
configuration.ip() = args.else_ip.value();
|
||||||
configuration.stack().entries().insert(configuration.stack().size() - parameter_count, end_label);
|
configuration.label_stack().append(end_label);
|
||||||
} else {
|
} else {
|
||||||
configuration.ip() = args.end_ip.value() + 1;
|
configuration.ip() = args.end_ip.value() + 1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
configuration.stack().entries().insert(configuration.stack().size() - parameter_count, end_label);
|
configuration.label_stack().append(end_label);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case Instructions::structured_end.value():
|
case Instructions::structured_end.value():
|
||||||
|
configuration.label_stack().take_last();
|
||||||
|
return;
|
||||||
case Instructions::structured_else.value(): {
|
case Instructions::structured_else.value(): {
|
||||||
auto index = configuration.nth_label_index(0);
|
auto label = configuration.label_stack().take_last();
|
||||||
auto label = configuration.stack().entries()[*index].get<Label>();
|
|
||||||
configuration.stack().entries().remove(*index, 1);
|
|
||||||
|
|
||||||
if (instruction.opcode() == Instructions::structured_end)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Jump to the end label
|
// Jump to the end label
|
||||||
configuration.ip() = label.continuation();
|
configuration.ip() = label.continuation();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case Instructions::return_.value(): {
|
case Instructions::return_.value(): {
|
||||||
auto& frame = configuration.frame();
|
while (configuration.label_stack().size() - 1 != configuration.frame().label_index())
|
||||||
Checked checked_index { configuration.stack().size() };
|
configuration.label_stack().take_last();
|
||||||
checked_index -= frame.arity();
|
|
||||||
VERIFY(!checked_index.has_overflow());
|
|
||||||
|
|
||||||
auto index = checked_index.value();
|
|
||||||
size_t i = 1;
|
|
||||||
for (; i <= index; ++i) {
|
|
||||||
auto& entry = configuration.stack().entries()[index - i];
|
|
||||||
if (entry.has<Label>()) {
|
|
||||||
if (configuration.stack().entries()[index - i - 1].has<Frame>())
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
configuration.stack().entries().remove(index - i + 1, i - 1);
|
|
||||||
|
|
||||||
// Jump past the call/indirect instruction
|
|
||||||
configuration.ip() = configuration.frame().expression().instructions().size();
|
configuration.ip() = configuration.frame().expression().instructions().size();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case Instructions::br.value():
|
case Instructions::br.value():
|
||||||
return branch_to_label(configuration, instruction.arguments().get<LabelIndex>());
|
return branch_to_label(configuration, instruction.arguments().get<LabelIndex>());
|
||||||
case Instructions::br_if.value(): {
|
case Instructions::br_if.value(): {
|
||||||
auto entry = configuration.stack().pop();
|
auto cond = configuration.value_stack().take_last().to<i32>();
|
||||||
if (entry.get<Value>().to<i32>().value_or(0) == 0)
|
if (*cond == 0)
|
||||||
return;
|
return;
|
||||||
return branch_to_label(configuration, instruction.arguments().get<LabelIndex>());
|
return branch_to_label(configuration, instruction.arguments().get<LabelIndex>());
|
||||||
}
|
}
|
||||||
case Instructions::br_table.value(): {
|
case Instructions::br_table.value(): {
|
||||||
auto& arguments = instruction.arguments().get<Instruction::TableBranchArgs>();
|
auto& arguments = instruction.arguments().get<Instruction::TableBranchArgs>();
|
||||||
auto entry = configuration.stack().pop();
|
auto i = *configuration.value_stack().take_last().to<u32>();
|
||||||
auto maybe_i = entry.get<Value>().to<i32>();
|
|
||||||
if (0 <= *maybe_i) {
|
if (i >= arguments.labels.size()) {
|
||||||
size_t i = *maybe_i;
|
return branch_to_label(configuration, arguments.default_);
|
||||||
if (i < arguments.labels.size())
|
|
||||||
return branch_to_label(configuration, arguments.labels[i]);
|
|
||||||
}
|
}
|
||||||
return branch_to_label(configuration, arguments.default_);
|
return branch_to_label(configuration, arguments.labels[i]);
|
||||||
}
|
}
|
||||||
case Instructions::call.value(): {
|
case Instructions::call.value(): {
|
||||||
auto index = instruction.arguments().get<FunctionIndex>();
|
auto index = instruction.arguments().get<FunctionIndex>();
|
||||||
|
@ -585,8 +554,7 @@ void BytecodeInterpreter::interpret(Configuration& configuration, InstructionPoi
|
||||||
auto& args = instruction.arguments().get<Instruction::IndirectCallArgs>();
|
auto& args = instruction.arguments().get<Instruction::IndirectCallArgs>();
|
||||||
auto table_address = configuration.frame().module().tables()[args.table.value()];
|
auto table_address = configuration.frame().module().tables()[args.table.value()];
|
||||||
auto table_instance = configuration.store().get(table_address);
|
auto table_instance = configuration.store().get(table_address);
|
||||||
auto entry = configuration.stack().pop();
|
auto index = configuration.value_stack().take_last().to<i32>();
|
||||||
auto index = entry.get<Value>().to<i32>();
|
|
||||||
TRAP_IF_NOT(index.value() >= 0);
|
TRAP_IF_NOT(index.value() >= 0);
|
||||||
TRAP_IF_NOT(static_cast<size_t>(index.value()) < table_instance->elements().size());
|
TRAP_IF_NOT(static_cast<size_t>(index.value()) < table_instance->elements().size());
|
||||||
auto element = table_instance->elements()[index.value()];
|
auto element = table_instance->elements()[index.value()];
|
||||||
|
@ -643,11 +611,10 @@ void BytecodeInterpreter::interpret(Configuration& configuration, InstructionPoi
|
||||||
case Instructions::i64_store32.value():
|
case Instructions::i64_store32.value():
|
||||||
return pop_and_store<i64, i32>(configuration, instruction);
|
return pop_and_store<i64, i32>(configuration, instruction);
|
||||||
case Instructions::local_tee.value(): {
|
case Instructions::local_tee.value(): {
|
||||||
auto& entry = configuration.stack().peek();
|
auto value = configuration.value_stack().last();
|
||||||
auto value = entry.get<Value>();
|
|
||||||
auto local_index = instruction.arguments().get<LocalIndex>();
|
auto local_index = instruction.arguments().get<LocalIndex>();
|
||||||
dbgln_if(WASM_TRACE_DEBUG, "stack:peek -> locals({})", local_index.value());
|
dbgln_if(WASM_TRACE_DEBUG, "stack:peek -> locals({})", local_index.value());
|
||||||
configuration.frame().locals()[local_index.value()] = move(value);
|
configuration.frame().locals()[local_index.value()] = value;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case Instructions::global_get.value(): {
|
case Instructions::global_get.value(): {
|
||||||
|
@ -658,17 +625,16 @@ void BytecodeInterpreter::interpret(Configuration& configuration, InstructionPoi
|
||||||
auto address = configuration.frame().module().globals()[global_index.value()];
|
auto address = configuration.frame().module().globals()[global_index.value()];
|
||||||
dbgln_if(WASM_TRACE_DEBUG, "global({}) -> stack", address.value());
|
dbgln_if(WASM_TRACE_DEBUG, "global({}) -> stack", address.value());
|
||||||
auto global = configuration.store().get(address);
|
auto global = configuration.store().get(address);
|
||||||
configuration.stack().push(Value(global->value()));
|
configuration.value_stack().append(global->value());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case Instructions::global_set.value(): {
|
case Instructions::global_set.value(): {
|
||||||
auto global_index = instruction.arguments().get<GlobalIndex>();
|
auto global_index = instruction.arguments().get<GlobalIndex>();
|
||||||
auto address = configuration.frame().module().globals()[global_index.value()];
|
auto address = configuration.frame().module().globals()[global_index.value()];
|
||||||
auto entry = configuration.stack().pop();
|
auto value = configuration.value_stack().take_last();
|
||||||
auto value = entry.get<Value>();
|
|
||||||
dbgln_if(WASM_TRACE_DEBUG, "stack -> global({})", address.value());
|
dbgln_if(WASM_TRACE_DEBUG, "stack -> global({})", address.value());
|
||||||
auto global = configuration.store().get(address);
|
auto global = configuration.store().get(address);
|
||||||
global->set_value(move(value));
|
global->set_value(value);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case Instructions::memory_size.value(): {
|
case Instructions::memory_size.value(): {
|
||||||
|
@ -677,7 +643,7 @@ void BytecodeInterpreter::interpret(Configuration& configuration, InstructionPoi
|
||||||
auto instance = configuration.store().get(address);
|
auto instance = configuration.store().get(address);
|
||||||
auto pages = instance->size() / Constants::page_size;
|
auto pages = instance->size() / Constants::page_size;
|
||||||
dbgln_if(WASM_TRACE_DEBUG, "memory.size -> stack({})", pages);
|
dbgln_if(WASM_TRACE_DEBUG, "memory.size -> stack({})", pages);
|
||||||
configuration.stack().push(Value((i32)pages));
|
configuration.value_stack().append(Value((i32)pages));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case Instructions::memory_grow.value(): {
|
case Instructions::memory_grow.value(): {
|
||||||
|
@ -685,13 +651,13 @@ void BytecodeInterpreter::interpret(Configuration& configuration, InstructionPoi
|
||||||
auto address = configuration.frame().module().memories()[args.memory_index.value()];
|
auto address = configuration.frame().module().memories()[args.memory_index.value()];
|
||||||
auto instance = configuration.store().get(address);
|
auto instance = configuration.store().get(address);
|
||||||
i32 old_pages = instance->size() / Constants::page_size;
|
i32 old_pages = instance->size() / Constants::page_size;
|
||||||
auto& entry = configuration.stack().peek();
|
auto& entry = configuration.value_stack().last();
|
||||||
auto new_pages = entry.get<Value>().to<i32>();
|
auto new_pages = entry.to<i32>();
|
||||||
dbgln_if(WASM_TRACE_DEBUG, "memory.grow({}), previously {} pages...", *new_pages, old_pages);
|
dbgln_if(WASM_TRACE_DEBUG, "memory.grow({}), previously {} pages...", *new_pages, old_pages);
|
||||||
if (instance->grow(new_pages.value() * Constants::page_size))
|
if (instance->grow(new_pages.value() * Constants::page_size))
|
||||||
configuration.stack().peek() = Value((i32)old_pages);
|
entry = Value((i32)old_pages);
|
||||||
else
|
else
|
||||||
configuration.stack().peek() = Value((i32)-1);
|
entry = Value((i32)-1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// https://webassembly.github.io/spec/core/bikeshed/#exec-memory-fill
|
// https://webassembly.github.io/spec/core/bikeshed/#exec-memory-fill
|
||||||
|
@ -699,9 +665,9 @@ void BytecodeInterpreter::interpret(Configuration& configuration, InstructionPoi
|
||||||
auto& args = instruction.arguments().get<Instruction::MemoryIndexArgument>();
|
auto& args = instruction.arguments().get<Instruction::MemoryIndexArgument>();
|
||||||
auto address = configuration.frame().module().memories()[args.memory_index.value()];
|
auto address = configuration.frame().module().memories()[args.memory_index.value()];
|
||||||
auto instance = configuration.store().get(address);
|
auto instance = configuration.store().get(address);
|
||||||
auto count = configuration.stack().pop().get<Value>().to<u32>().value();
|
auto count = configuration.value_stack().take_last().to<u32>().value();
|
||||||
u8 value = static_cast<u8>(configuration.stack().pop().get<Value>().to<u32>().value());
|
u8 value = static_cast<u8>(configuration.value_stack().take_last().to<u32>().value());
|
||||||
auto destination_offset = configuration.stack().pop().get<Value>().to<u32>().value();
|
auto destination_offset = configuration.value_stack().take_last().to<u32>().value();
|
||||||
|
|
||||||
TRAP_IF_NOT(static_cast<size_t>(destination_offset + count) <= instance->data().size());
|
TRAP_IF_NOT(static_cast<size_t>(destination_offset + count) <= instance->data().size());
|
||||||
|
|
||||||
|
@ -721,9 +687,9 @@ void BytecodeInterpreter::interpret(Configuration& configuration, InstructionPoi
|
||||||
auto source_instance = configuration.store().get(source_address);
|
auto source_instance = configuration.store().get(source_address);
|
||||||
auto destination_instance = configuration.store().get(destination_address);
|
auto destination_instance = configuration.store().get(destination_address);
|
||||||
|
|
||||||
auto count = configuration.stack().pop().get<Value>().to<i32>().value();
|
auto count = configuration.value_stack().take_last().to<i32>().value();
|
||||||
auto source_offset = configuration.stack().pop().get<Value>().to<i32>().value();
|
auto source_offset = configuration.value_stack().take_last().to<i32>().value();
|
||||||
auto destination_offset = configuration.stack().pop().get<Value>().to<i32>().value();
|
auto destination_offset = configuration.value_stack().take_last().to<i32>().value();
|
||||||
|
|
||||||
Checked<size_t> source_position = source_offset;
|
Checked<size_t> source_position = source_offset;
|
||||||
source_position.saturating_add(count);
|
source_position.saturating_add(count);
|
||||||
|
@ -757,9 +723,9 @@ void BytecodeInterpreter::interpret(Configuration& configuration, InstructionPoi
|
||||||
auto& data = *configuration.store().get(data_address);
|
auto& data = *configuration.store().get(data_address);
|
||||||
auto memory_address = configuration.frame().module().memories()[args.memory_index.value()];
|
auto memory_address = configuration.frame().module().memories()[args.memory_index.value()];
|
||||||
auto memory = configuration.store().get(memory_address);
|
auto memory = configuration.store().get(memory_address);
|
||||||
auto count = *configuration.stack().pop().get<Value>().to<u32>();
|
auto count = *configuration.value_stack().take_last().to<u32>();
|
||||||
auto source_offset = *configuration.stack().pop().get<Value>().to<u32>();
|
auto source_offset = *configuration.value_stack().take_last().to<u32>();
|
||||||
auto destination_offset = *configuration.stack().pop().get<Value>().to<u32>();
|
auto destination_offset = *configuration.value_stack().take_last().to<u32>();
|
||||||
|
|
||||||
Checked<size_t> source_position = source_offset;
|
Checked<size_t> source_position = source_offset;
|
||||||
source_position.saturating_add(count);
|
source_position.saturating_add(count);
|
||||||
|
@ -798,9 +764,9 @@ void BytecodeInterpreter::interpret(Configuration& configuration, InstructionPoi
|
||||||
auto table = configuration.store().get(table_address);
|
auto table = configuration.store().get(table_address);
|
||||||
auto element_address = configuration.frame().module().elements()[args.element_index.value()];
|
auto element_address = configuration.frame().module().elements()[args.element_index.value()];
|
||||||
auto element = configuration.store().get(element_address);
|
auto element = configuration.store().get(element_address);
|
||||||
auto count = *configuration.stack().pop().get<Value>().to<u32>();
|
auto count = *configuration.value_stack().take_last().to<u32>();
|
||||||
auto source_offset = *configuration.stack().pop().get<Value>().to<u32>();
|
auto source_offset = *configuration.value_stack().take_last().to<u32>();
|
||||||
auto destination_offset = *configuration.stack().pop().get<Value>().to<u32>();
|
auto destination_offset = *configuration.value_stack().take_last().to<u32>();
|
||||||
|
|
||||||
Checked<u32> checked_source_offset = source_offset;
|
Checked<u32> checked_source_offset = source_offset;
|
||||||
Checked<u32> checked_destination_offset = destination_offset;
|
Checked<u32> checked_destination_offset = destination_offset;
|
||||||
|
@ -820,9 +786,9 @@ void BytecodeInterpreter::interpret(Configuration& configuration, InstructionPoi
|
||||||
auto source_instance = configuration.store().get(source_address);
|
auto source_instance = configuration.store().get(source_address);
|
||||||
auto destination_instance = configuration.store().get(destination_address);
|
auto destination_instance = configuration.store().get(destination_address);
|
||||||
|
|
||||||
auto count = configuration.stack().pop().get<Value>().to<u32>().value();
|
auto count = configuration.value_stack().take_last().to<u32>().value();
|
||||||
auto source_offset = configuration.stack().pop().get<Value>().to<u32>().value();
|
auto source_offset = configuration.value_stack().take_last().to<u32>().value();
|
||||||
auto destination_offset = configuration.stack().pop().get<Value>().to<u32>().value();
|
auto destination_offset = configuration.value_stack().take_last().to<u32>().value();
|
||||||
|
|
||||||
Checked<size_t> source_position = source_offset;
|
Checked<size_t> source_position = source_offset;
|
||||||
source_position.saturating_add(count);
|
source_position.saturating_add(count);
|
||||||
|
@ -852,9 +818,9 @@ void BytecodeInterpreter::interpret(Configuration& configuration, InstructionPoi
|
||||||
auto table_index = instruction.arguments().get<TableIndex>();
|
auto table_index = instruction.arguments().get<TableIndex>();
|
||||||
auto address = configuration.frame().module().tables()[table_index.value()];
|
auto address = configuration.frame().module().tables()[table_index.value()];
|
||||||
auto table = configuration.store().get(address);
|
auto table = configuration.store().get(address);
|
||||||
auto count = *configuration.stack().pop().get<Value>().to<u32>();
|
auto count = *configuration.value_stack().take_last().to<u32>();
|
||||||
auto value = *configuration.stack().pop().get<Value>().to<Reference>();
|
auto value = *configuration.value_stack().take_last().to<Reference>();
|
||||||
auto start = *configuration.stack().pop().get<Value>().to<u32>();
|
auto start = *configuration.value_stack().take_last().to<u32>();
|
||||||
|
|
||||||
Checked<u32> checked_offset = start;
|
Checked<u32> checked_offset = start;
|
||||||
checked_offset += count;
|
checked_offset += count;
|
||||||
|
@ -865,8 +831,8 @@ void BytecodeInterpreter::interpret(Configuration& configuration, InstructionPoi
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case Instructions::table_set.value(): {
|
case Instructions::table_set.value(): {
|
||||||
auto ref = *configuration.stack().pop().get<Value>().to<Reference>();
|
auto ref = *configuration.value_stack().take_last().to<Reference>();
|
||||||
auto index = (size_t)(*configuration.stack().pop().get<Value>().to<i32>());
|
auto index = (size_t)(*configuration.value_stack().take_last().to<i32>());
|
||||||
auto table_index = instruction.arguments().get<TableIndex>();
|
auto table_index = instruction.arguments().get<TableIndex>();
|
||||||
auto address = configuration.frame().module().tables()[table_index.value()];
|
auto address = configuration.frame().module().tables()[table_index.value()];
|
||||||
auto table = configuration.store().get(address);
|
auto table = configuration.store().get(address);
|
||||||
|
@ -875,27 +841,27 @@ void BytecodeInterpreter::interpret(Configuration& configuration, InstructionPoi
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case Instructions::table_get.value(): {
|
case Instructions::table_get.value(): {
|
||||||
auto index = (size_t)(*configuration.stack().pop().get<Value>().to<i32>());
|
auto index = (size_t)(*configuration.value_stack().take_last().to<i32>());
|
||||||
auto table_index = instruction.arguments().get<TableIndex>();
|
auto table_index = instruction.arguments().get<TableIndex>();
|
||||||
auto address = configuration.frame().module().tables()[table_index.value()];
|
auto address = configuration.frame().module().tables()[table_index.value()];
|
||||||
auto table = configuration.store().get(address);
|
auto table = configuration.store().get(address);
|
||||||
TRAP_IF_NOT(index < table->elements().size());
|
TRAP_IF_NOT(index < table->elements().size());
|
||||||
auto ref = table->elements()[index];
|
auto ref = table->elements()[index];
|
||||||
configuration.stack().push(Value(ref));
|
configuration.value_stack().append(Value(ref));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case Instructions::table_grow.value(): {
|
case Instructions::table_grow.value(): {
|
||||||
auto size = *configuration.stack().pop().get<Value>().to<u32>();
|
auto size = *configuration.value_stack().take_last().to<u32>();
|
||||||
auto fill_value = *configuration.stack().pop().get<Value>().to<Reference>();
|
auto fill_value = *configuration.value_stack().take_last().to<Reference>();
|
||||||
auto table_index = instruction.arguments().get<TableIndex>();
|
auto table_index = instruction.arguments().get<TableIndex>();
|
||||||
auto address = configuration.frame().module().tables()[table_index.value()];
|
auto address = configuration.frame().module().tables()[table_index.value()];
|
||||||
auto table = configuration.store().get(address);
|
auto table = configuration.store().get(address);
|
||||||
auto previous_size = table->elements().size();
|
auto previous_size = table->elements().size();
|
||||||
auto did_grow = table->grow(size, fill_value);
|
auto did_grow = table->grow(size, fill_value);
|
||||||
if (!did_grow) {
|
if (!did_grow) {
|
||||||
configuration.stack().push(Value((i32)-1));
|
configuration.value_stack().append(Value((i32)-1));
|
||||||
} else {
|
} else {
|
||||||
configuration.stack().push(Value((i32)previous_size));
|
configuration.value_stack().append(Value((i32)previous_size));
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -903,42 +869,37 @@ void BytecodeInterpreter::interpret(Configuration& configuration, InstructionPoi
|
||||||
auto table_index = instruction.arguments().get<TableIndex>();
|
auto table_index = instruction.arguments().get<TableIndex>();
|
||||||
auto address = configuration.frame().module().tables()[table_index.value()];
|
auto address = configuration.frame().module().tables()[table_index.value()];
|
||||||
auto table = configuration.store().get(address);
|
auto table = configuration.store().get(address);
|
||||||
configuration.stack().push(Value((i32)table->elements().size()));
|
configuration.value_stack().append(Value((i32)table->elements().size()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case Instructions::ref_null.value(): {
|
case Instructions::ref_null.value(): {
|
||||||
auto type = instruction.arguments().get<ValueType>();
|
auto type = instruction.arguments().get<ValueType>();
|
||||||
configuration.stack().push(Value(Reference(Reference::Null { type })));
|
configuration.value_stack().append(Value(Reference(Reference::Null { type })));
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
case Instructions::ref_func.value(): {
|
case Instructions::ref_func.value(): {
|
||||||
auto index = instruction.arguments().get<FunctionIndex>().value();
|
auto index = instruction.arguments().get<FunctionIndex>().value();
|
||||||
auto& functions = configuration.frame().module().functions();
|
auto& functions = configuration.frame().module().functions();
|
||||||
auto address = functions[index];
|
auto address = functions[index];
|
||||||
configuration.stack().push(Value(ValueType(ValueType::FunctionReference), address.value()));
|
configuration.value_stack().append(Value(ValueType(ValueType::FunctionReference), address.value()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case Instructions::ref_is_null.value(): {
|
case Instructions::ref_is_null.value(): {
|
||||||
auto top = configuration.stack().peek().get_pointer<Value>();
|
auto ref = configuration.value_stack().take_last().to<Reference::Null>();
|
||||||
TRAP_IF_NOT(top->type().is_reference());
|
configuration.value_stack().append(Value(static_cast<i32>(ref.has_value() ? 1 : 0)));
|
||||||
auto is_null = top->to<Reference::Null>().has_value();
|
|
||||||
configuration.stack().peek() = Value(ValueType(ValueType::I32), static_cast<u64>(is_null ? 1 : 0));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case Instructions::drop.value():
|
case Instructions::drop.value():
|
||||||
configuration.stack().pop();
|
configuration.value_stack().take_last();
|
||||||
return;
|
return;
|
||||||
case Instructions::select.value():
|
case Instructions::select.value():
|
||||||
case Instructions::select_typed.value(): {
|
case Instructions::select_typed.value(): {
|
||||||
// Note: The type seems to only be used for validation.
|
// Note: The type seems to only be used for validation.
|
||||||
auto entry = configuration.stack().pop();
|
auto value = configuration.value_stack().take_last().to<i32>();
|
||||||
auto value = entry.get<Value>().to<i32>();
|
|
||||||
dbgln_if(WASM_TRACE_DEBUG, "select({})", value.value());
|
dbgln_if(WASM_TRACE_DEBUG, "select({})", value.value());
|
||||||
auto rhs_entry = configuration.stack().pop();
|
auto rhs = configuration.value_stack().take_last();
|
||||||
auto& lhs_entry = configuration.stack().peek();
|
auto& lhs = configuration.value_stack().last();
|
||||||
auto rhs = move(rhs_entry.get<Value>());
|
lhs = value.value() != 0 ? lhs : rhs;
|
||||||
auto lhs = move(lhs_entry.get<Value>());
|
|
||||||
configuration.stack().peek() = value.value() != 0 ? move(lhs) : move(rhs);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case Instructions::i32_eqz.value():
|
case Instructions::i32_eqz.value():
|
||||||
|
@ -1214,7 +1175,7 @@ void BytecodeInterpreter::interpret(Configuration& configuration, InstructionPoi
|
||||||
case Instructions::i64_trunc_sat_f64_u.value():
|
case Instructions::i64_trunc_sat_f64_u.value():
|
||||||
return unary_operation<double, i64, Operators::SaturatingTruncate<u64>>(configuration);
|
return unary_operation<double, i64, Operators::SaturatingTruncate<u64>>(configuration);
|
||||||
case Instructions::v128_const.value():
|
case Instructions::v128_const.value():
|
||||||
configuration.stack().push(Value(instruction.arguments().get<u128>()));
|
configuration.value_stack().append(Value(instruction.arguments().get<u128>()));
|
||||||
return;
|
return;
|
||||||
case Instructions::v128_load.value():
|
case Instructions::v128_load.value():
|
||||||
return load_and_push<u128, u128>(configuration, instruction);
|
return load_and_push<u128, u128>(configuration, instruction);
|
||||||
|
@ -1261,7 +1222,7 @@ void BytecodeInterpreter::interpret(Configuration& configuration, InstructionPoi
|
||||||
result[i] = a[arg.lanes[i]];
|
result[i] = a[arg.lanes[i]];
|
||||||
else
|
else
|
||||||
result[i] = b[arg.lanes[i] - 16];
|
result[i] = b[arg.lanes[i] - 16];
|
||||||
configuration.stack().push(Value(bit_cast<u128>(result)));
|
configuration.value_stack().append(Value(bit_cast<u128>(result)));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case Instructions::v128_store.value():
|
case Instructions::v128_store.value():
|
||||||
|
@ -1635,16 +1596,16 @@ void BytecodeInterpreter::interpret(Configuration& configuration, InstructionPoi
|
||||||
case Instructions::v128_andnot.value():
|
case Instructions::v128_andnot.value():
|
||||||
return binary_numeric_operation<u128, u128, Operators::BitAndNot>(configuration);
|
return binary_numeric_operation<u128, u128, Operators::BitAndNot>(configuration);
|
||||||
case Instructions::v128_bitselect.value(): {
|
case Instructions::v128_bitselect.value(): {
|
||||||
auto mask = *configuration.stack().pop().get<Value>().to<u128>();
|
auto mask = *configuration.value_stack().take_last().to<u128>();
|
||||||
auto false_vector = *configuration.stack().pop().get<Value>().to<u128>();
|
auto false_vector = *configuration.value_stack().take_last().to<u128>();
|
||||||
auto true_vector = *configuration.stack().pop().get<Value>().to<u128>();
|
auto true_vector = *configuration.value_stack().take_last().to<u128>();
|
||||||
u128 result = (true_vector & mask) | (false_vector & ~mask);
|
u128 result = (true_vector & mask) | (false_vector & ~mask);
|
||||||
configuration.stack().push(Value(result));
|
configuration.value_stack().append(Value(result));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case Instructions::v128_any_true.value(): {
|
case Instructions::v128_any_true.value(): {
|
||||||
auto vector = *configuration.stack().pop().get<Value>().to<u128>();
|
auto vector = *configuration.value_stack().take_last().to<u128>();
|
||||||
configuration.stack().push(Value(static_cast<i32>(vector != 0)));
|
configuration.value_stack().append(Value(static_cast<i32>(vector != 0)));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case Instructions::v128_load8_lane.value():
|
case Instructions::v128_load8_lane.value():
|
||||||
|
|
|
@ -11,30 +11,11 @@
|
||||||
|
|
||||||
namespace Wasm {
|
namespace Wasm {
|
||||||
|
|
||||||
Optional<size_t> Configuration::nth_label_index(size_t i)
|
|
||||||
{
|
|
||||||
for (size_t index = m_stack.size(); index > 0; --index) {
|
|
||||||
auto& entry = m_stack.entries()[index - 1];
|
|
||||||
if (entry.has<Label>()) {
|
|
||||||
if (i == 0)
|
|
||||||
return index - 1;
|
|
||||||
--i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
void Configuration::unwind(Badge<CallFrameHandle>, CallFrameHandle const& frame_handle)
|
void Configuration::unwind(Badge<CallFrameHandle>, CallFrameHandle const& frame_handle)
|
||||||
{
|
{
|
||||||
if (m_stack.size() == frame_handle.stack_size && frame_handle.frame_index == m_current_frame_index)
|
auto frame = m_frame_stack.take_last();
|
||||||
return;
|
|
||||||
|
|
||||||
VERIFY(m_stack.size() > frame_handle.stack_size);
|
|
||||||
m_stack.entries().remove(frame_handle.stack_size, m_stack.size() - frame_handle.stack_size);
|
|
||||||
m_current_frame_index = frame_handle.frame_index;
|
|
||||||
m_depth--;
|
m_depth--;
|
||||||
m_ip = frame_handle.ip;
|
m_ip = frame_handle.ip;
|
||||||
VERIFY(m_stack.size() == frame_handle.stack_size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result Configuration::call(Interpreter& interpreter, FunctionAddress address, Vector<Value> arguments)
|
Result Configuration::call(Interpreter& interpreter, FunctionAddress address, Vector<Value> arguments)
|
||||||
|
@ -71,17 +52,12 @@ Result Configuration::execute(Interpreter& interpreter)
|
||||||
if (interpreter.did_trap())
|
if (interpreter.did_trap())
|
||||||
return Trap { interpreter.trap_reason() };
|
return Trap { interpreter.trap_reason() };
|
||||||
|
|
||||||
if (stack().size() <= frame().arity() + 1)
|
|
||||||
return Trap { "Not enough values to return from call" };
|
|
||||||
|
|
||||||
Vector<Value> results;
|
Vector<Value> results;
|
||||||
results.ensure_capacity(frame().arity());
|
results.ensure_capacity(frame().arity());
|
||||||
for (size_t i = 0; i < frame().arity(); ++i)
|
for (size_t i = 0; i < frame().arity(); ++i)
|
||||||
results.append(move(stack().pop().get<Value>()));
|
results.unchecked_append(value_stack().take_last());
|
||||||
auto label = stack().pop();
|
|
||||||
// ASSERT: label == current frame
|
label_stack().take_last();
|
||||||
if (!label.has<Label>())
|
|
||||||
return Trap { "Invalid stack configuration" };
|
|
||||||
return Result { move(results) };
|
return Result { move(results) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,20 +70,8 @@ void Configuration::dump_stack()
|
||||||
memory_stream.read_until_filled(buffer).release_value_but_fixme_should_propagate_errors();
|
memory_stream.read_until_filled(buffer).release_value_but_fixme_should_propagate_errors();
|
||||||
dbgln(format.view(), StringView(buffer).trim_whitespace());
|
dbgln(format.view(), StringView(buffer).trim_whitespace());
|
||||||
};
|
};
|
||||||
for (auto const& entry : stack().entries()) {
|
for (auto const& value : value_stack()) {
|
||||||
entry.visit(
|
print_value(" {}", value);
|
||||||
[&](Value const& v) {
|
|
||||||
print_value(" {}", v);
|
|
||||||
},
|
|
||||||
[&](Frame const& f) {
|
|
||||||
dbgln(" frame({})", f.arity());
|
|
||||||
for (auto& local : f.locals()) {
|
|
||||||
print_value(" {}", local);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[](Label const& l) {
|
|
||||||
dbgln(" label({}) -> {}", l.arity(), l.continuation());
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,37 +17,29 @@ public:
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
Optional<Label> nth_label(size_t label)
|
void set_frame(Frame frame)
|
||||||
{
|
{
|
||||||
auto index = nth_label_index(label);
|
Label label(frame.arity(), frame.expression().instructions().size(), m_value_stack.size());
|
||||||
if (index.has_value())
|
frame.label_index() = m_label_stack.size();
|
||||||
return m_stack.entries()[index.value()].get<Label>();
|
m_frame_stack.append(move(frame));
|
||||||
return {};
|
m_label_stack.append(label);
|
||||||
}
|
}
|
||||||
Optional<size_t> nth_label_index(size_t);
|
ALWAYS_INLINE auto& frame() const { return m_frame_stack.last(); }
|
||||||
void set_frame(Frame&& frame)
|
ALWAYS_INLINE auto& frame() { return m_frame_stack.last(); }
|
||||||
{
|
|
||||||
m_current_frame_index = m_stack.size();
|
|
||||||
Label label(frame.arity(), frame.expression().instructions().size());
|
|
||||||
m_stack.push(move(frame));
|
|
||||||
m_stack.push(label);
|
|
||||||
}
|
|
||||||
ALWAYS_INLINE auto& frame() const { return m_stack.entries()[m_current_frame_index].get<Frame>(); }
|
|
||||||
ALWAYS_INLINE auto& frame() { return m_stack.entries()[m_current_frame_index].get<Frame>(); }
|
|
||||||
ALWAYS_INLINE auto& ip() const { return m_ip; }
|
ALWAYS_INLINE auto& ip() const { return m_ip; }
|
||||||
ALWAYS_INLINE auto& ip() { return m_ip; }
|
ALWAYS_INLINE auto& ip() { return m_ip; }
|
||||||
ALWAYS_INLINE auto& depth() const { return m_depth; }
|
ALWAYS_INLINE auto& depth() const { return m_depth; }
|
||||||
ALWAYS_INLINE auto& depth() { return m_depth; }
|
ALWAYS_INLINE auto& depth() { return m_depth; }
|
||||||
ALWAYS_INLINE auto& stack() const { return m_stack; }
|
ALWAYS_INLINE auto& value_stack() const { return m_value_stack; }
|
||||||
ALWAYS_INLINE auto& stack() { return m_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() const { return m_store; }
|
||||||
ALWAYS_INLINE auto& store() { return m_store; }
|
ALWAYS_INLINE auto& store() { return m_store; }
|
||||||
|
|
||||||
struct CallFrameHandle {
|
struct CallFrameHandle {
|
||||||
explicit CallFrameHandle(Configuration& configuration)
|
explicit CallFrameHandle(Configuration& configuration)
|
||||||
: frame_index(configuration.m_current_frame_index)
|
: ip(configuration.ip())
|
||||||
, stack_size(configuration.m_stack.size())
|
|
||||||
, ip(configuration.ip())
|
|
||||||
, configuration(configuration)
|
, configuration(configuration)
|
||||||
{
|
{
|
||||||
configuration.depth()++;
|
configuration.depth()++;
|
||||||
|
@ -58,8 +50,6 @@ public:
|
||||||
configuration.unwind({}, *this);
|
configuration.unwind({}, *this);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t frame_index { 0 };
|
|
||||||
size_t stack_size { 0 };
|
|
||||||
InstructionPointer ip { 0 };
|
InstructionPointer ip { 0 };
|
||||||
Configuration& configuration;
|
Configuration& configuration;
|
||||||
};
|
};
|
||||||
|
@ -75,8 +65,9 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Store& m_store;
|
Store& m_store;
|
||||||
size_t m_current_frame_index { 0 };
|
Vector<Value> m_value_stack;
|
||||||
Stack m_stack;
|
Vector<Label> m_label_stack;
|
||||||
|
Vector<Frame> m_frame_stack;
|
||||||
size_t m_depth { 0 };
|
size_t m_depth { 0 };
|
||||||
InstructionPointer m_ip;
|
InstructionPointer m_ip;
|
||||||
bool m_should_limit_instruction_count { false };
|
bool m_should_limit_instruction_count { false };
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue