ソースを参照

LibWasm: Implement the multi-memory proposal

Ali Mohammad Pur 1 年間 前
コミット
22d411345d

+ 28 - 21
Userland/Libraries/LibWasm/AbstractMachine/BytecodeInterpreter.cpp

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

+ 106 - 82
Userland/Libraries/LibWasm/AbstractMachine/Validator.cpp

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

+ 63 - 34
Userland/Libraries/LibWasm/Parser/Parser.cpp

@@ -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(): {

+ 5 - 2
Userland/Libraries/LibWasm/Printer/Printer.cpp

@@ -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("{{ {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} }}",

+ 19 - 0
Userland/Libraries/LibWasm/Types.h

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