소스 검색

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.
Diego Frias 11 달 전
부모
커밋
a3b077c641

+ 7 - 20
Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.h

@@ -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 };
-};
-
-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;
+    size_t m_label_index { 0 };
 };
 };
 
 
 using InstantiationResult = AK::ErrorOr<NonnullOwnPtr<ModuleInstance>, InstantiationError>;
 using InstantiationResult = AK::ErrorOr<NonnullOwnPtr<ModuleInstance>, InstantiationError>;

+ 132 - 171
Userland/Libraries/LibWasm/AbstractMachine/BytecodeInterpreter.cpp

@@ -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());
-    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;
-    for (; !configuration.stack().is_empty();) {
-        auto& entry = configuration.stack().peek();
-        if (entry.has<Label>()) {
-            if (--drop_count == 0)
-                break;
-        }
-        configuration.stack().pop();
-    }
+    for (size_t i = 0; i < index.value(); ++i)
+        configuration.label_stack().take_last();
+    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());
+
+    while (configuration.value_stack().size() != label.stack_height())
+        configuration.value_stack().take_last();
 
 
     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 base = *entry.get<Value>().to<i32>();
+    auto& entry = configuration.value_stack().last();
+    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 base = *entry.get<Value>().to<i32>();
+    auto& entry = configuration.value_stack().last();
+    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 base = *configuration.stack().pop().get<Value>().to<u32>();
+    auto vector = *configuration.value_stack().take_last().to<u128>();
+    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 base = *entry.get<Value>().to<i32>();
+    auto& entry = configuration.value_stack().last();
+    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 value = static_cast<ReadT>(*entry.get<Value>().to<PopT>());
+    auto entry = configuration.value_stack().last();
+    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());
-    for (auto& entry : span) {
-        auto* call_argument = entry.get_pointer<Value>();
-        TRAP_IF_NOT(call_argument);
-        args.unchecked_append(move(*call_argument));
-    }
+    auto span = configuration.value_stack().span().slice_from_end(type->parameters().size());
+    for (auto& value : span)
+        args.unchecked_append(value);
 
 
-    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& lhs_entry = configuration.stack().peek();
-    auto lhs = lhs_entry.get<Value>().to<PopTypeLHS>();
+    auto rhs = configuration.value_stack().take_last().to<PopTypeRHS>();
+    auto& lhs_entry = configuration.value_stack().last();
+    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 value = entry.get<Value>().to<PopType>();
+    auto& entry = configuration.value_stack().last();
+    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 value = ConvertToRaw<StoreT> {}(*entry.get<Value>().to<PopT>());
+    auto entry = configuration.value_stack().take_last();
+    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 = base_entry.get<Value>().to<i32>();
+    auto base = configuration.value_stack().take_last().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();
-        configuration.frame().locals()[instruction.arguments().get<LocalIndex>().value()] = move(entry.get<Value>());
+        auto value = configuration.value_stack().take_last();
+        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 = entry.get<Value>().to<i32>();
-        auto end_label = Label(arity, args.end_ip.value());
-        if (value.value() == 0) {
+        auto value = configuration.value_stack().take_last().to<i32>();
+        auto end_label = Label(arity, args.end_ip.value(), configuration.value_stack().size() - param_arity);
+        if (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.stack().entries()[*index].get<Label>();
-        configuration.stack().entries().remove(*index, 1);
-
-        if (instruction.opcode() == Instructions::structured_end)
-            return;
-
+        auto label = configuration.label_stack().take_last();
         // 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();
-        Checked checked_index { configuration.stack().size() };
-        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
+        while (configuration.label_stack().size() - 1 != configuration.frame().label_index())
+            configuration.label_stack().take_last();
         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();
-        if (entry.get<Value>().to<i32>().value_or(0) == 0)
+        auto cond = configuration.value_stack().take_last().to<i32>();
+        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 maybe_i = entry.get<Value>().to<i32>();
-        if (0 <= *maybe_i) {
-            size_t i = *maybe_i;
-            if (i < arguments.labels.size())
-                return branch_to_label(configuration, arguments.labels[i]);
+        auto i = *configuration.value_stack().take_last().to<u32>();
+
+        if (i >= arguments.labels.size()) {
+            return branch_to_label(configuration, arguments.default_);
         }
         }
-        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 = entry.get<Value>().to<i32>();
+        auto index = configuration.value_stack().take_last().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 = entry.get<Value>();
+        auto value = configuration.value_stack().last();
         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 = entry.get<Value>();
+        auto value = configuration.value_stack().take_last();
         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 new_pages = entry.get<Value>().to<i32>();
+        auto& entry = configuration.value_stack().last();
+        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();
-        u8 value = static_cast<u8>(configuration.stack().pop().get<Value>().to<u32>().value());
-        auto destination_offset = configuration.stack().pop().get<Value>().to<u32>().value();
+        auto count = configuration.value_stack().take_last().to<u32>().value();
+        u8 value = static_cast<u8>(configuration.value_stack().take_last().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 source_offset = configuration.stack().pop().get<Value>().to<i32>().value();
-        auto destination_offset = configuration.stack().pop().get<Value>().to<i32>().value();
+        auto count = configuration.value_stack().take_last().to<i32>().value();
+        auto source_offset = configuration.value_stack().take_last().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 source_offset = *configuration.stack().pop().get<Value>().to<u32>();
-        auto destination_offset = *configuration.stack().pop().get<Value>().to<u32>();
+        auto count = *configuration.value_stack().take_last().to<u32>();
+        auto source_offset = *configuration.value_stack().take_last().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 source_offset = *configuration.stack().pop().get<Value>().to<u32>();
-        auto destination_offset = *configuration.stack().pop().get<Value>().to<u32>();
+        auto count = *configuration.value_stack().take_last().to<u32>();
+        auto source_offset = *configuration.value_stack().take_last().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 source_offset = configuration.stack().pop().get<Value>().to<u32>().value();
-        auto destination_offset = configuration.stack().pop().get<Value>().to<u32>().value();
+        auto count = configuration.value_stack().take_last().to<u32>().value();
+        auto source_offset = configuration.value_stack().take_last().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 value = *configuration.stack().pop().get<Value>().to<Reference>();
-        auto start = *configuration.stack().pop().get<Value>().to<u32>();
+        auto count = *configuration.value_stack().take_last().to<u32>();
+        auto value = *configuration.value_stack().take_last().to<Reference>();
+        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 index = (size_t)(*configuration.stack().pop().get<Value>().to<i32>());
+        auto ref = *configuration.value_stack().take_last().to<Reference>();
+        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 fill_value = *configuration.stack().pop().get<Value>().to<Reference>();
+        auto size = *configuration.value_stack().take_last().to<u32>();
+        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>();
-        TRAP_IF_NOT(top->type().is_reference());
-        auto is_null = top->to<Reference::Null>().has_value();
-        configuration.stack().peek() = Value(ValueType(ValueType::I32), static_cast<u64>(is_null ? 1 : 0));
+        auto ref = configuration.value_stack().take_last().to<Reference::Null>();
+        configuration.value_stack().append(Value(static_cast<i32>(ref.has_value() ? 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 = entry.get<Value>().to<i32>();
+        auto value = configuration.value_stack().take_last().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& lhs_entry = configuration.stack().peek();
-        auto rhs = move(rhs_entry.get<Value>());
-        auto lhs = move(lhs_entry.get<Value>());
-        configuration.stack().peek() = value.value() != 0 ? move(lhs) : move(rhs);
+        auto rhs = configuration.value_stack().take_last();
+        auto& lhs = configuration.value_stack().last();
+        lhs = value.value() != 0 ? lhs : 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 false_vector = *configuration.stack().pop().get<Value>().to<u128>();
-        auto true_vector = *configuration.stack().pop().get<Value>().to<u128>();
+        auto mask = *configuration.value_stack().take_last().to<u128>();
+        auto false_vector = *configuration.value_stack().take_last().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>();
-        configuration.stack().push(Value(static_cast<i32>(vector != 0)));
+        auto vector = *configuration.value_stack().take_last().to<u128>();
+        configuration.value_stack().append(Value(static_cast<i32>(vector != 0)));
         return;
         return;
     }
     }
     case Instructions::v128_load8_lane.value():
     case Instructions::v128_load8_lane.value():

+ 6 - 42
Userland/Libraries/LibWasm/AbstractMachine/Configuration.cpp

@@ -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)
-        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;
+    auto frame = m_frame_stack.take_last();
     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>()));
-    auto label = stack().pop();
-    // ASSERT: label == current frame
-    if (!label.has<Label>())
-        return Trap { "Invalid stack configuration" };
+        results.unchecked_append(value_stack().take_last());
+
+    label_stack().take_last();
     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()) {
-        entry.visit(
-            [&](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());
-            });
+    for (auto const& value : value_stack()) {
+        print_value("    {}", value);
     }
     }
 }
 }
 
 

+ 15 - 24
Userland/Libraries/LibWasm/AbstractMachine/Configuration.h

@@ -17,37 +17,29 @@ public:
     {
     {
     }
     }
 
 
-    Optional<Label> nth_label(size_t label)
+    void set_frame(Frame frame)
     {
     {
-        auto index = nth_label_index(label);
-        if (index.has_value())
-            return m_stack.entries()[index.value()].get<Label>();
-        return {};
+        Label label(frame.arity(), frame.expression().instructions().size(), m_value_stack.size());
+        frame.label_index() = m_label_stack.size();
+        m_frame_stack.append(move(frame));
+        m_label_stack.append(label);
     }
     }
-    Optional<size_t> nth_label_index(size_t);
-    void set_frame(Frame&& frame)
-    {
-        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& frame() const { return m_frame_stack.last(); }
+    ALWAYS_INLINE auto& frame() { return m_frame_stack.last(); }
     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& stack() { return m_stack; }
+    ALWAYS_INLINE auto& value_stack() const { return m_value_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)
-            , stack_size(configuration.m_stack.size())
-            , ip(configuration.ip())
+            : 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 };
-    Stack m_stack;
+    Vector<Value> m_value_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 };