Skip to content

Update emmalloc #10145

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 43 commits into from
Jan 9, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
977ec7d
Update emmalloc
juj Jan 1, 2020
f883a95
Use 64bit ops in emmalloc.
juj Jan 1, 2020
2c0e6a5
Add emmalloc.h header
juj Jan 1, 2020
5218324
Ensure test_setjmp_noleak tests against dlmalloc
juj Jan 1, 2020
7ff0e12
Fix emmalloc test and 64-bit ctz
juj Jan 1, 2020
b4e7475
Update dlfcn test and aligned_alloc linkage
juj Jan 1, 2020
4fd3fac
Adjust last resort lookup for 64-bit buckets build
juj Jan 2, 2020
db14ec6
Remove support for non-sbrk emmalloc use.
juj Jan 2, 2020
8b3788a
Add emmalloc_usable_size().
juj Jan 2, 2020
14a83e4
Revise docs
juj Jan 3, 2020
643428e
Add validation functions and expand emmalloc api.
juj Jan 3, 2020
0acfc0b
Add use of EMMALLOC_DEBUG and EMMALLOC_DEBUG_LOG. Add testing for emm…
juj Jan 3, 2020
d75a62e
Remove old emmalloc code.
juj Jan 3, 2020
c2d48d9
Restore EMMALLOC_USE_64BIT_OPS
juj Jan 3, 2020
889c0bd
Be diligent at looking at all available memory if sbrk() fails.
juj Jan 3, 2020
e1f61ce
Add emmalloc_unclaimed_heap_memory().
juj Jan 3, 2020
73cd7a3
Implement malloc_trim() and promote emscripten_realloc_buffer() to a …
juj Jan 4, 2020
8892ec0
Micro-optimize compute_free_list_bucket().
juj Jan 4, 2020
f9421c5
Fix bad order of 32-bit and 64-bit code when looking for free memory …
juj Jan 4, 2020
710ca0c
Add missing include.
juj Jan 4, 2020
5cbffb5
flake
juj Jan 5, 2020
178ff31
Fix tests
juj Jan 5, 2020
a5746d7
Stop using LLVM attribute alias(xxx), as it does not link properly to…
juj Jan 6, 2020
26c9833
Remove incorrect JS->C dependency in PThread.
juj Jan 6, 2020
82071cd
Use emmalloc_trim()
juj Jan 6, 2020
aef4af4
Update emmalloc_trim() test
juj Jan 6, 2020
d0b8d99
Update tests.
juj Jan 6, 2020
3fee227
Restore dlmalloc as default allocator.
juj Jan 6, 2020
3a3e0c2
Fix use of 32-bit emmalloc in wasm backend wasm2js mode.
juj Jan 6, 2020
7f1ddfa
Fix emmalloc build
juj Jan 7, 2020
3d726a8
Fix test_emmalloc back to the original form to avoid malloc() calls g…
juj Jan 7, 2020
a1761a1
CI fix
juj Jan 7, 2020
c512595
Micro-optimize size
juj Jan 7, 2020
f61fa24
Move emmalloc tests
juj Jan 8, 2020
2f2fca6
Fix upstream wasm test
juj Jan 8, 2020
448d340
Drop malloc impl if building with USES_DYNAMIC_ALLOC=0
juj Jan 8, 2020
5369f8b
Make emscripten_realloc_buffer internal again
juj Jan 8, 2020
62b11ac
Update test_emmalloc_trim
juj Jan 9, 2020
5752116
Update test_minimal_runtime_code_size. Emmalloc regresses fastcomp wa…
juj Jan 9, 2020
d02a293
Update minimal runtime code size tests
juj Jan 9, 2020
1648386
Run tests only in debug
juj Jan 9, 2020
be84597
Work around bug https://github.com/emscripten-core/emscripten/issues/…
juj Jan 9, 2020
b8d5599
Update test
juj Jan 9, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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'],
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also looks like it just used in tests. Is it worth added this JS library just for sake of a test case? Maybe there is some way to avoid it?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The intent is to expose that function to public API for users of emmalloc, hence testing that it works in that test.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But isn't that function internal-only right now? So the fact that it has a bad name is currently not an issue?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that this should be split out into a separate PR btw. Although I'm still not convinced about the need to shrink memory (since its not possible with wasm).

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',
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Split out this change along with src/deps_info.json? Seems unrelated but maybe I'm wrong?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It only started failing in this branch because of the deps structure changing.

'$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);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to re-declare all the POSIX names here? Would it be better to use the ones from stdlib.h and only declare the specific emmalloc symbols in this header?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's more documentative to re-declare them here. No harm in doing so, in fact, if there ever were a change in signature of one of the names, then this would loudly complain, and it'd trigger to change the signature of the corresponding emmalloc_ name as well.

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