Skip to content

Commit

Permalink
Support SharedArrayBuffer in WASM Memory
Browse files Browse the repository at this point in the history
Signed-off-by: HyukWoo Park <[email protected]>
  • Loading branch information
clover2123 committed Jul 29, 2024
1 parent 0f6ea46 commit 29041c3
Show file tree
Hide file tree
Showing 16 changed files with 246 additions and 82 deletions.
4 changes: 4 additions & 0 deletions build/config.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,10 @@ ENDIF()

IF (ESCARGOT_WASM)
SET (ESCARGOT_DEFINITIONS ${ESCARGOT_DEFINITIONS} -DENABLE_WASM)
IF (NOT DEFINED ESCARGOT_THREADING)
# threading should be enabled for WASM (WASM threading feature)
SET (ESCARGOT_THREADING ON)
ENDIF()
ENDIF()

IF (ESCARGOT_THREADING)
Expand Down
1 change: 1 addition & 0 deletions build/escargot.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ IF (ESCARGOT_WASM)
SET (WALRUS_MODE ${ESCARGOT_MODE})
SET (WALRUS_OUTPUT "shared_lib")
SET (WALRUS_WASI OFF) # WASI should be OFF
SET (WALRUS_EXTENDED_FEATURES ON) # enable extended features

IF (${ESCARGOT_MODE} STREQUAL "release")
SET (WALRUS_CXXFLAGS ${WALRUS_CXXFLAGS} ${ESCARGOT_CXXFLAGS_RELEASE})
Expand Down
2 changes: 1 addition & 1 deletion src/Escargot.h
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ if (f.type == Type::B) { puts("failed in msvc."); }
#endif

// FIXME arm devices raise SIGBUS when using unaligned address to __atomic_* functions
#if (defined(COMPILER_GCC) || defined(COMPILER_CLANG)) && !defined(CPU_ARM32) && !defined(CPU_ARM64)
#if defined(COMPILER_GCC) && !defined(CPU_ARM32) && !defined(CPU_ARM64)
#define HAVE_BUILTIN_ATOMIC_FUNCTIONS
#endif

Expand Down
4 changes: 2 additions & 2 deletions src/api/EscargotPublic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,9 @@ class PlatformBridge : public Platform {
return m_platform->onMallocArrayBufferObjectDataBuffer(sizeInByte);
}

virtual void onFreeArrayBufferObjectDataBuffer(void* buffer, size_t sizeInByte) override
virtual void onFreeArrayBufferObjectDataBuffer(void* buffer, size_t sizeInByte, void* deleterData) override
{
m_platform->onFreeArrayBufferObjectDataBuffer(buffer, sizeInByte);
m_platform->onFreeArrayBufferObjectDataBuffer(buffer, sizeInByte, deleterData);
}

virtual void* onReallocArrayBufferObjectDataBuffer(void* oldBuffer, size_t oldSizeInByte, size_t newSizeInByte) override
Expand Down
2 changes: 1 addition & 1 deletion src/api/EscargotPublic.h
Original file line number Diff line number Diff line change
Expand Up @@ -2169,7 +2169,7 @@ class ESCARGOT_EXPORT PlatformRef {
{
return calloc(sizeInByte, 1);
}
virtual void onFreeArrayBufferObjectDataBuffer(void* buffer, size_t sizeInByte)
virtual void onFreeArrayBufferObjectDataBuffer(void* buffer, size_t sizeInByte, void* deleterData)
{
return free(buffer);
}
Expand Down
23 changes: 23 additions & 0 deletions src/runtime/ArrayBufferObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,20 @@ ArrayBufferObject* ArrayBufferObject::allocateArrayBuffer(ExecutionState& state,
return obj;
}

ArrayBufferObject* ArrayBufferObject::allocateExternalArrayBuffer(ExecutionState& state, void* dataBlock, size_t byteLength)
{
if (UNLIKELY(byteLength >= ArrayBuffer::maxArrayBufferSize)) {
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, state.context()->staticStrings().ArrayBuffer.string(), false, String::emptyString, ErrorObject::Messages::GlobalObject_InvalidArrayBufferSize);
}

// creating a fixed length memory buffer from memaddr.
// NOTE) deleter do nothing, dataBlock will be freed in external module
BackingStore* backingStore = BackingStore::createNonSharedBackingStore(dataBlock, byteLength,
[](void* data, size_t length, void* deleterData) {}, nullptr);

return new ArrayBufferObject(state, backingStore);
}

ArrayBufferObject* ArrayBufferObject::cloneArrayBuffer(ExecutionState& state, ArrayBuffer* srcBuffer, size_t srcByteOffset, uint64_t srcLength, Object* constructor)
{
// https://www.ecma-international.org/ecma-262/10.0/#sec-clonearraybuffer
Expand All @@ -84,6 +98,15 @@ ArrayBufferObject::ArrayBufferObject(ExecutionState& state, Object* proto)
{
}

ArrayBufferObject::ArrayBufferObject(ExecutionState& state, BackingStore* backingStore)
: ArrayBufferObject(state)
{
// BackingStore should be valid and non-shared
ASSERT(!!backingStore && !backingStore->isShared());

updateBackingStore(backingStore);
}

void ArrayBufferObject::allocateBuffer(ExecutionState& state, size_t byteLength)
{
detachArrayBuffer();
Expand Down
2 changes: 2 additions & 0 deletions src/runtime/ArrayBufferObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,11 @@ class ArrayBufferObject : public ArrayBuffer {
public:
explicit ArrayBufferObject(ExecutionState& state);
explicit ArrayBufferObject(ExecutionState& state, Object* proto);
explicit ArrayBufferObject(ExecutionState& state, BackingStore* backingStore);

static ArrayBufferObject* allocateArrayBuffer(ExecutionState& state, Object* constructor, uint64_t byteLength,
Optional<uint64_t> maxByteLength = Optional<uint64_t>(), bool resizeable = true);
static ArrayBufferObject* allocateExternalArrayBuffer(ExecutionState& state, void* dataBlock, size_t byteLength);
static ArrayBufferObject* cloneArrayBuffer(ExecutionState& state, ArrayBuffer* srcBuffer, size_t srcByteOffset, uint64_t srcLength, Object* constructor);

void allocateBuffer(ExecutionState& state, size_t bytelength);
Expand Down
26 changes: 14 additions & 12 deletions src/runtime/BackingStore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ namespace Escargot {
static void backingStorePlatformDeleter(void* data, size_t length, void* deleterData)
{
if (!!data) {
Global::platform()->onFreeArrayBufferObjectDataBuffer(data, length);
Global::platform()->onFreeArrayBufferObjectDataBuffer(data, length, deleterData);
}
}

Expand All @@ -46,15 +46,15 @@ BackingStore* BackingStore::createDefaultResizableNonSharedBackingStore(size_t b
byteLength, backingStorePlatformDeleter, maxByteLength, true);
}

BackingStore* BackingStore::createNonSharedBackingStore(void* data, size_t byteLength, BackingStoreDeleterCallback callback, void* callbackData)
BackingStore* BackingStore::createNonSharedBackingStore(void* data, size_t byteLength, BackingStoreDeleterCallback deleter, void* callbackData)
{
return new NonSharedBackingStore(data, byteLength, callback, callbackData, false);
return new NonSharedBackingStore(data, byteLength, deleter, callbackData, false);
}

NonSharedBackingStore::NonSharedBackingStore(void* data, size_t byteLength, BackingStoreDeleterCallback callback, void* callbackData, bool isAllocatedByPlatform)
NonSharedBackingStore::NonSharedBackingStore(void* data, size_t byteLength, BackingStoreDeleterCallback deleter, void* callbackData, bool isAllocatedByPlatform)
: m_data(data)
, m_byteLength(byteLength)
, m_deleter(callback)
, m_deleter(deleter)
, m_deleterData(callbackData)
, m_isAllocatedByPlatform(isAllocatedByPlatform)
, m_isResizable(false)
Expand All @@ -67,10 +67,10 @@ NonSharedBackingStore::NonSharedBackingStore(void* data, size_t byteLength, Back
nullptr, nullptr, nullptr);
}

NonSharedBackingStore::NonSharedBackingStore(void* data, size_t byteLength, BackingStoreDeleterCallback callback, size_t maxByteLength, bool isAllocatedByPlatform)
NonSharedBackingStore::NonSharedBackingStore(void* data, size_t byteLength, BackingStoreDeleterCallback deleter, size_t maxByteLength, bool isAllocatedByPlatform)
: m_data(data)
, m_byteLength(byteLength)
, m_deleter(callback)
, m_deleter(deleter)
, m_maxByteLength(maxByteLength)
, m_isAllocatedByPlatform(isAllocatedByPlatform)
, m_isResizable(true)
Expand Down Expand Up @@ -138,21 +138,23 @@ BackingStore* BackingStore::createDefaultSharedBackingStore(size_t byteLength)
{
SharedDataBlockInfo* sharedInfo = new SharedDataBlockInfo(
Global::platform()->onMallocArrayBufferObjectDataBuffer(byteLength),
byteLength);
byteLength,
backingStorePlatformDeleter);
return new SharedBackingStore(sharedInfo);
}

BackingStore* BackingStore::createDefaultGrowableSharedBackingStore(size_t byteLength, size_t maxByteLength)
{
SharedDataBlockInfo* sharedInfo = new GrowableSharedDataBlockInfo(
Global::platform()->onMallocArrayBufferObjectDataBuffer(maxByteLength),
byteLength, maxByteLength);
byteLength, maxByteLength,
backingStorePlatformDeleter);
return new SharedBackingStore(sharedInfo);
}

BackingStore* BackingStore::createSharedBackingStore(SharedDataBlockInfo* sharedInfo)
{
ASSERT(sharedInfo->hasValidReference());
// ASSERT(sharedInfo->hasValidReference());
return new SharedBackingStore(sharedInfo);
}

Expand All @@ -163,9 +165,9 @@ void SharedDataBlockInfo::deref()
auto oldValue = m_refCount.fetch_sub(1);
if (oldValue == 1) {
if (isGrowable()) {
Global::platform()->onFreeArrayBufferObjectDataBuffer(m_data, maxByteLength());
m_deleter(m_data, maxByteLength(), nullptr);
} else {
Global::platform()->onFreeArrayBufferObjectDataBuffer(m_data, m_byteLength);
m_deleter(m_data, m_byteLength, nullptr);
}

m_data = nullptr;
Expand Down
32 changes: 26 additions & 6 deletions src/runtime/BackingStore.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ class BackingStore : public gc, public BufferAddressObserverManager<BackingStore
public:
static BackingStore* createDefaultNonSharedBackingStore(size_t byteLength);
static BackingStore* createDefaultResizableNonSharedBackingStore(size_t byteLength, size_t maxByteLength);
static BackingStore* createNonSharedBackingStore(void* data, size_t byteLength, BackingStoreDeleterCallback callback, void* callbackData);
static BackingStore* createNonSharedBackingStore(void* data, size_t byteLength, BackingStoreDeleterCallback deleter, void* callbackData);

#if defined(ENABLE_THREADING)
static BackingStore* createDefaultSharedBackingStore(size_t byteLength);
Expand All @@ -153,6 +153,12 @@ class BackingStore : public gc, public BufferAddressObserverManager<BackingStore
virtual void* deleterData() const = 0;
virtual bool isResizable() const = 0;

virtual size_t byteLengthRMW(size_t newByteLength) // special function used only for WASMMemoryObject
{
ASSERT_NOT_REACHED();
return 0;
}

virtual SharedDataBlockInfo* sharedDataBlockInfo() const
{
ASSERT_NOT_REACHED();
Expand Down Expand Up @@ -221,8 +227,8 @@ class NonSharedBackingStore : public BackingStore {
void* operator new[](size_t size) = delete;

private:
NonSharedBackingStore(void* data, size_t byteLength, BackingStoreDeleterCallback callback, void* callbackData, bool isAllocatedByPlatform);
NonSharedBackingStore(void* data, size_t byteLength, BackingStoreDeleterCallback callback, size_t maxByteLength, bool isAllocatedByPlatform);
NonSharedBackingStore(void* data, size_t byteLength, BackingStoreDeleterCallback deleter, void* callbackData, bool isAllocatedByPlatform);
NonSharedBackingStore(void* data, size_t byteLength, BackingStoreDeleterCallback deleter, size_t maxByteLength, bool isAllocatedByPlatform);

void* m_data;
size_t m_byteLength;
Expand All @@ -238,11 +244,13 @@ class NonSharedBackingStore : public BackingStore {
#if defined(ENABLE_THREADING)
class SharedDataBlockInfo {
public:
SharedDataBlockInfo(void* data, size_t byteLength)
SharedDataBlockInfo(void* data, size_t byteLength, BackingStoreDeleterCallback deleter)
: m_data(data)
, m_byteLength(byteLength)
, m_refCount(0)
, m_deleter(deleter)
{
ASSERT(!!deleter);
}

virtual ~SharedDataBlockInfo() {}
Expand Down Expand Up @@ -276,6 +284,12 @@ class SharedDataBlockInfo {
return m_byteLength.load();
}

size_t byteLengthRMW(size_t newByteLength)
{
ASSERT(hasValidReference());
return m_byteLength.exchange(newByteLength);
}

void ref()
{
m_refCount++;
Expand All @@ -293,12 +307,13 @@ class SharedDataBlockInfo {
// defined as atomic value to not to use a lock
std::atomic<size_t> m_byteLength;
std::atomic<size_t> m_refCount;
BackingStoreDeleterCallback m_deleter;
};

class GrowableSharedDataBlockInfo : public SharedDataBlockInfo {
public:
GrowableSharedDataBlockInfo(void* data, size_t byteLength, size_t maxByteLength)
: SharedDataBlockInfo(data, byteLength)
GrowableSharedDataBlockInfo(void* data, size_t byteLength, size_t maxByteLength, BackingStoreDeleterCallback deleter)
: SharedDataBlockInfo(data, byteLength, deleter)
, m_maxByteLength(maxByteLength)
{
}
Expand Down Expand Up @@ -368,6 +383,11 @@ class SharedBackingStore : public BackingStore {
return m_sharedDataBlockInfo->isGrowable();
}

virtual size_t byteLengthRMW(size_t newByteLength) override
{
return m_sharedDataBlockInfo->byteLengthRMW(newByteLength);
}

virtual void resize(size_t newByteLength) override;

void* operator new(size_t size);
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/Platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class Platform {
virtual ~Platform() {}
// ArrayBuffer
virtual void* onMallocArrayBufferObjectDataBuffer(size_t sizeInByte) = 0;
virtual void onFreeArrayBufferObjectDataBuffer(void* buffer, size_t sizeInByte) = 0;
virtual void onFreeArrayBufferObjectDataBuffer(void* buffer, size_t sizeInByte, void* deleterData) = 0;
virtual void* onReallocArrayBufferObjectDataBuffer(void* oldBuffer, size_t oldSizeInByte, size_t newSizeInByte) = 0;

// Promise
Expand Down
20 changes: 20 additions & 0 deletions src/runtime/SharedArrayBufferObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,20 @@ SharedArrayBufferObject* SharedArrayBufferObject::allocateSharedArrayBuffer(Exec
return new SharedArrayBufferObject(state, proto, byteLength);
}

SharedArrayBufferObject* SharedArrayBufferObject::allocateExternalSharedArrayBuffer(ExecutionState& state, void* dataBlock, size_t byteLength)
{
if (UNLIKELY(byteLength >= ArrayBuffer::maxArrayBufferSize)) {
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, state.context()->staticStrings().SharedArrayBuffer.string(), false, String::emptyString, ErrorObject::Messages::GlobalObject_InvalidArrayBufferSize);
}

// creating a fixed length memory buffer from memaddr.
// NOTE) deleter do nothing, dataBlock will be freed in external module
SharedDataBlockInfo* sharedInfo = new SharedDataBlockInfo(dataBlock, byteLength,
[](void* data, size_t length, void* deleterData) {});

return new SharedArrayBufferObject(state, state.context()->globalObject()->sharedArrayBufferPrototype(), sharedInfo);
}

void* SharedArrayBufferObject::operator new(size_t size)
{
static MAY_THREAD_LOCAL bool typeInited = false;
Expand Down Expand Up @@ -272,6 +286,12 @@ void SharedArrayBufferObject::setValueInBuffer(ExecutionState& state, size_t byt
}
#endif
}

size_t SharedArrayBufferObject::byteLengthRMW(size_t newByteLength)
{
ASSERT(m_backingStore.hasValue());
return m_backingStore->byteLengthRMW(newByteLength);
}
} // namespace Escargot

#endif
3 changes: 3 additions & 0 deletions src/runtime/SharedArrayBufferObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class SharedArrayBufferObject : public ArrayBuffer {
SharedArrayBufferObject(ExecutionState& state, Object* proto, SharedDataBlockInfo* sharedInfo);

static SharedArrayBufferObject* allocateSharedArrayBuffer(ExecutionState& state, Object* constructor, uint64_t byteLength, Optional<uint64_t> maxByteLength);
static SharedArrayBufferObject* allocateExternalSharedArrayBuffer(ExecutionState& state, void* dataBlock, size_t byteLength);

virtual bool isSharedArrayBufferObject() const override
{
Expand All @@ -47,6 +48,8 @@ class SharedArrayBufferObject : public ArrayBuffer {

void* operator new(size_t size);
void* operator new[](size_t size) = delete;

size_t byteLengthRMW(size_t newByteLength);
};
} // namespace Escargot

Expand Down
1 change: 1 addition & 0 deletions src/runtime/StaticStrings.h
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,7 @@ namespace Escargot {
F(memory) \
F(module) \
F(table) \
F(shared) \
F(v128) \
F(validate)
#else
Expand Down
Loading

0 comments on commit 29041c3

Please sign in to comment.