瀏覽代碼

LibWasm: Remove type information from `Value`

Gets fib(30) from 380ms to 340ms.
Diego Frias 11 月之前
父節點
當前提交
a58704296c

+ 53 - 35
Tests/LibWasm/test-wasm.cpp

@@ -295,16 +295,27 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyModule::get_export)
             if (auto ptr = value.get_pointer<Wasm::FunctionAddress>())
                 return JS::Value(static_cast<unsigned long>(ptr->value()));
             if (auto v = value.get_pointer<Wasm::GlobalAddress>()) {
-                return m_machine.store().get(*v)->value().value().visit(
-                    [&](auto const& value) -> JS::Value { return JS::Value(static_cast<double>(value)); },
-                    [&](i32 value) { return JS::Value(static_cast<double>(value)); },
-                    [&](i64 value) -> JS::Value { return JS::BigInt::create(vm, Crypto::SignedBigInteger { value }); },
-                    [&](u128 value) -> JS::Value { return JS::BigInt::create(vm, Crypto::SignedBigInteger::import_data(bit_cast<u8 const*>(&value), sizeof(value))); },
-                    [&](Wasm::Reference const& reference) -> JS::Value {
-                        return reference.ref().visit(
-                            [&](Wasm::Reference::Null const&) -> JS::Value { return JS::js_null(); },
-                            [&](auto const& ref) -> JS::Value { return JS::Value(static_cast<double>(ref.address.value())); });
-                    });
+                auto global = m_machine.store().get(*v);
+                switch (global->type().type().kind()) {
+                case Wasm::ValueType::I32:
+                    return JS::Value(static_cast<double>(global->value().to<i32>()));
+                case Wasm::ValueType::I64:
+                    return JS::BigInt::create(vm, Crypto::SignedBigInteger { global->value().to<i64>() });
+                case Wasm::ValueType::F32:
+                    return JS::Value(static_cast<double>(global->value().to<float>()));
+                case Wasm::ValueType::F64:
+                    return JS::Value(global->value().to<double>());
+                case Wasm::ValueType::V128: {
+                    auto value = global->value().to<u128>();
+                    return JS::BigInt::create(vm, Crypto::SignedBigInteger::import_data(bit_cast<u8 const*>(&value), sizeof(u128)));
+                }
+                case Wasm::ValueType::FunctionReference:
+                case Wasm::ValueType::ExternReference:
+                    auto ref = global->value().to<Wasm::Reference>();
+                    return ref.ref().visit(
+                        [&](Wasm::Reference::Null const&) -> JS::Value { return JS::js_null(); },
+                        [&](auto const& ref) -> JS::Value { return JS::Value(static_cast<double>(ref.address.value())); });
+                }
             }
             return vm.throw_completion<JS::TypeError>(TRY_OR_THROW_OOM(vm, String::formatted("'{}' does not refer to a function or a global", name)));
         }
@@ -336,14 +347,14 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyModule::wasm_invoke)
             double_value = TRY(argument.to_double(vm));
         switch (param.kind()) {
         case Wasm::ValueType::Kind::I32:
-            arguments.append(Wasm::Value(param, static_cast<i64>(double_value)));
+            arguments.append(Wasm::Value(static_cast<i64>(double_value)));
             break;
         case Wasm::ValueType::Kind::I64:
             if (argument.is_bigint()) {
                 auto value = TRY(argument.to_bigint_int64(vm));
-                arguments.append(Wasm::Value(param, value));
+                arguments.append(Wasm::Value(value));
             } else {
-                arguments.append(Wasm::Value(param, static_cast<i64>(double_value)));
+                arguments.append(Wasm::Value(static_cast<i64>(double_value)));
             }
             break;
         case Wasm::ValueType::Kind::F32:
@@ -352,9 +363,9 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyModule::wasm_invoke)
         case Wasm::ValueType::Kind::F64:
             if (argument.is_bigint()) {
                 auto value = TRY(argument.to_bigint_uint64(vm));
-                arguments.append(Wasm::Value(param, bit_cast<double>(value)));
+                arguments.append(Wasm::Value(bit_cast<double>(value)));
             } else {
-                arguments.append(Wasm::Value(param, double_value));
+                arguments.append(Wasm::Value(double_value));
             }
             break;
         case Wasm::ValueType::Kind::V128: {
@@ -385,6 +396,7 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyModule::wasm_invoke)
         }
     }
 
+    auto functype = WebAssemblyModule::machine().store().get(function_address)->visit([&](auto& func) { return func.type(); });
     auto result = WebAssemblyModule::machine().invoke(function_address, arguments);
     if (result.is_trap())
         return vm.throw_completion<JS::TypeError>(TRY_OR_THROW_OOM(vm, String::formatted("Execution trapped: {}", result.trap().reason)));
@@ -395,30 +407,36 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyModule::wasm_invoke)
     if (result.values().is_empty())
         return JS::js_null();
 
-    auto to_js_value = [&](Wasm::Value const& value) {
-        return value.value().visit(
-            // For floating point values, we're testing with their bit representation, so we bit_cast them
-            [](f32 value) { return JS::Value(static_cast<double>(bit_cast<u32>(value))); },
-            [&](f64 value) { return JS::Value(JS::BigInt::create(vm, Crypto::SignedBigInteger { Crypto::UnsignedBigInteger { bit_cast<u64>(value) } })); },
-            [](i32 value) { return JS::Value(static_cast<double>(value)); },
-            [&](i64 value) { return JS::Value(JS::BigInt::create(vm, Crypto::SignedBigInteger { value })); },
-            [&](u128 value) {
-                // FIXME: remove the MUST here
-                auto buf = MUST(JS::ArrayBuffer::create(*vm.current_realm(), 16));
-                memcpy(buf->buffer().data(), value.bytes().data(), 16);
-                return JS::Value(buf);
-            },
-            [](Wasm::Reference const& reference) {
-                return reference.ref().visit(
-                    [](Wasm::Reference::Null const&) { return JS::js_null(); },
-                    [](auto const& ref) { return JS::Value(static_cast<double>(ref.address.value())); });
-            });
+    auto to_js_value = [&](Wasm::Value const& value, Wasm::ValueType type) {
+        switch (type.kind()) {
+        case Wasm::ValueType::I32:
+            return JS::Value(static_cast<double>(value.to<i32>()));
+        case Wasm::ValueType::I64:
+            return JS::Value(JS::BigInt::create(vm, Crypto::SignedBigInteger { value.to<i64>() }));
+        case Wasm::ValueType::F32:
+            return JS::Value(static_cast<double>(bit_cast<u32>(value.to<float>())));
+        case Wasm::ValueType::F64:
+            return JS::Value(JS::BigInt::create(vm, Crypto::SignedBigInteger { Crypto::UnsignedBigInteger { bit_cast<u64>(value.to<double>()) } }));
+        case Wasm::ValueType::V128: {
+            u128 val = value.to<u128>();
+            // FIXME: remove the MUST here
+            auto buf = MUST(JS::ArrayBuffer::create(*vm.current_realm(), 16));
+            memcpy(buf->buffer().data(), val.bytes().data(), 16);
+            return JS::Value(buf);
+        }
+        case Wasm::ValueType::FunctionReference:
+        case Wasm::ValueType::ExternReference:
+            return (value.to<Wasm::Reference>()).ref().visit([&](Wasm::Reference::Null) { return JS::js_null(); }, [&](auto const& ref) { return JS::Value(static_cast<double>(ref.address.value())); });
+        }
+        VERIFY_NOT_REACHED();
     };
 
     if (result.values().size() == 1)
-        return to_js_value(result.values().first());
+        return to_js_value(result.values().first(), functype.results().first());
 
+    size_t i = 0;
     return JS::Array::create_from<Wasm::Value>(*vm.current_realm(), result.values(), [&](Wasm::Value value) {
-        return to_js_value(value);
+        auto value_type = type->results()[i++];
+        return to_js_value(value, value_type);
     });
 }

+ 5 - 12
Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.cpp

@@ -55,7 +55,7 @@ Optional<MemoryAddress> Store::allocate(MemoryType const& type)
 Optional<GlobalAddress> Store::allocate(GlobalType const& type, Value value)
 {
     GlobalAddress address { m_globals.size() };
-    m_globals.append(GlobalInstance { move(value), type.is_mutable() });
+    m_globals.append(GlobalInstance { value, type.is_mutable(), type.type() });
     return address;
 }
 
@@ -138,7 +138,6 @@ ErrorOr<void, ValidationError> AbstractMachine::validate(Module& module)
 
     return {};
 }
-
 InstantiationResult AbstractMachine::instantiate(Module const& module, Vector<ExternValue> externs)
 {
     if (auto result = validate(const_cast<Module&>(module)); result.is_error())
@@ -267,7 +266,7 @@ InstantiationResult AbstractMachine::instantiate(Module const& module, Vector<Ex
 
             for (auto& value : result.values()) {
                 auto reference = value.to<Reference>();
-                references.append(reference.release_value());
+                references.append(reference);
             }
         }
         elements.append(move(references));
@@ -300,8 +299,6 @@ InstantiationResult AbstractMachine::instantiate(Module const& module, Vector<Ex
         if (result.is_trap())
             return InstantiationError { ByteString::formatted("Element section initialisation trapped: {}", result.trap().reason) };
         auto d = result.values().first().to<i32>();
-        if (!d.has_value())
-            return InstantiationError { "Element section initialisation returned invalid table initial offset" };
         auto table_instance = m_store.get(main_module_instance.tables()[active_ptr->index.value()]);
         if (current_index >= main_module_instance.elements().size())
             return InstantiationError { "Invalid element referenced by active element segment" };
@@ -309,14 +306,14 @@ InstantiationResult AbstractMachine::instantiate(Module const& module, Vector<Ex
             return InstantiationError { "Invalid element referenced by active element segment" };
 
         Checked<size_t> total_size = elem_instance->references().size();
-        total_size.saturating_add(d.value());
+        total_size.saturating_add(d);
 
         if (total_size.value() > table_instance->elements().size())
             return InstantiationError { "Table instantiation out of bounds" };
 
         size_t i = 0;
         for (auto it = elem_instance->references().begin(); it < elem_instance->references().end(); ++i, ++it)
-            table_instance->elements()[i + d.value()] = *it;
+            table_instance->elements()[i + d] = *it;
         // Drop element
         *m_store.get(main_module_instance.elements()[current_index]) = ElementInstance(elem_instance->type(), {});
     }
@@ -336,10 +333,7 @@ InstantiationResult AbstractMachine::instantiate(Module const& module, Vector<Ex
                 auto result = config.execute(interpreter).assert_wasm_result();
                 if (result.is_trap())
                     return InstantiationError { ByteString::formatted("Data section initialisation trapped: {}", result.trap().reason) };
-                size_t offset = TRY(result.values().first().value().visit(
-                    [&](auto const& value) { return ErrorOr<size_t, InstantiationError> { value }; },
-                    [&](u128 const&) { return ErrorOr<size_t, InstantiationError> { InstantiationError { "Data segment offset returned a vector type"sv } }; },
-                    [&](Reference const&) { return ErrorOr<size_t, InstantiationError> { InstantiationError { "Data segment offset returned a reference type"sv } }; }));
+                size_t offset = result.values().first().to<u64>();
                 if (main_module_instance.memories().size() <= data.index.value()) {
                     return InstantiationError {
                         ByteString::formatted("Data segment referenced out-of-bounds memory ({}) of max {} entries",
@@ -570,5 +564,4 @@ void Linker::populate()
         m_unresolved_imports.set(m_ordered_imports.last());
     }
 }
-
 }

+ 92 - 88
Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.h

@@ -75,45 +75,57 @@ private:
 class Value {
 public:
     Value()
-        : m_value(0)
+        : m_value(u128())
     {
     }
 
-    using AnyValueType = Variant<i32, i64, float, double, u128, Reference>;
-    explicit Value(AnyValueType value)
-        : m_value(move(value))
+    template<typename T>
+    requires(sizeof(T) == sizeof(u64)) explicit Value(T raw_value)
+        : m_value(u128(bit_cast<i64>(raw_value), 0))
     {
     }
 
     template<typename T>
-    requires(sizeof(T) == sizeof(u64)) explicit Value(ValueType type, T raw_value)
-        : m_value(0)
-    {
-        switch (type.kind()) {
-        case ValueType::Kind::ExternReference:
-            m_value = Reference { Reference::Extern { { bit_cast<u64>(raw_value) } } };
-            break;
-        case ValueType::Kind::FunctionReference:
-            m_value = Reference { Reference::Func { { bit_cast<u64>(raw_value) } } };
-            break;
-        case ValueType::Kind::I32:
-            m_value = static_cast<i32>(bit_cast<i64>(raw_value));
-            break;
-        case ValueType::Kind::I64:
-            m_value = static_cast<i64>(bit_cast<u64>(raw_value));
-            break;
-        case ValueType::Kind::F32:
-            m_value = static_cast<float>(bit_cast<double>(raw_value));
-            break;
-        case ValueType::Kind::F64:
-            m_value = bit_cast<double>(raw_value);
-            break;
-        case ValueType::Kind::V128:
-            m_value = u128(0ull, bit_cast<u64>(raw_value));
-            break;
-        default:
-            VERIFY_NOT_REACHED();
-        }
+    requires(sizeof(T) == sizeof(u32)) explicit Value(T raw_value)
+        : m_value(u128(static_cast<i64>(bit_cast<i32>(raw_value)), 0))
+    {
+    }
+
+    template<typename T>
+    requires(sizeof(T) == sizeof(u8) && Signed<T>) explicit Value(T raw_value)
+        : m_value(u128(static_cast<i64>(bit_cast<i8>(raw_value)), 0))
+    {
+    }
+
+    template<typename T>
+    requires(sizeof(T) == sizeof(u8) && Unsigned<T>) explicit Value(T raw_value)
+        : m_value(u128(static_cast<u64>(bit_cast<u8>(raw_value)), 0))
+    {
+    }
+
+    template<typename T>
+    requires(sizeof(T) == sizeof(u16) && Signed<T>) explicit Value(T raw_value)
+        : m_value(u128(static_cast<i64>(bit_cast<i16>(raw_value)), 0))
+    {
+    }
+
+    template<typename T>
+    requires(sizeof(T) == sizeof(u16) && Unsigned<T>) explicit Value(T raw_value)
+        : m_value(u128(static_cast<u64>(bit_cast<u16>(raw_value)), 0))
+    {
+    }
+
+    explicit Value(Reference ref)
+    {
+        // Reference variant is encoded in the high storage of the u128:
+        // 0: funcref
+        // 1: externref
+        // 2: null funcref
+        // 3: null externref
+        ref.ref().visit(
+            [&](Reference::Func const& func) { m_value = u128(bit_cast<u64>(func.address), 0); },
+            [&](Reference::Extern const& func) { m_value = u128(bit_cast<u64>(func.address), 1); },
+            [&](Reference::Null const& null) { m_value = u128(0, null.type.kind() == ValueType::Kind::FunctionReference ? 2 : 3); });
     }
 
     template<SameAs<u128> T>
@@ -128,63 +140,53 @@ public:
     ALWAYS_INLINE Value& operator=(Value const& value) = default;
 
     template<typename T>
-    ALWAYS_INLINE Optional<T> to() const
-    {
-        Optional<T> result;
-        m_value.visit(
-            [&](auto value) {
-                if constexpr (IsSame<T, decltype(value)> || (!IsFloatingPoint<T> && IsSame<decltype(value), MakeSigned<T>>)) {
-                    result = static_cast<T>(value);
-                } else if constexpr (!IsFloatingPoint<T> && IsConvertible<decltype(value), T>) {
-                    // NOTE: No implicit vector <-> scalar conversion.
-                    if constexpr (!IsSame<T, u128>) {
-                        if (AK::is_within_range<T>(value))
-                            result = static_cast<T>(value);
-                    }
-                }
-            },
-            [&](u128 value) {
-                if constexpr (IsSame<T, u128>)
-                    result = value;
-            },
-            [&](Reference const& value) {
-                if constexpr (IsSame<T, Reference>) {
-                    result = value;
-                } else if constexpr (IsSame<T, Reference::Func>) {
-                    if (auto ptr = value.ref().template get_pointer<Reference::Func>())
-                        result = *ptr;
-                } else if constexpr (IsSame<T, Reference::Extern>) {
-                    if (auto ptr = value.ref().template get_pointer<Reference::Extern>())
-                        result = *ptr;
-                } else if constexpr (IsSame<T, Reference::Null>) {
-                    if (auto ptr = value.ref().template get_pointer<Reference::Null>())
-                        result = *ptr;
-                }
-            });
-        return result;
-    }
-
-    ValueType type() const
-    {
-        return ValueType(m_value.visit(
-            [](i32) { return ValueType::Kind::I32; },
-            [](i64) { return ValueType::Kind::I64; },
-            [](float) { return ValueType::Kind::F32; },
-            [](double) { return ValueType::Kind::F64; },
-            [](u128) { return ValueType::Kind::V128; },
-            [&](Reference const& type) {
-                return type.ref().visit(
-                    [](Reference::Func const&) { return ValueType::Kind::FunctionReference; },
-                    [](Reference::Null const& null_type) {
-                        return null_type.type.kind();
-                    },
-                    [](Reference::Extern const&) { return ValueType::Kind::ExternReference; });
-            }));
+    ALWAYS_INLINE T to() const
+    {
+        if constexpr (IsSame<T, u128>) {
+            return m_value;
+        }
+        if constexpr (IsSame<T, u32>) {
+            u32 low = m_value.low() & 0xFFFFFFFF;
+            return low;
+        }
+        if constexpr (IsSame<T, i32>) {
+            u32 low = m_value.low() & 0xFFFFFFFF;
+            return bit_cast<i32>(low);
+        }
+        if constexpr (IsSame<T, u64>) {
+            return bit_cast<u64>(m_value.low());
+        }
+        if constexpr (IsSame<T, i64>) {
+            return bit_cast<i64>(m_value.low());
+        }
+        if constexpr (IsSame<T, f32>) {
+            u32 low = m_value.low() & 0xFFFFFFFF;
+            return bit_cast<f32>(low);
+        }
+        if constexpr (IsSame<T, f64>) {
+            return bit_cast<f64>(m_value.low());
+        }
+        if constexpr (IsSame<T, Reference>) {
+            switch (m_value.high()) {
+            case 0:
+                return Reference { Reference::Func(bit_cast<FunctionAddress>(m_value.low())) };
+            case 1:
+                return Reference { Reference::Extern(bit_cast<ExternAddress>(m_value.low())) };
+            case 2:
+                return Reference { Reference::Null(ValueType(ValueType::Kind::FunctionReference)) };
+            case 3:
+                return Reference { Reference::Null(ValueType(ValueType::Kind::ExternReference)) };
+            default:
+                VERIFY_NOT_REACHED();
+            }
+        }
+        VERIFY_NOT_REACHED();
     }
+
     auto& value() const { return m_value; }
 
 private:
-    AnyValueType m_value;
+    u128 m_value;
 };
 
 struct Trap {
@@ -481,15 +483,16 @@ private:
 
 class GlobalInstance {
 public:
-    explicit GlobalInstance(Value value, bool is_mutable)
+    explicit GlobalInstance(Value value, bool is_mutable, ValueType type)
         : m_mutable(is_mutable)
-        , m_value(move(value))
+        , m_value(value)
+        , m_type(type)
     {
     }
 
     auto is_mutable() const { return m_mutable; }
     auto& value() const { return m_value; }
-    GlobalType type() const { return { m_value.type(), is_mutable() }; }
+    GlobalType type() const { return { m_type, is_mutable() }; }
     void set_value(Value value)
     {
         VERIFY(is_mutable());
@@ -499,6 +502,7 @@ public:
 private:
     bool m_mutable { false };
     Value m_value;
+    ValueType m_type;
 };
 
 class DataInstance {

+ 58 - 58
Userland/Libraries/LibWasm/AbstractMachine/BytecodeInterpreter.cpp

@@ -75,7 +75,7 @@ void BytecodeInterpreter::load_and_push(Configuration& configuration, Instructio
     auto& address = configuration.frame().module().memories()[arg.memory_index.value()];
     auto memory = configuration.store().get(address);
     auto& entry = configuration.value_stack().last();
-    auto base = *entry.to<i32>();
+    auto base = entry.to<i32>();
     u64 instance_address = static_cast<u64>(bit_cast<u32>(base)) + arg.offset;
     if (instance_address + sizeof(ReadType) > memory->size()) {
         m_trap = Trap { "Memory access out of bounds" };
@@ -100,7 +100,7 @@ void BytecodeInterpreter::load_and_push_mxn(Configuration& configuration, Instru
     auto& address = configuration.frame().module().memories()[arg.memory_index.value()];
     auto memory = configuration.store().get(address);
     auto& entry = configuration.value_stack().last();
-    auto base = *entry.to<i32>();
+    auto base = entry.to<i32>();
     u64 instance_address = static_cast<u64>(bit_cast<u32>(base)) + arg.offset;
     if (instance_address + M * N / 8 > memory->size()) {
         m_trap = Trap { "Memory access out of bounds" };
@@ -127,8 +127,8 @@ void BytecodeInterpreter::load_and_push_lane_n(Configuration& configuration, Ins
     auto memarg_and_lane = instruction.arguments().get<Instruction::MemoryAndLaneArgument>();
     auto& address = configuration.frame().module().memories()[memarg_and_lane.memory.memory_index.value()];
     auto memory = configuration.store().get(address);
-    auto vector = *configuration.value_stack().take_last().to<u128>();
-    auto base = *configuration.value_stack().take_last().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;
     if (instance_address + N / 8 > memory->size()) {
         m_trap = Trap { "Memory access out of bounds" };
@@ -146,7 +146,7 @@ void BytecodeInterpreter::load_and_push_zero_n(Configuration& configuration, Ins
     auto memarg_and_lane = instruction.arguments().get<Instruction::MemoryArgument>();
     auto& address = configuration.frame().module().memories()[memarg_and_lane.memory_index.value()];
     auto memory = configuration.store().get(address);
-    auto base = *configuration.value_stack().take_last().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;
     if (instance_address + N / 8 > memory->size()) {
         m_trap = Trap { "Memory access out of bounds" };
@@ -165,7 +165,7 @@ void BytecodeInterpreter::load_and_push_m_splat(Configuration& configuration, In
     auto& address = configuration.frame().module().memories()[arg.memory_index.value()];
     auto memory = configuration.store().get(address);
     auto& entry = configuration.value_stack().last();
-    auto base = *entry.to<i32>();
+    auto base = entry.to<i32>();
     u64 instance_address = static_cast<u64>(bit_cast<u32>(base)) + arg.offset;
     if (instance_address + M / 8 > memory->size()) {
         m_trap = Trap { "Memory access out of bounds" };
@@ -212,7 +212,7 @@ void BytecodeInterpreter::pop_and_push_m_splat(Wasm::Configuration& configuratio
     using PopT = Conditional<M <= 32, NativeType<32>, NativeType<64>>;
     using ReadT = NativeType<M>;
     auto entry = configuration.value_stack().last();
-    auto value = static_cast<ReadT>(*entry.to<PopT>());
+    auto value = static_cast<ReadT>(entry.to<PopT>());
     dbgln_if(WASM_TRACE_DEBUG, "stack({}) -> splat({})", value, M);
     set_top_m_splat<M, NativeType>(configuration, value);
 }
@@ -220,7 +220,7 @@ void BytecodeInterpreter::pop_and_push_m_splat(Wasm::Configuration& configuratio
 template<typename M, template<typename> typename SetSign, typename VectorType>
 VectorType BytecodeInterpreter::pop_vector(Configuration& configuration)
 {
-    return bit_cast<VectorType>(*configuration.value_stack().take_last().to<u128>());
+    return bit_cast<VectorType>(configuration.value_stack().take_last().to<u128>());
 }
 
 void BytecodeInterpreter::call_address(Configuration& configuration, FunctionAddress address)
@@ -268,7 +268,7 @@ void BytecodeInterpreter::binary_numeric_operation(Configuration& configuration,
     auto& lhs_entry = configuration.value_stack().last();
     auto lhs = lhs_entry.to<PopTypeLHS>();
     PushType result;
-    auto call_result = Operator { forward<Args>(args)... }(lhs.value(), rhs.value());
+    auto call_result = Operator { forward<Args>(args)... }(lhs, rhs);
     if constexpr (IsSpecializationOf<decltype(call_result), AK::ErrorOr>) {
         if (call_result.is_error()) {
             trap_if_not(false, call_result.error());
@@ -287,7 +287,7 @@ void BytecodeInterpreter::unary_operation(Configuration& configuration, Args&&..
 {
     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;
     if constexpr (IsSpecializationOf<decltype(call_result), AK::ErrorOr>) {
         if (call_result.is_error()) {
@@ -337,19 +337,19 @@ void BytecodeInterpreter::pop_and_store(Configuration& configuration, Instructio
 {
     auto& memarg = instruction.arguments().get<Instruction::MemoryArgument>();
     auto entry = configuration.value_stack().take_last();
-    auto value = ConvertToRaw<StoreT> {}(*entry.to<PopT>());
+    auto value = ConvertToRaw<StoreT> {}(entry.to<PopT>());
     dbgln_if(WASM_TRACE_DEBUG, "stack({}) -> temporary({}b)", value, sizeof(StoreT));
     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);
 }
 
 template<size_t N>
 void BytecodeInterpreter::pop_and_store_lane_n(Configuration& configuration, Instruction const& instruction)
 {
     auto& memarg_and_lane = instruction.arguments().get<Instruction::MemoryAndLaneArgument>();
-    auto vector = *configuration.value_stack().take_last().to<u128>();
+    auto vector = configuration.value_stack().take_last().to<u128>();
     auto src = bit_cast<u8*>(&vector) + memarg_and_lane.lane * N / 8;
-    auto base = *configuration.value_stack().take_last().to<u32>();
+    auto base = configuration.value_stack().take_last().to<u32>();
     store_to_memory(configuration, memarg_and_lane.memory, { src, N / 8 }, base);
 }
 
@@ -422,16 +422,16 @@ ALWAYS_INLINE void BytecodeInterpreter::interpret_instruction(Configuration& con
         return;
     }
     case Instructions::i32_const.value():
-        configuration.value_stack().append(Value(ValueType { ValueType::I32 }, static_cast<i64>(instruction.arguments().get<i32>())));
+        configuration.value_stack().append(Value(instruction.arguments().get<i32>()));
         return;
     case Instructions::i64_const.value():
-        configuration.value_stack().append(Value(ValueType { ValueType::I64 }, instruction.arguments().get<i64>()));
+        configuration.value_stack().append(Value(instruction.arguments().get<i64>()));
         return;
     case Instructions::f32_const.value():
-        configuration.value_stack().append(Value(Value::AnyValueType(instruction.arguments().get<float>())));
+        configuration.value_stack().append(Value(instruction.arguments().get<float>()));
         return;
     case Instructions::f64_const.value():
-        configuration.value_stack().append(Value(Value::AnyValueType(instruction.arguments().get<double>())));
+        configuration.value_stack().append(Value(instruction.arguments().get<double>()));
         return;
     case Instructions::block.value(): {
         size_t arity = 0;
@@ -513,13 +513,13 @@ ALWAYS_INLINE void BytecodeInterpreter::interpret_instruction(Configuration& con
         return branch_to_label(configuration, instruction.arguments().get<LabelIndex>());
     case Instructions::br_if.value(): {
         auto cond = configuration.value_stack().take_last().to<i32>();
-        if (*cond == 0)
+        if (cond == 0)
             return;
         return branch_to_label(configuration, instruction.arguments().get<LabelIndex>());
     }
     case Instructions::br_table.value(): {
         auto& arguments = instruction.arguments().get<Instruction::TableBranchArgs>();
-        auto i = *configuration.value_stack().take_last().to<u32>();
+        auto i = configuration.value_stack().take_last().to<u32>();
 
         if (i >= arguments.labels.size()) {
             return branch_to_label(configuration, arguments.default_);
@@ -538,12 +538,12 @@ ALWAYS_INLINE void BytecodeInterpreter::interpret_instruction(Configuration& con
         auto table_address = configuration.frame().module().tables()[args.table.value()];
         auto table_instance = configuration.store().get(table_address);
         auto index = configuration.value_stack().take_last().to<i32>();
-        TRAP_IF_NOT(index.value() >= 0);
-        TRAP_IF_NOT(static_cast<size_t>(index.value()) < table_instance->elements().size());
-        auto element = table_instance->elements()[index.value()];
+        TRAP_IF_NOT(index >= 0);
+        TRAP_IF_NOT(static_cast<size_t>(index) < table_instance->elements().size());
+        auto element = table_instance->elements()[index];
         TRAP_IF_NOT(element.ref().has<Reference::Func>());
         auto address = element.ref().get<Reference::Func>().address;
-        dbgln_if(WASM_TRACE_DEBUG, "call_indirect({} -> {})", index.value(), address.value());
+        dbgln_if(WASM_TRACE_DEBUG, "call_indirect({} -> {})", index, address.value());
         call_address(configuration, address);
         return;
     }
@@ -636,8 +636,8 @@ ALWAYS_INLINE void BytecodeInterpreter::interpret_instruction(Configuration& con
         i32 old_pages = instance->size() / Constants::page_size;
         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);
-        if (instance->grow(new_pages.value() * Constants::page_size))
+        dbgln_if(WASM_TRACE_DEBUG, "memory.grow({}), previously {} pages...", new_pages, old_pages);
+        if (instance->grow(new_pages * Constants::page_size))
             entry = Value((i32)old_pages);
         else
             entry = Value((i32)-1);
@@ -648,9 +648,9 @@ ALWAYS_INLINE void BytecodeInterpreter::interpret_instruction(Configuration& con
         auto& args = instruction.arguments().get<Instruction::MemoryIndexArgument>();
         auto address = configuration.frame().module().memories()[args.memory_index.value()];
         auto instance = configuration.store().get(address);
-        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();
+        auto count = configuration.value_stack().take_last().to<u32>();
+        u8 value = static_cast<u8>(configuration.value_stack().take_last().to<u32>());
+        auto destination_offset = configuration.value_stack().take_last().to<u32>();
 
         TRAP_IF_NOT(static_cast<size_t>(destination_offset + count) <= instance->data().size());
 
@@ -670,9 +670,9 @@ ALWAYS_INLINE void BytecodeInterpreter::interpret_instruction(Configuration& con
         auto source_instance = configuration.store().get(source_address);
         auto destination_instance = configuration.store().get(destination_address);
 
-        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();
+        auto count = configuration.value_stack().take_last().to<i32>();
+        auto source_offset = configuration.value_stack().take_last().to<i32>();
+        auto destination_offset = configuration.value_stack().take_last().to<i32>();
 
         Checked<size_t> source_position = source_offset;
         source_position.saturating_add(count);
@@ -706,9 +706,9 @@ ALWAYS_INLINE void BytecodeInterpreter::interpret_instruction(Configuration& con
         auto& data = *configuration.store().get(data_address);
         auto memory_address = configuration.frame().module().memories()[args.memory_index.value()];
         auto memory = configuration.store().get(memory_address);
-        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>();
+        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;
         source_position.saturating_add(count);
@@ -747,9 +747,9 @@ ALWAYS_INLINE void BytecodeInterpreter::interpret_instruction(Configuration& con
         auto table = configuration.store().get(table_address);
         auto element_address = configuration.frame().module().elements()[args.element_index.value()];
         auto element = configuration.store().get(element_address);
-        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>();
+        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_destination_offset = destination_offset;
@@ -769,9 +769,9 @@ ALWAYS_INLINE void BytecodeInterpreter::interpret_instruction(Configuration& con
         auto source_instance = configuration.store().get(source_address);
         auto destination_instance = configuration.store().get(destination_address);
 
-        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();
+        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;
         source_position.saturating_add(count);
@@ -801,9 +801,9 @@ ALWAYS_INLINE void BytecodeInterpreter::interpret_instruction(Configuration& con
         auto table_index = instruction.arguments().get<TableIndex>();
         auto address = configuration.frame().module().tables()[table_index.value()];
         auto table = configuration.store().get(address);
-        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>();
+        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_offset += count;
@@ -814,8 +814,8 @@ ALWAYS_INLINE void BytecodeInterpreter::interpret_instruction(Configuration& con
         return;
     }
     case Instructions::table_set.value(): {
-        auto ref = *configuration.value_stack().take_last().to<Reference>();
-        auto index = (size_t)(*configuration.value_stack().take_last().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 address = configuration.frame().module().tables()[table_index.value()];
         auto table = configuration.store().get(address);
@@ -824,7 +824,7 @@ ALWAYS_INLINE void BytecodeInterpreter::interpret_instruction(Configuration& con
         return;
     }
     case Instructions::table_get.value(): {
-        auto index = (size_t)(*configuration.value_stack().take_last().to<i32>());
+        auto index = (size_t)(configuration.value_stack().take_last().to<i32>());
         auto table_index = instruction.arguments().get<TableIndex>();
         auto address = configuration.frame().module().tables()[table_index.value()];
         auto table = configuration.store().get(address);
@@ -834,8 +834,8 @@ ALWAYS_INLINE void BytecodeInterpreter::interpret_instruction(Configuration& con
         return;
     }
     case Instructions::table_grow.value(): {
-        auto size = *configuration.value_stack().take_last().to<u32>();
-        auto fill_value = *configuration.value_stack().take_last().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 address = configuration.frame().module().tables()[table_index.value()];
         auto table = configuration.store().get(address);
@@ -864,12 +864,12 @@ ALWAYS_INLINE void BytecodeInterpreter::interpret_instruction(Configuration& con
         auto index = instruction.arguments().get<FunctionIndex>().value();
         auto& functions = configuration.frame().module().functions();
         auto address = functions[index];
-        configuration.value_stack().append(Value(ValueType(ValueType::FunctionReference), address.value()));
+        configuration.value_stack().append(Value(address.value()));
         return;
     }
     case Instructions::ref_is_null.value(): {
-        auto ref = configuration.value_stack().take_last().to<Reference::Null>();
-        configuration.value_stack().append(Value(static_cast<i32>(ref.has_value() ? 1 : 0)));
+        auto ref = configuration.value_stack().take_last().to<Reference>();
+        configuration.value_stack().append(Value(static_cast<i32>(ref.ref().has<Reference::Null>() ? 1 : 0)));
         return;
     }
     case Instructions::drop.value():
@@ -879,10 +879,10 @@ ALWAYS_INLINE void BytecodeInterpreter::interpret_instruction(Configuration& con
     case Instructions::select_typed.value(): {
         // Note: The type seems to only be used for validation.
         auto value = configuration.value_stack().take_last().to<i32>();
-        dbgln_if(WASM_TRACE_DEBUG, "select({})", value.value());
+        dbgln_if(WASM_TRACE_DEBUG, "select({})", value);
         auto rhs = configuration.value_stack().take_last();
         auto& lhs = configuration.value_stack().last();
-        lhs = value.value() != 0 ? lhs : rhs;
+        lhs = value != 0 ? lhs : rhs;
         return;
     }
     case Instructions::i32_eqz.value():
@@ -1579,15 +1579,15 @@ ALWAYS_INLINE void BytecodeInterpreter::interpret_instruction(Configuration& con
     case Instructions::v128_andnot.value():
         return binary_numeric_operation<u128, u128, Operators::BitAndNot>(configuration);
     case Instructions::v128_bitselect.value(): {
-        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>();
+        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);
         configuration.value_stack().append(Value(result));
         return;
     }
     case Instructions::v128_any_true.value(): {
-        auto vector = *configuration.value_stack().take_last().to<u128>();
+        auto vector = configuration.value_stack().take_last().to<u128>();
         configuration.value_stack().append(Value(static_cast<i32>(vector != 0)));
         return;
     }

+ 1 - 1
Userland/Libraries/LibWasm/AbstractMachine/Configuration.cpp

@@ -28,7 +28,7 @@ Result Configuration::call(Interpreter& interpreter, FunctionAddress address, Ve
         locals.ensure_capacity(locals.size() + wasm_function->code().func().locals().size());
         for (auto& local : wasm_function->code().func().locals()) {
             for (size_t i = 0; i < local.n(); ++i)
-                locals.empend(local.type(), 0ull);
+                locals.append(Value());
         }
 
         set_frame(Frame {

+ 31 - 14
Userland/Libraries/LibWasm/Printer/Printer.cpp

@@ -654,24 +654,41 @@ void Printer::print(Wasm::ValueType const& type)
     print("(type {})\n", ValueType::kind_name(type.kind()));
 }
 
+void Printer::print(Wasm::Value const& value, Wasm::ValueType const& type)
+{
+    print_indent();
+    switch (type.kind()) {
+    case ValueType::I32:
+        print(ByteString::formatted("{}", value.to<i32>()));
+        break;
+    case ValueType::I64:
+        print(ByteString::formatted("{}", value.to<i64>()));
+        break;
+    case ValueType::F32:
+        print(ByteString::formatted("{}", value.to<f32>()));
+        break;
+    case ValueType::F64:
+        print(ByteString::formatted("{}", value.to<f64>()));
+        break;
+    case ValueType::V128:
+        print(ByteString::formatted("v128({:x})", value.value()));
+        break;
+    case ValueType::FunctionReference:
+    case ValueType::ExternReference:
+        print(ByteString::formatted("addr({})",
+            value.to<Reference>().ref().visit(
+                [](Wasm::Reference::Null const&) { return ByteString("null"); },
+                [](auto const& ref) { return ByteString::number(ref.address.value()); })));
+        break;
+    }
+    TemporaryChange<size_t> change { m_indent, 0 };
+}
+
 void Printer::print(Wasm::Value const& value)
 {
     print_indent();
-    print("{} ", value.value().visit([&]<typename T>(T const& value) {
-        if constexpr (IsSame<Wasm::Reference, T>) {
-            return ByteString::formatted(
-                "addr({})",
-                value.ref().visit(
-                    [](Wasm::Reference::Null const&) { return ByteString("null"); },
-                    [](auto const& ref) { return ByteString::number(ref.address.value()); }));
-        } else if constexpr (IsSame<u128, T>) {
-            return ByteString::formatted("v128({:x})", value);
-        } else {
-            return ByteString::formatted("{}", value);
-        }
-    }));
+    print("{:x}", value.value());
     TemporaryChange<size_t> change { m_indent, 0 };
-    print(value.type());
 }
 
 void Printer::print(Wasm::Reference const& value)

+ 1 - 0
Userland/Libraries/LibWasm/Printer/Printer.h

@@ -59,6 +59,7 @@ struct Printer {
     void print(Wasm::TypeSection const&);
     void print(Wasm::ValueType const&);
     void print(Wasm::Value const&);
+    void print(Wasm::Value const&, ValueType const&);
 
 private:
     void print_indent();

+ 4 - 18
Userland/Libraries/LibWasm/WASI/Wasi.cpp

@@ -68,7 +68,7 @@ CompatibleValue<T> to_compatible_value(Wasm::Value const& value)
 {
     using Type = typename ToCompatibleValue<T>::Type;
     // Note: the type can't be something else, we've already checked before through the function type's runtime checker.
-    auto converted_value = *value.template to<Type>();
+    auto converted_value = value.template to<Type>();
     return { .value = converted_value };
 }
 
@@ -868,7 +868,7 @@ static Array<Bytes, N> address_spans(Span<Value> values, Configuration& configur
     Array<Bytes, N> result;
     auto memory = configuration.store().get(MemoryAddress { 0 })->data().span();
     for (size_t i = 0; i < N; ++i)
-        result[i] = memory.slice(*values[i].to<i32>());
+        result[i] = memory.slice(values[i].to<i32>());
     return result;
 }
 
@@ -1027,7 +1027,7 @@ struct InvocationOf<impl> {
                 auto value = result.release_value();
                 if constexpr (IsSpecializationOf<RV, Result>) {
                     if (value.is_error())
-                        return Wasm::Result { Vector { Value { ValueType(ValueType::I32), static_cast<u64>(to_underlying(value.error().value())) } } };
+                        return Wasm::Result { Vector { Value { static_cast<u32>(to_underlying(value.error().value())) } } };
                 }
 
                 if constexpr (!IsVoid<R>) {
@@ -1040,7 +1040,7 @@ struct InvocationOf<impl> {
                     }
                 }
                 // Return value is errno, we have nothing to return.
-                return Wasm::Result { Vector<Value> { Value(ValueType(ValueType::I32), 0ull) } };
+                return Wasm::Result { Vector<Value> { Value() } };
             },
             FunctionType {
                 move(arguments_types),
@@ -1227,20 +1227,6 @@ FDFlags fd_flags_of(struct stat const&)
 }
 
 namespace AK {
-template<>
-struct Formatter<Wasm::Value> : AK::Formatter<FormatString> {
-    ErrorOr<void> format(FormatBuilder& builder, Wasm::Value const& value)
-    {
-        return value.value().visit(
-            [&](Wasm::Reference const&) {
-                return Formatter<FormatString>::format(builder, "({}) &r"sv, Wasm::ValueType::kind_name(value.type().kind()));
-            },
-            [&](auto const& v) {
-                return Formatter<FormatString>::format(builder, "({}) {}"sv, Wasm::ValueType::kind_name(value.type().kind()), v);
-            });
-    }
-};
-
 template<>
 struct Formatter<Wasm::Wasi::Errno> : AK::Formatter<FormatString> {
     ErrorOr<void> format(FormatBuilder& builder, Wasm::Wasi::Errno const& value)

+ 5 - 5
Userland/Libraries/LibWeb/WebAssembly/Table.cpp

@@ -32,7 +32,7 @@ static Wasm::ValueType table_kind_to_value_type(Bindings::TableKind kind)
 static JS::ThrowCompletionOr<Wasm::Value> value_to_reference(JS::VM& vm, JS::Value value, Wasm::ValueType const& reference_type)
 {
     if (value.is_undefined())
-        return Wasm::Value(reference_type, 0ull);
+        return Wasm::Value();
     return Detail::to_webassembly_value(vm, value, reference_type);
 }
 
@@ -51,7 +51,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<Table>> Table::construct_impl(JS::Realm& re
     if (!address.has_value())
         return vm.throw_completion<JS::TypeError>("Wasm Table allocation failed"sv);
 
-    auto const& reference = reference_value.value().get<Wasm::Reference>();
+    auto const& reference = reference_value.to<Wasm::Reference>();
     auto& table = *cache.abstract_machine().store().get(*address);
     for (auto& element : table.elements())
         element = reference;
@@ -84,7 +84,7 @@ WebIDL::ExceptionOr<u32> Table::grow(u32 delta, JS::Value value)
     auto initial_size = table->elements().size();
 
     auto reference_value = TRY(value_to_reference(vm, value, table->type().element_type()));
-    auto const& reference = reference_value.value().get<Wasm::Reference>();
+    auto const& reference = reference_value.to<Wasm::Reference>();
 
     if (!table->grow(delta, reference))
         return vm.throw_completion<JS::RangeError>("Failed to grow table"sv);
@@ -108,7 +108,7 @@ WebIDL::ExceptionOr<JS::Value> Table::get(u32 index) const
     auto& ref = table->elements()[index];
 
     Wasm::Value wasm_value { ref };
-    return Detail::to_js_value(vm, wasm_value);
+    return Detail::to_js_value(vm, wasm_value, table->type().element_type());
 }
 
 // https://webassembly.github.io/spec/js-api/#dom-table-set
@@ -125,7 +125,7 @@ WebIDL::ExceptionOr<void> Table::set(u32 index, JS::Value value)
         return vm.throw_completion<JS::RangeError>("Table element index out of range"sv);
 
     auto reference_value = TRY(value_to_reference(vm, value, table->type().element_type()));
-    auto const& reference = reference_value.value().get<Wasm::Reference>();
+    auto const& reference = reference_value.to<Wasm::Reference>();
 
     table->elements()[index] = reference;
 

+ 19 - 14
Userland/Libraries/LibWeb/WebAssembly/WebAssembly.cpp

@@ -193,8 +193,11 @@ JS::ThrowCompletionOr<NonnullOwnPtr<Wasm::ModuleInstance>> instantiate_module(JS
                     Wasm::HostFunction host_function {
                         [&](auto&, auto& arguments) -> Wasm::Result {
                             JS::MarkedVector<JS::Value> argument_values { vm.heap() };
-                            for (auto& entry : arguments)
-                                argument_values.append(to_js_value(vm, entry));
+                            size_t index = 0;
+                            for (auto& entry : arguments) {
+                                argument_values.append(to_js_value(vm, entry, type.parameters()[index]));
+                                ++index;
+                            }
 
                             auto result = TRY(JS::call(vm, function, JS::js_undefined(), argument_values.span()));
                             if (type.results().is_empty())
@@ -378,11 +381,13 @@ JS::NativeFunction* create_native_function(JS::VM& vm, Wasm::FunctionAddress add
                 return JS::js_undefined();
 
             if (result.values().size() == 1)
-                return to_js_value(vm, result.values().first());
+                return to_js_value(vm, result.values().first(), type.results().first());
 
-            return JS::Value(JS::Array::create_from<Wasm::Value>(realm, result.values(), [&](Wasm::Value value) {
-                return to_js_value(vm, value);
-            }));
+            VERIFY_NOT_REACHED();
+            // TODO
+            /*return JS::Value(JS::Array::create_from<Wasm::Value>(realm, result.values(), [&](Wasm::Value value) {*/
+            /*    return to_js_value(vm, value);*/
+            /*}));*/
         });
 
     cache.add_function_instance(address, function);
@@ -417,7 +422,7 @@ JS::ThrowCompletionOr<Wasm::Value> to_webassembly_value(JS::VM& vm, JS::Value va
     }
     case Wasm::ValueType::FunctionReference: {
         if (value.is_null())
-            return Wasm::Value { Wasm::ValueType(Wasm::ValueType::FunctionReference), 0ull };
+            return Wasm::Value();
 
         if (value.is_function()) {
             auto& function = value.as_function();
@@ -439,20 +444,20 @@ JS::ThrowCompletionOr<Wasm::Value> to_webassembly_value(JS::VM& vm, JS::Value va
     VERIFY_NOT_REACHED();
 }
 
-JS::Value to_js_value(JS::VM& vm, Wasm::Value& wasm_value)
+JS::Value to_js_value(JS::VM& vm, Wasm::Value& wasm_value, Wasm::ValueType type)
 {
     auto& realm = *vm.current_realm();
-    switch (wasm_value.type().kind()) {
+    switch (type.kind()) {
     case Wasm::ValueType::I64:
-        return realm.heap().allocate<JS::BigInt>(realm, ::Crypto::SignedBigInteger { wasm_value.to<i64>().value() });
+        return realm.heap().allocate<JS::BigInt>(realm, ::Crypto::SignedBigInteger { wasm_value.to<i64>() });
     case Wasm::ValueType::I32:
-        return JS::Value(wasm_value.to<i32>().value());
+        return JS::Value(wasm_value.to<i32>());
     case Wasm::ValueType::F64:
-        return JS::Value(wasm_value.to<double>().value());
+        return JS::Value(wasm_value.to<double>());
     case Wasm::ValueType::F32:
-        return JS::Value(static_cast<double>(wasm_value.to<float>().value()));
+        return JS::Value(static_cast<double>(wasm_value.to<float>()));
     case Wasm::ValueType::FunctionReference: {
-        auto ref_ = *wasm_value.to<Wasm::Reference>();
+        auto ref_ = wasm_value.to<Wasm::Reference>();
         if (ref_.ref().has<Wasm::Reference::Null>())
             return JS::js_null();
         auto address = ref_.ref().get<Wasm::Reference::Func>().address;

+ 1 - 1
Userland/Libraries/LibWeb/WebAssembly/WebAssembly.h

@@ -62,7 +62,7 @@ JS::ThrowCompletionOr<NonnullOwnPtr<Wasm::ModuleInstance>> instantiate_module(JS
 JS::ThrowCompletionOr<NonnullRefPtr<CompiledWebAssemblyModule>> parse_module(JS::VM&, JS::Object* buffer);
 JS::NativeFunction* create_native_function(JS::VM&, Wasm::FunctionAddress address, ByteString const& name, Instance* instance = nullptr);
 JS::ThrowCompletionOr<Wasm::Value> to_webassembly_value(JS::VM&, JS::Value value, Wasm::ValueType const& type);
-JS::Value to_js_value(JS::VM&, Wasm::Value& wasm_value);
+JS::Value to_js_value(JS::VM&, Wasm::Value& wasm_value, Wasm::ValueType type);
 
 extern HashMap<JS::GCPtr<JS::Object>, WebAssemblyCache> s_caches;
 

+ 79 - 41
Userland/Utilities/wasm.cpp

@@ -31,6 +31,11 @@ static void (*old_signal)(int);
 static StackInfo g_stack_info;
 static Wasm::DebuggerBytecodeInterpreter g_interpreter(g_stack_info);
 
+struct ParsedValue {
+    Wasm::Value value;
+    Wasm::ValueType type;
+};
+
 static void sigint_handler(int)
 {
     if (!g_continue) {
@@ -81,7 +86,7 @@ static Optional<u128> convert_to_uint_from_hex(StringView string)
     return value;
 }
 
-static ErrorOr<Wasm::Value> parse_value(StringView spec)
+static ErrorOr<ParsedValue> parse_value(StringView spec)
 {
     constexpr auto is_sep = [](char c) { return is_ascii_space(c) || c == ':'; };
     // Scalar: 'T.const[:\s]v' (i32.const 42)
@@ -127,42 +132,63 @@ static ErrorOr<Wasm::Value> parse_value(StringView spec)
         lexer.ignore_while(is_sep);
         // The rest of the string is the value
         auto text = lexer.consume_all();
-        return parse_u128(text);
+        return ParsedValue {
+            .value = TRY(parse_u128(text)),
+            .type = Wasm::ValueType(Wasm::ValueType::Kind::V128)
+        };
     }
 
     if (lexer.consume_specific("i8.const"sv)) {
         lexer.ignore_while(is_sep);
         auto text = lexer.consume_all();
-        return parse_scalar.operator()<i8>(text);
+        return ParsedValue {
+            .value = TRY(parse_scalar.operator()<i8>(text)),
+            .type = Wasm::ValueType(Wasm::ValueType::Kind::I32)
+        };
     }
     if (lexer.consume_specific("i16.const"sv)) {
         lexer.ignore_while(is_sep);
         auto text = lexer.consume_all();
-        return parse_scalar.operator()<i16>(text);
+        return ParsedValue {
+            .value = TRY(parse_scalar.operator()<i16>(text)),
+            .type = Wasm::ValueType(Wasm::ValueType::Kind::I32)
+        };
     }
     if (lexer.consume_specific("i32.const"sv)) {
         lexer.ignore_while(is_sep);
         auto text = lexer.consume_all();
-        return parse_scalar.operator()<i32>(text);
+        return ParsedValue {
+            .value = TRY(parse_scalar.operator()<i32>(text)),
+            .type = Wasm::ValueType(Wasm::ValueType::Kind::I32)
+        };
     }
     if (lexer.consume_specific("i64.const"sv)) {
         lexer.ignore_while(is_sep);
         auto text = lexer.consume_all();
-        return parse_scalar.operator()<i64>(text);
+        return ParsedValue {
+            .value = TRY(parse_scalar.operator()<i64>(text)),
+            .type = Wasm::ValueType(Wasm::ValueType::Kind::I64)
+        };
     }
     if (lexer.consume_specific("f32.const"sv)) {
         lexer.ignore_while(is_sep);
         auto text = lexer.consume_all();
-        return parse_scalar.operator()<float>(text);
+        return ParsedValue {
+            .value = TRY(parse_scalar.operator()<float>(text)),
+            .type = Wasm::ValueType(Wasm::ValueType::Kind::F32)
+        };
     }
     if (lexer.consume_specific("f64.const"sv)) {
         lexer.ignore_while(is_sep);
         auto text = lexer.consume_all();
-        return parse_scalar.operator()<double>(text);
+        return ParsedValue {
+            .value = TRY(parse_scalar.operator()<double>(text)),
+            .type = Wasm::ValueType(Wasm::ValueType::Kind::F64)
+        };
     }
 
     if (lexer.consume_specific("v("sv)) {
-        Vector<Wasm::Value> values;
+        Vector<ParsedValue> values;
         for (;;) {
             lexer.ignore_while(is_sep);
             if (lexer.consume_specific(")"sv))
@@ -181,9 +207,9 @@ static ErrorOr<Wasm::Value> parse_value(StringView spec)
         if (values.is_empty())
             return Error::from_string_literal("Empty vector");
 
-        auto element_type = values.first().type();
+        auto element_type = values.first().type;
         for (auto& value : values) {
-            if (value.type() != element_type)
+            if (value.type != element_type)
                 return Error::from_string_literal("Mixed types in vector");
         }
 
@@ -191,24 +217,25 @@ static ErrorOr<Wasm::Value> parse_value(StringView spec)
         unsigned width = 0;
         u128 result = 0;
         u128 last_value = 0;
-        for (auto& value : values) {
+        for (auto& parsed : values) {
             if (total_size >= 128)
                 return Error::from_string_literal("Vector too large");
 
-            width = value.value().visit(
-                [&](Integral auto x) {
-                    last_value = u128(x, 0);
-                    return sizeof(x);
-                },
-                [&](float x) {
-                    last_value = u128(bit_cast<u32>(x), 0);
-                    return sizeof(x);
-                },
-                [&](double x) {
-                    last_value = u128(bit_cast<u64>(x), 0);
-                    return sizeof(x);
-                },
-                [&](auto) -> size_t { VERIFY_NOT_REACHED(); });
+            switch (parsed.type.kind()) {
+            case Wasm::ValueType::F32:
+            case Wasm::ValueType::I32:
+                width = sizeof(u32);
+                break;
+            case Wasm::ValueType::F64:
+            case Wasm::ValueType::I64:
+                width = sizeof(u64);
+                break;
+            case Wasm::ValueType::V128:
+            case Wasm::ValueType::FunctionReference:
+            case Wasm::ValueType::ExternReference:
+                VERIFY_NOT_REACHED();
+            }
+            last_value = parsed.value.value();
 
             result |= last_value << total_size;
             total_size += width * 8;
@@ -222,7 +249,10 @@ static ErrorOr<Wasm::Value> parse_value(StringView spec)
             total_size += width * 8;
         }
 
-        return Wasm::Value { result };
+        return ParsedValue {
+            .value = Wasm::Value { result },
+            .type = Wasm::ValueType(Wasm::ValueType::Kind::V128)
+        };
     }
 
     return Error::from_string_literal("Invalid value");
@@ -390,7 +420,7 @@ static bool pre_interpret_hook(Wasm::Configuration& config, Wasm::InstructionPoi
                 warnln("Expected {} arguments for call, but found only {}", type.parameters().size(), args.size() - 2);
                 continue;
             }
-            Vector<Wasm::Value> values_to_push;
+            Vector<ParsedValue> values_to_push;
             Vector<Wasm::Value> values;
             auto ok = true;
             for (size_t index = 2; index < args.size(); ++index) {
@@ -406,12 +436,12 @@ static bool pre_interpret_hook(Wasm::Configuration& config, Wasm::InstructionPoi
                 continue;
             for (auto& param : type.parameters()) {
                 auto v = values_to_push.take_last();
-                if (v.type() != param) {
-                    warnln("Type mismatch in argument: expected {}, but got {}", Wasm::ValueType::kind_name(param.kind()), Wasm::ValueType::kind_name(v.type().kind()));
+                if (v.type != param) {
+                    warnln("Type mismatch in argument: expected {}, but got {}", Wasm::ValueType::kind_name(param.kind()), Wasm::ValueType::kind_name(v.type.kind()));
                     ok = false;
                     break;
                 }
-                values.append(v);
+                values.append(v.value);
             }
             if (!ok)
                 continue;
@@ -426,9 +456,11 @@ static bool pre_interpret_hook(Wasm::Configuration& config, Wasm::InstructionPoi
             } else {
                 if (!result.values().is_empty())
                     warnln("Returned:");
+                size_t index = 0;
                 for (auto& value : result.values()) {
                     g_stdout->write_until_depleted("  -> "sv.bytes()).release_value_but_fixme_should_propagate_errors();
-                    g_printer->print(value);
+                    g_printer->print(value, type.results()[index]);
+                    ++index;
                 }
             }
             continue;
@@ -492,7 +524,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
     bool shell_mode = false;
     bool wasi = false;
     ByteString exported_function_to_execute;
-    Vector<Wasm::Value> values_to_push;
+    Vector<ParsedValue> values_to_push;
     Vector<ByteString> modules_to_link_in;
     Vector<StringView> args_if_wasi;
     Vector<StringView> wasi_preopened_mappings;
@@ -677,9 +709,11 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
                     [name = entry.name, type = type](auto&, auto& arguments) -> Wasm::Result {
                         StringBuilder argument_builder;
                         bool first = true;
+                        size_t index = 0;
                         for (auto& argument : arguments) {
                             AllocatingMemoryStream stream;
-                            Wasm::Printer { stream }.print(argument);
+                            auto value_type = type.parameters()[index];
+                            Wasm::Printer { stream }.print(argument, value_type);
                             if (first)
                                 first = false;
                             else
@@ -687,12 +721,13 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
                             auto buffer = ByteBuffer::create_uninitialized(stream.used_buffer_size()).release_value_but_fixme_should_propagate_errors();
                             stream.read_until_filled(buffer).release_value_but_fixme_should_propagate_errors();
                             argument_builder.append(StringView(buffer).trim_whitespace());
+                            ++index;
                         }
                         dbgln("[wasm runtime] Stub function {} was called with the following arguments: {}", name, argument_builder.to_byte_string());
                         Vector<Wasm::Value> result;
                         result.ensure_capacity(type.results().size());
-                        for (auto& result_type : type.results())
-                            result.append(Wasm::Value { result_type, 0ull });
+                        for (size_t i = 0; i < type.results().size(); ++i)
+                            result.append(Wasm::Value());
                         return Wasm::Result { move(result) };
                     },
                     type,
@@ -783,11 +818,11 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
 
             for (auto& param : instance->get<Wasm::WasmFunction>().type().parameters()) {
                 if (values_to_push.is_empty()) {
-                    values.append(Wasm::Value { param, 0ull });
-                } else if (param == values_to_push.last().type()) {
-                    values.append(values_to_push.take_last());
+                    values.append(Wasm::Value());
+                } else if (param == values_to_push.last().type) {
+                    values.append(values_to_push.take_last().value);
                 } else {
-                    warnln("Type mismatch in argument: expected {}, but got {}", Wasm::ValueType::kind_name(param.kind()), Wasm::ValueType::kind_name(values_to_push.last().type().kind()));
+                    warnln("Type mismatch in argument: expected {}, but got {}", Wasm::ValueType::kind_name(param.kind()), Wasm::ValueType::kind_name(values_to_push.last().type.kind()));
                     return 1;
                 }
             }
@@ -810,9 +845,12 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
             } else {
                 if (!result.values().is_empty())
                     warnln("Returned:");
+                auto result_type = instance->get<Wasm::WasmFunction>().type().results();
+                size_t index = 0;
                 for (auto& value : result.values()) {
                     g_stdout->write_until_depleted("  -> "sv.bytes()).release_value_but_fixme_should_propagate_errors();
-                    g_printer->print(value);
+                    g_printer->print(value, result_type[index]);
+                    ++index;
                 }
             }
         }