diff --git a/src/runtime/Memory.cpp b/src/runtime/Memory.cpp index 0fced6104..9903d48a2 100644 --- a/src/runtime/Memory.cpp +++ b/src/runtime/Memory.cpp @@ -21,6 +21,11 @@ #include "runtime/Instance.h" #include "runtime/Module.h" +#if defined(OS_POSIX) +#define WALRUS_USE_MMAP +#include +#endif + namespace Walrus { Memory* Memory::createMemory(Store* store, uint64_t initialSizeInByte, uint64_t maximumSizeInByte) @@ -32,30 +37,85 @@ Memory* Memory::createMemory(Store* store, uint64_t initialSizeInByte, uint64_t Memory::Memory(uint64_t initialSizeInByte, uint64_t maximumSizeInByte) : m_sizeInByte(initialSizeInByte) + , m_reservedSizeInByte(0) , m_maximumSizeInByte(maximumSizeInByte) - , m_buffer(reinterpret_cast(calloc(1, initialSizeInByte))) + , m_buffer(nullptr) { + RELEASE_ASSERT(initialSizeInByte <= std::numeric_limits::max()); +#if defined(WALRUS_USE_MMAP) + if (m_maximumSizeInByte) { +#ifndef WALRUS_32_MEMORY_INITIAL_MMAP_RESERVED_ADDRESS_SIZE +#define WALRUS_32_MEMORY_INITIAL_MMAP_RESERVED_ADDRESS_SIZE (1024 * 1024 * 64) +#endif +#ifndef WALRUS_64_MEMORY_INITIAL_MMAP_RESERVED_ADDRESS_SIZE +#define WALRUS_64_MEMORY_INITIAL_MMAP_RESERVED_ADDRESS_SIZE (1024 * 1024 * 512) +#endif + uint64_t initialReservedSize = +#if defined(WALRUS_32) + WALRUS_32_MEMORY_INITIAL_MMAP_RESERVED_ADDRESS_SIZE; +#else + WALRUS_64_MEMORY_INITIAL_MMAP_RESERVED_ADDRESS_SIZE; +#endif + m_reservedSizeInByte = std::min(initialReservedSize, m_maximumSizeInByte); + m_buffer = reinterpret_cast(mmap(NULL, m_reservedSizeInByte, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)); + RELEASE_ASSERT(MAP_FAILED != m_buffer); + mprotect(m_buffer, initialSizeInByte, (PROT_READ | PROT_WRITE)); + } else { + m_reservedSizeInByte = 0; + m_buffer = nullptr; + } +#else + m_buffer = reinterpret_cast(calloc(1, initialSizeInByte)); + m_reservedSizeInByte = initialSizeInByte; RELEASE_ASSERT(m_buffer); +#endif } Memory::~Memory() { +#if defined(WALRUS_USE_MMAP) + if (m_buffer) { + munmap(m_buffer, m_reservedSizeInByte); + } +#else ASSERT(!!m_buffer); free(m_buffer); +#endif } bool Memory::grow(uint64_t growSizeInByte) { uint64_t newSizeInByte = growSizeInByte + m_sizeInByte; if (newSizeInByte > m_sizeInByte && newSizeInByte <= m_maximumSizeInByte) { - uint8_t* newBuffer = reinterpret_cast(calloc(1, newSizeInByte)); - if (newBuffer) { +#if defined(WALRUS_USE_MMAP) + if (newSizeInByte <= m_reservedSizeInByte) { + mprotect(m_buffer + m_sizeInByte, growSizeInByte, (PROT_READ | PROT_WRITE)); + m_sizeInByte = newSizeInByte; + } else { + auto newReservedSizeInByte = std::min(newSizeInByte * 2, m_maximumSizeInByte); + auto newBuffer = reinterpret_cast(mmap(NULL, newReservedSizeInByte, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)); + if (MAP_FAILED == newBuffer) { + return false; + } + mprotect(newBuffer, newSizeInByte, (PROT_READ | PROT_WRITE)); memcpy(newBuffer, m_buffer, m_sizeInByte); - free(m_buffer); + munmap(m_buffer, m_reservedSizeInByte); m_buffer = newBuffer; m_sizeInByte = newSizeInByte; - return true; + m_reservedSizeInByte = newReservedSizeInByte; } +#else + uint8_t* newBuffer = reinterpret_cast(calloc(1, newSizeInByte)); + if (newBuffer == nullptr || newSizeInByte >= std::numeric_limits::max()) { + return false; + } + m_reservedSizeInByte = newSizeInByte; + memcpy(newBuffer, m_buffer, m_sizeInByte); + free(m_buffer); + m_buffer = newBuffer; + m_sizeInByte = newSizeInByte; +#endif + return true; } else if (newSizeInByte == m_sizeInByte) { return true; } diff --git a/src/runtime/Memory.h b/src/runtime/Memory.h index 938a00c4b..34937d025 100644 --- a/src/runtime/Memory.h +++ b/src/runtime/Memory.h @@ -133,6 +133,7 @@ class Memory : public Extern { inline void fillMemory(uint32_t start, uint8_t value, uint32_t size); uint64_t m_sizeInByte; + uint64_t m_reservedSizeInByte; uint64_t m_maximumSizeInByte; uint8_t* m_buffer; }; diff --git a/test/basic/memory.wast b/test/basic/memory.wast index c9b3e2a91..1608dc6b0 100644 --- a/test/basic/memory.wast +++ b/test/basic/memory.wast @@ -17,4 +17,5 @@ (memory 0) (func (export "grow") (param i32) (result i32) (memory.grow (local.get 0))) ) -(assert_return (invoke "grow" (i32.const 0x10000)) (i32.const 0)) +(assert_return (invoke "grow" (i32.const 0x1000)) (i32.const 0)) +(assert_return (invoke "grow" (i32.const 0x10000)) (i32.const -1)) diff --git a/third_party/wabt/src/walrus/binary-reader-walrus.cc b/third_party/wabt/src/walrus/binary-reader-walrus.cc index 39475168b..cc6584da6 100644 --- a/third_party/wabt/src/walrus/binary-reader-walrus.cc +++ b/third_party/wabt/src/walrus/binary-reader-walrus.cc @@ -246,7 +246,8 @@ class BinaryReaderDelegateWalrus: public BinaryReaderDelegate { } Result OnImportMemory(Index import_index, nonstd::string_view module_name, nonstd::string_view field_name, Index memory_index, const Limits *page_limits) override { CHECK_RESULT(m_validator.OnMemory(GetLocation(), *page_limits)); - m_externalDelegate->OnImportMemory(import_index, std::string(module_name), std::string(field_name), memory_index, page_limits->initial, page_limits->has_max ? page_limits->max : (page_limits->is_64? WABT_MAX_PAGES64 : WABT_MAX_PAGES32)); + m_externalDelegate->OnImportMemory(import_index, std::string(module_name), std::string(field_name), memory_index, page_limits->initial, + page_limits->has_max ? page_limits->max : (page_limits->is_64? (WABT_MAX_PAGES64 - 1) : (WABT_MAX_PAGES32 - 1))); return Result::Ok; } Result OnImportGlobal(Index import_index, nonstd::string_view module_name, nonstd::string_view field_name, Index global_index, Type type, bool mutable_) override { @@ -308,7 +309,8 @@ class BinaryReaderDelegateWalrus: public BinaryReaderDelegate { } Result OnMemory(Index index, const Limits *limits) override { CHECK_RESULT(m_validator.OnMemory(GetLocation(), *limits)); - m_externalDelegate->OnMemory(index, limits->initial, limits->has_max ? limits->max : (limits->is_64? WABT_MAX_PAGES64 : WABT_MAX_PAGES32)); + m_externalDelegate->OnMemory(index, limits->initial, + limits->has_max ? limits->max : (limits->is_64 ? (WABT_MAX_PAGES64 - 1) : (WABT_MAX_PAGES32 - 1))); return Result::Ok; } Result EndMemorySection() override {