From 679aad065908ccf40fcf535705c6e2d4a1fbdb82 Mon Sep 17 00:00:00 2001 From: Daan Leijen Date: Fri, 1 Oct 2021 15:05:01 -0700 Subject: [PATCH] update wasm support with emscripten compilation; now using sbrk instead of wasm_memory_grow --- include/mimalloc-internal.h | 10 +++++--- src/os.c | 49 ++++++++++++++++++++++++++++++++++--- src/random.c | 4 ++- src/stats.c | 2 +- 4 files changed, 56 insertions(+), 9 deletions(-) diff --git a/include/mimalloc-internal.h b/include/mimalloc-internal.h index 1e1a7966..ecdcf860 100644 --- a/include/mimalloc-internal.h +++ b/include/mimalloc-internal.h @@ -32,6 +32,10 @@ terms of the MIT license. A copy of the license can be found in the file #define mi_decl_cache_align #endif +#if defined(__EMSCRIPTEN__) && !defined(__wasi__) +#define __wasi__ +#endif + // "options.c" void _mi_fputs(mi_output_fun* out, void* arg, const char* prefix, const char* message); void _mi_fprintf(mi_output_fun* out, void* arg, const char* fmt, ...); @@ -242,10 +246,10 @@ static inline bool mi_malloc_satisfies_alignment(size_t alignment, size_t size) #undef _CLOCK_T #endif static inline bool mi_mul_overflow(size_t count, size_t size, size_t* total) { - #if (SIZE_MAX == UINT_MAX) - return __builtin_umul_overflow(count, size, total); - #elif (SIZE_MAX == ULONG_MAX) + #if (SIZE_MAX == ULONG_MAX) return __builtin_umull_overflow(count, size, total); + #elif (SIZE_MAX == UINT_MAX) + return __builtin_umul_overflow(count, size, total); #else return __builtin_umulll_overflow(count, size, total); #endif diff --git a/src/os.c b/src/os.c index 85415232..5b6b0c7e 100644 --- a/src/os.c +++ b/src/os.c @@ -26,11 +26,14 @@ terms of the MIT license. A copy of the license can be found in the file #pragma warning(disable:4996) // strerror #endif +#if defined(__wasi__) +#define MI_USE_SBRK +#endif #if defined(_WIN32) #include #elif defined(__wasi__) -// stdlib.h is all we need, and has already been included in mimalloc.h +#include // sbrk #else #include // mmap #include // sysconf @@ -99,11 +102,13 @@ size_t _mi_os_large_page_size() { return (large_os_page_size != 0 ? large_os_page_size : _mi_os_page_size()); } +#if !defined(MI_USE_SBRK) && !defined(__wasi__) static bool use_large_os_page(size_t size, size_t alignment) { // if we have access, check the size and alignment requirements if (large_os_page_size == 0 || !mi_option_is_enabled(mi_option_large_os_pages)) return false; return ((size % large_os_page_size) == 0 && (alignment % large_os_page_size) == 0); } +#endif // round to a good OS allocation size (bounded by max 12.5% waste) size_t _mi_os_good_alloc_size(size_t size) { @@ -236,8 +241,8 @@ static bool mi_os_mem_free(void* addr, size_t size, bool was_committed, mi_stats bool err = false; #if defined(_WIN32) err = (VirtualFree(addr, 0, MEM_RELEASE) == 0); -#elif defined(__wasi__) - err = 0; // WebAssembly's heap cannot be shrunk +#elif defined(MI_USE_SBRK) + err = 0; // sbrk heap cannot be shrunk #else err = (munmap(addr, size) == -1); #endif @@ -252,7 +257,9 @@ static bool mi_os_mem_free(void* addr, size_t size, bool was_committed, mi_stats } } +#if !defined(MI_USE_SBRK) && !defined(__wasi__) static void* mi_os_get_aligned_hint(size_t try_alignment, size_t size); +#endif #ifdef _WIN32 static void* mi_win_virtual_allocx(void* addr, size_t size, size_t try_alignment, DWORD flags) { @@ -318,7 +325,32 @@ static void* mi_win_virtual_alloc(void* addr, size_t size, size_t try_alignment, return p; } -#elif defined(__wasi__) +#elif defined(MI_USE_SBRK) +#define MI_SBRK_FAIL ((void*)(-1)) +static void* mi_sbrk_heap_grow(size_t size, size_t try_alignment) { + void* pbase0 = sbrk(0); + if (pbase0 == MI_SBRK_FAIL) { + _mi_warning_message("unable to allocate sbrk() OS memory (%zu bytes)\n", size); + errno = ENOMEM; + return NULL; + } + uintptr_t base = (uintptr_t)pbase0; + uintptr_t aligned_base = _mi_align_up(base, (uintptr_t) try_alignment); + size_t alloc_size = _mi_align_up( aligned_base - base + size, _mi_os_page_size()); + mi_assert(alloc_size >= size && (alloc_size % _mi_os_page_size()) == 0); + if (alloc_size < size) return NULL; + void* pbase1 = sbrk(alloc_size); + if (pbase1 == MI_SBRK_FAIL) { + _mi_warning_message("unable to allocate sbrk() OS memory (%zu bytes, %zu requested)\n", size, alloc_size); + errno = ENOMEM; + return NULL; + } + mi_assert(pbase0 == pbase1); + return (void*)aligned_base; +} + +#elif defined(__wasi__) + // currently unused as we use sbrk() on wasm static void* mi_wasm_heap_grow(size_t size, size_t try_alignment) { uintptr_t base = __builtin_wasm_memory_size(0) * _mi_os_page_size(); uintptr_t aligned_base = _mi_align_up(base, (uintptr_t) try_alignment); @@ -326,11 +358,13 @@ static void* mi_wasm_heap_grow(size_t size, size_t try_alignment) { mi_assert(alloc_size >= size && (alloc_size % _mi_os_page_size()) == 0); if (alloc_size < size) return NULL; if (__builtin_wasm_memory_grow(0, alloc_size / _mi_os_page_size()) == SIZE_MAX) { + _mi_warning_message("unable to allocate wasm_memory_grow() OS memory (%zu bytes, %zu requested)\n", size, alloc_size); errno = ENOMEM; return NULL; } return (void*)aligned_base; } + #else #define MI_OS_USE_MMAP static void* mi_unix_mmapx(void* addr, size_t size, size_t try_alignment, int protect_flags, int flags, int fd) { @@ -506,6 +540,8 @@ static void* mi_os_get_aligned_hint(size_t try_alignment, size_t size) if (hint%try_alignment != 0) return NULL; return (void*)hint; } +#elif defined(__wasi__) || defined(MI_USE_SBRK) +// no need for mi_os_get_aligned_hint #else static void* mi_os_get_aligned_hint(size_t try_alignment, size_t size) { UNUSED(try_alignment); UNUSED(size); @@ -536,7 +572,12 @@ static void* mi_os_mem_alloc(size_t size, size_t try_alignment, bool commit, boo int flags = MEM_RESERVE; if (commit) flags |= MEM_COMMIT; p = mi_win_virtual_alloc(NULL, size, try_alignment, flags, false, allow_large, is_large); + #elif defined(MI_USE_SBRK) + KK_UNUSED(allow_large); + *is_large = false; + p = mi_sbrk_heap_grow(size, try_alignment); #elif defined(__wasi__) + KK_UNUSED(allow_large); *is_large = false; p = mi_wasm_heap_grow(size, try_alignment); #else diff --git a/src/random.c b/src/random.c index 255bede4..d5f75bf8 100644 --- a/src/random.c +++ b/src/random.c @@ -188,7 +188,7 @@ static bool os_random_buf(void* buf, size_t buf_len) { #elif defined(ANDROID) || defined(XP_DARWIN) || defined(__APPLE__) || defined(__DragonFly__) || \ defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || \ - defined(__sun) || defined(__wasi__) + defined(__sun) // todo: what to use with __wasi__? #include static bool os_random_buf(void* buf, size_t buf_len) { arc4random_buf(buf, buf_len); @@ -280,7 +280,9 @@ void _mi_random_init(mi_random_ctx_t* ctx) { if (!os_random_buf(key, sizeof(key))) { // if we fail to get random data from the OS, we fall back to a // weak random source based on the current time + #if !defined(__wasi__) _mi_warning_message("unable to use secure randomness\n"); + #endif uintptr_t x = _os_random_weak(0); for (size_t i = 0; i < 8; i++) { // key is eight 32-bit words. x = _mi_random_shuffle(x); diff --git a/src/stats.c b/src/stats.c index c94fbde9..ac728a1c 100644 --- a/src/stats.c +++ b/src/stats.c @@ -479,7 +479,7 @@ static void mi_stat_process_info(mi_msecs_t* elapsed, mi_msecs_t* utime, mi_msec *page_faults = (size_t)info.PageFaultCount; } -#elif defined(__unix__) || defined(__unix) || defined(unix) || defined(__APPLE__) || defined(__HAIKU__) +#elif !defined(__wasi__) && (defined(__unix__) || defined(__unix) || defined(unix) || defined(__APPLE__) || defined(__HAIKU__)) #include #include #include