LibWasm: Implement the multi-memory proposal

This commit is contained in:
Ali Mohammad Pur 2023-10-23 00:19:28 +03:30 committed by Andreas Kling
parent f4515aae80
commit 22d411345d
Notes: sideshowbarker 2024-07-17 08:55:54 +09:00
5 changed files with 221 additions and 139 deletions

View File

@ -88,13 +88,13 @@ void BytecodeInterpreter::branch_to_label(Configuration& configuration, LabelInd
template<typename ReadType, typename PushType>
void BytecodeInterpreter::load_and_push(Configuration& configuration, Instruction const& instruction)
{
auto& address = configuration.frame().module().memories().first();
auto& arg = instruction.arguments().get<Instruction::MemoryArgument>();
auto& address = configuration.frame().module().memories()[arg.memory_index.value()];
auto memory = configuration.store().get(address);
if (!memory) {
m_trap = Trap { "Nonexistent memory" };
return;
}
auto& arg = instruction.arguments().get<Instruction::MemoryArgument>();
auto& entry = configuration.stack().peek();
auto base = entry.get<Value>().to<i32>();
if (!base.has_value()) {
@ -123,13 +123,13 @@ ALWAYS_INLINE static TDst convert_vector(TSrc v)
template<size_t M, size_t N, template<typename> typename SetSign>
void BytecodeInterpreter::load_and_push_mxn(Configuration& configuration, Instruction const& instruction)
{
auto& address = configuration.frame().module().memories().first();
auto& arg = instruction.arguments().get<Instruction::MemoryArgument>();
auto& address = configuration.frame().module().memories()[arg.memory_index.value()];
auto memory = configuration.store().get(address);
if (!memory) {
m_trap = Trap { "Nonexistent memory" };
return;
}
auto& arg = instruction.arguments().get<Instruction::MemoryArgument>();
auto& entry = configuration.stack().peek();
auto base = entry.get<Value>().to<i32>();
if (!base.has_value()) {
@ -161,13 +161,13 @@ void BytecodeInterpreter::load_and_push_mxn(Configuration& configuration, Instru
template<size_t M>
void BytecodeInterpreter::load_and_push_m_splat(Configuration& configuration, Instruction const& instruction)
{
auto& address = configuration.frame().module().memories().first();
auto& arg = instruction.arguments().get<Instruction::MemoryArgument>();
auto& address = configuration.frame().module().memories()[arg.memory_index.value()];
auto memory = configuration.store().get(address);
if (!memory) {
m_trap = Trap { "Nonexistent memory" };
return;
}
auto& arg = instruction.arguments().get<Instruction::MemoryArgument>();
auto& entry = configuration.stack().peek();
auto base = entry.get<Value>().to<i32>();
if (!base.has_value()) {
@ -384,9 +384,9 @@ void BytecodeInterpreter::pop_and_store(Configuration& configuration, Instructio
void BytecodeInterpreter::store_to_memory(Configuration& configuration, Instruction const& instruction, ReadonlyBytes data, i32 base)
{
auto& address = configuration.frame().module().memories().first();
auto memory = configuration.store().get(address);
auto& arg = instruction.arguments().get<Instruction::MemoryArgument>();
auto& address = configuration.frame().module().memories()[arg.memory_index.value()];
auto memory = configuration.store().get(address);
u64 instance_address = static_cast<u64>(bit_cast<u32>(base)) + arg.offset;
Checked addition { instance_address };
addition += data.size();
@ -745,7 +745,8 @@ void BytecodeInterpreter::interpret(Configuration& configuration, InstructionPoi
return;
}
case Instructions::memory_size.value(): {
auto address = configuration.frame().module().memories()[0];
auto& args = instruction.arguments().get<Instruction::MemoryIndexArgument>();
auto address = configuration.frame().module().memories()[args.memory_index.value()];
auto instance = configuration.store().get(address);
auto pages = instance->size() / Constants::page_size;
dbgln_if(WASM_TRACE_DEBUG, "memory.size -> stack({})", pages);
@ -753,7 +754,8 @@ void BytecodeInterpreter::interpret(Configuration& configuration, InstructionPoi
return;
}
case Instructions::memory_grow.value(): {
auto address = configuration.frame().module().memories()[0];
auto& args = instruction.arguments().get<Instruction::MemoryIndexArgument>();
auto address = configuration.frame().module().memories()[args.memory_index.value()];
auto instance = configuration.store().get(address);
i32 old_pages = instance->size() / Constants::page_size;
auto& entry = configuration.stack().peek();
@ -767,7 +769,8 @@ void BytecodeInterpreter::interpret(Configuration& configuration, InstructionPoi
}
// https://webassembly.github.io/spec/core/bikeshed/#exec-memory-fill
case Instructions::memory_fill.value(): {
auto address = configuration.frame().module().memories()[0];
auto& args = instruction.arguments().get<Instruction::MemoryIndexArgument>();
auto address = configuration.frame().module().memories()[args.memory_index.value()];
auto instance = configuration.store().get(address);
auto count = configuration.stack().pop().get<Value>().to<i32>().value();
auto value = configuration.stack().pop().get<Value>().to<i32>().value();
@ -790,31 +793,35 @@ void BytecodeInterpreter::interpret(Configuration& configuration, InstructionPoi
}
// https://webassembly.github.io/spec/core/bikeshed/#exec-memory-copy
case Instructions::memory_copy.value(): {
auto address = configuration.frame().module().memories()[0];
auto instance = configuration.store().get(address);
auto& args = instruction.arguments().get<Instruction::MemoryCopyArgs>();
auto source_address = configuration.frame().module().memories()[args.src_index.value()];
auto destination_address = configuration.frame().module().memories()[args.dst_index.value()];
auto source_instance = configuration.store().get(source_address);
auto destination_instance = configuration.store().get(destination_address);
auto count = configuration.stack().pop().get<Value>().to<i32>().value();
auto source_offset = configuration.stack().pop().get<Value>().to<i32>().value();
auto destination_offset = configuration.stack().pop().get<Value>().to<i32>().value();
TRAP_IF_NOT(static_cast<size_t>(source_offset + count) <= instance->data().size());
TRAP_IF_NOT(static_cast<size_t>(destination_offset + count) <= instance->data().size());
TRAP_IF_NOT(static_cast<size_t>(source_offset + count) <= source_instance->data().size());
TRAP_IF_NOT(static_cast<size_t>(destination_offset + count) <= destination_instance->data().size());
if (count == 0)
return;
Instruction synthetic_store_instruction {
Instructions::i32_store8,
Instruction::MemoryArgument { 0, 0 }
Instruction::MemoryArgument { 0, 0, args.dst_index }
};
if (destination_offset <= source_offset) {
for (auto i = 0; i < count; ++i) {
auto value = instance->data()[source_offset + i];
auto value = source_instance->data()[source_offset + i];
store_to_memory(configuration, synthetic_store_instruction, { &value, sizeof(value) }, destination_offset + i);
}
} else {
for (auto i = count - 1; i >= 0; --i) {
auto value = instance->data()[source_offset + i];
auto value = source_instance->data()[source_offset + i];
store_to_memory(configuration, synthetic_store_instruction, { &value, sizeof(value) }, destination_offset + i);
}
}
@ -823,8 +830,8 @@ void BytecodeInterpreter::interpret(Configuration& configuration, InstructionPoi
}
// https://webassembly.github.io/spec/core/bikeshed/#exec-memory-init
case Instructions::memory_init.value(): {
auto data_index = instruction.arguments().get<DataIndex>();
auto& data_address = configuration.frame().module().datas()[data_index.value()];
auto& args = instruction.arguments().get<Instruction::MemoryInitArgs>();
auto& data_address = configuration.frame().module().datas()[args.data_index.value()];
auto& data = *configuration.store().get(data_address);
auto count = *configuration.stack().pop().get<Value>().to<i32>();
auto source_offset = *configuration.stack().pop().get<Value>().to<i32>();
@ -836,7 +843,7 @@ void BytecodeInterpreter::interpret(Configuration& configuration, InstructionPoi
Instruction synthetic_store_instruction {
Instructions::i32_store8,
Instruction::MemoryArgument { 0, 0 }
Instruction::MemoryArgument { 0, 0, args.memory_index }
};
for (size_t i = 0; i < (size_t)count; ++i) {

View File

@ -148,11 +148,6 @@ ErrorOr<void, ValidationError> Validator::validate(Module& module)
}
}
if (m_context.memories.size() > 1) {
module.set_validation_status(Module::ValidationStatus::Invalid, {});
return Errors::out_of_bounds("memory section count"sv, m_context.memories.size(), 1, 1);
}
module.set_validation_status(Module::ValidationStatus::Valid, {});
return {};
}
@ -1577,9 +1572,10 @@ VALIDATE_INSTRUCTION(elem_drop)
// https://webassembly.github.io/spec/core/bikeshed/#memory-instructions%E2%91%A2
VALIDATE_INSTRUCTION(i32_load)
{
TRY(validate(MemoryIndex { 0 }));
auto& arg = instruction.arguments().get<Instruction::MemoryArgument>();
TRY(validate(arg.memory_index));
if ((1ull << arg.align) > sizeof(i32))
return Errors::out_of_bounds("memory op alignment"sv, 1ull << arg.align, 0, sizeof(i32));
@ -1590,9 +1586,10 @@ VALIDATE_INSTRUCTION(i32_load)
VALIDATE_INSTRUCTION(i64_load)
{
TRY(validate(MemoryIndex { 0 }));
auto& arg = instruction.arguments().get<Instruction::MemoryArgument>();
TRY(validate(arg.memory_index));
if ((1ull << arg.align) > sizeof(i64))
return Errors::out_of_bounds("memory op alignment"sv, 1ull << arg.align, 0, sizeof(i64));
@ -1603,9 +1600,10 @@ VALIDATE_INSTRUCTION(i64_load)
VALIDATE_INSTRUCTION(f32_load)
{
TRY(validate(MemoryIndex { 0 }));
auto& arg = instruction.arguments().get<Instruction::MemoryArgument>();
TRY(validate(arg.memory_index));
if ((1ull << arg.align) > sizeof(float))
return Errors::out_of_bounds("memory op alignment"sv, 1ull << arg.align, 0, sizeof(float));
@ -1616,9 +1614,10 @@ VALIDATE_INSTRUCTION(f32_load)
VALIDATE_INSTRUCTION(f64_load)
{
TRY(validate(MemoryIndex { 0 }));
auto& arg = instruction.arguments().get<Instruction::MemoryArgument>();
TRY(validate(arg.memory_index));
if ((1ull << arg.align) > sizeof(double))
return Errors::out_of_bounds("memory op alignment"sv, 1ull << arg.align, 0, sizeof(double));
@ -1629,9 +1628,10 @@ VALIDATE_INSTRUCTION(f64_load)
VALIDATE_INSTRUCTION(i32_load16_s)
{
TRY(validate(MemoryIndex { 0 }));
auto& arg = instruction.arguments().get<Instruction::MemoryArgument>();
TRY(validate(arg.memory_index));
if ((1ull << arg.align) > 16 / 8)
return Errors::out_of_bounds("memory op alignment"sv, 1ull << arg.align, 0, 16 / 8);
@ -1642,9 +1642,10 @@ VALIDATE_INSTRUCTION(i32_load16_s)
VALIDATE_INSTRUCTION(i32_load16_u)
{
TRY(validate(MemoryIndex { 0 }));
auto& arg = instruction.arguments().get<Instruction::MemoryArgument>();
TRY(validate(arg.memory_index));
if ((1ull << arg.align) > 16 / 8)
return Errors::out_of_bounds("memory op alignment"sv, 1ull << arg.align, 0, 16 / 8);
@ -1655,9 +1656,10 @@ VALIDATE_INSTRUCTION(i32_load16_u)
VALIDATE_INSTRUCTION(i32_load8_s)
{
TRY(validate(MemoryIndex { 0 }));
auto& arg = instruction.arguments().get<Instruction::MemoryArgument>();
TRY(validate(arg.memory_index));
if ((1ull << arg.align) > 8 / 8)
return Errors::out_of_bounds("memory op alignment"sv, 1ull << arg.align, 0, 8 / 8);
@ -1668,9 +1670,10 @@ VALIDATE_INSTRUCTION(i32_load8_s)
VALIDATE_INSTRUCTION(i32_load8_u)
{
TRY(validate(MemoryIndex { 0 }));
auto& arg = instruction.arguments().get<Instruction::MemoryArgument>();
TRY(validate(arg.memory_index));
if ((1ull << arg.align) > 8 / 8)
return Errors::out_of_bounds("memory op alignment"sv, 1ull << arg.align, 0, 8 / 8);
@ -1681,9 +1684,10 @@ VALIDATE_INSTRUCTION(i32_load8_u)
VALIDATE_INSTRUCTION(i64_load32_s)
{
TRY(validate(MemoryIndex { 0 }));
auto& arg = instruction.arguments().get<Instruction::MemoryArgument>();
TRY(validate(arg.memory_index));
if ((1ull << arg.align) > 32 / 8)
return Errors::out_of_bounds("memory op alignment"sv, 1ull << arg.align, 0, 32 / 8);
@ -1694,9 +1698,10 @@ VALIDATE_INSTRUCTION(i64_load32_s)
VALIDATE_INSTRUCTION(i64_load32_u)
{
TRY(validate(MemoryIndex { 0 }));
auto& arg = instruction.arguments().get<Instruction::MemoryArgument>();
TRY(validate(arg.memory_index));
if ((1ull << arg.align) > 32 / 8)
return Errors::out_of_bounds("memory op alignment"sv, 1ull << arg.align, 0, 32 / 8);
@ -1707,9 +1712,10 @@ VALIDATE_INSTRUCTION(i64_load32_u)
VALIDATE_INSTRUCTION(i64_load16_s)
{
TRY(validate(MemoryIndex { 0 }));
auto& arg = instruction.arguments().get<Instruction::MemoryArgument>();
TRY(validate(arg.memory_index));
if ((1ull << arg.align) > 16 / 8)
return Errors::out_of_bounds("memory op alignment"sv, 1ull << arg.align, 0, 16 / 8);
@ -1720,9 +1726,10 @@ VALIDATE_INSTRUCTION(i64_load16_s)
VALIDATE_INSTRUCTION(i64_load16_u)
{
TRY(validate(MemoryIndex { 0 }));
auto& arg = instruction.arguments().get<Instruction::MemoryArgument>();
TRY(validate(arg.memory_index));
if ((1ull << arg.align) > 16 / 8)
return Errors::out_of_bounds("memory op alignment"sv, 1ull << arg.align, 0, 16 / 8);
@ -1733,9 +1740,10 @@ VALIDATE_INSTRUCTION(i64_load16_u)
VALIDATE_INSTRUCTION(i64_load8_s)
{
TRY(validate(MemoryIndex { 0 }));
auto& arg = instruction.arguments().get<Instruction::MemoryArgument>();
TRY(validate(arg.memory_index));
if ((1ull << arg.align) > 8 / 8)
return Errors::out_of_bounds("memory op alignment"sv, 1ull << arg.align, 0, 8 / 8);
@ -1746,9 +1754,10 @@ VALIDATE_INSTRUCTION(i64_load8_s)
VALIDATE_INSTRUCTION(i64_load8_u)
{
TRY(validate(MemoryIndex { 0 }));
auto& arg = instruction.arguments().get<Instruction::MemoryArgument>();
TRY(validate(arg.memory_index));
if ((1ull << arg.align) > 8 / 8)
return Errors::out_of_bounds("memory op alignment"sv, 1ull << arg.align, 0, 8 / 8);
@ -1759,9 +1768,10 @@ VALIDATE_INSTRUCTION(i64_load8_u)
VALIDATE_INSTRUCTION(i32_store)
{
TRY(validate(MemoryIndex { 0 }));
auto& arg = instruction.arguments().get<Instruction::MemoryArgument>();
TRY(validate(arg.memory_index));
if ((1ull << arg.align) > sizeof(i32))
return Errors::out_of_bounds("memory op alignment"sv, 1ull << arg.align, 0, sizeof(i32));
@ -1772,9 +1782,10 @@ VALIDATE_INSTRUCTION(i32_store)
VALIDATE_INSTRUCTION(i64_store)
{
TRY(validate(MemoryIndex { 0 }));
auto& arg = instruction.arguments().get<Instruction::MemoryArgument>();
TRY(validate(arg.memory_index));
if ((1ull << arg.align) > sizeof(i64))
return Errors::out_of_bounds("memory op alignment"sv, 1ull << arg.align, 0, sizeof(i64));
@ -1785,9 +1796,10 @@ VALIDATE_INSTRUCTION(i64_store)
VALIDATE_INSTRUCTION(f32_store)
{
TRY(validate(MemoryIndex { 0 }));
auto& arg = instruction.arguments().get<Instruction::MemoryArgument>();
TRY(validate(arg.memory_index));
if ((1ull << arg.align) > sizeof(float))
return Errors::out_of_bounds("memory op alignment"sv, 1ull << arg.align, 0, sizeof(float));
@ -1798,9 +1810,10 @@ VALIDATE_INSTRUCTION(f32_store)
VALIDATE_INSTRUCTION(f64_store)
{
TRY(validate(MemoryIndex { 0 }));
auto& arg = instruction.arguments().get<Instruction::MemoryArgument>();
TRY(validate(arg.memory_index));
if ((1ull << arg.align) > sizeof(double))
return Errors::out_of_bounds("memory op alignment"sv, 1ull << arg.align, 0, sizeof(double));
@ -1811,9 +1824,10 @@ VALIDATE_INSTRUCTION(f64_store)
VALIDATE_INSTRUCTION(i32_store16)
{
TRY(validate(MemoryIndex { 0 }));
auto& arg = instruction.arguments().get<Instruction::MemoryArgument>();
TRY(validate(arg.memory_index));
if ((1ull << arg.align) > 16 / 8)
return Errors::out_of_bounds("memory op alignment"sv, 1ull << arg.align, 0, 16 / 8);
@ -1824,9 +1838,10 @@ VALIDATE_INSTRUCTION(i32_store16)
VALIDATE_INSTRUCTION(i32_store8)
{
TRY(validate(MemoryIndex { 0 }));
auto& arg = instruction.arguments().get<Instruction::MemoryArgument>();
TRY(validate(arg.memory_index));
if ((1ull << arg.align) > 8 / 8)
return Errors::out_of_bounds("memory op alignment"sv, 1ull << arg.align, 0, 8 / 8);
@ -1837,9 +1852,10 @@ VALIDATE_INSTRUCTION(i32_store8)
VALIDATE_INSTRUCTION(i64_store32)
{
TRY(validate(MemoryIndex { 0 }));
auto& arg = instruction.arguments().get<Instruction::MemoryArgument>();
TRY(validate(arg.memory_index));
if ((1ull << arg.align) > 32 / 8)
return Errors::out_of_bounds("memory op alignment"sv, 1ull << arg.align, 0, 32 / 8);
@ -1850,9 +1866,10 @@ VALIDATE_INSTRUCTION(i64_store32)
VALIDATE_INSTRUCTION(i64_store16)
{
TRY(validate(MemoryIndex { 0 }));
auto& arg = instruction.arguments().get<Instruction::MemoryArgument>();
TRY(validate(arg.memory_index));
if ((1ull << arg.align) > 16 / 8)
return Errors::out_of_bounds("memory op alignment"sv, 1ull << arg.align, 0, 16 / 8);
@ -1863,9 +1880,10 @@ VALIDATE_INSTRUCTION(i64_store16)
VALIDATE_INSTRUCTION(i64_store8)
{
TRY(validate(MemoryIndex { 0 }));
auto& arg = instruction.arguments().get<Instruction::MemoryArgument>();
TRY(validate(arg.memory_index));
if ((1ull << arg.align) > 8 / 8)
return Errors::out_of_bounds("memory op alignment"sv, 1ull << arg.align, 0, 8 / 8);
@ -1876,7 +1894,7 @@ VALIDATE_INSTRUCTION(i64_store8)
VALIDATE_INSTRUCTION(memory_size)
{
TRY(validate(MemoryIndex { 0 }));
TRY(validate(instruction.arguments().get<Instruction::MemoryIndexArgument>().memory_index));
stack.append(ValueType(ValueType::I32));
return {};
@ -1884,7 +1902,8 @@ VALIDATE_INSTRUCTION(memory_size)
VALIDATE_INSTRUCTION(memory_grow)
{
TRY(validate(MemoryIndex { 0 }));
TRY(validate(instruction.arguments().get<Instruction::MemoryIndexArgument>().memory_index));
TRY((stack.take<ValueType::I32>()));
stack.append(ValueType(ValueType::I32));
@ -1893,7 +1912,7 @@ VALIDATE_INSTRUCTION(memory_grow)
VALIDATE_INSTRUCTION(memory_fill)
{
TRY(validate(MemoryIndex { 0 }));
TRY(validate(instruction.arguments().get<Instruction::MemoryIndexArgument>().memory_index));
TRY((stack.take<ValueType::I32, ValueType::I32, ValueType::I32>()));
@ -1902,7 +1921,9 @@ VALIDATE_INSTRUCTION(memory_fill)
VALIDATE_INSTRUCTION(memory_copy)
{
TRY(validate(MemoryIndex { 0 }));
auto& args = instruction.arguments().get<Instruction::MemoryCopyArgs>();
TRY(validate(args.src_index));
TRY(validate(args.dst_index));
TRY((stack.take<ValueType::I32, ValueType::I32, ValueType::I32>()));
@ -1911,10 +1932,11 @@ VALIDATE_INSTRUCTION(memory_copy)
VALIDATE_INSTRUCTION(memory_init)
{
TRY(validate(MemoryIndex { 0 }));
auto index = instruction.arguments().get<DataIndex>();
TRY(validate(index));
auto& args = instruction.arguments().get<Instruction::MemoryInitArgs>();
TRY(validate(args.memory_index));
TRY(validate(args.data_index));
TRY((stack.take<ValueType::I32, ValueType::I32, ValueType::I32>()));
@ -2183,9 +2205,10 @@ VALIDATE_INSTRUCTION(call_indirect)
VALIDATE_INSTRUCTION(v128_load)
{
TRY(validate(MemoryIndex { 0 }));
auto& arg = instruction.arguments().get<Instruction::MemoryArgument>();
TRY(validate(arg.memory_index));
if ((1ull << arg.align) > sizeof(u128))
return Errors::out_of_bounds("memory op alignment"sv, 1ull << arg.align, 0, sizeof(u128));
@ -2201,7 +2224,7 @@ VALIDATE_INSTRUCTION(v128_load8x8_s)
constexpr auto M = 8;
constexpr auto max_alignment = N * M / 8;
TRY(validate(MemoryIndex { 0 }));
TRY(validate(arg.memory_index));
if ((1 << arg.align) > max_alignment)
return Errors::out_of_bounds("memory op alignment"sv, 1 << arg.align, 0u, max_alignment);
@ -2216,7 +2239,7 @@ VALIDATE_INSTRUCTION(v128_load8x8_u)
constexpr auto M = 8;
constexpr auto max_alignment = N * M / 8;
TRY(validate(MemoryIndex { 0 }));
TRY(validate(arg.memory_index));
if ((1 << arg.align) > max_alignment)
return Errors::out_of_bounds("memory op alignment"sv, 1 << arg.align, 0u, max_alignment);
@ -2231,7 +2254,7 @@ VALIDATE_INSTRUCTION(v128_load16x4_s)
constexpr auto M = 4;
constexpr auto max_alignment = N * M / 8;
TRY(validate(MemoryIndex { 0 }));
TRY(validate(arg.memory_index));
if ((1 << arg.align) > max_alignment)
return Errors::out_of_bounds("memory op alignment"sv, 1 << arg.align, 0u, max_alignment);
@ -2246,7 +2269,7 @@ VALIDATE_INSTRUCTION(v128_load16x4_u)
constexpr auto M = 4;
constexpr auto max_alignment = N * M / 8;
TRY(validate(MemoryIndex { 0 }));
TRY(validate(arg.memory_index));
if ((1 << arg.align) > max_alignment)
return Errors::out_of_bounds("memory op alignment"sv, 1 << arg.align, 0u, max_alignment);
@ -2261,7 +2284,7 @@ VALIDATE_INSTRUCTION(v128_load32x2_s)
constexpr auto M = 2;
constexpr auto max_alignment = N * M / 8;
TRY(validate(MemoryIndex { 0 }));
TRY(validate(arg.memory_index));
if ((1 << arg.align) > max_alignment)
return Errors::out_of_bounds("memory op alignment"sv, 1 << arg.align, 0u, max_alignment);
@ -2276,7 +2299,7 @@ VALIDATE_INSTRUCTION(v128_load32x2_u)
constexpr auto M = 2;
constexpr auto max_alignment = N * M / 8;
TRY(validate(MemoryIndex { 0 }));
TRY(validate(arg.memory_index));
if ((1 << arg.align) > max_alignment)
return Errors::out_of_bounds("memory op alignment"sv, 1 << arg.align, 0u, max_alignment);
@ -2290,7 +2313,7 @@ VALIDATE_INSTRUCTION(v128_load8_splat)
constexpr auto N = 8;
constexpr auto max_alignment = N / 8;
TRY(validate(MemoryIndex { 0 }));
TRY(validate(arg.memory_index));
if ((1 << arg.align) > max_alignment)
return Errors::out_of_bounds("memory op alignment"sv, 1 << arg.align, 0u, max_alignment);
@ -2304,7 +2327,7 @@ VALIDATE_INSTRUCTION(v128_load16_splat)
constexpr auto N = 16;
constexpr auto max_alignment = N / 8;
TRY(validate(MemoryIndex { 0 }));
TRY(validate(arg.memory_index));
if ((1 << arg.align) > max_alignment)
return Errors::out_of_bounds("memory op alignment"sv, 1 << arg.align, 0u, max_alignment);
@ -2318,7 +2341,7 @@ VALIDATE_INSTRUCTION(v128_load32_splat)
constexpr auto N = 32;
constexpr auto max_alignment = N / 8;
TRY(validate(MemoryIndex { 0 }));
TRY(validate(arg.memory_index));
if ((1 << arg.align) > max_alignment)
return Errors::out_of_bounds("memory op alignment"sv, 1 << arg.align, 0u, max_alignment);
@ -2332,7 +2355,7 @@ VALIDATE_INSTRUCTION(v128_load64_splat)
constexpr auto N = 64;
constexpr auto max_alignment = N / 8;
TRY(validate(MemoryIndex { 0 }));
TRY(validate(arg.memory_index));
if ((1 << arg.align) > max_alignment)
return Errors::out_of_bounds("memory op alignment"sv, 1 << arg.align, 0u, max_alignment);
@ -2342,9 +2365,10 @@ VALIDATE_INSTRUCTION(v128_load64_splat)
VALIDATE_INSTRUCTION(v128_store)
{
TRY(validate(MemoryIndex { 0 }));
auto& arg = instruction.arguments().get<Instruction::MemoryArgument>();
TRY(validate(arg.memory_index));
if ((1ull << arg.align) > sizeof(u128))
return Errors::out_of_bounds("memory op alignment"sv, 1ull << arg.align, 0, sizeof(u128));
@ -2865,7 +2889,7 @@ VALIDATE_INSTRUCTION(v128_load8_lane)
if (arg.lane > max_lane)
return Errors::out_of_bounds("lane index"sv, arg.lane, 0u, max_lane);
TRY(validate(MemoryIndex { 0 }));
TRY(validate(arg.memory.memory_index));
if ((1 << arg.memory.align) > max_alignment)
return Errors::out_of_bounds("memory op alignment"sv, 1 << arg.memory.align, 0u, max_alignment);
@ -2883,7 +2907,7 @@ VALIDATE_INSTRUCTION(v128_load16_lane)
if (arg.lane >= max_lane)
return Errors::out_of_bounds("lane index"sv, arg.lane, 0u, max_lane);
TRY(validate(MemoryIndex { 0 }));
TRY(validate(arg.memory.memory_index));
if ((1 << arg.memory.align) > max_alignment)
return Errors::out_of_bounds("memory op alignment"sv, 1 << arg.memory.align, 0u, max_alignment);
@ -2901,7 +2925,7 @@ VALIDATE_INSTRUCTION(v128_load32_lane)
if (arg.lane >= max_lane)
return Errors::out_of_bounds("lane index"sv, arg.lane, 0u, max_lane);
TRY(validate(MemoryIndex { 0 }));
TRY(validate(arg.memory.memory_index));
if ((1 << arg.memory.align) > max_alignment)
return Errors::out_of_bounds("memory op alignment"sv, 1 << arg.memory.align, 0u, max_alignment);
@ -2919,7 +2943,7 @@ VALIDATE_INSTRUCTION(v128_load64_lane)
if (arg.lane >= max_lane)
return Errors::out_of_bounds("lane index"sv, arg.lane, 0u, max_lane);
TRY(validate(MemoryIndex { 0 }));
TRY(validate(arg.memory.memory_index));
if (arg.memory.align > max_alignment)
return Errors::out_of_bounds("memory op alignment"sv, 1 << arg.memory.align, 0u, max_alignment);
@ -2937,7 +2961,7 @@ VALIDATE_INSTRUCTION(v128_store8_lane)
if (arg.lane >= max_lane)
return Errors::out_of_bounds("lane index"sv, arg.lane, 0u, max_lane);
TRY(validate(MemoryIndex { 0 }));
TRY(validate(arg.memory.memory_index));
if ((1 << arg.memory.align) > max_alignment)
return Errors::out_of_bounds("memory op alignment"sv, 1 << arg.memory.align, 0u, max_alignment);
@ -2955,7 +2979,7 @@ VALIDATE_INSTRUCTION(v128_store16_lane)
if (arg.lane >= max_lane)
return Errors::out_of_bounds("lane index"sv, arg.lane, 0u, max_lane);
TRY(validate(MemoryIndex { 0 }));
TRY(validate(arg.memory.memory_index));
if ((1 << arg.memory.align) > max_alignment)
return Errors::out_of_bounds("memory op alignment"sv, 1 << arg.memory.align, 0u, max_alignment);
@ -2973,7 +2997,7 @@ VALIDATE_INSTRUCTION(v128_store32_lane)
if (arg.lane >= max_lane)
return Errors::out_of_bounds("lane index"sv, arg.lane, 0u, max_lane);
TRY(validate(MemoryIndex { 0 }));
TRY(validate(arg.memory.memory_index));
if ((1 << arg.memory.align) > max_alignment)
return Errors::out_of_bounds("memory op alignment"sv, 1 << arg.memory.align, 0u, max_alignment);
@ -2991,7 +3015,7 @@ VALIDATE_INSTRUCTION(v128_store64_lane)
if (arg.lane >= max_lane)
return Errors::out_of_bounds("lane index"sv, arg.lane, 0u, max_lane);
TRY(validate(MemoryIndex { 0 }));
TRY(validate(arg.memory.memory_index));
if ((1 << arg.memory.align) > max_alignment)
return Errors::out_of_bounds("memory op alignment"sv, 1 << arg.memory.align, 0u, max_alignment);
@ -3005,7 +3029,7 @@ VALIDATE_INSTRUCTION(v128_load32_zero)
constexpr auto N = 32;
constexpr auto max_alignment = N / 8;
TRY(validate(MemoryIndex { 0 }));
TRY(validate(arg.memory_index));
if ((1 << arg.align) > max_alignment)
return Errors::out_of_bounds("memory op alignment"sv, 1 << arg.align, 0u, max_alignment);
@ -3019,7 +3043,7 @@ VALIDATE_INSTRUCTION(v128_load64_zero)
constexpr auto N = 64;
constexpr auto max_alignment = N / 8;
TRY(validate(MemoryIndex { 0 }));
TRY(validate(arg.memory_index));
if ((1 << arg.align) > max_alignment)
return Errors::out_of_bounds("memory op alignment"sv, 1 << arg.align, 0u, max_alignment);

View File

@ -418,18 +418,28 @@ ParseResult<Vector<Instruction>> Instruction::parse(Stream& stream, InstructionP
case Instructions::i64_store8.value():
case Instructions::i64_store16.value():
case Instructions::i64_store32.value(): {
// op (align offset)
// op (align [multi-memory: memindex] offset)
auto align_or_error = stream.read_value<LEB128<size_t>>();
if (align_or_error.is_error())
return with_eof_check(stream, ParseError::InvalidInput);
size_t align = align_or_error.release_value();
// Proposal "multi-memory", if bit 6 of alignment is set, then a memory index follows the alignment.
size_t memory_index = 0;
if ((align & 0x20) != 0) {
align &= ~0x20;
auto memory_index_or_error = stream.read_value<LEB128<size_t>>();
if (memory_index_or_error.is_error())
return with_eof_check(stream, ParseError::InvalidInput);
memory_index = memory_index_or_error.release_value();
}
auto offset_or_error = stream.read_value<LEB128<size_t>>();
if (offset_or_error.is_error())
return with_eof_check(stream, ParseError::InvalidInput);
size_t offset = offset_or_error.release_value();
resulting_instructions.append(Instruction { opcode, MemoryArgument { static_cast<u32>(align), static_cast<u32>(offset) } });
resulting_instructions.append(Instruction { opcode, MemoryArgument { static_cast<u32>(align), static_cast<u32>(offset), MemoryIndex(memory_index) } });
break;
}
case Instructions::local_get.value():
@ -453,19 +463,15 @@ ParseResult<Vector<Instruction>> Instruction::parse(Stream& stream, InstructionP
}
case Instructions::memory_size.value():
case Instructions::memory_grow.value(): {
// op 0x0
// The zero is currently unused.
auto unused_or_error = stream.read_value<u8>();
if (unused_or_error.is_error())
// op [multi-memory: memindex]|0x00
auto memory_index_or_error = stream.read_value<u8>();
if (memory_index_or_error.is_error())
return with_eof_check(stream, ParseError::ExpectedKindTag);
auto unused = unused_or_error.release_value();
if (unused != 0x00) {
dbgln("Invalid tag in memory_grow {}", unused);
return with_eof_check(stream, ParseError::InvalidTag);
}
auto memory_index = memory_index_or_error.release_value();
resulting_instructions.append(Instruction { opcode });
resulting_instructions.append(Instruction { opcode, MemoryIndexArgument { MemoryIndex(memory_index) } });
break;
}
case Instructions::i32_const.value(): {
@ -704,14 +710,15 @@ ParseResult<Vector<Instruction>> Instruction::parse(Stream& stream, InstructionP
auto index = GenericIndexParser<DataIndex>::parse(stream);
if (index.is_error())
return index.error();
auto unused_or_error = stream.read_value<u8>();
if (unused_or_error.is_error())
// Proposal "multi-memory", literal 0x00 is replaced with a memory index.
auto memory_index_or_error = stream.read_value<u8>();
if (memory_index_or_error.is_error())
return with_eof_check(stream, ParseError::InvalidInput);
auto unused = unused_or_error.release_value();
if (unused != 0x00)
return ParseError::InvalidImmediate;
resulting_instructions.append(Instruction { full_opcode, index.release_value() });
auto memory_index = memory_index_or_error.release_value();
resulting_instructions.append(Instruction { full_opcode, MemoryInitArgs { index.release_value(), MemoryIndex(memory_index) } });
break;
}
case Instructions::data_drop.value(): {
@ -722,27 +729,27 @@ ParseResult<Vector<Instruction>> Instruction::parse(Stream& stream, InstructionP
break;
}
case Instructions::memory_copy.value(): {
// Proposal "multi-memory", literal 0x00 is replaced with two memory indices, destination and source, respectively.
MemoryIndex indices[] = { 0, 0 };
for (size_t i = 0; i < 2; ++i) {
auto unused_or_error = stream.read_value<u8>();
if (unused_or_error.is_error())
auto memory_index_or_error = stream.read_value<u8>();
if (memory_index_or_error.is_error())
return with_eof_check(stream, ParseError::InvalidInput);
auto unused = unused_or_error.release_value();
if (unused != 0x00)
return ParseError::InvalidImmediate;
indices[i] = memory_index_or_error.release_value();
}
resulting_instructions.append(Instruction { full_opcode });
resulting_instructions.append(Instruction { full_opcode, MemoryCopyArgs { indices[1], indices[0] } });
break;
}
case Instructions::memory_fill.value(): {
auto unused_or_error = stream.read_value<u8>();
if (unused_or_error.is_error())
// Proposal "multi-memory", literal 0x00 is replaced with a memory index.
auto memory_index_or_error = stream.read_value<u8>();
if (memory_index_or_error.is_error())
return with_eof_check(stream, ParseError::InvalidInput);
auto unused = unused_or_error.release_value();
if (unused != 0x00)
return ParseError::InvalidImmediate;
resulting_instructions.append(Instruction { full_opcode });
auto memory_index = memory_index_or_error.release_value();
resulting_instructions.append(Instruction { full_opcode, MemoryIndexArgument { MemoryIndex { memory_index } } });
break;
}
case Instructions::table_init.value(): {
@ -793,17 +800,28 @@ ParseResult<Vector<Instruction>> Instruction::parse(Stream& stream, InstructionP
case Instructions::v128_load32_splat.value():
case Instructions::v128_load64_splat.value():
case Instructions::v128_store.value(): {
// op (align offset)
// op (align [multi-memory memindex] offset)
auto align_or_error = stream.read_value<LEB128<size_t>>();
if (align_or_error.is_error())
return with_eof_check(stream, ParseError::ExpectedIndex);
size_t align = align_or_error.release_value();
// Proposal "multi-memory", if bit 6 of alignment is set, then a memory index follows the alignment.
size_t memory_index = 0;
if ((align & 0x20) != 0) {
align &= ~0x20;
auto memory_index_or_error = stream.read_value<LEB128<size_t>>();
if (memory_index_or_error.is_error())
return with_eof_check(stream, ParseError::InvalidInput);
memory_index = memory_index_or_error.release_value();
}
auto offset_or_error = stream.read_value<LEB128<size_t>>();
if (offset_or_error.is_error())
return with_eof_check(stream, ParseError::ExpectedIndex);
size_t offset = offset_or_error.release_value();
resulting_instructions.append(Instruction { full_opcode, MemoryArgument { static_cast<u32>(align), static_cast<u32>(offset) } });
resulting_instructions.append(Instruction { full_opcode, MemoryArgument { static_cast<u32>(align), static_cast<u32>(offset), MemoryIndex(memory_index) } });
break;
}
case Instructions::v128_load8_lane.value():
@ -814,11 +832,22 @@ ParseResult<Vector<Instruction>> Instruction::parse(Stream& stream, InstructionP
case Instructions::v128_store16_lane.value():
case Instructions::v128_store32_lane.value():
case Instructions::v128_store64_lane.value(): {
// op (align offset) (index)
// op (align [multi-memory: memindex] offset) (index)
auto align_or_error = stream.read_value<LEB128<size_t>>();
if (align_or_error.is_error())
return with_eof_check(stream, ParseError::ExpectedIndex);
size_t align = align_or_error.release_value();
// Proposal "multi-memory", if bit 6 of alignment is set, then a memory index follows the alignment.
size_t memory_index = 0;
if ((align & 0x20) != 0) {
align &= ~0x20;
auto memory_index_or_error = stream.read_value<LEB128<size_t>>();
if (memory_index_or_error.is_error())
return with_eof_check(stream, ParseError::InvalidInput);
memory_index = memory_index_or_error.release_value();
}
auto offset_or_error = stream.read_value<LEB128<size_t>>();
if (offset_or_error.is_error())
return with_eof_check(stream, ParseError::ExpectedIndex);
@ -829,7 +858,7 @@ ParseResult<Vector<Instruction>> Instruction::parse(Stream& stream, InstructionP
return with_eof_check(stream, ParseError::InvalidInput);
auto index = index_or_error.release_value();
resulting_instructions.append(Instruction { full_opcode, MemoryAndLaneArgument { { static_cast<u32>(align), static_cast<u32>(offset) }, index } });
resulting_instructions.append(Instruction { full_opcode, MemoryAndLaneArgument { { static_cast<u32>(align), static_cast<u32>(offset), MemoryIndex(memory_index) }, index } });
break;
}
case Instructions::v128_const.value(): {

View File

@ -432,8 +432,11 @@ void Printer::print(Wasm::Instruction const& instruction)
[&](LocalIndex const& index) { print("(local index {})", index.value()); },
[&](TableIndex const& index) { print("(table index {})", index.value()); },
[&](Instruction::IndirectCallArgs const& args) { print("(indirect (type index {}) (table index {}))", args.type.value(), args.table.value()); },
[&](Instruction::MemoryArgument const& args) { print("(memory (align {}) (offset {}))", args.align, args.offset); },
[&](Instruction::MemoryAndLaneArgument const& args) { print("(memory (align {}) (offset {})) (lane {})", args.memory.align, args.memory.offset, args.lane); },
[&](Instruction::MemoryArgument const& args) { print("(memory index {} (align {}) (offset {}))", args.memory_index.value(), args.align, args.offset); },
[&](Instruction::MemoryAndLaneArgument const& args) { print("(memory index {} (align {}) (offset {})) (lane {})", args.memory.memory_index.value(), args.memory.align, args.memory.offset, args.lane); },
[&](Instruction::MemoryInitArgs const& args) { print("(memory index {}) (data index {})", args.memory_index.value(), args.data_index.value()); },
[&](Instruction::MemoryCopyArgs const& args) { print("(from (memory index {}) to (memory index {}))", args.src_index.value(), args.dst_index.value()); },
[&](Instruction::MemoryIndexArgument const& args) { print("(memory index {})", args.memory_index.value()); },
[&](Instruction::LaneIndex const& args) { print("(lane {})", args.lane); },
[&](Instruction::ShuffleArgument const& args) {
print("{{ {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} }}",

View File

@ -410,6 +410,7 @@ public:
struct MemoryArgument {
u32 align;
u32 offset;
MemoryIndex memory_index { 0 };
};
struct MemoryAndLaneArgument {
@ -421,6 +422,21 @@ public:
u8 lane;
};
// Proposal "multi-memory"
struct MemoryCopyArgs {
MemoryIndex src_index;
MemoryIndex dst_index;
};
struct MemoryInitArgs {
DataIndex data_index;
MemoryIndex memory_index;
};
struct MemoryIndexArgument {
MemoryIndex memory_index;
};
struct ShuffleArgument {
explicit ShuffleArgument(u8 (&lanes)[16])
: lanes {
@ -460,6 +476,9 @@ private:
LocalIndex,
MemoryArgument,
MemoryAndLaneArgument,
MemoryCopyArgs,
MemoryIndexArgument,
MemoryInitArgs,
StructuredInstructionArgs,
ShuffleArgument,
TableBranchArgs,