Skip to content

Commit

Permalink
Update emmalloc (emscripten-core#10145)
Browse files Browse the repository at this point in the history
* 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 emscripten-core#10173

* Update test
  • Loading branch information
juj authored Jan 9, 2020
1 parent 4e442df commit d4c3592
Show file tree
Hide file tree
Showing 22 changed files with 1,608 additions and 1,051 deletions.
4 changes: 2 additions & 2 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ commands:
- run:
name: embuilder (LTO)
command: |
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
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
python3 tests/runner.py test_hello_world
- run:
name: embuilder (PIC)
Expand All @@ -60,7 +60,7 @@ commands:
- run:
name: embuilder (PIC+LTO)
command: |
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
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
python3 tests/runner.py test_hello_world
- run:
name: freeze cache
Expand Down
7 changes: 7 additions & 0 deletions emcc.py
Original file line number Diff line number Diff line change
Expand Up @@ -1291,6 +1291,13 @@ def is_supported_link_flag(f):
shared.Settings.FETCH = 1
shared.Settings.SYSTEM_JS_LIBRARIES.append(shared.path_from_root('src', 'library_asmfs.js'))

# Explicitly drop linking in a malloc implementation if program is not using any dynamic allocation calls.
if not shared.Settings.USES_DYNAMIC_ALLOC:
shared.Settings.MALLOC = 'none'

if shared.Settings.MALLOC == 'emmalloc':
shared.Settings.SYSTEM_JS_LIBRARIES.append(shared.path_from_root('src', 'library_emmalloc.js'))

if shared.Settings.FETCH and final_suffix in JS_CONTAINING_ENDINGS:
forced_stdlibs.append('libfetch')
next_arg_index += 1
Expand Down
3 changes: 1 addition & 2 deletions src/deps_info.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,5 @@
"_embind_register_std_string": ["malloc", "free"],
"_embind_register_std_wstring": ["malloc", "free"],
"__syscall192": ["emscripten_builtin_memalign"],
"pthread_create": ["malloc", "free"]
"pthread_create": ["malloc", "free", "emscripten_main_thread_process_queued_calls"]
}

24 changes: 24 additions & 0 deletions src/library_emmalloc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
mergeInto(LibraryManager.library, {
emmalloc_unclaimed_heap_memory__deps: ['emscripten_get_sbrk_ptr'],
emmalloc_unclaimed_heap_memory: function() {
var dynamicTop = HEAPU32[_emscripten_get_sbrk_ptr()>>2];
#if ALLOW_MEMORY_GROWTH
#if WASM
#if WASM_MEM_MAX != -1
// Using WASM_MEM_MAX to constrain max heap size.
return {{{ WASM_MEM_MAX }}} - dynamicTop;
#else
// Not using a Wasm memory bound.
return 2*1024*1024*1024 - 65536 - dynamicTop;
#endif
#else
// asm.js:
return 2*1024*1024*1024 - 16777216 - dynamicTop;
#endif
#else
// ALLOW_MEMORY_GROWTH is disabled, the current heap size
// is all we got.
return HEAPU8.length - dynamicTop;
#endif
}
});
1 change: 0 additions & 1 deletion src/library_pthread.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
var LibraryPThread = {
$PThread__postset: 'if (!ENVIRONMENT_IS_PTHREAD) PThread.initMainThreadBlock(); else PThread.initWorker();',
$PThread__deps: ['$PROCINFO', '_register_pthread_ptr',
'emscripten_main_thread_process_queued_calls',
'$ERRNO_CODES', 'emscripten_futex_wake', '_kill_thread',
'_cancel_thread', '_cleanup_thread'],
$PThread: {
Expand Down
4 changes: 2 additions & 2 deletions src/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,8 @@ var TOTAL_MEMORY = 16777216;
// * emmalloc - a simple and compact malloc designed for emscripten
// * none - no malloc() implementation is provided, but you must implement
// malloc() and free() yourself.
// dlmalloc is necessary for multithreading, split memory, and other special
// modes, and will be used automatically in those cases.
// dlmalloc is necessary for split memory and other special modes, and will be
// used automatically in those cases.
// In general, if you don't need one of those special modes, and if you don't
// allocate very many small objects, you should use emmalloc since it's
// smaller. Otherwise, if you do allocate many small objects, dlmalloc
Expand Down
122 changes: 122 additions & 0 deletions system/include/emscripten/emmalloc.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
#pragma once

#include <stddef.h>

#ifdef __cplusplus
extern "C" {
#endif

// emmalloc: A lightweight web-friendly memory allocator suitable for very small applications.
// Enable the usage of emmalloc by passing the linker flag -s MALLOC=emmalloc to the application.

// A debug function that dumps the whole structure of malloc internal memory blocks to console.
// *extremely slow*, use for debugging allocation test cases.
void emmalloc_dump_memory_regions(void);

// Allocates size bytes with the given pow-2 alignment.
void *memalign(size_t alignment, size_t size);
void *emmalloc_memalign(size_t alignment, size_t size);
void *emscripten_builtin_memalign(size_t alignment, size_t size);
void *aligned_alloc(size_t alignment, size_t size);

// Allocates size bytes with default alignment (8 bytes)
void *malloc(size_t size);
void *emmalloc_malloc(size_t size);
void *emscripten_builtin_malloc(size_t size);

// Returns the number of bytes that are actually allocated to the given pointer ptr.
// E.g. due to alignment or size requirements, the actual size of the allocation can be
// larger than what was requested.
size_t malloc_usable_size(void *ptr);
size_t emmalloc_usable_size(void *ptr);

// Frees a memory pointer allocated with any of
// emmalloc_memalign, emmalloc_malloc,
void free(void *ptr);
void emmalloc_free(void *ptr);
void emscripten_builtin_free(void *ptr);

// Performs a reallocation of the given memory pointer to a new size. If the memory region
// pointed by ptr cannot be resized in place, a new memory region will be allocated, old
// memory copied over, and the old memory area freed. The pointer ptr must have been
// allocated with one of the emmalloc memory allocation functions (malloc, memalign, ...).
// If called with size == 0, the pointer ptr is freed, and a null pointer is returned. If
// called with null ptr, a new pointer is allocated.
void *realloc(void *ptr, size_t size);
void *emmalloc_realloc(void *ptr, size_t size);

// emmalloc_realloc_try() is like realloc(), but only attempts to try to resize the existing
// memory area. If resizing the existing memory area fails, then realloc_try() will return 0
// (the original memory block is not freed or modified). If resizing succeeds, previous
// memory contents will be valid up to min(old length, new length) bytes.
// If a null pointer is passed, no allocation is attempted but the function will return 0.
// If zero size is passed, the function will behave like free().
void *emmalloc_realloc_try(void *ptr, size_t size);

// emmalloc_realloc_uninitialized() is like realloc(), but old memory contents
// will be undefined after reallocation. (old memory is not preserved in any case)
void *emmalloc_realloc_uninitialized(void *ptr, size_t size);

// Like realloc(), but allows specifying the alignment to allocate to. This function cannot
// be used to change the alignment of an existing allocation, but the original pointer should
// be aligned to the given alignment already.
void *aligned_realloc(void *ptr, size_t alignment, size_t size);
void *emmalloc_aligned_realloc(void *ptr, size_t alignment, size_t size);

// emmalloc_aligned_realloc_uninitialized() is like aligned_realloc(), but old memory contents
// will be undefined after reallocation. (old memory is not preserved in any case)
void *emmalloc_aligned_realloc_uninitialized(void *ptr, size_t alignment, size_t size);

// posix_memalign allocates memory with a given alignment, like memalign, but with a slightly
// different usage signature.
int posix_memalign(void **memptr, size_t alignment, size_t size);
int emmalloc_posix_memalign(void **memptr, size_t alignment, size_t size);

// calloc allocates memory that is initialized to zero.
void *calloc(size_t num, size_t size);
void *emmalloc_calloc(size_t num, size_t size);

// mallinfo() returns information about current emmalloc allocation state. This function
// is very slow, only good for debugging. Avoid calling it for "routine" diagnostics.
struct mallinfo mallinfo();
struct mallinfo emmalloc_mallinfo();

// malloc_trim() returns unused dynamic memory back to the WebAssembly heap. Returns 1 if it
// actually freed any memory, and 0 if not. Note: this function does not release memory back to
// the system, but it only marks memory held by emmalloc back to unused state for other users
// of sbrk() to claim.
int malloc_trim(size_t pad);
int emmalloc_trim(size_t pad);

// Validates the consistency of the malloc heap. Returns non-zero and prints an error to console
// if memory map is corrupt. Returns 0 (and does not print anything) if memory is intact.
int emmalloc_validate_memory_regions(void);

// Computes the size of the dynamic memory region governed by emmalloc. This represents the
// amount of memory that emmalloc has sbrk()ed in for itself to manage. Use this function
// for memory statistics tracking purposes. Calling this function is quite fast, practically
// O(1) time.
size_t emmalloc_dynamic_heap_size(void);

// Computes the amount of memory currently reserved under emmalloc's governance that is free
// for the application to allocate. Use this function for memory statistics tracking purposes.
// Note that calling this function is very slow, as it walks through each free memory block in
// linear time.
size_t emmalloc_free_dynamic_memory(void);

// Estimates the amount of untapped memory that emmalloc could expand its dynamic memory area
// via sbrk()ing. Theoretically the maximum amount of memory that can still be malloc()ed can
// be calculated via emmalloc_free_dynamic_memory() + emmalloc_unclaimed_heap_memory().
// Calling this function is very fast constant time lookup.
size_t emmalloc_unclaimed_heap_memory(void);

// Computes a detailed fragmentation map of available free memory. Pass in a pointer to a
// 32 element long array. This function populates into each array index i the number of free
// memory regions that have a size 2^i <= size < 2^(i+1), and returns the total number of
// free memory regions (the sum of the array entries). This function runs very slowly, as it
// iterates through all free memory blocks.
size_t emmalloc_compute_free_dynamic_memory_fragmentation_map(size_t freeMemorySizeMap[32]);

#ifdef __cplusplus
}
#endif
12 changes: 11 additions & 1 deletion system/include/emscripten/heap.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,18 @@
extern "C" {
#endif

// Returns a pointer to a memory location that contains the heap DYNAMICTOP
// variable (the end of the dynamic memory region)
intptr_t *emscripten_get_sbrk_ptr(void);
int emscripten_resize_heap(size_t requested_size);

// Attempts to geometrically or linearly increase the heap so that it
// grows by at least requested_growth_bytes new bytes. The heap size may
// be overallocated, see src/settings.js variables MEMORY_GROWTH_GEOMETRIC_STEP,
// MEMORY_GROWTH_GEOMETRIC_CAP and MEMORY_GROWTH_LINEAR_STEP. This function
// cannot be used to shrink the size of the heap.
int emscripten_resize_heap(size_t requested_growth_bytes);

// Returns the current size of the WebAssembly heap.
size_t emscripten_get_heap_size(void);

#ifdef __cplusplus
Expand Down
Loading

0 comments on commit d4c3592

Please sign in to comment.