Compare commits

...

8 commits

Author SHA1 Message Date
Ali Mohammad Pur
92c0cbc453 LibWasm+LibWeb: Stub wasm-gc's heap reference types
WPT inserts these into all modules regardless of whether they're used,
so let's just parse and ignore them.
2025-10-15 01:26:29 +02:00
Ali Mohammad Pur
33d2959a4c LibWeb: Stub wasm exceptions and memory64 API modifications 2025-10-15 01:26:29 +02:00
Ali Mohammad Pur
d99f663b1a LibWasm: Implement parsing/validation for proposal exception-handling
Actual execution traps for now.
2025-10-15 01:26:29 +02:00
Ali Mohammad Pur
8138c2f48b LibWasm: Follow the updated spec on instantiation
The spec now permits access to all globals for all segment initializers,
as well as previously-defined globals for the global initializers.
2025-10-15 01:26:29 +02:00
Ali Mohammad Pur
ddb35dcb5f LibWasm: Accept proposal 'memory64' (but don't actually run it)
This is a WIP implementation.
2025-10-15 01:26:29 +02:00
Ali Mohammad Pur
d6f3f5fd51 LibWasm: Implement proposal 'relaxed-simd' 2025-10-15 01:26:29 +02:00
Ali Mohammad Pur
77237af33f LibWasm: Add support for proposal 'extended-const' 2025-10-15 01:26:29 +02:00
Ali Mohammad Pur
6a6f747701 LibWasm: Add support for proposal 'tail-call' 2025-10-15 01:26:29 +02:00
24 changed files with 1660 additions and 495 deletions

View file

@ -76,6 +76,20 @@ Optional<ElementAddress> Store::allocate(ValueType const& type, Vector<Reference
return address;
}
Optional<TagAddress> Store::allocate(FunctionType const& type, TagType::Flags flags)
{
TagAddress address { m_tags.size() };
m_tags.append({ type, flags });
return address;
}
Optional<ExceptionAddress> Store::allocate(TagInstance const& tag_instance, Vector<Value> params)
{
ExceptionAddress address { m_exceptions.size() };
m_exceptions.append(ExceptionInstance { tag_instance, move(params) });
return address;
}
FunctionInstance* Store::get(FunctionAddress address)
{
auto value = address.value();
@ -132,6 +146,22 @@ DataInstance* Store::get(DataAddress address)
return &m_datas[value];
}
TagInstance* Store::get(TagAddress address)
{
auto value = address.value();
if (m_tags.size() <= value)
return nullptr;
return &m_tags[value];
}
ExceptionInstance* Store::get(ExceptionAddress address)
{
auto value = address.value();
if (m_exceptions.size() <= value)
return nullptr;
return &m_exceptions[value];
}
ErrorOr<void, ValidationError> AbstractMachine::validate(Module& module)
{
if (module.validation_status() != Module::ValidationStatus::Unchecked) {
@ -203,6 +233,19 @@ InstantiationResult AbstractMachine::instantiate(Module const& module, Vector<Ex
return ByteString::formatted("Function import and extern do not match, parameters: {} vs {}", type.parameters(), other_type.parameters());
return {};
},
[&](TagType const& type) -> Optional<ByteString> {
if (!extern_.has<TagAddress>())
return "Expected tag import"sv;
auto other_tag_instance = m_store.get(extern_.get<TagAddress>());
if (other_tag_instance->flags() != type.flags())
return "Tag import and extern do not match"sv;
auto& this_type = module.type_section().types()[type.type().value()];
if (other_tag_instance->type().parameters() != this_type.parameters())
return "Tag import and extern do not match"sv;
return {};
},
[&](TypeIndex type_index) -> Optional<ByteString> {
if (!extern_.has<FunctionAddress>())
return "Expected function import"sv;
@ -255,6 +298,7 @@ InstantiationResult AbstractMachine::instantiate(Module const& module, Vector<Ex
if (result.is_trap())
return InstantiationError { "Global instantiation trapped", move(result.trap()) };
global_values.append(result.values().first());
auxiliary_instance.globals().append(m_store.allocate(entry.type(), result.values().first()).release_value());
}
if (auto result = allocate_all_initial_phase(module, main_module_instance, externs, global_values, module_functions); result.has_value())
@ -267,7 +311,7 @@ InstantiationResult AbstractMachine::instantiate(Module const& module, Vector<Ex
if (m_should_limit_instruction_count)
config.enable_instruction_count_limit();
config.set_frame(Frame {
auxiliary_instance,
main_module_instance,
Vector<Value> {},
entry,
entry.instructions().size() - 1,
@ -302,7 +346,7 @@ InstantiationResult AbstractMachine::instantiate(Module const& module, Vector<Ex
if (m_should_limit_instruction_count)
config.enable_instruction_count_limit();
config.set_frame(Frame {
auxiliary_instance,
main_module_instance,
Vector<Value> {},
active_ptr->expression,
1,
@ -337,7 +381,7 @@ InstantiationResult AbstractMachine::instantiate(Module const& module, Vector<Ex
if (m_should_limit_instruction_count)
config.enable_instruction_count_limit();
config.set_frame(Frame {
auxiliary_instance,
main_module_instance,
Vector<Value> {},
data.offset,
1,
@ -406,7 +450,8 @@ Optional<InstantiationError> AbstractMachine::allocate_all_initial_phase(Module
[&](FunctionAddress const& address) { module_instance.functions().append(address); },
[&](TableAddress const& address) { module_instance.tables().append(address); },
[&](MemoryAddress const& address) { module_instance.memories().append(address); },
[&](GlobalAddress const& address) { module_instance.globals().append(address); });
[&](GlobalAddress const& address) { module_instance.globals().append(address); },
[&](TagAddress const& address) { module_instance.tags().append(address); });
}
module_instance.functions().extend(own_functions);
@ -433,8 +478,15 @@ Optional<InstantiationError> AbstractMachine::allocate_all_initial_phase(Module
index++;
}
for (auto& entry : module.tag_section().tags()) {
auto& type = module.type_section().types()[entry.type().value()];
auto address = m_store.allocate(type, entry.flags());
VERIFY(address.has_value());
module_instance.tags().append(*address);
}
for (auto& entry : module.export_section().entries()) {
Variant<FunctionAddress, TableAddress, MemoryAddress, GlobalAddress, Empty> address {};
Variant<FunctionAddress, TableAddress, MemoryAddress, GlobalAddress, TagAddress, Empty> address {};
entry.description().visit(
[&](FunctionIndex const& index) {
if (module_instance.functions().size() > index.value())
@ -459,6 +511,12 @@ Optional<InstantiationError> AbstractMachine::allocate_all_initial_phase(Module
address = GlobalAddress { module_instance.globals()[index.value()] };
else
dbgln("Failed to export '{}', the exported address ({}) was out of bounds (min: 0, max: {})", entry.name(), index.value(), module_instance.globals().size());
},
[&](TagIndex const& index) {
if (module_instance.tags().size() > index.value())
address = TagAddress { module_instance.tags()[index.value()] };
else
dbgln("Failed to export '{}', the exported address ({}) was out of bounds (min: 0, max: {})", entry.name(), index.value(), module_instance.tags().size());
});
if (address.has<Empty>()) {
@ -468,7 +526,7 @@ Optional<InstantiationError> AbstractMachine::allocate_all_initial_phase(Module
module_instance.exports().append(ExportInstance {
entry.name(),
move(address).downcast<FunctionAddress, TableAddress, MemoryAddress, GlobalAddress>(),
move(address).downcast<FunctionAddress, TableAddress, MemoryAddress, GlobalAddress, TagAddress>(),
});
}

View file

@ -38,6 +38,8 @@ AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(u64, GlobalAddress, Arithmetic, Comparison,
AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(u64, ElementAddress, Arithmetic, Comparison, Increment);
AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(u64, DataAddress, Arithmetic, Comparison, Increment);
AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(u64, MemoryAddress, Arithmetic, Comparison, Increment);
AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(u64, TagAddress, Arithmetic, Comparison, Increment);
AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(u64, ExceptionAddress, Arithmetic, Comparison, Increment);
// FIXME: These should probably be made generic/virtual if/when we decide to do something more
// fancy than just a dumb interpreter.
@ -53,8 +55,11 @@ public:
struct Extern {
ExternAddress address;
};
struct Exception {
ExceptionAddress address;
};
using RefType = Variant<Null, Func, Extern>;
using RefType = Variant<Null, Func, Extern, Exception>;
explicit Reference(RefType ref)
: m_ref(move(ref))
{
@ -90,6 +95,14 @@ public:
// ref.null externref
m_value = u128(0, 3);
break;
case ValueType::ExceptionReference:
// ref.null exnref
m_value = u128(0, 4);
break;
case ValueType::UnsupportedHeapReference:
// ref.null (todo)
m_value = u128(0, 5);
break;
}
}
@ -136,10 +149,14 @@ public:
// 1: externref
// 2: null funcref
// 3: null externref
// 4: null exnref
// 5: exnref
ref.ref().visit(
[&](Reference::Func const& func) { m_value = u128(bit_cast<u64>(func.address), bit_cast<u64>(func.source_module.ptr())); },
[&](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); });
[&](Reference::Null const& null) { m_value = u128(0, null.type.kind() == ValueType::Kind::FunctionReference ? 2 : null.type.kind() == ValueType::Kind::ExceptionReference ? 4
: 3); },
[&](Reference::Exception const& exn) { m_value = u128(bit_cast<u64>(exn.address), 5); });
}
template<SameAs<u128> T>
@ -184,6 +201,10 @@ public:
return Reference { Reference::Null { ValueType(ValueType::Kind::FunctionReference) } };
case 3:
return Reference { Reference::Null { ValueType(ValueType::Kind::ExternReference) } };
case 4:
return Reference { Reference::Null { ValueType(ValueType::Kind::ExceptionReference) } };
case 5:
return Reference { Reference::Exception { bit_cast<ExceptionAddress>(m_value.low()) } };
}
}
VERIFY_NOT_REACHED();
@ -273,7 +294,7 @@ struct InstantiationError {
InstantiationErrorSource source { InstantiationErrorSource::Linking };
};
using ExternValue = Variant<FunctionAddress, TableAddress, MemoryAddress, GlobalAddress>;
using ExternValue = Variant<FunctionAddress, TableAddress, MemoryAddress, GlobalAddress, TagAddress>;
class ExportInstance {
public:
@ -296,13 +317,16 @@ public:
explicit ModuleInstance(
Vector<FunctionType> types, Vector<FunctionAddress> function_addresses, Vector<TableAddress> table_addresses,
Vector<MemoryAddress> memory_addresses, Vector<GlobalAddress> global_addresses, Vector<DataAddress> data_addresses,
Vector<TagAddress> tag_addresses, Vector<TagType> tag_types,
Vector<ExportInstance> exports)
: m_types(move(types))
, m_tag_types(move(tag_types))
, m_functions(move(function_addresses))
, m_tables(move(table_addresses))
, m_memories(move(memory_addresses))
, m_globals(move(global_addresses))
, m_datas(move(data_addresses))
, m_tags(move(tag_addresses))
, m_exports(move(exports))
{
}
@ -317,6 +341,8 @@ public:
auto& elements() const { return m_elements; }
auto& datas() const { return m_datas; }
auto& exports() const { return m_exports; }
auto& tags() const { return m_tags; }
auto& tag_types() const { return m_tag_types; }
auto& types() { return m_types; }
auto& functions() { return m_functions; }
@ -326,15 +352,19 @@ public:
auto& elements() { return m_elements; }
auto& datas() { return m_datas; }
auto& exports() { return m_exports; }
auto& tags() { return m_tags; }
auto& tag_types() { return m_tag_types; }
private:
Vector<FunctionType> m_types;
Vector<TagType> m_tag_types;
Vector<FunctionAddress> m_functions;
Vector<TableAddress> m_tables;
Vector<MemoryAddress> m_memories;
Vector<GlobalAddress> m_globals;
Vector<ElementAddress> m_elements;
Vector<DataAddress> m_datas;
Vector<TagAddress> m_tags;
Vector<ExportInstance> m_exports;
};
@ -411,7 +441,7 @@ public:
for (size_t i = previous_size; i < m_elements.size(); ++i)
m_elements[i] = fill_value;
m_type = TableType { m_type.element_type(), Limits(m_type.limits().min() + size_to_grow, m_type.limits().max()) };
m_type = TableType { m_type.element_type(), Limits(m_type.limits().address_type(), m_type.limits().min() + size_to_grow, m_type.limits().max()) };
return true;
}
@ -477,7 +507,7 @@ public:
//
// See relevant spec link:
// https://www.w3.org/TR/wasm-core-2/#growing-memories%E2%91%A0
m_type = MemoryType { Limits(m_type.limits().min() + size_to_grow / Constants::page_size, m_type.limits().max()) };
m_type = MemoryType { Limits(m_type.limits().address_type(), m_type.limits().min() + size_to_grow / Constants::page_size, m_type.limits().max()) };
}
return true;
@ -552,6 +582,38 @@ private:
Vector<Reference> m_references;
};
class TagInstance {
public:
TagInstance(FunctionType const& type, TagType::Flags flags)
: m_type(type)
, m_flags(flags)
{
}
auto& type() const { return m_type; }
auto flags() const { return m_flags; }
private:
FunctionType m_type;
TagType::Flags m_flags;
};
class ExceptionInstance {
public:
explicit ExceptionInstance(TagInstance const& type, Vector<Value> params)
: m_type(type)
, m_params(move(params))
{
}
auto& type() const { return m_type; }
auto& params() const { return m_params; }
private:
TagInstance m_type;
Vector<Value> m_params;
};
class WASM_API Store {
public:
Store() = default;
@ -563,6 +625,8 @@ public:
Optional<DataAddress> allocate_data(Vector<u8>);
Optional<GlobalAddress> allocate(GlobalType const&, Value);
Optional<ElementAddress> allocate(ValueType const&, Vector<Reference>);
Optional<TagAddress> allocate(FunctionType const&, TagType::Flags);
Optional<ExceptionAddress> allocate(TagInstance const&, Vector<Value>);
Module const* get_module_for(FunctionAddress);
FunctionInstance* get(FunctionAddress);
@ -571,6 +635,8 @@ public:
GlobalInstance* get(GlobalAddress);
DataInstance* get(DataAddress);
ElementInstance* get(ElementAddress);
TagInstance* get(TagAddress);
ExceptionInstance* get(ExceptionAddress);
MemoryInstance* unsafe_get(MemoryAddress address) { return &m_memories.data()[address.value()]; }
@ -581,6 +647,8 @@ private:
Vector<GlobalInstance> m_globals;
Vector<ElementInstance> m_elements;
Vector<DataInstance> m_datas;
Vector<TagInstance> m_tags;
Vector<ExceptionInstance> m_exceptions;
};
class Label {

View file

@ -68,7 +68,7 @@ struct ConvertToRaw<double> {
do { \
if (trap_if_not(x, #x##sv __VA_OPT__(, ) __VA_ARGS__)) { \
dbgln_if(WASM_TRACE_DEBUG, "Trapped because {} failed, at line {}", #x, __LINE__); \
return true; \
return Outcome::Return; \
} \
} while (false)
@ -98,11 +98,7 @@ void BytecodeInterpreter::interpret(Configuration& configuration)
return interpret_impl<false, false, false>(configuration, expression);
}
enum class Outcome : u64 {
// 0..Constants::max_allowed_executed_instructions_per_call -> next IP.
Continue = Constants::max_allowed_executed_instructions_per_call + 1,
Return,
};
constexpr static u32 default_sources_and_destination = (to_underlying(Dispatch::RegisterOrStack::Stack) | (to_underlying(Dispatch::RegisterOrStack::Stack) << 2) | (to_underlying(Dispatch::RegisterOrStack::Stack) << 4));
template<u64 opcode>
struct InstructionHandler { };
@ -126,6 +122,16 @@ struct InstructionHandler { };
}; \
template<bool HasDynamicInsnLimit, typename Continue> \
Outcome InstructionHandler<Instructions::name.value()>::operator()(HANDLER_PARAMS(DECOMPOSE_PARAMS))
#define ALIAS_INSTRUCTION(new_name, existing_name) \
template<> \
struct InstructionHandler<Instructions::new_name.value()> { \
template<bool HasDynamicInsnLimit, typename Continue> \
static Outcome operator()(HANDLER_PARAMS(DECOMPOSE_PARAMS)) \
{ \
TAILCALL return InstructionHandler<Instructions::existing_name.value()>::operator()<HasDynamicInsnLimit, Continue>( \
HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); \
} \
};
struct Continue {
static Outcome operator()(BytecodeInterpreter& interpreter, Configuration& configuration, Instruction const*, SourcesAndDestination addresses, u64 current_ip_value, Dispatch const* cc)
@ -1071,7 +1077,7 @@ HANDLE_INSTRUCTION(synthetic_call_00)
auto index = instruction->arguments().get<FunctionIndex>();
auto address = configuration.frame().module().functions()[index.value()];
dbgln_if(WASM_TRACE_DEBUG, "[{}] call(#{} -> {})", current_ip_value, index.value(), address.value());
if (interpreter.call_address(configuration, address))
if (interpreter.call_address(configuration, address) == Outcome::Return)
return Outcome::Return;
configuration.regs = regs_copy;
TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY));
@ -1083,7 +1089,7 @@ HANDLE_INSTRUCTION(synthetic_call_01)
auto index = instruction->arguments().get<FunctionIndex>();
auto address = configuration.frame().module().functions()[index.value()];
dbgln_if(WASM_TRACE_DEBUG, "[{}] call(#{} -> {})", current_ip_value, index.value(), address.value());
if (interpreter.call_address(configuration, address))
if (interpreter.call_address(configuration, address) == Outcome::Return)
return Outcome::Return;
configuration.regs = regs_copy;
TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY));
@ -1095,7 +1101,7 @@ HANDLE_INSTRUCTION(synthetic_call_10)
auto index = instruction->arguments().get<FunctionIndex>();
auto address = configuration.frame().module().functions()[index.value()];
dbgln_if(WASM_TRACE_DEBUG, "[{}] call(#{} -> {})", current_ip_value, index.value(), address.value());
if (interpreter.call_address(configuration, address))
if (interpreter.call_address(configuration, address) == Outcome::Return)
return Outcome::Return;
configuration.regs = regs_copy;
TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY));
@ -1107,7 +1113,7 @@ HANDLE_INSTRUCTION(synthetic_call_11)
auto index = instruction->arguments().get<FunctionIndex>();
auto address = configuration.frame().module().functions()[index.value()];
dbgln_if(WASM_TRACE_DEBUG, "[{}] call(#{} -> {})", current_ip_value, index.value(), address.value());
if (interpreter.call_address(configuration, address))
if (interpreter.call_address(configuration, address) == Outcome::Return)
return Outcome::Return;
configuration.regs = regs_copy;
TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY));
@ -1119,7 +1125,7 @@ HANDLE_INSTRUCTION(synthetic_call_20)
auto index = instruction->arguments().get<FunctionIndex>();
auto address = configuration.frame().module().functions()[index.value()];
dbgln_if(WASM_TRACE_DEBUG, "[{}] call(#{} -> {})", current_ip_value, index.value(), address.value());
if (interpreter.call_address(configuration, address))
if (interpreter.call_address(configuration, address) == Outcome::Return)
return Outcome::Return;
configuration.regs = regs_copy;
TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY));
@ -1131,7 +1137,7 @@ HANDLE_INSTRUCTION(synthetic_call_21)
auto index = instruction->arguments().get<FunctionIndex>();
auto address = configuration.frame().module().functions()[index.value()];
dbgln_if(WASM_TRACE_DEBUG, "[{}] call(#{} -> {})", current_ip_value, index.value(), address.value());
if (interpreter.call_address(configuration, address))
if (interpreter.call_address(configuration, address) == Outcome::Return)
return Outcome::Return;
configuration.regs = regs_copy;
TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY));
@ -1143,7 +1149,7 @@ HANDLE_INSTRUCTION(synthetic_call_30)
auto index = instruction->arguments().get<FunctionIndex>();
auto address = configuration.frame().module().functions()[index.value()];
dbgln_if(WASM_TRACE_DEBUG, "[{}] call(#{} -> {})", current_ip_value, index.value(), address.value());
if (interpreter.call_address(configuration, address))
if (interpreter.call_address(configuration, address) == Outcome::Return)
return Outcome::Return;
configuration.regs = regs_copy;
TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY));
@ -1155,7 +1161,7 @@ HANDLE_INSTRUCTION(synthetic_call_31)
auto index = instruction->arguments().get<FunctionIndex>();
auto address = configuration.frame().module().functions()[index.value()];
dbgln_if(WASM_TRACE_DEBUG, "[{}] call(#{} -> {})", current_ip_value, index.value(), address.value());
if (interpreter.call_address(configuration, address))
if (interpreter.call_address(configuration, address) == Outcome::Return)
return Outcome::Return;
configuration.regs = regs_copy;
TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY));
@ -1321,11 +1327,31 @@ HANDLE_INSTRUCTION(call)
auto index = instruction->arguments().get<FunctionIndex>();
auto address = configuration.frame().module().functions()[index.value()];
dbgln_if(WASM_TRACE_DEBUG, "call({})", address.value());
if (interpreter.call_address(configuration, address))
if (interpreter.call_address(configuration, address) == Outcome::Return)
return Outcome::Return;
TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY));
}
HANDLE_INSTRUCTION(return_call)
{
auto index = instruction->arguments().get<FunctionIndex>();
auto address = configuration.frame().module().functions()[index.value()];
configuration.label_stack().shrink(configuration.frame().label_index() + 1, true);
dbgln_if(WASM_TRACE_DEBUG, "tail call({})", address.value());
switch (auto const outcome = interpreter.call_address(configuration, address, BytecodeInterpreter::CallAddressSource::DirectTailCall)) {
default:
// Some IP we have to continue from.
current_ip_value = to_underlying(outcome) - 1;
addresses = { .sources_and_destination = default_sources_and_destination };
cc = configuration.frame().expression().compiled_instructions.dispatches.data();
[[fallthrough]];
case Outcome::Continue:
TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY));
case Outcome::Return:
return Outcome::Return;
}
}
HANDLE_INSTRUCTION(call_indirect)
{
auto& args = instruction->arguments().get<Instruction::IndirectCallArgs>();
@ -1346,11 +1372,45 @@ HANDLE_INSTRUCTION(call_indirect)
TRAP_IN_LOOP_IF_NOT(type_actual.results() == type_expected.results());
dbgln_if(WASM_TRACE_DEBUG, "call_indirect({} -> {})", index, address.value());
if (interpreter.call_address(configuration, address, BytecodeInterpreter::CallAddressSource::IndirectCall))
if (interpreter.call_address(configuration, address, BytecodeInterpreter::CallAddressSource::IndirectCall) == Outcome::Return)
return Outcome::Return;
TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY));
}
HANDLE_INSTRUCTION(return_call_indirect)
{
auto& args = instruction->arguments().get<Instruction::IndirectCallArgs>();
auto table_address = configuration.frame().module().tables()[args.table.value()];
auto table_instance = configuration.store().get(table_address);
// bounds checked by verifier.
auto index = configuration.take_source(0, addresses.sources).to<i32>();
TRAP_IN_LOOP_IF_NOT(index >= 0);
TRAP_IN_LOOP_IF_NOT(static_cast<size_t>(index) < table_instance->elements().size());
auto& element = table_instance->elements()[index];
TRAP_IN_LOOP_IF_NOT(element.ref().has<Reference::Func>());
auto address = element.ref().get<Reference::Func>().address;
auto const& type_actual = configuration.store().get(address)->visit([](auto& f) -> decltype(auto) { return f.type(); });
auto const& type_expected = configuration.frame().module().types()[args.type.value()];
TRAP_IN_LOOP_IF_NOT(type_actual.parameters().size() == type_expected.parameters().size());
TRAP_IN_LOOP_IF_NOT(type_actual.results().size() == type_expected.results().size());
TRAP_IN_LOOP_IF_NOT(type_actual.parameters() == type_expected.parameters());
TRAP_IN_LOOP_IF_NOT(type_actual.results() == type_expected.results());
dbgln_if(WASM_TRACE_DEBUG, "tail call_indirect({} -> {})", index, address.value());
switch (auto const outcome = interpreter.call_address(configuration, address, BytecodeInterpreter::CallAddressSource::IndirectTailCall)) {
default:
// Some IP we have to continue from.
current_ip_value = to_underlying(outcome) - 1;
addresses = { .sources_and_destination = default_sources_and_destination };
cc = configuration.frame().expression().compiled_instructions.dispatches.data();
[[fallthrough]];
case Outcome::Continue:
TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY));
case Outcome::Return:
return Outcome::Return;
}
}
HANDLE_INSTRUCTION(i32_load)
{
if (interpreter.load_and_push<i32, i32>(configuration, *instruction, addresses))
@ -3620,6 +3680,110 @@ HANDLE_INSTRUCTION(i32x4_extmul_high_i16x8_s)
TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY));
}
ALIAS_INSTRUCTION(i8x16_relaxed_swizzle, i8x16_swizzle)
ALIAS_INSTRUCTION(i32x4_relaxed_trunc_f32x4_s, i32x4_trunc_sat_f32x4_s)
ALIAS_INSTRUCTION(i32x4_relaxed_trunc_f32x4_u, i32x4_trunc_sat_f32x4_u)
ALIAS_INSTRUCTION(i32x4_relaxed_trunc_f64x2_s_zero, i32x4_trunc_sat_f64x2_s_zero)
ALIAS_INSTRUCTION(i32x4_relaxed_trunc_f64x2_u_zero, i32x4_trunc_sat_f64x2_u_zero)
HANDLE_INSTRUCTION(f32x4_relaxed_madd)
{
auto a = configuration.take_source(0, addresses.sources).to<u128>();
auto b = configuration.take_source(1, addresses.sources).to<u128>();
auto& c_slot = configuration.source_value(2, addresses.sources);
auto c = c_slot.to<u128>();
c_slot = Value { Operators::VectorMultiplyAdd<4> {}(a, b, c) };
TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY));
}
HANDLE_INSTRUCTION(f32x4_relaxed_nmadd)
{
auto a = configuration.take_source(0, addresses.sources).to<u128>();
auto b = configuration.take_source(1, addresses.sources).to<u128>();
auto& c_slot = configuration.source_value(2, addresses.sources);
auto c = c_slot.to<u128>();
c_slot = Value { Operators::VectorMultiplySub<4> {}(a, b, c) };
TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY));
}
HANDLE_INSTRUCTION(f64x2_relaxed_madd)
{
auto a = configuration.take_source(0, addresses.sources).to<u128>();
auto b = configuration.take_source(1, addresses.sources).to<u128>();
auto& c_slot = configuration.source_value(2, addresses.sources);
auto c = c_slot.to<u128>();
c_slot = Value { Operators::VectorMultiplyAdd<2> {}(a, b, c) };
TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY));
}
HANDLE_INSTRUCTION(f64x2_relaxed_nmadd)
{
auto a = configuration.take_source(0, addresses.sources).to<u128>();
auto b = configuration.take_source(1, addresses.sources).to<u128>();
auto& c_slot = configuration.source_value(2, addresses.sources);
auto c = c_slot.to<u128>();
c_slot = Value { Operators::VectorMultiplySub<2> {}(a, b, c) };
TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY));
}
ALIAS_INSTRUCTION(i8x16_relaxed_laneselect, v128_bitselect)
ALIAS_INSTRUCTION(i16x8_relaxed_laneselect, v128_bitselect)
ALIAS_INSTRUCTION(i32x4_relaxed_laneselect, v128_bitselect)
ALIAS_INSTRUCTION(i64x2_relaxed_laneselect, v128_bitselect)
ALIAS_INSTRUCTION(f32x4_relaxed_min, f32x4_min)
ALIAS_INSTRUCTION(f32x4_relaxed_max, f32x4_max)
ALIAS_INSTRUCTION(f64x2_relaxed_min, f64x2_min)
ALIAS_INSTRUCTION(f64x2_relaxed_max, f64x2_max)
ALIAS_INSTRUCTION(i16x8_relaxed_q15mulr_s, i16x8_q15mulr_sat_s)
HANDLE_INSTRUCTION(i16x8_relaxed_dot_i8x16_i7x16_s)
{
if (interpreter.binary_numeric_operation<u128, u128, Operators::VectorDotProduct<8>>(configuration, addresses))
return Outcome::Return;
TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY));
}
HANDLE_INSTRUCTION(i32x4_relaxed_dot_i8x16_i7x16_add_s)
{
// do i16x8 dot first, then fold back down to i32, then do the final component add.
auto rhs = configuration.take_source(0, addresses.sources).to<u128>();
auto lhs = configuration.take_source(1, addresses.sources).to<u128>(); // bounds checked by verifier.
auto result = Operators::VectorDotProduct<4, Operators::VectorIntegerExtOpPairwise<4, Operators::Add>> {}(lhs, rhs);
auto& c_slot = configuration.source_value(2, addresses.sources);
c_slot = Value { Operators::VectorIntegerBinaryOp<4, Operators::Add, MakeSigned> {}(result, c_slot.to<u128>()) };
TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY));
}
HANDLE_INSTRUCTION(throw_ref)
{
interpreter.set_trap("Not Implemented: Proposal 'Exception-handling'"sv);
return Outcome::Return;
}
HANDLE_INSTRUCTION(throw_)
{
{
auto tag_address = configuration.frame().module().tags()[instruction->arguments().get<TagIndex>().value()];
auto& tag_instance = *configuration.store().get(tag_address);
auto& type = tag_instance.type();
auto values = Vector<Value>(configuration.value_stack().span().slice_from_end(type.parameters().size()));
configuration.value_stack().shrink(configuration.value_stack().size() - type.parameters().size());
auto exception_address = configuration.store().allocate(tag_instance, move(values));
if (!exception_address.has_value()) {
interpreter.set_trap("Out of memory"sv);
return Outcome::Return;
}
configuration.value_stack().append(Value(Reference { Reference::Exception { *exception_address } }));
}
TAILCALL return InstructionHandler<Instructions::throw_ref.value()>::operator()<HasDynamicInsnLimit, Continue>(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY));
}
HANDLE_INSTRUCTION(try_table)
{
interpreter.set_trap("Not Implemented: Proposal 'Exception-handling'"sv);
return Outcome::Return;
}
template<u64 opcode, bool HasDynamicInsnLimit, typename Continue, typename... Args>
constexpr static auto handle_instruction(Args&&... a)
{
@ -3633,10 +3797,9 @@ FLATTEN void BytecodeInterpreter::interpret_impl(Configuration& configuration, E
auto current_ip_value = configuration.ip();
u64 executed_instructions = 0;
constexpr static u32 default_sources_and_destination = (to_underlying(Dispatch::RegisterOrStack::Stack) | (to_underlying(Dispatch::RegisterOrStack::Stack) << 2) | (to_underlying(Dispatch::RegisterOrStack::Stack) << 4));
SourcesAndDestination addresses { .sources_and_destination = default_sources_and_destination };
auto const cc = expression.compiled_instructions.dispatches.data();
auto cc = expression.compiled_instructions.dispatches.data();
if constexpr (HaveDirectThreadingInfo) {
static_assert(HasCompiledList, "Direct threading requires a compiled instruction list");
@ -3678,6 +3841,8 @@ FLATTEN void BytecodeInterpreter::interpret_impl(Configuration& configuration, E
if (outcome == Outcome::Return) \
return; \
current_ip_value = to_underlying(outcome); \
if constexpr (Instructions::name == Instructions::return_call || Instructions::name == Instructions::return_call_indirect) \
cc = configuration.frame().expression().compiled_instructions.dispatches.data(); \
RUN_NEXT_INSTRUCTION(); \
}
@ -3869,14 +4034,14 @@ VectorType BytecodeInterpreter::pop_vector(Configuration& configuration, size_t
return bit_cast<VectorType>(configuration.take_source(source, addresses.sources).to<u128>());
}
bool BytecodeInterpreter::call_address(Configuration& configuration, FunctionAddress address, CallAddressSource source)
Outcome BytecodeInterpreter::call_address(Configuration& configuration, FunctionAddress address, CallAddressSource source)
{
TRAP_IF_NOT(m_stack_info.size_free() >= Constants::minimum_stack_space_to_keep_free, "{}: {}", Constants::stack_exhaustion_message);
auto instance = configuration.store().get(address);
FunctionType const* type { nullptr };
instance->visit([&](auto const& function) { type = &function.type(); });
if (source == CallAddressSource::IndirectCall) {
if (source == CallAddressSource::IndirectCall || source == CallAddressSource::IndirectTailCall) {
TRAP_IF_NOT(type->parameters().size() <= configuration.value_stack().size());
}
Vector<Value> args;
@ -3890,16 +4055,34 @@ bool BytecodeInterpreter::call_address(Configuration& configuration, FunctionAdd
}
Result result { Trap::from_string("") };
if (instance->has<WasmFunction>()) {
CallFrameHandle handle { *this, configuration };
result = configuration.call(*this, address, move(args));
Outcome final_outcome = Outcome::Continue;
if (source == CallAddressSource::DirectTailCall || source == CallAddressSource::IndirectTailCall) {
auto prep_outcome = configuration.prepare_call(address, args, true);
if (prep_outcome.is_error()) {
m_trap = prep_outcome.release_error();
return Outcome::Return;
}
final_outcome = Outcome::Return; // At this point we can only ever return (unless we succeed in tail-calling).
if (prep_outcome.value().has_value()) {
result = prep_outcome.value()->function()(configuration, args);
} else {
configuration.ip() = 0;
return static_cast<Outcome>(0); // Continue from IP 0 in the new frame.
}
} else {
result = configuration.call(*this, address, move(args));
if (instance->has<WasmFunction>()) {
CallFrameHandle handle { *this, configuration };
result = configuration.call(*this, address, move(args));
} else {
result = configuration.call(*this, address, move(args));
}
}
if (result.is_trap()) {
m_trap = move(result.trap());
return true;
return Outcome::Return;
}
if (!result.values().is_empty()) {
@ -3908,7 +4091,7 @@ bool BytecodeInterpreter::call_address(Configuration& configuration, FunctionAdd
configuration.value_stack().unchecked_append(entry);
}
return false;
return final_outcome;
}
template<typename PopTypeLHS, typename PushType, typename Operator, typename PopTypeRHS, typename... Args>
@ -3931,10 +4114,10 @@ bool BytecodeInterpreter::binary_numeric_operation(Configuration& configuration,
return false;
}
template<typename PopType, typename PushType, typename Operator, typename... Args>
template<typename PopType, typename PushType, typename Operator, size_t input_arg, typename... Args>
bool BytecodeInterpreter::unary_operation(Configuration& configuration, SourcesAndDestination const& addresses, Args&&... args)
{
auto& entry = configuration.source_value(0, addresses.sources); // bounds checked by verifier.
auto& entry = configuration.source_value(input_arg, addresses.sources); // bounds checked by verifier.
auto value = entry.to<PopType>();
auto call_result = Operator { forward<Args>(args)... }(value);
PushType result;

View file

@ -21,6 +21,12 @@ union SourcesAndDestination {
u32 sources_and_destination;
};
enum class Outcome : u64 {
// 0..Constants::max_allowed_executed_instructions_per_call -> next IP.
Continue = Constants::max_allowed_executed_instructions_per_call + 1,
Return,
};
struct WASM_API BytecodeInterpreter final : public Interpreter {
explicit BytecodeInterpreter(StackInfo const& stack_info)
: m_stack_info(stack_info)
@ -59,6 +65,8 @@ struct WASM_API BytecodeInterpreter final : public Interpreter {
enum class CallAddressSource {
DirectCall,
IndirectCall,
DirectTailCall,
IndirectTailCall,
};
template<bool HasCompiledList, bool HasDynamicInsnLimit, bool HaveDirectThreadingInfo>
@ -88,7 +96,7 @@ struct WASM_API BytecodeInterpreter final : public Interpreter {
template<typename M, template<typename> typename SetSign, typename VectorType = Native128ByteVectorOf<M, SetSign>>
VectorType pop_vector(Configuration&, size_t source, SourcesAndDestination const&);
bool store_to_memory(Configuration&, Instruction::MemoryArgument const&, ReadonlyBytes data, u32 base);
bool call_address(Configuration&, FunctionAddress, CallAddressSource = CallAddressSource::DirectCall);
Outcome call_address(Configuration&, FunctionAddress, CallAddressSource = CallAddressSource::DirectCall);
template<typename T>
bool store_to_memory(MemoryInstance&, u64 address, T value);
@ -96,7 +104,7 @@ struct WASM_API BytecodeInterpreter final : public Interpreter {
template<typename PopTypeLHS, typename PushType, typename Operator, typename PopTypeRHS = PopTypeLHS, typename... Args>
bool binary_numeric_operation(Configuration&, SourcesAndDestination const&, Args&&...);
template<typename PopType, typename PushType, typename Operator, typename... Args>
template<typename PopType, typename PushType, typename Operator, size_t input_arg = 0, typename... Args>
bool unary_operation(Configuration&, SourcesAndDestination const&, Args&&...);
ALWAYS_INLINE bool set_trap(StringView reason)

View file

@ -11,7 +11,7 @@
namespace Wasm {
void Configuration::unwind(Badge<CallFrameHandle>, CallFrameHandle const&)
void Configuration::unwind_impl()
{
m_frame_stack.take_last();
m_depth--;
@ -19,11 +19,22 @@ void Configuration::unwind(Badge<CallFrameHandle>, CallFrameHandle const&)
}
Result Configuration::call(Interpreter& interpreter, FunctionAddress address, Vector<Value> arguments)
{
if (auto fn = TRY(prepare_call(address, arguments)); fn.has_value())
return fn->function()(*this, arguments);
m_ip = 0;
return execute(interpreter);
}
ErrorOr<Optional<HostFunction&>, Trap> Configuration::prepare_call(FunctionAddress address, Vector<Value>& arguments, bool is_tailcall)
{
auto* function = m_store.get(address);
if (!function)
return Trap::from_string("Attempt to call nonexistent function by address");
if (auto* wasm_function = function->get_pointer<WasmFunction>()) {
if (is_tailcall)
unwind_impl(); // Unwind the current frame, the "return" in the tail-called function will unwind the frame we're gonna push now.
Vector<Value> locals = move(arguments);
locals.ensure_capacity(locals.size() + wasm_function->code().func().locals().size());
for (auto& local : wasm_function->code().func().locals()) {
@ -32,18 +43,16 @@ Result Configuration::call(Interpreter& interpreter, FunctionAddress address, Ve
}
set_frame(Frame {
wasm_function->module(),
move(locals),
wasm_function->code().func().body(),
wasm_function->type().results().size(),
});
m_ip = 0;
return execute(interpreter);
wasm_function->module(),
move(locals),
wasm_function->code().func().body(),
wasm_function->type().results().size(),
},
is_tailcall);
return OptionalNone {};
}
// It better be a host function, else something is really wrong.
auto& host_function = function->get<HostFunction>();
return host_function.function()(*this, arguments);
return function->get<HostFunction>();
}
Result Configuration::execute(Interpreter& interpreter)

View file

@ -19,7 +19,7 @@ public:
{
}
void set_frame(Frame frame)
void set_frame(Frame frame, bool is_tailcall = false)
{
auto continuation = frame.expression().instructions().size() - 1;
if (auto size = frame.expression().compiled_instructions.dispatches.size(); size > 0)
@ -28,8 +28,10 @@ public:
frame.label_index() = m_label_stack.size();
if (auto hint = frame.expression().stack_usage_hint(); hint.has_value())
m_value_stack.ensure_capacity(*hint + m_value_stack.size());
if (auto hint = frame.expression().frame_usage_hint(); hint.has_value())
m_label_stack.ensure_capacity(*hint + m_label_stack.size());
if (!is_tailcall) {
if (auto hint = frame.expression().frame_usage_hint(); hint.has_value())
m_label_stack.ensure_capacity(*hint + m_label_stack.size());
}
m_frame_stack.append(move(frame));
m_label_stack.append(label);
m_locals_base = m_frame_stack.unchecked_last().locals().data();
@ -66,7 +68,8 @@ public:
Configuration& configuration;
};
void unwind(Badge<CallFrameHandle>, CallFrameHandle const&);
void unwind(Badge<CallFrameHandle>, CallFrameHandle const&) { unwind_impl(); }
ErrorOr<Optional<HostFunction&>, Trap> prepare_call(FunctionAddress, Vector<Value>& arguments, bool is_tailcall = false);
Result call(Interpreter&, FunctionAddress, Vector<Value> arguments);
Result execute(Interpreter&);
@ -113,6 +116,8 @@ public:
};
private:
void unwind_impl();
Store& m_store;
Vector<Value, 64, FastLastAccess::Yes> m_value_stack;
Vector<Label, 64> m_label_stack;

View file

@ -51,6 +51,10 @@ DEFINE_BINARY_OPERATOR(BitXor, ^);
#undef DEFINE_BINARY_OPERATOR
struct Identity {
auto operator()(auto x) const { return x; }
};
struct Divide {
template<typename Lhs, typename Rhs>
auto operator()(Lhs lhs, Rhs rhs) const
@ -738,8 +742,35 @@ struct VectorBitmask {
};
template<size_t VectorSize>
struct VectorMultiplyAdd {
auto operator()(u128 a1, u128 a2, u128 a3) const
{
using VectorInput = NativeFloatingVectorType<128 / VectorSize, VectorSize, NativeFloatingType<128 / VectorSize>>;
auto a = bit_cast<VectorInput>(a1);
auto b = bit_cast<VectorInput>(a2);
auto c = bit_cast<VectorInput>(a3);
// Spec talks about rounding and such, but v8's arm impl just does vmul + vadd with nothing in between.
return bit_cast<u128>(a * b + c);
}
};
template<size_t VectorSize>
struct VectorMultiplySub {
auto operator()(u128 a1, u128 a2, u128 a3) const
{
using VectorInput = NativeFloatingVectorType<128 / VectorSize, VectorSize, NativeFloatingType<128 / VectorSize>>;
auto a = bit_cast<VectorInput>(a1);
auto b = bit_cast<VectorInput>(a2);
auto c = bit_cast<VectorInput>(a3);
// Spec talks about rounding and such, but v8's arm impl just does vmul + vsub with nothing in between.
return bit_cast<u128>(a * b - c);
}
};
template<size_t VectorSize, typename ContinuationOp = Identity>
struct VectorDotProduct {
auto operator()(u128 lhs, u128 rhs) const
template<typename... ContinuationArgs>
auto operator()(u128 lhs, u128 rhs, ContinuationArgs&&... args) const
{
using VectorInput = NativeVectorType<128 / (VectorSize * 2), VectorSize * 2, MakeSigned>;
using VectorResult = NativeVectorType<128 / VectorSize, VectorSize, MakeSigned>;
@ -754,7 +785,7 @@ struct VectorDotProduct {
result[i] = low + high;
}
return bit_cast<u128>(result);
return ContinuationOp { forward<ContinuationArgs>(args)... }(bit_cast<u128>(result));
}
static StringView name() { return "dot"sv; }

File diff suppressed because it is too large Load diff

View file

@ -30,6 +30,7 @@ struct Context {
COWVector<ValueType> elements;
COWVector<bool> datas;
COWVector<ValueType> locals;
COWVector<TagType> tags;
Optional<u32> data_count;
RefPtr<RefRBTree> references { make_ref_counted<RefRBTree>() };
size_t imported_function_count { 0 };
@ -68,6 +69,7 @@ public:
ErrorOr<void, ValidationError> validate(MemorySection const&);
ErrorOr<void, ValidationError> validate(TableSection const&);
ErrorOr<void, ValidationError> validate(CodeSection const&);
ErrorOr<void, ValidationError> validate(TagSection const&);
ErrorOr<void, ValidationError> validate(FunctionSection const&) { return {}; }
ErrorOr<void, ValidationError> validate(DataCountSection const&) { return {}; }
ErrorOr<void, ValidationError> validate(TypeSection const&) { return {}; }
@ -87,10 +89,10 @@ public:
return Errors::invalid("FunctionIndex"sv);
}
ErrorOr<void, ValidationError> validate(MemoryIndex index) const
ErrorOr<MemoryType, ValidationError> validate(MemoryIndex index) const
{
if (index.value() < m_context.memories.size())
return {};
return m_context.memories[index.value()];
return Errors::invalid("MemoryIndex"sv);
}
@ -129,13 +131,20 @@ public:
return Errors::invalid("LocalIndex"sv);
}
ErrorOr<void, ValidationError> validate(TableIndex index) const
ErrorOr<TableType, ValidationError> validate(TableIndex index) const
{
if (index.value() < m_context.tables.size())
return {};
return m_context.tables[index.value()];
return Errors::invalid("TableIndex"sv);
}
ErrorOr<void, ValidationError> validate(TagIndex index) const
{
if (index.value() < m_context.tags.size())
return {};
return Errors::invalid("TagIndex"sv);
}
enum class FrameKind {
Block,
Loop,
@ -293,6 +302,20 @@ public:
ErrorOr<void, ValidationError> validate(TableType const&);
ErrorOr<void, ValidationError> validate(MemoryType const&);
ErrorOr<void, ValidationError> validate(GlobalType const&) { return {}; }
ErrorOr<void, ValidationError> validate(TagType const&);
// Proposal 'memory64'
ErrorOr<void, ValidationError> take_memory_address(Stack& stack, MemoryType const& memory, Instruction::MemoryArgument const& arg)
{
if (memory.limits().address_type() == AddressType::I64) {
TRY((stack.take<ValueType::I64>()));
} else {
if (arg.offset > NumericLimits<u32>::max())
return Errors::out_of_bounds("memory op offset"sv, arg.offset, 0, NumericLimits<u32>::max());
TRY((stack.take<ValueType::I32>()));
}
return {};
}
private:
explicit Validator(Context context)
@ -301,7 +324,13 @@ private:
}
struct Errors {
static ValidationError invalid(StringView name) { return ByteString::formatted("Invalid {}", name); }
static ValidationError invalid(StringView name, SourceLocation location = SourceLocation::current())
{
if constexpr (WASM_VALIDATOR_DEBUG)
return ByteString::formatted("Invalid {} in {}", name, find_instruction_name(location));
else
return ByteString::formatted("Invalid {}", name);
}
template<typename Expected, typename Given>
static ValidationError invalid(StringView name, Expected expected, Given given, SourceLocation location = SourceLocation::current())

View file

@ -19,6 +19,20 @@ static constexpr auto v128_tag = 0x7b;
static constexpr auto function_reference_tag = 0x70;
static constexpr auto extern_reference_tag = 0x6f;
// wasm-gc references
static constexpr auto array_reference_tag = 0x6a;
static constexpr auto struct_reference_tag = 0x6b;
static constexpr auto i31_reference_tag = 0x6c;
static constexpr auto eq_reference_tag = 0x6d;
static constexpr auto any_reference_tag = 0x6e;
static constexpr auto none_reference_tag = 0x71;
static constexpr auto noextern_reference_tag = 0x72;
static constexpr auto nofunc_reference_tag = 0x73;
static constexpr auto noexn_heap_reference_tag = 0x74;
static constexpr auto nullable_reference_tag_tag = 0x63;
static constexpr auto non_nullable_reference_tag_tag = 0x64;
// Function
static constexpr auto function_signature_tag = 0x60;
@ -34,6 +48,7 @@ static constexpr auto extern_function_tag = 0x00;
static constexpr auto extern_table_tag = 0x01;
static constexpr auto extern_memory_tag = 0x02;
static constexpr auto extern_global_tag = 0x03;
static constexpr auto extern_tag_tag = 0x04; // Proposal "exception-handling"
static constexpr auto page_size = 64 * KiB;

View file

@ -22,6 +22,8 @@ namespace Instructions {
M(loop, 0x03, 0, -1) \
M(if_, 0x04, 1, -1) \
M(structured_else, 0x05, -1, -1) \
M(throw_, 0x08, -1, -1) \
M(throw_ref, 0x0a, 1, -1) \
M(structured_end, 0x0b, -1, -1) \
M(br, 0x0c, 0, -1) \
M(br_if, 0x0d, 1, -1) \
@ -29,9 +31,12 @@ namespace Instructions {
M(return_, 0x0f, -1, -1) \
M(call, 0x10, -1, -1) \
M(call_indirect, 0x11, -1, -1) \
M(return_call, 0x12, -1, -1) \
M(return_call_indirect, 0x13, -1, -1) \
M(drop, 0x1a, 1, 0) \
M(select, 0x1b, 3, 1) \
M(select_typed, 0x1c, 3, 1) \
M(try_table, 0x1f, 0, 0) \
M(local_get, 0x20, 0, 1) \
M(local_set, 0x21, 1, 0) \
M(local_tee, 0x22, 1, 1) \
@ -201,262 +206,282 @@ namespace Instructions {
M(ref_func, 0xd2, 0, 1)
// These are synthetic opcodes, they are _not_ seen in wasm with these values.
#define ENUMERATE_MULTI_BYTE_WASM_OPCODES(M) \
M(i32_trunc_sat_f32_s, 0xfc00000000000000ull, 1, 1) \
M(i32_trunc_sat_f32_u, 0xfc00000000000001ull, 1, 1) \
M(i32_trunc_sat_f64_s, 0xfc00000000000002ull, 1, 1) \
M(i32_trunc_sat_f64_u, 0xfc00000000000003ull, 1, 1) \
M(i64_trunc_sat_f32_s, 0xfc00000000000004ull, 1, 1) \
M(i64_trunc_sat_f32_u, 0xfc00000000000005ull, 1, 1) \
M(i64_trunc_sat_f64_s, 0xfc00000000000006ull, 1, 1) \
M(i64_trunc_sat_f64_u, 0xfc00000000000007ull, 1, 1) \
M(memory_init, 0xfc00000000000008ull, 3, 0) \
M(data_drop, 0xfc00000000000009ull, 0, 0) \
M(memory_copy, 0xfc0000000000000aull, 3, 0) \
M(memory_fill, 0xfc0000000000000bull, 3, 0) \
M(table_init, 0xfc0000000000000cull, 3, 0) \
M(elem_drop, 0xfc0000000000000dull, 0, 0) \
M(table_copy, 0xfc0000000000000eull, 3, 0) \
M(table_grow, 0xfc0000000000000full, 2, 1) \
M(table_size, 0xfc00000000000010ull, 0, 1) \
M(table_fill, 0xfc00000000000011ull, 3, 0) \
M(v128_load, 0xfd00000000000000ull, 1, 1) \
M(v128_load8x8_s, 0xfd00000000000001ull, 1, 1) \
M(v128_load8x8_u, 0xfd00000000000002ull, 1, 1) \
M(v128_load16x4_s, 0xfd00000000000003ull, 1, 1) \
M(v128_load16x4_u, 0xfd00000000000004ull, 1, 1) \
M(v128_load32x2_s, 0xfd00000000000005ull, 1, 1) \
M(v128_load32x2_u, 0xfd00000000000006ull, 1, 1) \
M(v128_load8_splat, 0xfd00000000000007ull, 1, 1) \
M(v128_load16_splat, 0xfd00000000000008ull, 1, 1) \
M(v128_load32_splat, 0xfd00000000000009ull, 1, 1) \
M(v128_load64_splat, 0xfd0000000000000aull, 1, 1) \
M(v128_store, 0xfd0000000000000bull, 2, 0) \
M(v128_const, 0xfd0000000000000cull, 0, 1) \
M(i8x16_shuffle, 0xfd0000000000000dull, 2, 1) \
M(i8x16_swizzle, 0xfd0000000000000eull, 2, 1) \
M(i8x16_splat, 0xfd0000000000000full, 1, 1) \
M(i16x8_splat, 0xfd00000000000010ull, 1, 1) \
M(i32x4_splat, 0xfd00000000000011ull, 1, 1) \
M(i64x2_splat, 0xfd00000000000012ull, 1, 1) \
M(f32x4_splat, 0xfd00000000000013ull, 1, 1) \
M(f64x2_splat, 0xfd00000000000014ull, 1, 1) \
M(i8x16_extract_lane_s, 0xfd00000000000015ull, 1, 1) \
M(i8x16_extract_lane_u, 0xfd00000000000016ull, 1, 1) \
M(i8x16_replace_lane, 0xfd00000000000017ull, 2, 1) \
M(i16x8_extract_lane_s, 0xfd00000000000018ull, 1, 1) \
M(i16x8_extract_lane_u, 0xfd00000000000019ull, 1, 1) \
M(i16x8_replace_lane, 0xfd0000000000001aull, 2, 1) \
M(i32x4_extract_lane, 0xfd0000000000001bull, 1, 1) \
M(i32x4_replace_lane, 0xfd0000000000001cull, 2, 1) \
M(i64x2_extract_lane, 0xfd0000000000001dull, 1, 1) \
M(i64x2_replace_lane, 0xfd0000000000001eull, 2, 1) \
M(f32x4_extract_lane, 0xfd0000000000001full, 1, 1) \
M(f32x4_replace_lane, 0xfd00000000000020ull, 2, 1) \
M(f64x2_extract_lane, 0xfd00000000000021ull, 1, 1) \
M(f64x2_replace_lane, 0xfd00000000000022ull, 2, 1) \
M(i8x16_eq, 0xfd00000000000023ull, 2, 1) \
M(i8x16_ne, 0xfd00000000000024ull, 2, 1) \
M(i8x16_lt_s, 0xfd00000000000025ull, 2, 1) \
M(i8x16_lt_u, 0xfd00000000000026ull, 2, 1) \
M(i8x16_gt_s, 0xfd00000000000027ull, 2, 1) \
M(i8x16_gt_u, 0xfd00000000000028ull, 2, 1) \
M(i8x16_le_s, 0xfd00000000000029ull, 2, 1) \
M(i8x16_le_u, 0xfd0000000000002aull, 2, 1) \
M(i8x16_ge_s, 0xfd0000000000002bull, 2, 1) \
M(i8x16_ge_u, 0xfd0000000000002cull, 2, 1) \
M(i16x8_eq, 0xfd0000000000002dull, 2, 1) \
M(i16x8_ne, 0xfd0000000000002eull, 2, 1) \
M(i16x8_lt_s, 0xfd0000000000002full, 2, 1) \
M(i16x8_lt_u, 0xfd00000000000030ull, 2, 1) \
M(i16x8_gt_s, 0xfd00000000000031ull, 2, 1) \
M(i16x8_gt_u, 0xfd00000000000032ull, 2, 1) \
M(i16x8_le_s, 0xfd00000000000033ull, 2, 1) \
M(i16x8_le_u, 0xfd00000000000034ull, 2, 1) \
M(i16x8_ge_s, 0xfd00000000000035ull, 2, 1) \
M(i16x8_ge_u, 0xfd00000000000036ull, 2, 1) \
M(i32x4_eq, 0xfd00000000000037ull, 2, 1) \
M(i32x4_ne, 0xfd00000000000038ull, 2, 1) \
M(i32x4_lt_s, 0xfd00000000000039ull, 2, 1) \
M(i32x4_lt_u, 0xfd0000000000003aull, 2, 1) \
M(i32x4_gt_s, 0xfd0000000000003bull, 2, 1) \
M(i32x4_gt_u, 0xfd0000000000003cull, 2, 1) \
M(i32x4_le_s, 0xfd0000000000003dull, 2, 1) \
M(i32x4_le_u, 0xfd0000000000003eull, 2, 1) \
M(i32x4_ge_s, 0xfd0000000000003full, 2, 1) \
M(i32x4_ge_u, 0xfd00000000000040ull, 2, 1) \
M(f32x4_eq, 0xfd00000000000041ull, 2, 1) \
M(f32x4_ne, 0xfd00000000000042ull, 2, 1) \
M(f32x4_lt, 0xfd00000000000043ull, 2, 1) \
M(f32x4_gt, 0xfd00000000000044ull, 2, 1) \
M(f32x4_le, 0xfd00000000000045ull, 2, 1) \
M(f32x4_ge, 0xfd00000000000046ull, 2, 1) \
M(f64x2_eq, 0xfd00000000000047ull, 2, 1) \
M(f64x2_ne, 0xfd00000000000048ull, 2, 1) \
M(f64x2_lt, 0xfd00000000000049ull, 2, 1) \
M(f64x2_gt, 0xfd0000000000004aull, 2, 1) \
M(f64x2_le, 0xfd0000000000004bull, 2, 1) \
M(f64x2_ge, 0xfd0000000000004cull, 2, 1) \
M(v128_not, 0xfd0000000000004dull, 1, 1) \
M(v128_and, 0xfd0000000000004eull, 2, 1) \
M(v128_andnot, 0xfd0000000000004full, 2, 1) \
M(v128_or, 0xfd00000000000050ull, 2, 1) \
M(v128_xor, 0xfd00000000000051ull, 2, 1) \
M(v128_bitselect, 0xfd00000000000052ull, 3, 1) \
M(v128_any_true, 0xfd00000000000053ull, 1, 1) \
M(v128_load8_lane, 0xfd00000000000054ull, 2, 1) \
M(v128_load16_lane, 0xfd00000000000055ull, 2, 1) \
M(v128_load32_lane, 0xfd00000000000056ull, 2, 1) \
M(v128_load64_lane, 0xfd00000000000057ull, 2, 1) \
M(v128_store8_lane, 0xfd00000000000058ull, 2, 0) \
M(v128_store16_lane, 0xfd00000000000059ull, 2, 0) \
M(v128_store32_lane, 0xfd0000000000005aull, 2, 0) \
M(v128_store64_lane, 0xfd0000000000005bull, 2, 0) \
M(v128_load32_zero, 0xfd0000000000005cull, 1, 1) \
M(v128_load64_zero, 0xfd0000000000005dull, 1, 1) \
M(f32x4_demote_f64x2_zero, 0xfd0000000000005eull, 1, 1) \
M(f64x2_promote_low_f32x4, 0xfd0000000000005full, 1, 1) \
M(i8x16_abs, 0xfd00000000000060ull, 1, 1) \
M(i8x16_neg, 0xfd00000000000061ull, 1, 1) \
M(i8x16_popcnt, 0xfd00000000000062ull, 1, 1) \
M(i8x16_all_true, 0xfd00000000000063ull, 1, 1) \
M(i8x16_bitmask, 0xfd00000000000064ull, 1, 1) \
M(i8x16_narrow_i16x8_s, 0xfd00000000000065ull, 2, 1) \
M(i8x16_narrow_i16x8_u, 0xfd00000000000066ull, 2, 1) \
M(f32x4_ceil, 0xfd00000000000067ull, 1, 1) \
M(f32x4_floor, 0xfd00000000000068ull, 1, 1) \
M(f32x4_trunc, 0xfd00000000000069ull, 1, 1) \
M(f32x4_nearest, 0xfd0000000000006aull, 1, 1) \
M(i8x16_shl, 0xfd0000000000006bull, 2, 1) \
M(i8x16_shr_s, 0xfd0000000000006cull, 2, 1) \
M(i8x16_shr_u, 0xfd0000000000006dull, 2, 1) \
M(i8x16_add, 0xfd0000000000006eull, 2, 1) \
M(i8x16_add_sat_s, 0xfd0000000000006full, 2, 1) \
M(i8x16_add_sat_u, 0xfd00000000000070ull, 2, 1) \
M(i8x16_sub, 0xfd00000000000071ull, 2, 1) \
M(i8x16_sub_sat_s, 0xfd00000000000072ull, 2, 1) \
M(i8x16_sub_sat_u, 0xfd00000000000073ull, 2, 1) \
M(f64x2_ceil, 0xfd00000000000074ull, 1, 1) \
M(f64x2_floor, 0xfd00000000000075ull, 1, 1) \
M(i8x16_min_s, 0xfd00000000000076ull, 2, 1) \
M(i8x16_min_u, 0xfd00000000000077ull, 2, 1) \
M(i8x16_max_s, 0xfd00000000000078ull, 2, 1) \
M(i8x16_max_u, 0xfd00000000000079ull, 2, 1) \
M(f64x2_trunc, 0xfd0000000000007aull, 1, 1) \
M(i8x16_avgr_u, 0xfd0000000000007bull, 2, 1) \
M(i16x8_extadd_pairwise_i8x16_s, 0xfd0000000000007cull, 1, 1) \
M(i16x8_extadd_pairwise_i8x16_u, 0xfd0000000000007dull, 1, 1) \
M(i32x4_extadd_pairwise_i16x8_s, 0xfd0000000000007eull, 1, 1) \
M(i32x4_extadd_pairwise_i16x8_u, 0xfd0000000000007full, 1, 1) \
M(i16x8_abs, 0xfd00000000000080ull, 1, 1) \
M(i16x8_neg, 0xfd00000000000081ull, 1, 1) \
M(i16x8_q15mulr_sat_s, 0xfd00000000000082ull, 2, 1) \
M(i16x8_all_true, 0xfd00000000000083ull, 1, 1) \
M(i16x8_bitmask, 0xfd00000000000084ull, 1, 1) \
M(i16x8_narrow_i32x4_s, 0xfd00000000000085ull, 2, 1) \
M(i16x8_narrow_i32x4_u, 0xfd00000000000086ull, 2, 1) \
M(i16x8_extend_low_i8x16_s, 0xfd00000000000087ull, 1, 1) \
M(i16x8_extend_high_i8x16_s, 0xfd00000000000088ull, 1, 1) \
M(i16x8_extend_low_i8x16_u, 0xfd00000000000089ull, 1, 1) \
M(i16x8_extend_high_i8x16_u, 0xfd0000000000008aull, 1, 1) \
M(i16x8_shl, 0xfd0000000000008bull, 2, 1) \
M(i16x8_shr_s, 0xfd0000000000008cull, 2, 1) \
M(i16x8_shr_u, 0xfd0000000000008dull, 2, 1) \
M(i16x8_add, 0xfd0000000000008eull, 2, 1) \
M(i16x8_add_sat_s, 0xfd0000000000008full, 2, 1) \
M(i16x8_add_sat_u, 0xfd00000000000090ull, 2, 1) \
M(i16x8_sub, 0xfd00000000000091ull, 2, 1) \
M(i16x8_sub_sat_s, 0xfd00000000000092ull, 2, 1) \
M(i16x8_sub_sat_u, 0xfd00000000000093ull, 2, 1) \
M(f64x2_nearest, 0xfd00000000000094ull, 1, 1) \
M(i16x8_mul, 0xfd00000000000095ull, 2, 1) \
M(i16x8_min_s, 0xfd00000000000096ull, 2, 1) \
M(i16x8_min_u, 0xfd00000000000097ull, 2, 1) \
M(i16x8_max_s, 0xfd00000000000098ull, 2, 1) \
M(i16x8_max_u, 0xfd00000000000099ull, 2, 1) \
M(i16x8_avgr_u, 0xfd0000000000009bull, 2, 1) \
M(i16x8_extmul_low_i8x16_s, 0xfd0000000000009cull, 2, 1) \
M(i16x8_extmul_high_i8x16_s, 0xfd0000000000009dull, 2, 1) \
M(i16x8_extmul_low_i8x16_u, 0xfd0000000000009eull, 2, 1) \
M(i16x8_extmul_high_i8x16_u, 0xfd0000000000009full, 2, 1) \
M(i32x4_abs, 0xfd000000000000a0ull, 1, 1) \
M(i32x4_neg, 0xfd000000000000a1ull, 1, 1) \
M(i32x4_all_true, 0xfd000000000000a3ull, 1, 1) \
M(i32x4_bitmask, 0xfd000000000000a4ull, 1, 1) \
M(i32x4_extend_low_i16x8_s, 0xfd000000000000a7ull, 1, 1) \
M(i32x4_extend_high_i16x8_s, 0xfd000000000000a8ull, 1, 1) \
M(i32x4_extend_low_i16x8_u, 0xfd000000000000a9ull, 1, 1) \
M(i32x4_extend_high_i16x8_u, 0xfd000000000000aaull, 1, 1) \
M(i32x4_shl, 0xfd000000000000abull, 2, 1) \
M(i32x4_shr_s, 0xfd000000000000acull, 2, 1) \
M(i32x4_shr_u, 0xfd000000000000adull, 2, 1) \
M(i32x4_add, 0xfd000000000000aeull, 2, 1) \
M(i32x4_sub, 0xfd000000000000b1ull, 2, 1) \
M(i32x4_mul, 0xfd000000000000b5ull, 2, 1) \
M(i32x4_min_s, 0xfd000000000000b6ull, 2, 1) \
M(i32x4_min_u, 0xfd000000000000b7ull, 2, 1) \
M(i32x4_max_s, 0xfd000000000000b8ull, 2, 1) \
M(i32x4_max_u, 0xfd000000000000b9ull, 2, 1) \
M(i32x4_dot_i16x8_s, 0xfd000000000000baull, 2, 1) \
M(i32x4_extmul_low_i16x8_s, 0xfd000000000000bcull, 2, 1) \
M(i32x4_extmul_high_i16x8_s, 0xfd000000000000bdull, 2, 1) \
M(i32x4_extmul_low_i16x8_u, 0xfd000000000000beull, 2, 1) \
M(i32x4_extmul_high_i16x8_u, 0xfd000000000000bfull, 2, 1) \
M(i64x2_abs, 0xfd000000000000c0ull, 1, 1) \
M(i64x2_neg, 0xfd000000000000c1ull, 1, 1) \
M(i64x2_all_true, 0xfd000000000000c3ull, 1, 1) \
M(i64x2_bitmask, 0xfd000000000000c4ull, 1, 1) \
M(i64x2_extend_low_i32x4_s, 0xfd000000000000c7ull, 1, 1) \
M(i64x2_extend_high_i32x4_s, 0xfd000000000000c8ull, 1, 1) \
M(i64x2_extend_low_i32x4_u, 0xfd000000000000c9ull, 1, 1) \
M(i64x2_extend_high_i32x4_u, 0xfd000000000000caull, 1, 1) \
M(i64x2_shl, 0xfd000000000000cbull, 2, 1) \
M(i64x2_shr_s, 0xfd000000000000ccull, 2, 1) \
M(i64x2_shr_u, 0xfd000000000000cdull, 2, 1) \
M(i64x2_add, 0xfd000000000000ceull, 2, 1) \
M(i64x2_sub, 0xfd000000000000d1ull, 2, 1) \
M(i64x2_mul, 0xfd000000000000d5ull, 2, 1) \
M(i64x2_eq, 0xfd000000000000d6ull, 2, 1) \
M(i64x2_ne, 0xfd000000000000d7ull, 2, 1) \
M(i64x2_lt_s, 0xfd000000000000d8ull, 2, 1) \
M(i64x2_gt_s, 0xfd000000000000d9ull, 2, 1) \
M(i64x2_le_s, 0xfd000000000000daull, 2, 1) \
M(i64x2_ge_s, 0xfd000000000000dbull, 2, 1) \
M(i64x2_extmul_low_i32x4_s, 0xfd000000000000dcull, 2, 1) \
M(i64x2_extmul_high_i32x4_s, 0xfd000000000000ddull, 2, 1) \
M(i64x2_extmul_low_i32x4_u, 0xfd000000000000deull, 2, 1) \
M(i64x2_extmul_high_i32x4_u, 0xfd000000000000dfull, 2, 1) \
M(f32x4_abs, 0xfd000000000000e0ull, 1, 1) \
M(f32x4_neg, 0xfd000000000000e1ull, 1, 1) \
M(f32x4_sqrt, 0xfd000000000000e3ull, 1, 1) \
M(f32x4_add, 0xfd000000000000e4ull, 2, 1) \
M(f32x4_sub, 0xfd000000000000e5ull, 2, 1) \
M(f32x4_mul, 0xfd000000000000e6ull, 2, 1) \
M(f32x4_div, 0xfd000000000000e7ull, 2, 1) \
M(f32x4_min, 0xfd000000000000e8ull, 2, 1) \
M(f32x4_max, 0xfd000000000000e9ull, 2, 1) \
M(f32x4_pmin, 0xfd000000000000eaull, 2, 1) \
M(f32x4_pmax, 0xfd000000000000ebull, 2, 1) \
M(f64x2_abs, 0xfd000000000000ecull, 1, 1) \
M(f64x2_neg, 0xfd000000000000edull, 1, 1) \
M(f64x2_sqrt, 0xfd000000000000efull, 1, 1) \
M(f64x2_add, 0xfd000000000000f0ull, 2, 1) \
M(f64x2_sub, 0xfd000000000000f1ull, 2, 1) \
M(f64x2_mul, 0xfd000000000000f2ull, 2, 1) \
M(f64x2_div, 0xfd000000000000f3ull, 2, 1) \
M(f64x2_min, 0xfd000000000000f4ull, 2, 1) \
M(f64x2_max, 0xfd000000000000f5ull, 2, 1) \
M(f64x2_pmin, 0xfd000000000000f6ull, 2, 1) \
M(f64x2_pmax, 0xfd000000000000f7ull, 2, 1) \
M(i32x4_trunc_sat_f32x4_s, 0xfd000000000000f8ull, 1, 1) \
M(i32x4_trunc_sat_f32x4_u, 0xfd000000000000f9ull, 1, 1) \
M(f32x4_convert_i32x4_s, 0xfd000000000000faull, 1, 1) \
M(f32x4_convert_i32x4_u, 0xfd000000000000fbull, 1, 1) \
M(i32x4_trunc_sat_f64x2_s_zero, 0xfd000000000000fcull, 1, 1) \
M(i32x4_trunc_sat_f64x2_u_zero, 0xfd000000000000fdull, 1, 1) \
M(f64x2_convert_low_i32x4_s, 0xfd000000000000feull, 1, 1) \
M(f64x2_convert_low_i32x4_u, 0xfd000000000000ffull, 1, 1) \
/* Synthetic fused insns */ \
#define ENUMERATE_MULTI_BYTE_WASM_OPCODES(M) \
M(i32_trunc_sat_f32_s, 0xfc00000000000000ull, 1, 1) \
M(i32_trunc_sat_f32_u, 0xfc00000000000001ull, 1, 1) \
M(i32_trunc_sat_f64_s, 0xfc00000000000002ull, 1, 1) \
M(i32_trunc_sat_f64_u, 0xfc00000000000003ull, 1, 1) \
M(i64_trunc_sat_f32_s, 0xfc00000000000004ull, 1, 1) \
M(i64_trunc_sat_f32_u, 0xfc00000000000005ull, 1, 1) \
M(i64_trunc_sat_f64_s, 0xfc00000000000006ull, 1, 1) \
M(i64_trunc_sat_f64_u, 0xfc00000000000007ull, 1, 1) \
M(memory_init, 0xfc00000000000008ull, 3, 0) \
M(data_drop, 0xfc00000000000009ull, 0, 0) \
M(memory_copy, 0xfc0000000000000aull, 3, 0) \
M(memory_fill, 0xfc0000000000000bull, 3, 0) \
M(table_init, 0xfc0000000000000cull, 3, 0) \
M(elem_drop, 0xfc0000000000000dull, 0, 0) \
M(table_copy, 0xfc0000000000000eull, 3, 0) \
M(table_grow, 0xfc0000000000000full, 2, 1) \
M(table_size, 0xfc00000000000010ull, 0, 1) \
M(table_fill, 0xfc00000000000011ull, 3, 0) \
M(v128_load, 0xfd00000000000000ull, 1, 1) \
M(v128_load8x8_s, 0xfd00000000000001ull, 1, 1) \
M(v128_load8x8_u, 0xfd00000000000002ull, 1, 1) \
M(v128_load16x4_s, 0xfd00000000000003ull, 1, 1) \
M(v128_load16x4_u, 0xfd00000000000004ull, 1, 1) \
M(v128_load32x2_s, 0xfd00000000000005ull, 1, 1) \
M(v128_load32x2_u, 0xfd00000000000006ull, 1, 1) \
M(v128_load8_splat, 0xfd00000000000007ull, 1, 1) \
M(v128_load16_splat, 0xfd00000000000008ull, 1, 1) \
M(v128_load32_splat, 0xfd00000000000009ull, 1, 1) \
M(v128_load64_splat, 0xfd0000000000000aull, 1, 1) \
M(v128_store, 0xfd0000000000000bull, 2, 0) \
M(v128_const, 0xfd0000000000000cull, 0, 1) \
M(i8x16_shuffle, 0xfd0000000000000dull, 2, 1) \
M(i8x16_swizzle, 0xfd0000000000000eull, 2, 1) \
M(i8x16_splat, 0xfd0000000000000full, 1, 1) \
M(i16x8_splat, 0xfd00000000000010ull, 1, 1) \
M(i32x4_splat, 0xfd00000000000011ull, 1, 1) \
M(i64x2_splat, 0xfd00000000000012ull, 1, 1) \
M(f32x4_splat, 0xfd00000000000013ull, 1, 1) \
M(f64x2_splat, 0xfd00000000000014ull, 1, 1) \
M(i8x16_extract_lane_s, 0xfd00000000000015ull, 1, 1) \
M(i8x16_extract_lane_u, 0xfd00000000000016ull, 1, 1) \
M(i8x16_replace_lane, 0xfd00000000000017ull, 2, 1) \
M(i16x8_extract_lane_s, 0xfd00000000000018ull, 1, 1) \
M(i16x8_extract_lane_u, 0xfd00000000000019ull, 1, 1) \
M(i16x8_replace_lane, 0xfd0000000000001aull, 2, 1) \
M(i32x4_extract_lane, 0xfd0000000000001bull, 1, 1) \
M(i32x4_replace_lane, 0xfd0000000000001cull, 2, 1) \
M(i64x2_extract_lane, 0xfd0000000000001dull, 1, 1) \
M(i64x2_replace_lane, 0xfd0000000000001eull, 2, 1) \
M(f32x4_extract_lane, 0xfd0000000000001full, 1, 1) \
M(f32x4_replace_lane, 0xfd00000000000020ull, 2, 1) \
M(f64x2_extract_lane, 0xfd00000000000021ull, 1, 1) \
M(f64x2_replace_lane, 0xfd00000000000022ull, 2, 1) \
M(i8x16_eq, 0xfd00000000000023ull, 2, 1) \
M(i8x16_ne, 0xfd00000000000024ull, 2, 1) \
M(i8x16_lt_s, 0xfd00000000000025ull, 2, 1) \
M(i8x16_lt_u, 0xfd00000000000026ull, 2, 1) \
M(i8x16_gt_s, 0xfd00000000000027ull, 2, 1) \
M(i8x16_gt_u, 0xfd00000000000028ull, 2, 1) \
M(i8x16_le_s, 0xfd00000000000029ull, 2, 1) \
M(i8x16_le_u, 0xfd0000000000002aull, 2, 1) \
M(i8x16_ge_s, 0xfd0000000000002bull, 2, 1) \
M(i8x16_ge_u, 0xfd0000000000002cull, 2, 1) \
M(i16x8_eq, 0xfd0000000000002dull, 2, 1) \
M(i16x8_ne, 0xfd0000000000002eull, 2, 1) \
M(i16x8_lt_s, 0xfd0000000000002full, 2, 1) \
M(i16x8_lt_u, 0xfd00000000000030ull, 2, 1) \
M(i16x8_gt_s, 0xfd00000000000031ull, 2, 1) \
M(i16x8_gt_u, 0xfd00000000000032ull, 2, 1) \
M(i16x8_le_s, 0xfd00000000000033ull, 2, 1) \
M(i16x8_le_u, 0xfd00000000000034ull, 2, 1) \
M(i16x8_ge_s, 0xfd00000000000035ull, 2, 1) \
M(i16x8_ge_u, 0xfd00000000000036ull, 2, 1) \
M(i32x4_eq, 0xfd00000000000037ull, 2, 1) \
M(i32x4_ne, 0xfd00000000000038ull, 2, 1) \
M(i32x4_lt_s, 0xfd00000000000039ull, 2, 1) \
M(i32x4_lt_u, 0xfd0000000000003aull, 2, 1) \
M(i32x4_gt_s, 0xfd0000000000003bull, 2, 1) \
M(i32x4_gt_u, 0xfd0000000000003cull, 2, 1) \
M(i32x4_le_s, 0xfd0000000000003dull, 2, 1) \
M(i32x4_le_u, 0xfd0000000000003eull, 2, 1) \
M(i32x4_ge_s, 0xfd0000000000003full, 2, 1) \
M(i32x4_ge_u, 0xfd00000000000040ull, 2, 1) \
M(f32x4_eq, 0xfd00000000000041ull, 2, 1) \
M(f32x4_ne, 0xfd00000000000042ull, 2, 1) \
M(f32x4_lt, 0xfd00000000000043ull, 2, 1) \
M(f32x4_gt, 0xfd00000000000044ull, 2, 1) \
M(f32x4_le, 0xfd00000000000045ull, 2, 1) \
M(f32x4_ge, 0xfd00000000000046ull, 2, 1) \
M(f64x2_eq, 0xfd00000000000047ull, 2, 1) \
M(f64x2_ne, 0xfd00000000000048ull, 2, 1) \
M(f64x2_lt, 0xfd00000000000049ull, 2, 1) \
M(f64x2_gt, 0xfd0000000000004aull, 2, 1) \
M(f64x2_le, 0xfd0000000000004bull, 2, 1) \
M(f64x2_ge, 0xfd0000000000004cull, 2, 1) \
M(v128_not, 0xfd0000000000004dull, 1, 1) \
M(v128_and, 0xfd0000000000004eull, 2, 1) \
M(v128_andnot, 0xfd0000000000004full, 2, 1) \
M(v128_or, 0xfd00000000000050ull, 2, 1) \
M(v128_xor, 0xfd00000000000051ull, 2, 1) \
M(v128_bitselect, 0xfd00000000000052ull, 3, 1) \
M(v128_any_true, 0xfd00000000000053ull, 1, 1) \
M(v128_load8_lane, 0xfd00000000000054ull, 2, 1) \
M(v128_load16_lane, 0xfd00000000000055ull, 2, 1) \
M(v128_load32_lane, 0xfd00000000000056ull, 2, 1) \
M(v128_load64_lane, 0xfd00000000000057ull, 2, 1) \
M(v128_store8_lane, 0xfd00000000000058ull, 2, 0) \
M(v128_store16_lane, 0xfd00000000000059ull, 2, 0) \
M(v128_store32_lane, 0xfd0000000000005aull, 2, 0) \
M(v128_store64_lane, 0xfd0000000000005bull, 2, 0) \
M(v128_load32_zero, 0xfd0000000000005cull, 1, 1) \
M(v128_load64_zero, 0xfd0000000000005dull, 1, 1) \
M(f32x4_demote_f64x2_zero, 0xfd0000000000005eull, 1, 1) \
M(f64x2_promote_low_f32x4, 0xfd0000000000005full, 1, 1) \
M(i8x16_abs, 0xfd00000000000060ull, 1, 1) \
M(i8x16_neg, 0xfd00000000000061ull, 1, 1) \
M(i8x16_popcnt, 0xfd00000000000062ull, 1, 1) \
M(i8x16_all_true, 0xfd00000000000063ull, 1, 1) \
M(i8x16_bitmask, 0xfd00000000000064ull, 1, 1) \
M(i8x16_narrow_i16x8_s, 0xfd00000000000065ull, 2, 1) \
M(i8x16_narrow_i16x8_u, 0xfd00000000000066ull, 2, 1) \
M(f32x4_ceil, 0xfd00000000000067ull, 1, 1) \
M(f32x4_floor, 0xfd00000000000068ull, 1, 1) \
M(f32x4_trunc, 0xfd00000000000069ull, 1, 1) \
M(f32x4_nearest, 0xfd0000000000006aull, 1, 1) \
M(i8x16_shl, 0xfd0000000000006bull, 2, 1) \
M(i8x16_shr_s, 0xfd0000000000006cull, 2, 1) \
M(i8x16_shr_u, 0xfd0000000000006dull, 2, 1) \
M(i8x16_add, 0xfd0000000000006eull, 2, 1) \
M(i8x16_add_sat_s, 0xfd0000000000006full, 2, 1) \
M(i8x16_add_sat_u, 0xfd00000000000070ull, 2, 1) \
M(i8x16_sub, 0xfd00000000000071ull, 2, 1) \
M(i8x16_sub_sat_s, 0xfd00000000000072ull, 2, 1) \
M(i8x16_sub_sat_u, 0xfd00000000000073ull, 2, 1) \
M(f64x2_ceil, 0xfd00000000000074ull, 1, 1) \
M(f64x2_floor, 0xfd00000000000075ull, 1, 1) \
M(i8x16_min_s, 0xfd00000000000076ull, 2, 1) \
M(i8x16_min_u, 0xfd00000000000077ull, 2, 1) \
M(i8x16_max_s, 0xfd00000000000078ull, 2, 1) \
M(i8x16_max_u, 0xfd00000000000079ull, 2, 1) \
M(f64x2_trunc, 0xfd0000000000007aull, 1, 1) \
M(i8x16_avgr_u, 0xfd0000000000007bull, 2, 1) \
M(i16x8_extadd_pairwise_i8x16_s, 0xfd0000000000007cull, 1, 1) \
M(i16x8_extadd_pairwise_i8x16_u, 0xfd0000000000007dull, 1, 1) \
M(i32x4_extadd_pairwise_i16x8_s, 0xfd0000000000007eull, 1, 1) \
M(i32x4_extadd_pairwise_i16x8_u, 0xfd0000000000007full, 1, 1) \
M(i16x8_abs, 0xfd00000000000080ull, 1, 1) \
M(i16x8_neg, 0xfd00000000000081ull, 1, 1) \
M(i16x8_q15mulr_sat_s, 0xfd00000000000082ull, 2, 1) \
M(i16x8_all_true, 0xfd00000000000083ull, 1, 1) \
M(i16x8_bitmask, 0xfd00000000000084ull, 1, 1) \
M(i16x8_narrow_i32x4_s, 0xfd00000000000085ull, 2, 1) \
M(i16x8_narrow_i32x4_u, 0xfd00000000000086ull, 2, 1) \
M(i16x8_extend_low_i8x16_s, 0xfd00000000000087ull, 1, 1) \
M(i16x8_extend_high_i8x16_s, 0xfd00000000000088ull, 1, 1) \
M(i16x8_extend_low_i8x16_u, 0xfd00000000000089ull, 1, 1) \
M(i16x8_extend_high_i8x16_u, 0xfd0000000000008aull, 1, 1) \
M(i16x8_shl, 0xfd0000000000008bull, 2, 1) \
M(i16x8_shr_s, 0xfd0000000000008cull, 2, 1) \
M(i16x8_shr_u, 0xfd0000000000008dull, 2, 1) \
M(i16x8_add, 0xfd0000000000008eull, 2, 1) \
M(i16x8_add_sat_s, 0xfd0000000000008full, 2, 1) \
M(i16x8_add_sat_u, 0xfd00000000000090ull, 2, 1) \
M(i16x8_sub, 0xfd00000000000091ull, 2, 1) \
M(i16x8_sub_sat_s, 0xfd00000000000092ull, 2, 1) \
M(i16x8_sub_sat_u, 0xfd00000000000093ull, 2, 1) \
M(f64x2_nearest, 0xfd00000000000094ull, 1, 1) \
M(i16x8_mul, 0xfd00000000000095ull, 2, 1) \
M(i16x8_min_s, 0xfd00000000000096ull, 2, 1) \
M(i16x8_min_u, 0xfd00000000000097ull, 2, 1) \
M(i16x8_max_s, 0xfd00000000000098ull, 2, 1) \
M(i16x8_max_u, 0xfd00000000000099ull, 2, 1) \
M(i16x8_avgr_u, 0xfd0000000000009bull, 2, 1) \
M(i16x8_extmul_low_i8x16_s, 0xfd0000000000009cull, 2, 1) \
M(i16x8_extmul_high_i8x16_s, 0xfd0000000000009dull, 2, 1) \
M(i16x8_extmul_low_i8x16_u, 0xfd0000000000009eull, 2, 1) \
M(i16x8_extmul_high_i8x16_u, 0xfd0000000000009full, 2, 1) \
M(i32x4_abs, 0xfd000000000000a0ull, 1, 1) \
M(i32x4_neg, 0xfd000000000000a1ull, 1, 1) \
M(i32x4_all_true, 0xfd000000000000a3ull, 1, 1) \
M(i32x4_bitmask, 0xfd000000000000a4ull, 1, 1) \
M(i32x4_extend_low_i16x8_s, 0xfd000000000000a7ull, 1, 1) \
M(i32x4_extend_high_i16x8_s, 0xfd000000000000a8ull, 1, 1) \
M(i32x4_extend_low_i16x8_u, 0xfd000000000000a9ull, 1, 1) \
M(i32x4_extend_high_i16x8_u, 0xfd000000000000aaull, 1, 1) \
M(i32x4_shl, 0xfd000000000000abull, 2, 1) \
M(i32x4_shr_s, 0xfd000000000000acull, 2, 1) \
M(i32x4_shr_u, 0xfd000000000000adull, 2, 1) \
M(i32x4_add, 0xfd000000000000aeull, 2, 1) \
M(i32x4_sub, 0xfd000000000000b1ull, 2, 1) \
M(i32x4_mul, 0xfd000000000000b5ull, 2, 1) \
M(i32x4_min_s, 0xfd000000000000b6ull, 2, 1) \
M(i32x4_min_u, 0xfd000000000000b7ull, 2, 1) \
M(i32x4_max_s, 0xfd000000000000b8ull, 2, 1) \
M(i32x4_max_u, 0xfd000000000000b9ull, 2, 1) \
M(i32x4_dot_i16x8_s, 0xfd000000000000baull, 2, 1) \
M(i32x4_extmul_low_i16x8_s, 0xfd000000000000bcull, 2, 1) \
M(i32x4_extmul_high_i16x8_s, 0xfd000000000000bdull, 2, 1) \
M(i32x4_extmul_low_i16x8_u, 0xfd000000000000beull, 2, 1) \
M(i32x4_extmul_high_i16x8_u, 0xfd000000000000bfull, 2, 1) \
M(i64x2_abs, 0xfd000000000000c0ull, 1, 1) \
M(i64x2_neg, 0xfd000000000000c1ull, 1, 1) \
M(i64x2_all_true, 0xfd000000000000c3ull, 1, 1) \
M(i64x2_bitmask, 0xfd000000000000c4ull, 1, 1) \
M(i64x2_extend_low_i32x4_s, 0xfd000000000000c7ull, 1, 1) \
M(i64x2_extend_high_i32x4_s, 0xfd000000000000c8ull, 1, 1) \
M(i64x2_extend_low_i32x4_u, 0xfd000000000000c9ull, 1, 1) \
M(i64x2_extend_high_i32x4_u, 0xfd000000000000caull, 1, 1) \
M(i64x2_shl, 0xfd000000000000cbull, 2, 1) \
M(i64x2_shr_s, 0xfd000000000000ccull, 2, 1) \
M(i64x2_shr_u, 0xfd000000000000cdull, 2, 1) \
M(i64x2_add, 0xfd000000000000ceull, 2, 1) \
M(i64x2_sub, 0xfd000000000000d1ull, 2, 1) \
M(i64x2_mul, 0xfd000000000000d5ull, 2, 1) \
M(i64x2_eq, 0xfd000000000000d6ull, 2, 1) \
M(i64x2_ne, 0xfd000000000000d7ull, 2, 1) \
M(i64x2_lt_s, 0xfd000000000000d8ull, 2, 1) \
M(i64x2_gt_s, 0xfd000000000000d9ull, 2, 1) \
M(i64x2_le_s, 0xfd000000000000daull, 2, 1) \
M(i64x2_ge_s, 0xfd000000000000dbull, 2, 1) \
M(i64x2_extmul_low_i32x4_s, 0xfd000000000000dcull, 2, 1) \
M(i64x2_extmul_high_i32x4_s, 0xfd000000000000ddull, 2, 1) \
M(i64x2_extmul_low_i32x4_u, 0xfd000000000000deull, 2, 1) \
M(i64x2_extmul_high_i32x4_u, 0xfd000000000000dfull, 2, 1) \
M(f32x4_abs, 0xfd000000000000e0ull, 1, 1) \
M(f32x4_neg, 0xfd000000000000e1ull, 1, 1) \
M(f32x4_sqrt, 0xfd000000000000e3ull, 1, 1) \
M(f32x4_add, 0xfd000000000000e4ull, 2, 1) \
M(f32x4_sub, 0xfd000000000000e5ull, 2, 1) \
M(f32x4_mul, 0xfd000000000000e6ull, 2, 1) \
M(f32x4_div, 0xfd000000000000e7ull, 2, 1) \
M(f32x4_min, 0xfd000000000000e8ull, 2, 1) \
M(f32x4_max, 0xfd000000000000e9ull, 2, 1) \
M(f32x4_pmin, 0xfd000000000000eaull, 2, 1) \
M(f32x4_pmax, 0xfd000000000000ebull, 2, 1) \
M(f64x2_abs, 0xfd000000000000ecull, 1, 1) \
M(f64x2_neg, 0xfd000000000000edull, 1, 1) \
M(f64x2_sqrt, 0xfd000000000000efull, 1, 1) \
M(f64x2_add, 0xfd000000000000f0ull, 2, 1) \
M(f64x2_sub, 0xfd000000000000f1ull, 2, 1) \
M(f64x2_mul, 0xfd000000000000f2ull, 2, 1) \
M(f64x2_div, 0xfd000000000000f3ull, 2, 1) \
M(f64x2_min, 0xfd000000000000f4ull, 2, 1) \
M(f64x2_max, 0xfd000000000000f5ull, 2, 1) \
M(f64x2_pmin, 0xfd000000000000f6ull, 2, 1) \
M(f64x2_pmax, 0xfd000000000000f7ull, 2, 1) \
M(i32x4_trunc_sat_f32x4_s, 0xfd000000000000f8ull, 1, 1) \
M(i32x4_trunc_sat_f32x4_u, 0xfd000000000000f9ull, 1, 1) \
M(f32x4_convert_i32x4_s, 0xfd000000000000faull, 1, 1) \
M(f32x4_convert_i32x4_u, 0xfd000000000000fbull, 1, 1) \
M(i32x4_trunc_sat_f64x2_s_zero, 0xfd000000000000fcull, 1, 1) \
M(i32x4_trunc_sat_f64x2_u_zero, 0xfd000000000000fdull, 1, 1) \
M(f64x2_convert_low_i32x4_s, 0xfd000000000000feull, 1, 1) \
M(f64x2_convert_low_i32x4_u, 0xfd000000000000ffull, 1, 1) \
M(i8x16_relaxed_swizzle, 0xfd00000000000100, 2, 1) \
M(i32x4_relaxed_trunc_f32x4_s, 0xfd00000000000101, 1, 1) \
M(i32x4_relaxed_trunc_f32x4_u, 0xfd00000000000102, 1, 1) \
M(i32x4_relaxed_trunc_f64x2_s_zero, 0xfd00000000000103, 1, 1) \
M(i32x4_relaxed_trunc_f64x2_u_zero, 0xfd00000000000104, 1, 1) \
M(f32x4_relaxed_madd, 0xfd00000000000105, 3, 1) \
M(f32x4_relaxed_nmadd, 0xfd00000000000106, 3, 1) \
M(f64x2_relaxed_madd, 0xfd00000000000107, 3, 1) \
M(f64x2_relaxed_nmadd, 0xfd00000000000108, 3, 1) \
M(i8x16_relaxed_laneselect, 0xfd00000000000109, 3, 1) \
M(i16x8_relaxed_laneselect, 0xfd0000000000010a, 3, 1) \
M(i32x4_relaxed_laneselect, 0xfd0000000000010b, 3, 1) \
M(i64x2_relaxed_laneselect, 0xfd0000000000010c, 3, 1) \
M(f32x4_relaxed_min, 0xfd0000000000010d, 2, 1) \
M(f32x4_relaxed_max, 0xfd0000000000010e, 2, 1) \
M(f64x2_relaxed_min, 0xfd0000000000010f, 2, 1) \
M(f64x2_relaxed_max, 0xfd00000000000110, 2, 1) \
M(i16x8_relaxed_q15mulr_s, 0xfd00000000000111, 2, 1) \
M(i16x8_relaxed_dot_i8x16_i7x16_s, 0xfd00000000000112, 2, 1) \
M(i32x4_relaxed_dot_i8x16_i7x16_add_s, 0xfd00000000000113, 3, 1) \
/* Synthetic fused insns */ \
ENUMERATE_SYNTHETIC_INSTRUCTION_OPCODES(M)
#define ENUMERATE_SYNTHETIC_INSTRUCTION_OPCODES(M) \

View file

@ -118,6 +118,22 @@ ParseResult<ValueType> ValueType::parse(Stream& stream)
return ValueType(FunctionReference);
case Constants::extern_reference_tag:
return ValueType(ExternReference);
case Constants::array_reference_tag:
case Constants::struct_reference_tag:
case Constants::i31_reference_tag:
case Constants::eq_reference_tag:
case Constants::any_reference_tag:
case Constants::none_reference_tag:
case Constants::noextern_reference_tag:
case Constants::nofunc_reference_tag:
case Constants::noexn_heap_reference_tag:
// FIXME: Implement these when we support wasm-gc properly.
return ValueType(UnsupportedHeapReference);
case Constants::nullable_reference_tag_tag:
case Constants::non_nullable_reference_tag_tag:
tag = TRY_READ(stream, u8, ParseError::ExpectedKindTag);
(void)tag;
return ValueType(UnsupportedHeapReference);
default:
return ParseError::InvalidTag;
}
@ -151,23 +167,26 @@ ParseResult<Limits> Limits::parse(ConstrainedStream& stream)
ScopeLogger<WASM_BINPARSER_DEBUG> logger("Limits"sv);
auto flag = TRY_READ(stream, u8, ParseError::ExpectedKindTag);
if (flag > 1)
// Proposal 'memory64': flags 0/1 refer to 32-bit limits, flags 4/5 refer to 64-bit limits.
if (flag & ~0b00000101)
return with_eof_check(stream, ParseError::InvalidTag);
auto address_type = (flag & 0b00000100) ? AddressType::I64 : AddressType::I32;
auto min_or_error = stream.read_value<LEB128<u32>>();
if (min_or_error.is_error())
return with_eof_check(stream, ParseError::ExpectedSize);
size_t min = min_or_error.release_value();
Optional<u32> max;
if (flag) {
auto value_or_error = stream.read_value<LEB128<u32>>();
Optional<u64> max;
if (flag & 1) {
auto value_or_error = stream.read_value<LEB128<u64>>();
if (value_or_error.is_error())
return with_eof_check(stream, ParseError::ExpectedSize);
max = value_or_error.release_value();
}
return Limits { static_cast<u32>(min), move(max) };
return Limits { address_type, static_cast<u64>(min), move(max) };
}
ParseResult<MemoryType> MemoryType::parse(ConstrainedStream& stream)
@ -199,6 +218,16 @@ ParseResult<GlobalType> GlobalType::parse(ConstrainedStream& stream)
return GlobalType { type_result, mutable_ == 0x01 };
}
ParseResult<TagType> TagType::parse(ConstrainedStream& stream)
{
ScopeLogger<WASM_BINPARSER_DEBUG> logger("TagType"sv);
auto flags = TRY_READ(stream, u8, ParseError::ExpectedKindTag);
if (flags != 0)
return ParseError::InvalidTag;
auto index = TRY(GenericIndexParser<TypeIndex>::parse(stream));
return TagType { index, static_cast<TagType::Flags>(flags) };
}
ParseResult<BlockType> BlockType::parse(ConstrainedStream& stream)
{
ScopeLogger<WASM_BINPARSER_DEBUG> logger("BlockType"sv);
@ -228,6 +257,38 @@ ParseResult<BlockType> BlockType::parse(ConstrainedStream& stream)
return BlockType { TypeIndex(index_value) };
}
ParseResult<Catch> Catch::parse(ConstrainedStream& stream)
{
ScopeLogger<WASM_BINPARSER_DEBUG> logger("Catch"sv);
auto kind = TRY_READ(stream, u8, ParseError::ExpectedKindTag);
switch (kind) {
case 0: {
// catch x l
auto tag_index = TRY(GenericIndexParser<TagIndex>::parse(stream));
auto label_index = TRY(GenericIndexParser<LabelIndex>::parse(stream));
return Catch { false, tag_index, label_index };
}
case 1: {
// catch_ref x l
auto tag_index = TRY(GenericIndexParser<TagIndex>::parse(stream));
auto label_index = TRY(GenericIndexParser<LabelIndex>::parse(stream));
return Catch { true, tag_index, label_index };
}
case 2: {
// catch_all l
auto label_index = TRY(GenericIndexParser<LabelIndex>::parse(stream));
return Catch { false, {}, label_index };
}
case 3: {
// catch_all_ref l
auto label_index = TRY(GenericIndexParser<LabelIndex>::parse(stream));
return Catch { true, {}, label_index };
}
default:
return ParseError::InvalidTag;
}
}
ParseResult<Instruction> Instruction::parse(ConstrainedStream& stream)
{
ScopeLogger<WASM_BINPARSER_DEBUG> logger("Instruction"sv);
@ -246,6 +307,19 @@ ParseResult<Instruction> Instruction::parse(ConstrainedStream& stream)
opcode, StructuredInstructionArgs { block_type, {}, {} }
};
}
case Instructions::try_table.value(): {
// try_table block_type (catch*) (instruction*) end
auto block_type = TRY(BlockType::parse(stream));
auto catch_types = TRY(parse_vector<Catch>(stream));
auto structured_args = StructuredInstructionArgs { block_type, {}, {} };
return Instruction {
opcode, TryTableArgs { move(structured_args), move(catch_types) }
};
}
case Instructions::throw_.value(): {
auto tag_index = TRY(GenericIndexParser<TagIndex>::parse(stream));
return Instruction { opcode, tag_index };
}
case Instructions::br.value():
case Instructions::br_if.value(): {
// branches with a single label immediate
@ -269,6 +343,17 @@ ParseResult<Instruction> Instruction::parse(ConstrainedStream& stream)
auto table_index = TRY(GenericIndexParser<TableIndex>::parse(stream));
return Instruction { opcode, IndirectCallArgs { type_index, table_index } };
}
case Instructions::return_call.value(): {
// return_call function
auto function_index = TRY(GenericIndexParser<FunctionIndex>::parse(stream));
return Instruction { opcode, function_index };
}
case Instructions::return_call_indirect.value(): {
// return_call_indirect type table
auto type_index = TRY(GenericIndexParser<TypeIndex>::parse(stream));
auto table_index = TRY(GenericIndexParser<TableIndex>::parse(stream));
return Instruction { opcode, IndirectCallArgs { type_index, table_index } };
}
case Instructions::i32_load.value():
case Instructions::i64_load.value():
case Instructions::f32_load.value():
@ -302,7 +387,8 @@ ParseResult<Instruction> Instruction::parse(ConstrainedStream& stream)
memory_index = TRY_READ(stream, LEB128<u32>, ParseError::InvalidInput);
}
auto offset = TRY_READ(stream, LEB128<u32>, ParseError::InvalidInput);
// Proposal 'memory64': memarg offsets are u64 instead of u32.
auto offset = TRY_READ(stream, LEB128<u64>, ParseError::InvalidInput);
return Instruction { opcode, MemoryArgument { align, offset, MemoryIndex(memory_index) } };
}
@ -369,6 +455,7 @@ ParseResult<Instruction> Instruction::parse(ConstrainedStream& stream)
auto index = TRY(GenericIndexParser<FunctionIndex>::parse(stream));
return Instruction { opcode, index };
}
case Instructions::throw_ref.value():
case Instructions::structured_end.value():
case Instructions::structured_else.value():
case Instructions::ref_is_null.value():
@ -593,7 +680,8 @@ ParseResult<Instruction> Instruction::parse(ConstrainedStream& stream)
memory_index = TRY_READ(stream, LEB128<u32>, ParseError::InvalidInput);
}
auto offset = TRY_READ(stream, LEB128<u32>, ParseError::ExpectedIndex);
// Proposal 'memory64': memarg offsets are u64 instead of u32.
auto offset = TRY_READ(stream, LEB128<u64>, ParseError::ExpectedIndex);
return Instruction { full_opcode, MemoryArgument { align, offset, MemoryIndex(memory_index) } };
}
@ -615,7 +703,8 @@ ParseResult<Instruction> Instruction::parse(ConstrainedStream& stream)
memory_index = TRY_READ(stream, LEB128<u32>, ParseError::InvalidInput);
}
auto offset = TRY_READ(stream, LEB128<u32>, ParseError::ExpectedIndex);
// Proposal 'memory64': memarg offsets are u64 instead of u32.
auto offset = TRY_READ(stream, LEB128<u64>, ParseError::ExpectedIndex);
auto index = TRY_READ(stream, u8, ParseError::InvalidInput);
return Instruction { full_opcode, MemoryAndLaneArgument { { align, offset, MemoryIndex(memory_index) }, index } };
@ -850,6 +939,26 @@ ParseResult<Instruction> Instruction::parse(ConstrainedStream& stream)
case Instructions::i32x4_trunc_sat_f64x2_u_zero.value():
case Instructions::f64x2_convert_low_i32x4_s.value():
case Instructions::f64x2_convert_low_i32x4_u.value():
case Instructions::i8x16_relaxed_swizzle.value():
case Instructions::i32x4_relaxed_trunc_f32x4_s.value():
case Instructions::i32x4_relaxed_trunc_f32x4_u.value():
case Instructions::i32x4_relaxed_trunc_f64x2_s_zero.value():
case Instructions::i32x4_relaxed_trunc_f64x2_u_zero.value():
case Instructions::f32x4_relaxed_madd.value():
case Instructions::f32x4_relaxed_nmadd.value():
case Instructions::f64x2_relaxed_madd.value():
case Instructions::f64x2_relaxed_nmadd.value():
case Instructions::i8x16_relaxed_laneselect.value():
case Instructions::i16x8_relaxed_laneselect.value():
case Instructions::i32x4_relaxed_laneselect.value():
case Instructions::i64x2_relaxed_laneselect.value():
case Instructions::f32x4_relaxed_min.value():
case Instructions::f32x4_relaxed_max.value():
case Instructions::f64x2_relaxed_min.value():
case Instructions::f64x2_relaxed_max.value():
case Instructions::i16x8_relaxed_q15mulr_s.value():
case Instructions::i16x8_relaxed_dot_i8x16_i7x16_s.value():
case Instructions::i32x4_relaxed_dot_i8x16_i7x16_add_s.value():
// op
return Instruction { full_opcode };
default:
@ -907,6 +1016,8 @@ ParseResult<ImportSection::Import> ImportSection::Import::parse(ConstrainedStrea
return parse_with_type<MemoryType>(stream, module, name);
case Constants::extern_global_tag:
return parse_with_type<GlobalType>(stream, module, name);
case Constants::extern_tag_tag:
return parse_with_type<TagType>(stream, module, name);
default:
return ParseError::InvalidTag;
}
@ -975,6 +1086,7 @@ ParseResult<Expression> Expression::parse(ConstrainedStream& stream, Optional<si
case Instructions::block.value():
case Instructions::loop.value():
case Instructions::if_.value():
case Instructions::try_table.value():
stack.append(ip);
break;
case Instructions::structured_end.value(): {
@ -982,10 +1094,16 @@ ParseResult<Expression> Expression::parse(ConstrainedStream& stream, Optional<si
instructions.empend(Instructions::synthetic_end_expression); // Synthetic noop to mark the end of the expression.
return Expression { move(instructions) };
}
auto entry = stack.take_last();
auto& args = instructions[entry.value()].arguments().get<Instruction::StructuredInstructionArgs>();
// Patch the end_ip of the last structured instruction
args.end_ip = ip + (args.else_ip.has_value() ? 1 : 0);
auto entry = stack.take_last();
instructions[entry.value()].arguments().visit(
[&](Instruction::StructuredInstructionArgs& args) {
args.end_ip = ip + (args.else_ip.has_value() ? 1 : 0);
},
[&](Instruction::TryTableArgs& args) {
args.try_.end_ip = ip + 1;
},
[](auto&) { VERIFY_NOT_REACHED(); });
break;
}
case Instructions::structured_else.value(): {
@ -1038,6 +1156,8 @@ ParseResult<ExportSection::Export> ExportSection::Export::parse(ConstrainedStrea
return Export { name, ExportDesc { MemoryIndex { index } } };
case Constants::extern_global_tag:
return Export { name, ExportDesc { GlobalIndex { index } } };
case Constants::extern_tag_tag:
return Export { name, ExportDesc { TagIndex { index } } };
default:
return ParseError::InvalidTag;
}
@ -1219,6 +1339,25 @@ ParseResult<DataCountSection> DataCountSection::parse(ConstrainedStream& stream)
return DataCountSection { value };
}
ParseResult<TagSection> TagSection::parse(ConstrainedStream& stream)
{
ScopeLogger<WASM_BINPARSER_DEBUG> logger("TagSection"sv);
// https://webassembly.github.io/exception-handling/core/binary/modules.html#binary-tagsec
auto tags = TRY(parse_vector<Tag>(stream));
return TagSection { move(tags) };
}
ParseResult<TagSection::Tag> TagSection::Tag::parse(ConstrainedStream& stream)
{
// https://webassembly.github.io/exception-handling/core/binary/modules.html#binary-tagsec
ScopeLogger<WASM_BINPARSER_DEBUG> logger("Tag"sv);
auto flag = TRY_READ(stream, u8, ParseError::ExpectedKindTag);
if (flag != 0)
return ParseError::InvalidTag; // currently the only valid flag is 0
auto type_index = TRY(GenericIndexParser<TypeIndex>::parse(stream));
return TagSection::Tag { type_index, static_cast<TagSection::Tag::Flags>(flag) };
}
ParseResult<SectionId> SectionId::parse(Stream& stream)
{
u8 id = TRY_READ(stream, u8, ParseError::ExpectedIndex);
@ -1249,6 +1388,8 @@ ParseResult<SectionId> SectionId::parse(Stream& stream)
return SectionId(SectionIdKind::Data);
case 0x0c:
return SectionId(SectionIdKind::DataCount);
case 0x0d:
return SectionId(SectionIdKind::Tag);
default:
return ParseError::InvalidIndex;
}
@ -1320,14 +1461,15 @@ ParseResult<NonnullRefPtr<Module>> Module::parse(Stream& stream)
case SectionId::SectionIdKind::DataCount:
module.data_count_section() = TRY(DataCountSection::parse(section_stream));
break;
case SectionId::SectionIdKind::Tag:
module.tag_section() = TRY(TagSection::parse(section_stream));
break;
default:
return ParseError::InvalidIndex;
}
if (section_id.kind() != SectionId::SectionIdKind::Custom) {
if (section_id.kind() < last_section_id)
return ParseError::SectionOutOfOrder;
last_section_id = section_id.kind();
}
if (!section_id.can_appear_after(last_section_id))
return ParseError::SectionOutOfOrder;
last_section_id = section_id.kind();
if (section_stream.remaining() != 0)
return ParseError::SectionSizeMismatch;
}

View file

@ -252,7 +252,8 @@ void Printer::print(Wasm::ExportSection::Export const& entry)
[this](FunctionIndex const& index) { print("(function index {})\n", index.value()); },
[this](TableIndex const& index) { print("(table index {})\n", index.value()); },
[this](MemoryIndex const& index) { print("(memory index {})\n", index.value()); },
[this](GlobalIndex const& index) { print("(global index {})\n", index.value()); });
[this](GlobalIndex const& index) { print("(global index {})\n", index.value()); },
[this](TagIndex const& index) { print("(tag index {})\n", index.value()); });
}
print_indent();
print(")\n");
@ -398,6 +399,18 @@ void Printer::print(Wasm::GlobalType const& type)
print(")\n");
}
void Printer::print(Wasm::TagType const& type)
{
print_indent();
print("(type tag\n");
{
TemporaryChange change { m_indent, m_indent + 1 };
print(type.type());
}
print_indent();
print(")\n");
}
void Printer::print(Wasm::ImportSection const& section)
{
if (section.imports().is_empty())
@ -432,6 +445,35 @@ void Printer::print(Wasm::ImportSection::Import const& import)
print(")\n");
}
void Printer::print(Wasm::TagSection const& section)
{
// (section tag\n[ ](tag type)*)
if (section.tags().is_empty())
return;
print_indent();
print("(section tag\n");
{
TemporaryChange change { m_indent, m_indent + 1 };
for (auto& tag : section.tags())
print(tag);
}
print_indent();
print(")\n");
}
void Printer::print(Wasm::TagSection::Tag const& tag)
{
print_indent();
print("(tag (type index {}))\n", tag.type().value());
}
void Printer::print(Wasm::TypeIndex const& index)
{
print_indent();
print("(type index {})\n", index.value());
}
void Printer::print(Wasm::Instruction const& instruction)
{
print_indent();
@ -472,6 +514,16 @@ void Printer::print(Wasm::Instruction const& instruction)
print_indent();
print("(else {}) (end {}))", args.else_ip.has_value() ? ByteString::number(args.else_ip->value()) : "(none)", args.end_ip.value());
},
[&](Instruction::TryTableArgs const& args) {
print("(try_table ");
print(args.try_.block_type);
print(" (catches\n");
TemporaryChange change { m_indent, m_indent + 1 };
for (auto& catch_ : args.catches)
print(catch_);
print_indent();
print(") (end {}))", args.try_.end_ip.value());
},
[&](Instruction::TableBranchArgs const& args) {
print("(table_branch");
for (auto& label : args.labels)
@ -491,6 +543,24 @@ void Printer::print(Wasm::Instruction const& instruction)
}
}
void Printer::print(Catch const& catch_)
{
print_indent();
StringBuilder name_builder;
name_builder.appendff("catch{}{}", catch_.matching_tag_index().has_value() ? ""sv : "_all"sv, catch_.is_ref() ? "_ref"sv : ""sv);
print("({} ", name_builder.string_view());
if (auto index = catch_.matching_tag_index(); index.has_value())
print("(tag index {})", index.value());
print("\n");
{
TemporaryChange change { m_indent, m_indent + 1 };
print_indent();
print("(label index {})\n", catch_.target_label().value());
}
print_indent();
print(")\n");
}
void Printer::print(Wasm::Limits const& limits)
{
print_indent();
@ -566,6 +636,7 @@ void Printer::print(Wasm::Module const& module)
print(module.function_section());
print(module.table_section());
print(module.memory_section());
print(module.tag_section());
print(module.global_section());
print(module.export_section());
print(module.start_section());
@ -682,11 +753,16 @@ void Printer::print(Wasm::Value const& value, Wasm::ValueType const& type)
break;
case ValueType::FunctionReference:
case ValueType::ExternReference:
case ValueType::ExceptionReference:
print("addr({})",
value.to<Reference>().ref().visit(
[](Wasm::Reference::Null const&) { return ByteString("null"); },
[](Wasm::Reference::Exception const&) { return ByteString("exception"); },
[](auto const& ref) { return ByteString::number(ref.address.value()); }));
break;
case ValueType::UnsupportedHeapReference:
print("unsupported-heap-ref");
break;
}
TemporaryChange<size_t> change { m_indent, 0 };
}
@ -705,6 +781,7 @@ void Printer::print(Wasm::Reference const& value)
"addr({})\n",
value.ref().visit(
[](Wasm::Reference::Null const&) { return ByteString("null"); },
[](Wasm::Reference::Exception const&) { return ByteString("exception"); },
[](auto const& ref) { return ByteString::number(ref.address.value()); }));
}
@ -716,12 +793,17 @@ HashMap<Wasm::OpCode, ByteString> Wasm::Names::instruction_names {
{ Instructions::block, "block" },
{ Instructions::loop, "loop" },
{ Instructions::if_, "if" },
{ Instructions::try_table, "try_table" },
{ Instructions::throw_, "throw" },
{ Instructions::throw_ref, "throw_ref" },
{ Instructions::br, "br" },
{ Instructions::br_if, "br.if" },
{ Instructions::br_table, "br.table" },
{ Instructions::return_, "return" },
{ Instructions::call, "call" },
{ Instructions::return_call, "return_call" },
{ Instructions::call_indirect, "call.indirect" },
{ Instructions::return_call_indirect, "return_call.indirect" },
{ Instructions::drop, "drop" },
{ Instructions::select, "select" },
{ Instructions::select_typed, "select.typed" },
@ -1146,6 +1228,26 @@ HashMap<Wasm::OpCode, ByteString> Wasm::Names::instruction_names {
{ Instructions::i32x4_trunc_sat_f64x2_u_zero, "i32x4.trunc_sat_f64x2_u_zero" },
{ Instructions::f64x2_convert_low_i32x4_s, "f64x2.convert_low_i32x4_s" },
{ Instructions::f64x2_convert_low_i32x4_u, "f64x2.convert_low_i32x4_u" },
{ Instructions::i8x16_relaxed_swizzle, "i8x16.relaxed_swizzle" },
{ Instructions::i32x4_relaxed_trunc_f32x4_s, "i32x4.relaxed_trunc_f32x4_s" },
{ Instructions::i32x4_relaxed_trunc_f32x4_u, "i32x4.relaxed_trunc_f32x4_u" },
{ Instructions::i32x4_relaxed_trunc_f64x2_s_zero, "i32x4.relaxed_trunc_f64x2_s_zero" },
{ Instructions::i32x4_relaxed_trunc_f64x2_u_zero, "i32x4.relaxed_trunc_f64x2_u_zero" },
{ Instructions::f32x4_relaxed_madd, "f32x4.relaxed_madd" },
{ Instructions::f32x4_relaxed_nmadd, "f32x4.relaxed_nmadd" },
{ Instructions::f64x2_relaxed_madd, "f64x2.relaxed_madd" },
{ Instructions::f64x2_relaxed_nmadd, "f64x2.relaxed_nmadd" },
{ Instructions::i8x16_relaxed_laneselect, "i8x16.relaxed_laneselect" },
{ Instructions::i16x8_relaxed_laneselect, "i16x8.relaxed_laneselect" },
{ Instructions::i32x4_relaxed_laneselect, "i32x4.relaxed_laneselect" },
{ Instructions::i64x2_relaxed_laneselect, "i64x2.relaxed_laneselect" },
{ Instructions::f32x4_relaxed_min, "f32x4.relaxed_min" },
{ Instructions::f32x4_relaxed_max, "f32x4.relaxed_max" },
{ Instructions::f64x2_relaxed_min, "f64x2.relaxed_min" },
{ Instructions::f64x2_relaxed_max, "f64x2.relaxed_max" },
{ Instructions::i16x8_relaxed_q15mulr_s, "i16x8.relaxed_q15mulr_s" },
{ Instructions::i16x8_relaxed_dot_i8x16_i7x16_s, "i16x8.relaxed_dot_i8x16_i7x16_s" },
{ Instructions::i32x4_relaxed_dot_i8x16_i7x16_add_s, "i32x4.relaxed_dot_i8x16_i7x16_add_s" },
{ Instructions::structured_else, "synthetic:else" },
{ Instructions::structured_end, "synthetic:end" },
{ Instructions::synthetic_i32_add2local, "synthetic:i32.add2local" },

View file

@ -44,6 +44,8 @@ struct WASM_API Printer {
void print(Wasm::GlobalType const&);
void print(Wasm::ImportSection const&);
void print(Wasm::ImportSection::Import const&);
void print(Wasm::TagSection const&);
void print(Wasm::TagSection::Tag const&);
void print(Wasm::Instruction const&);
void print(Wasm::Limits const&);
void print(Wasm::Locals const&);
@ -59,8 +61,11 @@ struct WASM_API Printer {
void print(Wasm::TableType const&);
void print(Wasm::TypeSection const&);
void print(Wasm::ValueType const&);
void print(Wasm::TagType const&);
void print(Wasm::Value const&);
void print(Wasm::Value const&, ValueType const&);
void print(Wasm::Catch const&);
void print(Wasm::TypeIndex const&);
private:
void print_indent();

View file

@ -72,6 +72,7 @@ AK_TYPEDEF_DISTINCT_ORDERED_ID(size_t, FunctionIndex);
AK_TYPEDEF_DISTINCT_ORDERED_ID(size_t, TableIndex);
AK_TYPEDEF_DISTINCT_ORDERED_ID(size_t, ElementIndex);
AK_TYPEDEF_DISTINCT_ORDERED_ID(size_t, MemoryIndex);
AK_TYPEDEF_DISTINCT_ORDERED_ID(size_t, TagIndex);
AK_TYPEDEF_DISTINCT_ORDERED_ID(size_t, LocalIndex);
AK_TYPEDEF_DISTINCT_ORDERED_ID(size_t, GlobalIndex);
AK_TYPEDEF_DISTINCT_ORDERED_ID(size_t, LabelIndex);
@ -167,6 +168,8 @@ public:
V128,
FunctionReference,
ExternReference,
ExceptionReference,
UnsupportedHeapReference, // Stub for wasm-gc proposal's reference types.
};
explicit ValueType(Kind kind)
@ -176,7 +179,7 @@ public:
bool operator==(ValueType const&) const = default;
auto is_reference() const { return m_kind == ExternReference || m_kind == FunctionReference; }
auto is_reference() const { return m_kind == ExternReference || m_kind == FunctionReference || m_kind == UnsupportedHeapReference; }
auto is_vector() const { return m_kind == V128; }
auto is_numeric() const { return !is_reference() && !is_vector(); }
auto kind() const { return m_kind; }
@ -200,6 +203,10 @@ public:
return "funcref";
case ExternReference:
return "externref";
case ExceptionReference:
return "exnref";
case UnsupportedHeapReference:
return "todo.heapref";
}
VERIFY_NOT_REACHED();
}
@ -243,28 +250,42 @@ private:
Vector<ValueType> m_results;
};
// https://webassembly.github.io/memory64/core/bikeshed/#address-type%E2%91%A0
enum class AddressType : u8 {
I32,
I64,
};
// https://webassembly.github.io/spec/core/bikeshed/#limits%E2%91%A5
class Limits {
public:
explicit Limits(u32 min, Optional<u32> max = {})
: m_min(min)
explicit Limits(AddressType address_type, u64 min, Optional<u64> max = {})
: m_address_type(address_type)
, m_min(min)
, m_max(move(max))
{
}
ValueType address_value_type() const
{
return m_address_type == AddressType::I32 ? ValueType(ValueType::I32) : ValueType(ValueType::I64);
}
auto address_type() const { return m_address_type; }
auto min() const { return m_min; }
auto& max() const { return m_max; }
bool is_subset_of(Limits other) const
{
return m_min >= other.min()
&& (!other.max().has_value() || (m_max.has_value() && *m_max <= *other.max()));
&& (!other.max().has_value() || (m_max.has_value() && *m_max <= *other.max()))
&& m_address_type == other.m_address_type;
}
static ParseResult<Limits> parse(ConstrainedStream& stream);
private:
u32 m_min { 0 };
Optional<u32> m_max;
AddressType m_address_type { AddressType::I32 };
u64 m_min { 0 };
Optional<u64> m_max;
};
// https://webassembly.github.io/spec/core/bikeshed/#memory-types%E2%91%A4
@ -322,6 +343,29 @@ private:
bool m_is_mutable { false };
};
// https://webassembly.github.io/exception-handling/core/binary/types.html#tag-types
class TagType {
public:
enum Flags : u8 {
None = 0
};
TagType(TypeIndex type, Flags flags)
: m_flags(flags)
, m_type(type)
{
}
auto& type() const { return m_type; }
auto flags() const { return m_flags; }
static ParseResult<TagType> parse(ConstrainedStream& stream);
private:
Flags m_flags { None };
TypeIndex m_type;
};
// https://webassembly.github.io/spec/core/bikeshed/#binary-blocktype
class BlockType {
public:
@ -372,6 +416,35 @@ private:
};
};
// Proposal "exception-handling"
// https://webassembly.github.io/exception-handling/core/binary/instructions.html
class Catch {
public:
Catch(bool ref, TagIndex index, LabelIndex label) // catch[_ref] x l
: m_matching_tag_index(index)
, m_target_label(label)
, m_is_ref(ref)
{
}
explicit Catch(bool ref, LabelIndex label) // catch_all[_ref] l
: m_target_label(label)
, m_is_ref(ref)
{
}
auto& matching_tag_index() const { return m_matching_tag_index; }
auto& target_label() const { return m_target_label; }
auto is_ref() const { return m_is_ref; }
static ParseResult<Catch> parse(ConstrainedStream& stream);
private:
Optional<TagIndex> m_matching_tag_index; // None for catch_all
LabelIndex m_target_label;
bool m_is_ref = false; // true if catch*_ref
};
// https://webassembly.github.io/spec/core/bikeshed/#binary-instr
// https://webassembly.github.io/spec/core/bikeshed/#reference-instructions%E2%91%A6
// https://webassembly.github.io/spec/core/bikeshed/#parametric-instructions%E2%91%A6
@ -415,7 +488,7 @@ public:
struct MemoryArgument {
u32 align;
u32 offset;
u64 offset;
MemoryIndex memory_index { 0 };
};
@ -443,6 +516,12 @@ public:
MemoryIndex memory_index;
};
// Proposal "exception-handling"
struct TryTableArgs {
StructuredInstructionArgs try_; // "else" unused.
Vector<Catch> catches;
};
struct ShuffleArgument {
explicit ShuffleArgument(u8 (&lanes)[16])
: lanes {
@ -495,6 +574,7 @@ private:
ElementIndex,
FunctionIndex,
GlobalIndex,
TagIndex,
IndirectCallArgs,
LabelIndex,
LaneIndex,
@ -510,6 +590,7 @@ private:
TableElementArgs,
TableIndex,
TableTableArgs,
TryTableArgs,
ValueType,
Vector<ValueType>,
double,
@ -554,6 +635,17 @@ struct CompiledInstructions {
bool direct = false; // true if all dispatches contain handler_ptr, otherwise false and all contain instruction_opcode.
};
template<Enum auto... Vs>
consteval auto as_ordered()
{
using Type = CommonType<decltype(to_underlying(Vs))...>;
Array<Type, sizeof...(Vs)> result;
[&]<Type... Is>(IntegerSequence<Type, Is...>) {
(void)((result[to_underlying(Vs)] = Is), ...);
}(MakeIntegerSequence<Type, static_cast<Type>(sizeof...(Vs))>());
return result;
}
struct SectionId {
public:
enum class SectionIdKind : u8 {
@ -570,14 +662,44 @@ public:
DataCount,
Code,
Data,
Tag,
};
constexpr inline static auto section_order = as_ordered<
SectionIdKind::Type,
SectionIdKind::Import,
SectionIdKind::Function,
SectionIdKind::Table,
SectionIdKind::Memory,
SectionIdKind::Tag,
SectionIdKind::Global,
SectionIdKind::Export,
SectionIdKind::Start,
SectionIdKind::Element,
SectionIdKind::DataCount,
SectionIdKind::Code,
SectionIdKind::Data,
SectionIdKind::Custom>();
explicit SectionId(SectionIdKind kind)
: m_kind(kind)
{
}
SectionIdKind kind() const { return m_kind; }
bool can_appear_after(SectionIdKind other) const
{
if (kind() == SectionIdKind::Custom || other == SectionIdKind::Custom)
return true;
auto index = section_order[to_underlying(kind())];
auto other_index = section_order[to_underlying(other)];
return index >= other_index;
}
SectionIdKind kind() const
{
return m_kind;
}
static ParseResult<SectionId> parse(Stream& stream);
@ -624,7 +746,7 @@ class ImportSection {
public:
class Import {
public:
using ImportDesc = Variant<TypeIndex, TableType, MemoryType, GlobalType, FunctionType>;
using ImportDesc = Variant<TypeIndex, TableType, MemoryType, GlobalType, FunctionType, TagType>;
Import(ByteString module, ByteString name, ImportDesc description)
: m_module(move(module))
, m_name(move(name))
@ -812,7 +934,7 @@ private:
class ExportSection {
private:
using ExportDesc = Variant<FunctionIndex, TableIndex, MemoryIndex, GlobalIndex>;
using ExportDesc = Variant<FunctionIndex, TableIndex, MemoryIndex, GlobalIndex, TagIndex>;
public:
class Export {
@ -1045,6 +1167,43 @@ private:
Optional<u32> m_count;
};
class TagSection {
public:
class Tag {
public:
using Flags = TagType::Flags;
Tag(TypeIndex type, Flags flags)
: m_type(type)
, m_flags(flags)
{
}
auto type() const { return m_type; }
auto flags() const { return m_flags; }
static ParseResult<Tag> parse(ConstrainedStream& stream);
private:
TypeIndex m_type;
Flags m_flags { Flags::None };
};
TagSection() = default;
explicit TagSection(Vector<Tag> tags)
: m_tags(move(tags))
{
}
auto& tags() const { return m_tags; }
static ParseResult<TagSection> parse(ConstrainedStream& stream);
private:
Vector<Tag> m_tags;
};
class WASM_API Module : public RefCounted<Module>
, public Weakable<Module> {
public:
@ -1085,6 +1244,8 @@ public:
auto& data_section() const { return m_data_section; }
auto& data_count_section() { return m_data_count_section; }
auto& data_count_section() const { return m_data_count_section; }
auto& tag_section() { return m_tag_section; }
auto& tag_section() const { return m_tag_section; }
void set_validation_status(ValidationStatus status, Badge<Validator>) { set_validation_status(status); }
ValidationStatus validation_status() const { return m_validation_status; }
@ -1109,6 +1270,7 @@ private:
CodeSection m_code_section;
DataSection m_data_section;
DataCountSection m_data_count_section;
TagSection m_tag_section;
ValidationStatus m_validation_status { ValidationStatus::Unchecked };
Optional<ByteString> m_validation_error;

View file

@ -88,7 +88,8 @@ void Instance::initialize(JS::Realm& realm)
}
m_exports->define_direct_property(name, *object, JS::default_attributes);
});
},
[&](Wasm::TagAddress const&) { dbgln("Not yet implemented: tags"); });
}
MUST(m_exports->set_integrity_level(IntegrityLevel::Frozen));

View file

@ -34,7 +34,7 @@ WebIDL::ExceptionOr<GC::Ref<Memory>> Memory::construct_impl(JS::Realm& realm, Me
if (shared && !descriptor.maximum.has_value())
return vm.throw_completion<JS::TypeError>("Maximum has to be specified for shared memory."sv);
Wasm::Limits limits { descriptor.initial, move(descriptor.maximum) };
Wasm::Limits limits { Wasm::AddressType::I32, descriptor.initial, descriptor.maximum.map([](auto x) -> u64 { return x; }) };
Wasm::MemoryType memory_type { move(limits) };
auto& cache = Detail::get_cache(realm);

View file

@ -45,13 +45,19 @@ WebIDL::ExceptionOr<Vector<ModuleImportDescriptor>> Module::imports(JS::VM&, GC:
auto& imports = module_object->m_compiled_module->module->import_section().imports();
import_objects.ensure_capacity(imports.size());
for (auto& import : imports) {
if (import.description().has<Wasm::TagType>()) {
dbgln("Not yet implemented: importing tags");
continue;
}
// 3.1. Let kind be the string value of the extern type type.
auto const kind = import.description().visit(
[](Wasm::TypeIndex) { return Bindings::ImportExportKind::Function; },
[](Wasm::TableType) { return Bindings::ImportExportKind::Table; },
[](Wasm::MemoryType) { return Bindings::ImportExportKind::Memory; },
[](Wasm::GlobalType) { return Bindings::ImportExportKind::Global; },
[](Wasm::FunctionType) { return Bindings::ImportExportKind::Function; });
[](Wasm::FunctionType) { return Bindings::ImportExportKind::Function; },
[](Wasm::TagType) -> Bindings::ImportExportKind { TODO(); });
// 3.2. Let obj be «[ "module" → moduleName, "name" → name, "kind" → kind ]».
ModuleImportDescriptor descriptor {
@ -78,12 +84,18 @@ WebIDL::ExceptionOr<Vector<ModuleExportDescriptor>> Module::exports(JS::VM&, GC:
auto& exports = module_object->m_compiled_module->module->export_section().entries();
export_objects.ensure_capacity(exports.size());
for (auto& entry : exports) {
if (entry.description().has<Wasm::TagIndex>()) {
dbgln("Not yet implemented: exporting tags");
continue;
}
// 3.1. Let kind be the string value of the extern type type.
auto const kind = entry.description().visit(
[](Wasm::FunctionIndex) { return Bindings::ImportExportKind::Function; },
[](Wasm::TableIndex) { return Bindings::ImportExportKind::Table; },
[](Wasm::MemoryIndex) { return Bindings::ImportExportKind::Memory; },
[](Wasm::GlobalIndex) { return Bindings::ImportExportKind::Global; });
[](Wasm::GlobalIndex) { return Bindings::ImportExportKind::Global; },
[](Wasm::TagIndex) -> Bindings::ImportExportKind { TODO(); });
// 3.2. Let obj be «[ "name" → name, "kind" → kind ]».
ModuleExportDescriptor descriptor {
.name = String::from_utf8_with_replacement_character(entry.name()),

View file

@ -41,7 +41,7 @@ WebIDL::ExceptionOr<GC::Ref<Table>> Table::construct_impl(JS::Realm& realm, Tabl
if (descriptor.maximum.has_value() && descriptor.maximum.value() < descriptor.initial)
return vm.throw_completion<JS::RangeError>("Maximum should not be less than initial in table type"sv);
Wasm::Limits limits { descriptor.initial, move(descriptor.maximum) };
Wasm::Limits limits { Wasm::AddressType::I32, descriptor.initial, descriptor.maximum.map([](auto x) -> u64 { return x; }) };
Wasm::TableType table_type { reference_type, move(limits) };
auto& cache = Detail::get_cache(realm);

View file

@ -617,8 +617,12 @@ JS::ThrowCompletionOr<Wasm::Value> to_webassembly_value(JS::VM& vm, JS::Value va
cache.add_extern_value(extern_addr, value);
return Wasm::Value { Wasm::Reference { Wasm::Reference::Extern { extern_addr } } };
}
case Wasm::ValueType::ExceptionReference:
return Wasm::Value(Wasm::ValueType { Wasm::ValueType::Kind::ExceptionReference });
case Wasm::ValueType::V128:
return vm.throw_completion<JS::TypeError>("Cannot convert a vector value to a javascript value"sv);
case Wasm::ValueType::UnsupportedHeapReference:
return vm.throw_completion<JS::TypeError>("Unsupported heap reference"sv);
}
VERIFY_NOT_REACHED();
@ -636,6 +640,10 @@ Wasm::Value default_webassembly_value(JS::VM& vm, Wasm::ValueType type)
return Wasm::Value(type);
case Wasm::ValueType::ExternReference:
return MUST(to_webassembly_value(vm, JS::js_undefined(), type));
case Wasm::ValueType::ExceptionReference:
return Wasm::Value(type);
case Wasm::ValueType::UnsupportedHeapReference:
return Wasm::Value(type);
}
VERIFY_NOT_REACHED();
}
@ -680,6 +688,8 @@ JS::Value to_js_value(JS::VM& vm, Wasm::Value& wasm_value, Wasm::ValueType type)
return value.release_value();
}
case Wasm::ValueType::V128:
case Wasm::ValueType::ExceptionReference:
case Wasm::ValueType::UnsupportedHeapReference:
VERIFY_NOT_REACHED();
}
VERIFY_NOT_REACHED();

View file

@ -3,7 +3,7 @@
#
if(INCLUDE_WASM_SPEC_TESTS)
set(WASM_SPEC_TEST_COMMIT a8101597d3c3c660086c3cd1eedee608ff18d3c3) # 2025-09-10
set(WASM_SPEC_TEST_COMMIT 4b24564c844e3d34bf46dfcb3c774ee5163e31cc) # 2025-09-10
set(WASM_SPEC_TEST_GZ_URL https://github.com/WebAssembly/testsuite/archive/${WASM_SPEC_TEST_COMMIT}.tar.gz)
set(WASM_SPEC_TEST_GZ_PATH ${CMAKE_BINARY_DIR}/wasm-spec-testsuite.tar.gz CACHE PATH "")
set(WASM_SPEC_TEST_PATH ${CMAKE_CURRENT_BINARY_DIR}/Tests/Fixtures/SpecTests CACHE PATH "")

View file

@ -197,7 +197,7 @@ def escape(s: str) -> str:
def make_description(input_path: Path, name: str, out_path: Path) -> WastDescription:
out_json_path = out_path / f"{name}.json"
result = subprocess.run(
["wast2json", input_path, f"--output={out_json_path}", "--no-check"],
["wast2json", "--enable-all", input_path, f"--output={out_json_path}", "--no-check"],
)
result.check_returncode()
with open(out_json_path, "r") as f:

View file

@ -109,11 +109,11 @@ private:
auto address_f64_f64 = alloc_noop_function(print_f64_f64_type);
s_spec_test_namespace.set({ "spectest", "print_f64_f64", print_f64_f64_type }, Wasm::ExternValue { *address_f64_f64 });
Wasm::TableType table_type { Wasm::ValueType(Wasm::ValueType::FunctionReference), Wasm::Limits(10, 20) };
Wasm::TableType table_type { Wasm::ValueType(Wasm::ValueType::FunctionReference), Wasm::Limits(Wasm::AddressType::I32, 10, 20) };
auto table_address = m_machine.store().allocate(table_type);
s_spec_test_namespace.set({ "spectest", "table", table_type }, Wasm::ExternValue { *table_address });
Wasm::MemoryType memory_type { Wasm::Limits(1, 2) };
Wasm::MemoryType memory_type { Wasm::Limits(Wasm::AddressType::I32, 1, 2) };
auto memory_address = m_machine.store().allocate(memory_type);
s_spec_test_namespace.set({ "spectest", "memory", memory_type }, Wasm::ExternValue { *memory_address });
@ -314,11 +314,16 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyModule::get_export)
}
case Wasm::ValueType::FunctionReference:
case Wasm::ValueType::ExternReference:
case Wasm::ValueType::ExceptionReference: {
auto ref = global->value().to<Wasm::Reference>();
return ref.ref().visit(
[&](Wasm::Reference::Null const&) -> JS::Value { return JS::js_null(); },
[](Wasm::Reference::Exception const&) -> JS::Value { return JS::js_undefined(); },
[&](auto const& ref) -> JS::Value { return JS::Value(static_cast<double>(ref.address.value())); });
}
case Wasm::ValueType::UnsupportedHeapReference:
return vm.throw_completion<JS::TypeError>("Unsupported heap reference"sv);
}
}
return vm.throw_completion<JS::TypeError>(TRY_OR_THROW_OOM(vm, String::formatted("'{}' does not refer to a function or a global", name)));
}
@ -398,6 +403,14 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyModule::wasm_invoke)
}
arguments.append(Wasm::Value(Wasm::Reference { Wasm::Reference::Extern { static_cast<u64>(double_value) } }));
break;
case Wasm::ValueType::Kind::ExceptionReference:
if (argument.is_null())
arguments.append(Wasm::Value(Wasm::Reference { Wasm::Reference::Null { Wasm::ValueType(Wasm::ValueType::Kind::ExceptionReference) } }));
else
return vm.throw_completion<JS::TypeError>("Exception references are not supported"sv);
break;
case Wasm::ValueType::Kind::UnsupportedHeapReference:
return vm.throw_completion<JS::TypeError>("GC Heap references are not supported"sv);
}
}
@ -412,7 +425,7 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyModule::wasm_invoke)
if (result.values().is_empty())
return JS::js_null();
auto to_js_value = [&](Wasm::Value const& value, Wasm::ValueType type) {
auto to_js_value = [&](Wasm::Value const& value, Wasm::ValueType type) -> JS::ThrowCompletionOr<JS::Value> {
switch (type.kind()) {
case Wasm::ValueType::I32:
return JS::Value(static_cast<double>(value.to<i32>()));
@ -431,7 +444,11 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyModule::wasm_invoke)
}
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())); });
return (value.to<Wasm::Reference>()).ref().visit([&](Wasm::Reference::Null) { return JS::js_null(); }, [&](Wasm::Reference::Exception) { return JS::Value(); }, [&](auto const& ref) { return JS::Value(static_cast<double>(ref.address.value())); });
case Wasm::ValueType::ExceptionReference:
return JS::js_null();
case Wasm::ValueType::UnsupportedHeapReference:
return vm.throw_completion<JS::TypeError>("Unsupported heap reference"sv);
}
VERIFY_NOT_REACHED();
};
@ -442,6 +459,6 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyModule::wasm_invoke)
size_t i = 0;
return JS::Array::create_from<Wasm::Value>(*vm.current_realm(), result.values(), [&](Wasm::Value value) {
auto value_type = type->results()[i++];
return to_js_value(value, value_type);
return MUST(to_js_value(value, value_type));
});
}

View file

@ -233,6 +233,8 @@ static ErrorOr<ParsedValue> parse_value(StringView spec)
case Wasm::ValueType::V128:
case Wasm::ValueType::FunctionReference:
case Wasm::ValueType::ExternReference:
case Wasm::ValueType::ExceptionReference:
case Wasm::ValueType::UnsupportedHeapReference:
VERIFY_NOT_REACHED();
}
last_value = parsed.value.value();