Skip to content

Commit d4c3592

Browse files
authored
Update emmalloc (#10145)
* Update emmalloc * Use 64bit ops in emmalloc. * Add emmalloc.h header * Ensure test_setjmp_noleak tests against dlmalloc * Fix emmalloc test and 64-bit ctz * Update dlfcn test and aligned_alloc linkage * Adjust last resort lookup for 64-bit buckets build * Remove support for non-sbrk emmalloc use. * Add emmalloc_usable_size(). * Revise docs * Add validation functions and expand emmalloc api. * Add use of EMMALLOC_DEBUG and EMMALLOC_DEBUG_LOG. Add testing for emmalloc memory statistics. * Remove old emmalloc code. * Restore EMMALLOC_USE_64BIT_OPS * Be diligent at looking at all available memory if sbrk() fails. * Add emmalloc_unclaimed_heap_memory(). * Implement malloc_trim() and promote emscripten_realloc_buffer() to a public API. * Micro-optimize compute_free_list_bucket(). * Fix bad order of 32-bit and 64-bit code when looking for free memory buckets * Add missing include. * flake * Fix tests * Stop using LLVM attribute alias(xxx), as it does not link properly to JS code * Remove incorrect JS->C dependency in PThread. * Use emmalloc_trim() * Update emmalloc_trim() test * Update tests. * Restore dlmalloc as default allocator. * Fix use of 32-bit emmalloc in wasm backend wasm2js mode. * Fix emmalloc build * Fix test_emmalloc back to the original form to avoid malloc() calls getting optimized out in -O2 and higher * CI fix * Micro-optimize size * Move emmalloc tests * Fix upstream wasm test * Drop malloc impl if building with USES_DYNAMIC_ALLOC=0 * Make emscripten_realloc_buffer internal again * Update test_emmalloc_trim * Update test_minimal_runtime_code_size. Emmalloc regresses fastcomp wasm backend sizes by 90 bytes, but improves wasm backend wasm sizes by 100 bytes, so that's ok. * Update minimal runtime code size tests * Run tests only in debug * Work around bug #10173 * Update test
1 parent 4e442df commit d4c3592

22 files changed

+1608
-1051
lines changed

.circleci/config.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ commands:
5050
- run:
5151
name: embuilder (LTO)
5252
command: |
53-
python3 ./embuilder.py build libcompiler_rt libc libc++abi libc++abi-noexcept libc++ libc++-noexcept libal libdlmalloc libpthread_stub libc_rt_wasm struct_info libc-wasm --lto
53+
python3 ./embuilder.py build libcompiler_rt libc libc++abi libc++abi-noexcept libc++ libc++-noexcept libal libdlmalloc libdlmalloc-debug libemmalloc libemmalloc-64bit libpthread_stub libc_rt_wasm struct_info libc-wasm --lto
5454
python3 tests/runner.py test_hello_world
5555
- run:
5656
name: embuilder (PIC)
@@ -60,7 +60,7 @@ commands:
6060
- run:
6161
name: embuilder (PIC+LTO)
6262
command: |
63-
python3 ./embuilder.py build libcompiler_rt libc libc++abi libc++abi-noexcept libc++ libc++-noexcept libal libdlmalloc libpthread_stub libc_rt_wasm struct_info libc-wasm --pic --lto
63+
python3 ./embuilder.py build libcompiler_rt libc libc++abi libc++abi-noexcept libc++ libc++-noexcept libal libdlmalloc libdlmalloc-debug libemmalloc libemmalloc-64bit libpthread_stub libc_rt_wasm struct_info libc-wasm --pic --lto
6464
python3 tests/runner.py test_hello_world
6565
- run:
6666
name: freeze cache

emcc.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1291,6 +1291,13 @@ def is_supported_link_flag(f):
12911291
shared.Settings.FETCH = 1
12921292
shared.Settings.SYSTEM_JS_LIBRARIES.append(shared.path_from_root('src', 'library_asmfs.js'))
12931293

1294+
# Explicitly drop linking in a malloc implementation if program is not using any dynamic allocation calls.
1295+
if not shared.Settings.USES_DYNAMIC_ALLOC:
1296+
shared.Settings.MALLOC = 'none'
1297+
1298+
if shared.Settings.MALLOC == 'emmalloc':
1299+
shared.Settings.SYSTEM_JS_LIBRARIES.append(shared.path_from_root('src', 'library_emmalloc.js'))
1300+
12941301
if shared.Settings.FETCH and final_suffix in JS_CONTAINING_ENDINGS:
12951302
forced_stdlibs.append('libfetch')
12961303
next_arg_index += 1

src/deps_info.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,5 @@
9494
"_embind_register_std_string": ["malloc", "free"],
9595
"_embind_register_std_wstring": ["malloc", "free"],
9696
"__syscall192": ["emscripten_builtin_memalign"],
97-
"pthread_create": ["malloc", "free"]
97+
"pthread_create": ["malloc", "free", "emscripten_main_thread_process_queued_calls"]
9898
}
99-

src/library_emmalloc.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
mergeInto(LibraryManager.library, {
2+
emmalloc_unclaimed_heap_memory__deps: ['emscripten_get_sbrk_ptr'],
3+
emmalloc_unclaimed_heap_memory: function() {
4+
var dynamicTop = HEAPU32[_emscripten_get_sbrk_ptr()>>2];
5+
#if ALLOW_MEMORY_GROWTH
6+
#if WASM
7+
#if WASM_MEM_MAX != -1
8+
// Using WASM_MEM_MAX to constrain max heap size.
9+
return {{{ WASM_MEM_MAX }}} - dynamicTop;
10+
#else
11+
// Not using a Wasm memory bound.
12+
return 2*1024*1024*1024 - 65536 - dynamicTop;
13+
#endif
14+
#else
15+
// asm.js:
16+
return 2*1024*1024*1024 - 16777216 - dynamicTop;
17+
#endif
18+
#else
19+
// ALLOW_MEMORY_GROWTH is disabled, the current heap size
20+
// is all we got.
21+
return HEAPU8.length - dynamicTop;
22+
#endif
23+
}
24+
});

src/library_pthread.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
var LibraryPThread = {
77
$PThread__postset: 'if (!ENVIRONMENT_IS_PTHREAD) PThread.initMainThreadBlock(); else PThread.initWorker();',
88
$PThread__deps: ['$PROCINFO', '_register_pthread_ptr',
9-
'emscripten_main_thread_process_queued_calls',
109
'$ERRNO_CODES', 'emscripten_futex_wake', '_kill_thread',
1110
'_cancel_thread', '_cleanup_thread'],
1211
$PThread: {

src/settings.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,8 @@ var TOTAL_MEMORY = 16777216;
114114
// * emmalloc - a simple and compact malloc designed for emscripten
115115
// * none - no malloc() implementation is provided, but you must implement
116116
// malloc() and free() yourself.
117-
// dlmalloc is necessary for multithreading, split memory, and other special
118-
// modes, and will be used automatically in those cases.
117+
// dlmalloc is necessary for split memory and other special modes, and will be
118+
// used automatically in those cases.
119119
// In general, if you don't need one of those special modes, and if you don't
120120
// allocate very many small objects, you should use emmalloc since it's
121121
// smaller. Otherwise, if you do allocate many small objects, dlmalloc

system/include/emscripten/emmalloc.h

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
#pragma once
2+
3+
#include <stddef.h>
4+
5+
#ifdef __cplusplus
6+
extern "C" {
7+
#endif
8+
9+
// emmalloc: A lightweight web-friendly memory allocator suitable for very small applications.
10+
// Enable the usage of emmalloc by passing the linker flag -s MALLOC=emmalloc to the application.
11+
12+
// A debug function that dumps the whole structure of malloc internal memory blocks to console.
13+
// *extremely slow*, use for debugging allocation test cases.
14+
void emmalloc_dump_memory_regions(void);
15+
16+
// Allocates size bytes with the given pow-2 alignment.
17+
void *memalign(size_t alignment, size_t size);
18+
void *emmalloc_memalign(size_t alignment, size_t size);
19+
void *emscripten_builtin_memalign(size_t alignment, size_t size);
20+
void *aligned_alloc(size_t alignment, size_t size);
21+
22+
// Allocates size bytes with default alignment (8 bytes)
23+
void *malloc(size_t size);
24+
void *emmalloc_malloc(size_t size);
25+
void *emscripten_builtin_malloc(size_t size);
26+
27+
// Returns the number of bytes that are actually allocated to the given pointer ptr.
28+
// E.g. due to alignment or size requirements, the actual size of the allocation can be
29+
// larger than what was requested.
30+
size_t malloc_usable_size(void *ptr);
31+
size_t emmalloc_usable_size(void *ptr);
32+
33+
// Frees a memory pointer allocated with any of
34+
// emmalloc_memalign, emmalloc_malloc,
35+
void free(void *ptr);
36+
void emmalloc_free(void *ptr);
37+
void emscripten_builtin_free(void *ptr);
38+
39+
// Performs a reallocation of the given memory pointer to a new size. If the memory region
40+
// pointed by ptr cannot be resized in place, a new memory region will be allocated, old
41+
// memory copied over, and the old memory area freed. The pointer ptr must have been
42+
// allocated with one of the emmalloc memory allocation functions (malloc, memalign, ...).
43+
// If called with size == 0, the pointer ptr is freed, and a null pointer is returned. If
44+
// called with null ptr, a new pointer is allocated.
45+
void *realloc(void *ptr, size_t size);
46+
void *emmalloc_realloc(void *ptr, size_t size);
47+
48+
// emmalloc_realloc_try() is like realloc(), but only attempts to try to resize the existing
49+
// memory area. If resizing the existing memory area fails, then realloc_try() will return 0
50+
// (the original memory block is not freed or modified). If resizing succeeds, previous
51+
// memory contents will be valid up to min(old length, new length) bytes.
52+
// If a null pointer is passed, no allocation is attempted but the function will return 0.
53+
// If zero size is passed, the function will behave like free().
54+
void *emmalloc_realloc_try(void *ptr, size_t size);
55+
56+
// emmalloc_realloc_uninitialized() is like realloc(), but old memory contents
57+
// will be undefined after reallocation. (old memory is not preserved in any case)
58+
void *emmalloc_realloc_uninitialized(void *ptr, size_t size);
59+
60+
// Like realloc(), but allows specifying the alignment to allocate to. This function cannot
61+
// be used to change the alignment of an existing allocation, but the original pointer should
62+
// be aligned to the given alignment already.
63+
void *aligned_realloc(void *ptr, size_t alignment, size_t size);
64+
void *emmalloc_aligned_realloc(void *ptr, size_t alignment, size_t size);
65+
66+
// emmalloc_aligned_realloc_uninitialized() is like aligned_realloc(), but old memory contents
67+
// will be undefined after reallocation. (old memory is not preserved in any case)
68+
void *emmalloc_aligned_realloc_uninitialized(void *ptr, size_t alignment, size_t size);
69+
70+
// posix_memalign allocates memory with a given alignment, like memalign, but with a slightly
71+
// different usage signature.
72+
int posix_memalign(void **memptr, size_t alignment, size_t size);
73+
int emmalloc_posix_memalign(void **memptr, size_t alignment, size_t size);
74+
75+
// calloc allocates memory that is initialized to zero.
76+
void *calloc(size_t num, size_t size);
77+
void *emmalloc_calloc(size_t num, size_t size);
78+
79+
// mallinfo() returns information about current emmalloc allocation state. This function
80+
// is very slow, only good for debugging. Avoid calling it for "routine" diagnostics.
81+
struct mallinfo mallinfo();
82+
struct mallinfo emmalloc_mallinfo();
83+
84+
// malloc_trim() returns unused dynamic memory back to the WebAssembly heap. Returns 1 if it
85+
// actually freed any memory, and 0 if not. Note: this function does not release memory back to
86+
// the system, but it only marks memory held by emmalloc back to unused state for other users
87+
// of sbrk() to claim.
88+
int malloc_trim(size_t pad);
89+
int emmalloc_trim(size_t pad);
90+
91+
// Validates the consistency of the malloc heap. Returns non-zero and prints an error to console
92+
// if memory map is corrupt. Returns 0 (and does not print anything) if memory is intact.
93+
int emmalloc_validate_memory_regions(void);
94+
95+
// Computes the size of the dynamic memory region governed by emmalloc. This represents the
96+
// amount of memory that emmalloc has sbrk()ed in for itself to manage. Use this function
97+
// for memory statistics tracking purposes. Calling this function is quite fast, practically
98+
// O(1) time.
99+
size_t emmalloc_dynamic_heap_size(void);
100+
101+
// Computes the amount of memory currently reserved under emmalloc's governance that is free
102+
// for the application to allocate. Use this function for memory statistics tracking purposes.
103+
// Note that calling this function is very slow, as it walks through each free memory block in
104+
// linear time.
105+
size_t emmalloc_free_dynamic_memory(void);
106+
107+
// Estimates the amount of untapped memory that emmalloc could expand its dynamic memory area
108+
// via sbrk()ing. Theoretically the maximum amount of memory that can still be malloc()ed can
109+
// be calculated via emmalloc_free_dynamic_memory() + emmalloc_unclaimed_heap_memory().
110+
// Calling this function is very fast constant time lookup.
111+
size_t emmalloc_unclaimed_heap_memory(void);
112+
113+
// Computes a detailed fragmentation map of available free memory. Pass in a pointer to a
114+
// 32 element long array. This function populates into each array index i the number of free
115+
// memory regions that have a size 2^i <= size < 2^(i+1), and returns the total number of
116+
// free memory regions (the sum of the array entries). This function runs very slowly, as it
117+
// iterates through all free memory blocks.
118+
size_t emmalloc_compute_free_dynamic_memory_fragmentation_map(size_t freeMemorySizeMap[32]);
119+
120+
#ifdef __cplusplus
121+
}
122+
#endif

system/include/emscripten/heap.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,18 @@
1313
extern "C" {
1414
#endif
1515

16+
// Returns a pointer to a memory location that contains the heap DYNAMICTOP
17+
// variable (the end of the dynamic memory region)
1618
intptr_t *emscripten_get_sbrk_ptr(void);
17-
int emscripten_resize_heap(size_t requested_size);
19+
20+
// Attempts to geometrically or linearly increase the heap so that it
21+
// grows by at least requested_growth_bytes new bytes. The heap size may
22+
// be overallocated, see src/settings.js variables MEMORY_GROWTH_GEOMETRIC_STEP,
23+
// MEMORY_GROWTH_GEOMETRIC_CAP and MEMORY_GROWTH_LINEAR_STEP. This function
24+
// cannot be used to shrink the size of the heap.
25+
int emscripten_resize_heap(size_t requested_growth_bytes);
26+
27+
// Returns the current size of the WebAssembly heap.
1828
size_t emscripten_get_heap_size(void);
1929

2030
#ifdef __cplusplus

0 commit comments

Comments
 (0)