LibWasm: Remove type information from Value

Gets fib(30) from 380ms to 340ms.
This commit is contained in:
Diego Frias 2024-08-04 08:06:50 -07:00 committed by Ali Mohammad Pur
parent a2448308fd
commit a58704296c
Notes: github-actions[bot] 2024-08-06 23:11:13 +00:00
12 changed files with 349 additions and 287 deletions

View file

@ -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);
});
}

View file

@ -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());
}
}
}

View file

@ -75,45 +75,57 @@ private:
class Value {
public:
Value()
: m_value(0)
{
}
using AnyValueType = Variant<i32, i64, float, double, u128, Reference>;
explicit Value(AnyValueType value)
: m_value(move(value))
: m_value(u128())
{
}
template<typename T>
requires(sizeof(T) == sizeof(u64)) explicit Value(ValueType type, T raw_value)
: m_value(0)
requires(sizeof(T) == sizeof(u64)) explicit Value(T raw_value)
: m_value(u128(bit_cast<i64>(raw_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();
}
}
template<typename T>
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
ALWAYS_INLINE 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;
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();
}
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; });
}));
}
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 {

View file

@ -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;
}

View file

@ -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 {

View file

@ -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)

View file

@ -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();

View file

@ -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)

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;
}
}
}