Skip to content

Commit 6b03ebd

Browse files
authored
[wasm] Add custom mmap/munmap implementation for anonymous mappings (#101871)
* emscripten libc implements mmap/munmap as a broken adapter on top of malloc (not calloc), which means it has no choice but to invoke memset on every allocation in order to provide properly zeroed bytes for our allocation requests. this commit adds a custom mmap/munmap implementation that can skip zeroing already-zeroed pages * re-enable freeing of pages in sgen on wasm if custom mmap is active * add runtime option for custom mmap * add warning switches to fix build on debian
1 parent aa4b0ab commit 6b03ebd

File tree

10 files changed

+623
-8
lines changed

10 files changed

+623
-8
lines changed

src/mono/mono/sgen/sgen-internal.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ sgen_report_internal_mem_usage (void)
273273
void
274274
sgen_init_internal_allocator (void)
275275
{
276-
int i, size;
276+
int i;
277277

278278
for (i = 0; i < INTERNAL_MEM_MAX; ++i)
279279
fixed_type_allocator_indexes [i] = -1;
@@ -284,7 +284,10 @@ sgen_init_internal_allocator (void)
284284
mono_lock_free_allocator_init_allocator (&allocators [i], &size_classes [i], MONO_MEM_ACCOUNT_SGEN_INTERNAL);
285285
}
286286

287-
for (size = mono_pagesize (); size <= LOCK_FREE_ALLOC_SB_MAX_SIZE; size <<= 1) {
287+
// FIXME: This whole algorithm is broken on WASM due to its 64KB page size.
288+
// Previously SB_MAX_SIZE was < mono_pagesize, so none of this ran.
289+
#ifndef HOST_WASM
290+
for (int size = mono_pagesize (); size <= LOCK_FREE_ALLOC_SB_MAX_SIZE; size <<= 1) {
288291
int max_size = (LOCK_FREE_ALLOC_SB_USABLE_SIZE (size) / 2) & ~(SIZEOF_VOID_P - 1);
289292
/*
290293
* we assert that allocator_sizes contains the biggest possible object size
@@ -297,6 +300,7 @@ sgen_init_internal_allocator (void)
297300
if (size < LOCK_FREE_ALLOC_SB_MAX_SIZE)
298301
g_assert (block_size (max_size + 1) == size << 1);
299302
}
303+
#endif
300304
}
301305

302306
#endif

src/mono/mono/sgen/sgen-marksweep.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "mono/sgen/sgen-client.h"
3434
#include "mono/utils/mono-memory-model.h"
3535
#include "mono/utils/mono-proclib.h"
36+
#include "mono/utils/options.h"
3637

3738
static int ms_block_size;
3839

@@ -2133,12 +2134,15 @@ major_free_swept_blocks (size_t section_reserve)
21332134
{
21342135
SGEN_ASSERT (0, sweep_state == SWEEP_STATE_SWEPT, "Sweeping must have finished before freeing blocks");
21352136

2136-
#if defined(HOST_WIN32) || defined(HOST_ORBIS) || defined (HOST_WASM)
2137+
#if defined(HOST_WIN32) || defined(HOST_ORBIS)
21372138
/*
21382139
* sgen_free_os_memory () asserts in mono_vfree () because windows doesn't like freeing the middle of
21392140
* a VirtualAlloc ()-ed block.
21402141
*/
21412142
return;
2143+
#elif defined(HOST_WASM)
2144+
if (!mono_opt_wasm_mmap)
2145+
return;
21422146
#endif
21432147

21442148
{

src/mono/mono/utils/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ set(utils_arch_sources "${utils_arch_sources};mono-hwcap-riscv.c")
206206
elseif(TARGET_S390X)
207207
set(utils_arch_sources "${utils_arch_sources};mono-hwcap-s390x.c")
208208
elseif(TARGET_WASM)
209-
set(utils_arch_sources "${utils_arch_sources};mono-hwcap-wasm.c;mono-mmap-wasm.c")
209+
set(utils_arch_sources "${utils_arch_sources};mono-hwcap-wasm.c;mono-mmap-wasm.c;mono-wasm-pagemgr.c")
210210
elseif(TARGET_WASI)
211211
set(utils_arch_sources "${utils_arch_sources};mono-hwcap-wasm.c")
212212
elseif(TARGET_POWERPC OR TARGET_POWERPC64)

src/mono/mono/utils/lock-free-alloc.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ typedef struct {
4545
MonoMemAccountType account_type;
4646
} MonoLockFreeAllocator;
4747

48+
// FIXME: On WASM the page size is 64KB, so this isn't enough.
4849
#define LOCK_FREE_ALLOC_SB_MAX_SIZE 16384
4950
#define LOCK_FREE_ALLOC_SB_HEADER_SIZE (sizeof (gpointer))
5051
#define LOCK_FREE_ALLOC_SB_USABLE_SIZE(block_size) ((block_size) - LOCK_FREE_ALLOC_SB_HEADER_SIZE)

src/mono/mono/utils/mono-mmap-wasm.c

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
#include "mono-proclib.h"
2323
#include <mono/utils/mono-threads.h>
2424
#include <mono/utils/atomic.h>
25+
#include <mono/utils/options.h>
26+
27+
#include "mono-wasm-pagemgr.h"
2528

2629
#define BEGIN_CRITICAL_SECTION do { \
2730
MonoThreadInfo *__info = mono_thread_info_current_unchecked (); \
@@ -34,6 +37,9 @@
3437
int
3538
mono_pagesize (void)
3639
{
40+
if (mono_opt_wasm_mmap)
41+
return MWPM_PAGE_SIZE;
42+
3743
static int saved_pagesize = 0;
3844

3945
if (saved_pagesize)
@@ -108,7 +114,16 @@ valloc_impl (void *addr, size_t size, int flags, MonoMemAccountType type)
108114
mflags |= MAP_PRIVATE;
109115

110116
BEGIN_CRITICAL_SECTION;
111-
ptr = mmap (addr, size, prot, mflags, -1, 0);
117+
if (mono_opt_wasm_mmap) {
118+
// FIXME: Make this work if the requested address range is free
119+
if ((flags & MONO_MMAP_FIXED) && addr)
120+
return NULL;
121+
122+
ptr = mwpm_alloc_range (size, 1);
123+
if (!ptr)
124+
return NULL;
125+
} else
126+
ptr = mmap (addr, size, prot, mflags, -1, 0);
112127
END_CRITICAL_SECTION;
113128

114129
if (ptr == MAP_FAILED)
@@ -142,6 +157,10 @@ typedef struct {
142157
void*
143158
mono_valloc_aligned (size_t size, size_t alignment, int flags, MonoMemAccountType type)
144159
{
160+
// We don't need padding if the alignment is compatible with the page size
161+
if (mono_opt_wasm_mmap && ((MWPM_PAGE_SIZE % alignment) == 0))
162+
return valloc_impl (NULL, size, flags, type);
163+
145164
/* Allocate twice the memory to be able to put the block on an aligned address */
146165
char *mem = (char *) valloc_impl (NULL, size + alignment, flags, type);
147166
char *aligned;
@@ -175,13 +194,22 @@ mono_vfree (void *addr, size_t length, MonoMemAccountType type)
175194
* mono_valloc_align (), free the original mapping.
176195
*/
177196
BEGIN_CRITICAL_SECTION;
178-
munmap (info->addr, info->size);
197+
if (mono_opt_wasm_mmap)
198+
mwpm_free_range (info->addr, info->size);
199+
else
200+
munmap (info->addr, info->size);
179201
END_CRITICAL_SECTION;
180202
g_free (info);
181203
g_hash_table_remove (valloc_hash, addr);
182204
} else {
205+
// FIXME: We could be trying to unmap part of an aligned mapping, in which case the
206+
// hash lookup failed because addr isn't exactly the start of the mapping.
207+
// Ideally if the custom page manager is enabled, we won't have done aligned alloc.
183208
BEGIN_CRITICAL_SECTION;
184-
munmap (addr, length);
209+
if (mono_opt_wasm_mmap)
210+
mwpm_free_range (addr, length);
211+
else
212+
munmap (addr, length);
185213
END_CRITICAL_SECTION;
186214
}
187215

src/mono/mono/utils/mono-mmap.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ enum {
2222
MONO_MMAP_ANON = 1 << 6,
2323
MONO_MMAP_FIXED = 1 << 7,
2424
MONO_MMAP_32BIT = 1 << 8,
25-
MONO_MMAP_JIT = 1 << 9
25+
MONO_MMAP_JIT = 1 << 9,
26+
MONO_MMAP_NOZERO = 1 << 10,
2627
};
2728

2829
typedef enum {

0 commit comments

Comments
 (0)