Skip to content

Commit

Permalink
WIP: multi-memory support
Browse files Browse the repository at this point in the history
Signed-off-by: Máté Tokodi [email protected]
  • Loading branch information
matetokodi committed Feb 19, 2025
1 parent b64a1ce commit 599bf56
Show file tree
Hide file tree
Showing 49 changed files with 3,792 additions and 113 deletions.
717 changes: 680 additions & 37 deletions src/interpreter/ByteCode.h

Large diffs are not rendered by default.

245 changes: 239 additions & 6 deletions src/interpreter/Interpreter.cpp

Large diffs are not rendered by default.

281 changes: 226 additions & 55 deletions src/parser/WASMParser.cpp

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions src/runtime/Instance.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#ifndef __WalrusInstance__
#define __WalrusInstance__

#include "Module.h"
#include "runtime/Object.h"

namespace Walrus {
Expand Down Expand Up @@ -110,8 +111,7 @@ class Instance : public Object {
Function* function(uint32_t index) const { return m_functions[index]; }
Memory* memory(uint32_t index) const
{
// now only one memory is allowed for each Instance/Module
ASSERT(index == 0);
ASSERT(index < module()->numberOfMemoryTypes());
return m_memories[index];
}
Table* table(uint32_t index) const { return m_tables[index]; }
Expand Down
24 changes: 21 additions & 3 deletions src/runtime/Memory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -180,12 +180,12 @@ void Memory::init(ExecutionState& state, DataSegment* source, uint32_t dstStart,
this->initMemory(source, dstStart, srcStart, srcSize);
}

void Memory::copy(ExecutionState& state, uint32_t dstStart, uint32_t srcStart, uint32_t size)
void Memory::copy(ExecutionState& state, uint32_t dstStart, uint32_t srcStart, uint32_t size, Memory* dstMem)
{
checkAccess(state, srcStart, size);
checkAccess(state, dstStart, size);
checkAccess(state, dstStart, size, 0, dstMem);

this->copyMemory(dstStart, srcStart, size);
this->copyMemoryMulti(dstMem, dstStart, srcStart, size);
}

void Memory::fill(ExecutionState& state, uint32_t start, uint8_t value, uint32_t size)
Expand Down Expand Up @@ -224,6 +224,24 @@ void Memory::copyMemory(uint32_t dstStart, uint32_t srcStart, uint32_t size)
}
}

void Memory::copyMemoryMulti(Memory* dstMemory, uint32_t dstStart, uint32_t srcStart, uint32_t size)
{
#if defined(WALRUS_BIG_ENDIAN)
auto srcBegin = m_buffer + m_sizeInByte + srcStart - size;
auto dstBegin = dstMemory->m_buffer + dstMemory->m_sizeInByte + dstStart - size;
#else
auto srcBegin = m_buffer + srcStart;
auto dstBegin = dstMemory->m_buffer + dstStart;
#endif
auto srcEnd = srcBegin + size;
auto dstEnd = dstBegin + size;
if (srcBegin < dstBegin) {
std::move_backward(srcBegin, srcEnd, dstEnd);
} else {
std::move(srcBegin, srcEnd, dstBegin);
}
}

void Memory::fillMemory(uint32_t start, uint8_t value, uint32_t size)
{
#if defined(WALRUS_BIG_ENDIAN)
Expand Down
15 changes: 10 additions & 5 deletions src/runtime/Memory.h
Original file line number Diff line number Diff line change
Expand Up @@ -317,26 +317,31 @@ class Memory : public Extern {
#endif

void init(ExecutionState& state, DataSegment* source, uint32_t dstStart, uint32_t srcStart, uint32_t srcSize);
void copy(ExecutionState& state, uint32_t dstStart, uint32_t srcStart, uint32_t size);
void copy(ExecutionState& state, uint32_t dstStart, uint32_t srcStart, uint32_t size, Memory* dstMem = nullptr);
void fill(ExecutionState& state, uint32_t start, uint8_t value, uint32_t size);

inline bool checkAccess(uint32_t offset, uint32_t size, uint32_t addend = 0) const
inline bool checkAccess(uint32_t offset, uint32_t size, uint32_t addend = 0, Memory* dstMem = nullptr) const
{
return !UNLIKELY(!((uint64_t)offset + (uint64_t)addend + (uint64_t)size <= m_sizeInByte));
if (dstMem == nullptr) {
return !UNLIKELY(!((uint64_t)offset + (uint64_t)addend + (uint64_t)size <= m_sizeInByte));
} else {
return !UNLIKELY(!((uint64_t)offset + (uint64_t)addend + (uint64_t)size <= dstMem->m_sizeInByte));
}
}

void initMemory(DataSegment* source, uint32_t dstStart, uint32_t srcStart, uint32_t srcSize);
void copyMemory(uint32_t dstStart, uint32_t srcStart, uint32_t size);
void copyMemoryMulti(Memory* dstMemory, uint32_t dstStart, uint32_t srcStart, uint32_t size);
void fillMemory(uint32_t start, uint8_t value, uint32_t size);

private:
Memory(uint64_t initialSizeInByte, uint64_t maximumSizeInByte, bool isShared);

void throwRangeException(ExecutionState& state, uint32_t offset, uint32_t addend, uint32_t size) const;

inline void checkAccess(ExecutionState& state, uint32_t offset, uint32_t size, uint32_t addend = 0) const
inline void checkAccess(ExecutionState& state, uint32_t offset, uint32_t size, uint32_t addend = 0, Memory* dstMem = nullptr) const
{
if (!this->checkAccess(offset, size, addend)) {
if (!this->checkAccess(offset, size, addend, dstMem)) {
throwRangeException(state, offset, addend, size);
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/Module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ Instance* Module::instantiate(ExecutionState& state, const ExternVector& imports
Value offset;
fakeFunction.call(state, nullptr, &offset);

Memory* m = data->instance->memory(0);
Memory* m = data->instance->memory(data->init->memIndex());
const auto& initData = data->init->initData();
if (m->sizeInByte() >= initData.size() && (offset.asI32() + initData.size()) <= m->sizeInByte() && offset.asI32() >= 0) {
memcpyEndianAware(m->buffer(), initData.data(), m->sizeInByte(), initData.size(), offset.asI32(), 0, initData.size());
Expand Down
6 changes: 5 additions & 1 deletion src/runtime/Module.h
Original file line number Diff line number Diff line change
Expand Up @@ -264,9 +264,10 @@ class ModuleFunction {

class Data {
public:
Data(ModuleFunction* moduleFunction, Vector<uint8_t, std::allocator<uint8_t>>&& initData)
Data(uint32_t index, ModuleFunction* moduleFunction, Vector<uint8_t, std::allocator<uint8_t>>&& initData)
: m_moduleFunction(moduleFunction)
, m_initData(std::move(initData))
, m_memIndex(index)
{
}

Expand All @@ -282,6 +283,8 @@ class Data {
return m_moduleFunction;
}

uint16_t memIndex() { return m_memIndex; }

const Vector<uint8_t, std::allocator<uint8_t>>& initData() const
{
return m_initData;
Expand All @@ -290,6 +293,7 @@ class Data {
private:
ModuleFunction* m_moduleFunction;
Vector<uint8_t, std::allocator<uint8_t>> m_initData;
uint16_t m_memIndex;
};

class Element {
Expand Down
2 changes: 2 additions & 0 deletions src/shell/Shell.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -917,6 +917,8 @@ static void executeWAST(Store* store, const std::string& filename, const std::ve
printf("Expected exception:%s\n", assertUnlinkable->text.data());
RELEASE_ASSERT_NOT_REACHED();
}
std::string& actual = trapResult.exception->message();
printf("assertUnlinkable (expect compile error: '%s', actual '%s'(line: %d)) : OK\n", assertUnlinkable->text.data(), actual.data(), assertUnlinkable->module->location().line);
break;
}
case wabt::CommandType::AssertExhaustion: {
Expand Down
3 changes: 1 addition & 2 deletions test/wasm-spec/core/imports.wast
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@
(table (export "table-10-inf") 10 funcref)
(table (export "table-10-20") 10 20 funcref)
(memory (export "memory-2-inf") 2)
;; Multiple memories are not yet supported
;; (memory (export "memory-2-4") 2 4)
(memory (export "memory-2-4") 2 4)
)

(register "test")
Expand Down
212 changes: 212 additions & 0 deletions test/wasm-spec/core/multi-memory/address0.wast
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
;; Load i32 data with different offset/align arguments

(module
(memory $mem0 0)
(memory $mem1 1)
(data (memory $mem1) (i32.const 0) "abcdefghijklmnopqrstuvwxyz")

(func (export "8u_good1") (param $i i32) (result i32)
(i32.load8_u $mem1 offset=0 (local.get $i)) ;; 97 'a'
)
(func (export "8u_good2") (param $i i32) (result i32)
(i32.load8_u $mem1 align=1 (local.get $i)) ;; 97 'a'
)
(func (export "8u_good3") (param $i i32) (result i32)
(i32.load8_u $mem1 offset=1 align=1 (local.get $i)) ;; 98 'b'
)
(func (export "8u_good4") (param $i i32) (result i32)
(i32.load8_u $mem1 offset=2 align=1 (local.get $i)) ;; 99 'c'
)
(func (export "8u_good5") (param $i i32) (result i32)
(i32.load8_u $mem1 offset=25 align=1 (local.get $i)) ;; 122 'z'
)

(func (export "8s_good1") (param $i i32) (result i32)
(i32.load8_s $mem1 offset=0 (local.get $i)) ;; 97 'a'
)
(func (export "8s_good2") (param $i i32) (result i32)
(i32.load8_s $mem1 align=1 (local.get $i)) ;; 97 'a'
)
(func (export "8s_good3") (param $i i32) (result i32)
(i32.load8_s $mem1 offset=1 align=1 (local.get $i)) ;; 98 'b'
)
(func (export "8s_good4") (param $i i32) (result i32)
(i32.load8_s $mem1 offset=2 align=1 (local.get $i)) ;; 99 'c'
)
(func (export "8s_good5") (param $i i32) (result i32)
(i32.load8_s $mem1 offset=25 align=1 (local.get $i)) ;; 122 'z'
)

(func (export "16u_good1") (param $i i32) (result i32)
(i32.load16_u $mem1 offset=0 (local.get $i)) ;; 25185 'ab'
)
(func (export "16u_good2") (param $i i32) (result i32)
(i32.load16_u $mem1 align=1 (local.get $i)) ;; 25185 'ab'
)
(func (export "16u_good3") (param $i i32) (result i32)
(i32.load16_u $mem1 offset=1 align=1 (local.get $i)) ;; 25442 'bc'
)
(func (export "16u_good4") (param $i i32) (result i32)
(i32.load16_u $mem1 offset=2 align=2 (local.get $i)) ;; 25699 'cd'
)
(func (export "16u_good5") (param $i i32) (result i32)
(i32.load16_u $mem1 offset=25 align=2 (local.get $i)) ;; 122 'z\0'
)

(func (export "16s_good1") (param $i i32) (result i32)
(i32.load16_s $mem1 offset=0 (local.get $i)) ;; 25185 'ab'
)
(func (export "16s_good2") (param $i i32) (result i32)
(i32.load16_s $mem1 align=1 (local.get $i)) ;; 25185 'ab'
)
(func (export "16s_good3") (param $i i32) (result i32)
(i32.load16_s $mem1 offset=1 align=1 (local.get $i)) ;; 25442 'bc'
)
(func (export "16s_good4") (param $i i32) (result i32)
(i32.load16_s $mem1 offset=2 align=2 (local.get $i)) ;; 25699 'cd'
)
(func (export "16s_good5") (param $i i32) (result i32)
(i32.load16_s $mem1 offset=25 align=2 (local.get $i)) ;; 122 'z\0'
)

(func (export "32_good1") (param $i i32) (result i32)
(i32.load $mem1 offset=0 (local.get $i)) ;; 1684234849 'abcd'
)
(func (export "32_good2") (param $i i32) (result i32)
(i32.load $mem1 align=1 (local.get $i)) ;; 1684234849 'abcd'
)
(func (export "32_good3") (param $i i32) (result i32)
(i32.load $mem1 offset=1 align=1 (local.get $i)) ;; 1701077858 'bcde'
)
(func (export "32_good4") (param $i i32) (result i32)
(i32.load $mem1 offset=2 align=2 (local.get $i)) ;; 1717920867 'cdef'
)
(func (export "32_good5") (param $i i32) (result i32)
(i32.load $mem1 offset=25 align=4 (local.get $i)) ;; 122 'z\0\0\0'
)

(func (export "8u_bad") (param $i i32)
(drop (i32.load8_u $mem1 offset=4294967295 (local.get $i)))
)
(func (export "8s_bad") (param $i i32)
(drop (i32.load8_s $mem1 offset=4294967295 (local.get $i)))
)
(func (export "16u_bad") (param $i i32)
(drop (i32.load16_u $mem1 offset=4294967295 (local.get $i)))
)
(func (export "16s_bad") (param $i i32)
(drop (i32.load16_s $mem1 offset=4294967295 (local.get $i)))
)
(func (export "32_bad") (param $i i32)
(drop (i32.load $mem1 offset=4294967295 (local.get $i)))
)
)

(assert_return (invoke "8u_good1" (i32.const 0)) (i32.const 97))
(assert_return (invoke "8u_good2" (i32.const 0)) (i32.const 97))
(assert_return (invoke "8u_good3" (i32.const 0)) (i32.const 98))
(assert_return (invoke "8u_good4" (i32.const 0)) (i32.const 99))
(assert_return (invoke "8u_good5" (i32.const 0)) (i32.const 122))

(assert_return (invoke "8s_good1" (i32.const 0)) (i32.const 97))
(assert_return (invoke "8s_good2" (i32.const 0)) (i32.const 97))
(assert_return (invoke "8s_good3" (i32.const 0)) (i32.const 98))
(assert_return (invoke "8s_good4" (i32.const 0)) (i32.const 99))
(assert_return (invoke "8s_good5" (i32.const 0)) (i32.const 122))

(assert_return (invoke "16u_good1" (i32.const 0)) (i32.const 25185))
(assert_return (invoke "16u_good2" (i32.const 0)) (i32.const 25185))
(assert_return (invoke "16u_good3" (i32.const 0)) (i32.const 25442))
(assert_return (invoke "16u_good4" (i32.const 0)) (i32.const 25699))
(assert_return (invoke "16u_good5" (i32.const 0)) (i32.const 122))

(assert_return (invoke "16s_good1" (i32.const 0)) (i32.const 25185))
(assert_return (invoke "16s_good2" (i32.const 0)) (i32.const 25185))
(assert_return (invoke "16s_good3" (i32.const 0)) (i32.const 25442))
(assert_return (invoke "16s_good4" (i32.const 0)) (i32.const 25699))
(assert_return (invoke "16s_good5" (i32.const 0)) (i32.const 122))

(assert_return (invoke "32_good1" (i32.const 0)) (i32.const 1684234849))
(assert_return (invoke "32_good2" (i32.const 0)) (i32.const 1684234849))
(assert_return (invoke "32_good3" (i32.const 0)) (i32.const 1701077858))
(assert_return (invoke "32_good4" (i32.const 0)) (i32.const 1717920867))
(assert_return (invoke "32_good5" (i32.const 0)) (i32.const 122))

(assert_return (invoke "8u_good1" (i32.const 65507)) (i32.const 0))
(assert_return (invoke "8u_good2" (i32.const 65507)) (i32.const 0))
(assert_return (invoke "8u_good3" (i32.const 65507)) (i32.const 0))
(assert_return (invoke "8u_good4" (i32.const 65507)) (i32.const 0))
(assert_return (invoke "8u_good5" (i32.const 65507)) (i32.const 0))

(assert_return (invoke "8s_good1" (i32.const 65507)) (i32.const 0))
(assert_return (invoke "8s_good2" (i32.const 65507)) (i32.const 0))
(assert_return (invoke "8s_good3" (i32.const 65507)) (i32.const 0))
(assert_return (invoke "8s_good4" (i32.const 65507)) (i32.const 0))
(assert_return (invoke "8s_good5" (i32.const 65507)) (i32.const 0))

(assert_return (invoke "16u_good1" (i32.const 65507)) (i32.const 0))
(assert_return (invoke "16u_good2" (i32.const 65507)) (i32.const 0))
(assert_return (invoke "16u_good3" (i32.const 65507)) (i32.const 0))
(assert_return (invoke "16u_good4" (i32.const 65507)) (i32.const 0))
(assert_return (invoke "16u_good5" (i32.const 65507)) (i32.const 0))

(assert_return (invoke "16s_good1" (i32.const 65507)) (i32.const 0))
(assert_return (invoke "16s_good2" (i32.const 65507)) (i32.const 0))
(assert_return (invoke "16s_good3" (i32.const 65507)) (i32.const 0))
(assert_return (invoke "16s_good4" (i32.const 65507)) (i32.const 0))
(assert_return (invoke "16s_good5" (i32.const 65507)) (i32.const 0))

(assert_return (invoke "32_good1" (i32.const 65507)) (i32.const 0))
(assert_return (invoke "32_good2" (i32.const 65507)) (i32.const 0))
(assert_return (invoke "32_good3" (i32.const 65507)) (i32.const 0))
(assert_return (invoke "32_good4" (i32.const 65507)) (i32.const 0))
(assert_return (invoke "32_good5" (i32.const 65507)) (i32.const 0))

(assert_return (invoke "8u_good1" (i32.const 65508)) (i32.const 0))
(assert_return (invoke "8u_good2" (i32.const 65508)) (i32.const 0))
(assert_return (invoke "8u_good3" (i32.const 65508)) (i32.const 0))
(assert_return (invoke "8u_good4" (i32.const 65508)) (i32.const 0))
(assert_return (invoke "8u_good5" (i32.const 65508)) (i32.const 0))

(assert_return (invoke "8s_good1" (i32.const 65508)) (i32.const 0))
(assert_return (invoke "8s_good2" (i32.const 65508)) (i32.const 0))
(assert_return (invoke "8s_good3" (i32.const 65508)) (i32.const 0))
(assert_return (invoke "8s_good4" (i32.const 65508)) (i32.const 0))
(assert_return (invoke "8s_good5" (i32.const 65508)) (i32.const 0))

(assert_return (invoke "16u_good1" (i32.const 65508)) (i32.const 0))
(assert_return (invoke "16u_good2" (i32.const 65508)) (i32.const 0))
(assert_return (invoke "16u_good3" (i32.const 65508)) (i32.const 0))
(assert_return (invoke "16u_good4" (i32.const 65508)) (i32.const 0))
(assert_return (invoke "16u_good5" (i32.const 65508)) (i32.const 0))

(assert_return (invoke "16s_good1" (i32.const 65508)) (i32.const 0))
(assert_return (invoke "16s_good2" (i32.const 65508)) (i32.const 0))
(assert_return (invoke "16s_good3" (i32.const 65508)) (i32.const 0))
(assert_return (invoke "16s_good4" (i32.const 65508)) (i32.const 0))
(assert_return (invoke "16s_good5" (i32.const 65508)) (i32.const 0))

(assert_return (invoke "32_good1" (i32.const 65508)) (i32.const 0))
(assert_return (invoke "32_good2" (i32.const 65508)) (i32.const 0))
(assert_return (invoke "32_good3" (i32.const 65508)) (i32.const 0))
(assert_return (invoke "32_good4" (i32.const 65508)) (i32.const 0))
(assert_trap (invoke "32_good5" (i32.const 65508)) "out of bounds memory access")

(assert_trap (invoke "8u_good3" (i32.const -1)) "out of bounds memory access")
(assert_trap (invoke "8s_good3" (i32.const -1)) "out of bounds memory access")
(assert_trap (invoke "16u_good3" (i32.const -1)) "out of bounds memory access")
(assert_trap (invoke "16s_good3" (i32.const -1)) "out of bounds memory access")
(assert_trap (invoke "32_good3" (i32.const -1)) "out of bounds memory access")
(assert_trap (invoke "32_good3" (i32.const -1)) "out of bounds memory access")

(assert_trap (invoke "8u_bad" (i32.const 0)) "out of bounds memory access")
(assert_trap (invoke "8s_bad" (i32.const 0)) "out of bounds memory access")
(assert_trap (invoke "16u_bad" (i32.const 0)) "out of bounds memory access")
(assert_trap (invoke "16s_bad" (i32.const 0)) "out of bounds memory access")
(assert_trap (invoke "32_bad" (i32.const 0)) "out of bounds memory access")

(assert_trap (invoke "8u_bad" (i32.const 1)) "out of bounds memory access")
(assert_trap (invoke "8s_bad" (i32.const 1)) "out of bounds memory access")
(assert_trap (invoke "16u_bad" (i32.const 1)) "out of bounds memory access")
(assert_trap (invoke "16s_bad" (i32.const 1)) "out of bounds memory access")
(assert_trap (invoke "32_bad" (i32.const 1)) "out of bounds memory access")
Loading

0 comments on commit 599bf56

Please sign in to comment.