From b5fdee4ade148162830ab4bfe3bc0fe7df10544f Mon Sep 17 00:00:00 2001 From: daanx Date: Sun, 2 Aug 2026 13:44:41 -0700 Subject: [PATCH 01/27] fix mingw detection: __GCC__ -> __GNUC__ (see also PR #1349) --- src/prim/windows/prim.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/prim/windows/prim.c b/src/prim/windows/prim.c index 7f5c6be12..b2fab4934 100644 --- a/src/prim/windows/prim.c +++ b/src/prim/windows/prim.c @@ -876,7 +876,7 @@ static void NTAPI mi_win_main(PVOID module, DWORD reason, LPVOID reserved) { #pragma data_seg(".CRT$XIB") mi_crt_callback_t _mi_crt_callback_init[] = { &mi_crt_init }; #pragma data_seg() - #elif defined(__GCC__) // mingw + #elif defined(__GNUC__) // mingw extern const IMAGE_TLS_DIRECTORY _tls_used; __attribute__((used)) static const void* const mi_tls_used_ref = &_tls_used; // pull in the CRT tls __attribute__((used, section(".CRT$XLB"))) PIMAGE_TLS_CALLBACK _mi_tls_callback_pre = &mi_tls_attach; @@ -973,7 +973,7 @@ static void NTAPI mi_win_main(PVOID module, DWORD reason, LPVOID reserved) { #pragma data_seg(".CRT$XLY") PIMAGE_TLS_CALLBACK _mi_tls_callback_post[] = { &mi_tls_detach }; #pragma data_seg() - #elif defined(__GCC__) // mingw + #elif defined(__GNUC__) // mingw extern const IMAGE_TLS_DIRECTORY _tls_used; __attribute__((used)) static const void* const mi_tls_used_ref = &_tls_used; // pull in the CRT tls __attribute__((used, section(".CRT$XLB"))) PIMAGE_TLS_CALLBACK _mi_tls_callback_pre = &mi_tls_attach; @@ -1047,7 +1047,7 @@ static void NTAPI mi_win_main(PVOID module, DWORD reason, LPVOID reserved) { #pragma data_seg(".CRT$XLY") PIMAGE_TLS_CALLBACK _mi_tls_callback_post[] = { &mi_win_main_detach }; #pragma data_seg() - #elif defined(__GCC__) // mingw + #elif defined(__GNUC__) // mingw extern const IMAGE_TLS_DIRECTORY _tls_used; __attribute__((used)) static const void* const mi_tls_used_ref = &_tls_used; // pull in the CRT tls __attribute__((used, section(".CRT$XLB"))) PIMAGE_TLS_CALLBACK _mi_tls_callback_pre = &mi_tls_attach; From 11e68532e7ec5856f00fdbb90a06032af64afbca Mon Sep 17 00:00:00 2001 From: daanx Date: Mon, 3 Aug 2026 10:37:05 -0700 Subject: [PATCH 02/27] fix error condition in XMALLOC, PR #1353 by @mdionisio --- src/options.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/options.c b/src/options.c index 5b803a6b1..a36acfae2 100644 --- a/src/options.c +++ b/src/options.c @@ -570,7 +570,7 @@ static void mi_error_default(int err) { } #endif #if defined(MI_XMALLOC) - if (err==ENOMEM || err==EOVERFLOW || err=EINVAL) { // abort on memory allocation fails in xmalloc mode + if (err==ENOMEM || err==EOVERFLOW || err==EINVAL) { // abort on memory allocation fails in xmalloc mode abort(); } #endif From b7ee6b62c255ddc011b23bace455ac6ea48ab1f4 Mon Sep 17 00:00:00 2001 From: daanx Date: Mon, 3 Aug 2026 10:40:44 -0700 Subject: [PATCH 03/27] make build deterministic, issue #1355 --- src/options.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/options.c b/src/options.c index a36acfae2..cc2a4f7cb 100644 --- a/src/options.c +++ b/src/options.c @@ -207,7 +207,7 @@ void mi_options_print(void) mi_attr_noexcept const int vermajor = MI_MALLOC_VERSION/10000; const int verminor = (MI_MALLOC_VERSION%10000)/100; const int verpatch = (MI_MALLOC_VERSION%100); - _mi_message("v%i.%i.%i%s%s (built on %s, %s)\n", vermajor, verminor, verpatch, + _mi_message("v%i.%i.%i%s%s\n", vermajor, verminor, verpatch, #if defined(MI_CMAKE_BUILD_TYPE) ", " mi_stringify(MI_CMAKE_BUILD_TYPE) #else @@ -219,7 +219,7 @@ void mi_options_print(void) mi_attr_noexcept #else "" #endif - , __DATE__, __TIME__); + ); // show options for (int i = 0; i < _mi_option_last; i++) { From 6724c03d2d91a288b30fd763059939f1a93c5c7f Mon Sep 17 00:00:00 2001 From: daanx Date: Mon, 3 Aug 2026 11:05:03 -0700 Subject: [PATCH 04/27] use direct WriteFile or console output on Windows, issue #1354 --- src/prim/windows/prim.c | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/src/prim/windows/prim.c b/src/prim/windows/prim.c index b2fab4934..8fffd4475 100644 --- a/src/prim/windows/prim.c +++ b/src/prim/windows/prim.c @@ -612,12 +612,17 @@ void _mi_prim_out_stderr( const char* msg ) // on windows with redirection, the C runtime cannot handle locale dependent output // after the main thread closes so we use direct console output. if (!_mi_preloading()) { - // _cputs(msg); // _cputs cannot be used as it aborts when failing to lock the console static HANDLE hcon = INVALID_HANDLE_VALUE; + #if MI_WIN_DESKTOP static bool hconIsConsole = false; + #endif if (hcon == INVALID_HANDLE_VALUE) { - hcon = GetStdHandle(STD_ERROR_HANDLE); // returns NULL on error + hcon = GetStdHandle(STD_ERROR_HANDLE); // returns NULL if no stderr is available #if MI_WIN_DESKTOP + if (hcon==NULL) { + AttachConsole(ATTACH_PARENT_PROCESS); // if started from a parent console, try to attach to that + hcon = GetStdHandle(STD_ERROR_HANDLE); + } CONSOLE_SCREEN_BUFFER_INFO sbi; hconIsConsole = ((hcon != NULL && hcon != INVALID_HANDLE_VALUE) && GetConsoleScreenBufferInfo(hcon, &sbi)); #endif @@ -625,19 +630,19 @@ void _mi_prim_out_stderr( const char* msg ) const size_t len = _mi_strlen(msg); if (len > 0 && len < UINT32_MAX) { DWORD written = 0; - if (hconIsConsole) { - #if MI_WIN_DESKTOP - WriteConsoleA(hcon, msg, (DWORD)len, &written, NULL); + if (hcon != NULL && hcon != INVALID_HANDLE_VALUE) { + #if MI_WIN_DESKTOP + if (hconIsConsole) { + WriteConsoleA(hcon, msg, (DWORD)len, &written, NULL); + } + else #endif + { + // use direct write in case stderr was redirected + WriteFile(hcon, msg, (DWORD)len, &written, NULL); + } } - else if (hcon != NULL && hcon != INVALID_HANDLE_VALUE) { - // use direct write if stderr was redirected - WriteFile(hcon, msg, (DWORD)len, &written, NULL); - } - else { - // finally fall back to fputs after all - fputs(msg, stderr); - } + // don't fall back to fputs or _cputs as the crt can have it locked } } } From 97e198c33a5cad78ecdb09481217ab52878946a8 Mon Sep 17 00:00:00 2001 From: daanx Date: Mon, 3 Aug 2026 11:28:56 -0700 Subject: [PATCH 05/27] ensure the full usable block size is zerod on reallocation, issue #763 --- src/alloc-aligned.c | 8 +++++--- test/test-api.c | 21 +++++++++++++++++++-- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/alloc-aligned.c b/src/alloc-aligned.c index 8306fe5e1..f6fb74dcf 100644 --- a/src/alloc-aligned.c +++ b/src/alloc-aligned.c @@ -296,7 +296,7 @@ static void* mi_heap_realloc_zero_aligned_at(mi_heap_t* heap, void* p, size_t ne } if (alignment <= sizeof(uintptr_t) && offset==0) return _mi_heap_realloc_zero(heap,p,newsize,zero,NULL,NULL); if (p == NULL) return mi_heap_malloc_zero_aligned_at(heap,newsize,alignment,offset,zero,NULL); - size_t size = mi_usable_size(p); + const size_t size = mi_usable_size(p); if (newsize <= size && newsize >= (size - (size / 2)) && (((uintptr_t)p + offset) & (alignment-1)) == 0) { return p; // reallocation still fits, is aligned and not more than 50% waste } @@ -306,8 +306,10 @@ static void* mi_heap_realloc_zero_aligned_at(mi_heap_t* heap, void* p, size_t ne if (newp != NULL) { if (zero && newsize > size) { // also set last word in the previous allocation to zero to ensure any padding is zero-initialized - size_t start = (size >= sizeof(intptr_t) ? size - sizeof(intptr_t) : 0); - _mi_memzero((uint8_t*)newp + start, newsize - start); + const size_t start = (size >= sizeof(intptr_t) ? size - sizeof(intptr_t) : 0); + const size_t zsize = mi_usable_size(newp); // issue #763 + mi_assert_internal(zsize >= newsize); + _mi_memzero((uint8_t*)newp + start, zsize - start); } _mi_memcpy(newp, p, (newsize > size ? size : newsize)); // cannot be aligned due to abitrary offset... (todo: require offset to be a multiple of sizeof(void*)?) mi_free(p); // only free if successful diff --git a/test/test-api.c b/test/test-api.c index 566d8b321..fd13da1bd 100644 --- a/test/test-api.c +++ b/test/test-api.c @@ -54,13 +54,16 @@ bool test_stl_heap_allocator4(void); static bool test_zero_aligned_first(void); -bool mem_is_zero(uint8_t* p, size_t size) { +bool mem_has_vals(uint8_t* p, size_t size, uint8_t val) { if (p==NULL) return false; for (size_t i = 0; i < size; ++i) { - if (p[i] != 0) return false; + if (p[i] != val) return false; } return true; } +bool mem_is_zero(uint8_t* p, size_t size) { + return mem_has_vals(p,size,0); +} // --------------------------------------------------------------------------- // Main testing @@ -335,6 +338,20 @@ int main(void) { mi_free(p); }; + CHECK_BODY("rezalloc_aligned_zeros") { // issue #763 + size_t alignment = 1024; + size_t n = 1024 * 6; + void* ptr = mi_zalloc_aligned(n, alignment); + assert(mem_is_zero(ptr,n)); + memset(ptr,123,n/2); + + ptr = mi_rezalloc_aligned(ptr, n/2, alignment); + assert(mem_has_vals(ptr,n/2,123)); + + ptr = mi_rezalloc_aligned(ptr, n, alignment); + assert(mem_has_vals(ptr,n/2,123)); + result = mem_is_zero((uint8_t*)ptr + n/2, n/2); + } // --------------------------------------------------- // Reallocation // --------------------------------------------------- From 11d655a9925a76fe310b24d5dfa742aa11fbe8f0 Mon Sep 17 00:00:00 2001 From: daanx Date: Mon, 3 Aug 2026 11:44:00 -0700 Subject: [PATCH 06/27] ensure the full usable block size is zero'd on reallocation, issue #763 --- src/alloc.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/alloc.c b/src/alloc.c index c52afe0e0..385357651 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -310,12 +310,15 @@ void* _mi_heap_realloc_zero(mi_heap_t* heap, void* p, size_t newsize, bool zero, if (usable_post!=NULL) { *usable_post = mi_page_usable_block_size(page); } return p; // reallocation still fits and not more than 50% waste } - void* newp = mi_heap_umalloc(heap,newsize,usable_post); + size_t usable_new; + void* newp = mi_heap_umalloc(heap,newsize,&usable_new); + if (usable_post != NULL) { *usable_post = usable_new; } if mi_likely(newp != NULL) { if (zero && newsize > size) { // also set last word in the previous allocation to zero to ensure any padding is zero-initialized const size_t start = (size >= sizeof(intptr_t) ? size - sizeof(intptr_t) : 0); - _mi_memzero((uint8_t*)newp + start, newsize - start); + mi_assert_internal(usable_new >= newsize); // use usable_new for zeroing, issue #763 + _mi_memzero((uint8_t*)newp + start, usable_new - start); } else if (newsize == 0) { ((uint8_t*)newp)[0] = 0; // work around for applications that expect zero-reallocation to be zero initialized (issue #725) From 591e8897bc8ee9cf1e77023c3930c76f4030449f Mon Sep 17 00:00:00 2001 From: daanx Date: Mon, 3 Aug 2026 12:26:05 -0700 Subject: [PATCH 07/27] ensure the full usable block size is zero'd on reallocation, issue #763 --- src/alloc-aligned.c | 10 +++++----- src/alloc.c | 14 +++++++------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/alloc-aligned.c b/src/alloc-aligned.c index f6fb74dcf..3924a7dc2 100644 --- a/src/alloc-aligned.c +++ b/src/alloc-aligned.c @@ -302,14 +302,14 @@ static void* mi_heap_realloc_zero_aligned_at(mi_heap_t* heap, void* p, size_t ne } else { // note: we don't zero allocate upfront so we only zero initialize the expanded part - void* newp = mi_heap_malloc_aligned_at(heap,newsize,alignment,offset); + void* const newp = mi_heap_malloc_aligned_at(heap,newsize,alignment,offset); if (newp != NULL) { - if (zero && newsize > size) { + const size_t usable = mi_usable_size(newp); + mi_assert_internal(usable >= newsize); // use usable for zero'ing, issue #763 + if (zero && usable > size) { // also set last word in the previous allocation to zero to ensure any padding is zero-initialized const size_t start = (size >= sizeof(intptr_t) ? size - sizeof(intptr_t) : 0); - const size_t zsize = mi_usable_size(newp); // issue #763 - mi_assert_internal(zsize >= newsize); - _mi_memzero((uint8_t*)newp + start, zsize - start); + _mi_memzero((uint8_t*)newp + start, usable - start); } _mi_memcpy(newp, p, (newsize > size ? size : newsize)); // cannot be aligned due to abitrary offset... (todo: require offset to be a multiple of sizeof(void*)?) mi_free(p); // only free if successful diff --git a/src/alloc.c b/src/alloc.c index 385357651..9b32dc3b9 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -310,15 +310,15 @@ void* _mi_heap_realloc_zero(mi_heap_t* heap, void* p, size_t newsize, bool zero, if (usable_post!=NULL) { *usable_post = mi_page_usable_block_size(page); } return p; // reallocation still fits and not more than 50% waste } - size_t usable_new; - void* newp = mi_heap_umalloc(heap,newsize,&usable_new); - if (usable_post != NULL) { *usable_post = usable_new; } + // note: we don't zero allocate upfront so we only zero initialize the expanded part + void* const newp = mi_heap_umalloc(heap,newsize,usable_post); if mi_likely(newp != NULL) { - if (zero && newsize > size) { + const size_t usable = mi_usable_size(newp); + mi_assert_internal(usable >= newsize); // use usable for zero-ing, issue #763 + if (zero && usable > size) { // also set last word in the previous allocation to zero to ensure any padding is zero-initialized - const size_t start = (size >= sizeof(intptr_t) ? size - sizeof(intptr_t) : 0); - mi_assert_internal(usable_new >= newsize); // use usable_new for zeroing, issue #763 - _mi_memzero((uint8_t*)newp + start, usable_new - start); + const size_t start = (size >= sizeof(intptr_t) ? size - sizeof(intptr_t) : 0); + _mi_memzero((uint8_t*)newp + start, usable - start); } else if (newsize == 0) { ((uint8_t*)newp)[0] = 0; // work around for applications that expect zero-reallocation to be zero initialized (issue #725) From 9e27e1c1fee017fce5f89921de9ec64dbb0c6547 Mon Sep 17 00:00:00 2001 From: daanx Date: Mon, 3 Aug 2026 16:19:16 -0700 Subject: [PATCH 08/27] improve rezalloc and rezalloc aligned, issue #763 --- include/mimalloc/internal.h | 19 +++++++++++++++++-- src/alloc-aligned.c | 13 +++++++------ src/alloc.c | 25 ++++++++++++++----------- src/os.c | 16 ---------------- 4 files changed, 38 insertions(+), 35 deletions(-) diff --git a/include/mimalloc/internal.h b/include/mimalloc/internal.h index 15e8c6cda..d1ee49b0d 100644 --- a/include/mimalloc/internal.h +++ b/include/mimalloc/internal.h @@ -390,7 +390,7 @@ static inline bool _mi_is_aligned(void* p, size_t alignment) { // Align upwards static inline uintptr_t _mi_align_up(uintptr_t sz, size_t alignment) { mi_assert_internal(alignment != 0); - uintptr_t mask = alignment - 1; + const uintptr_t mask = alignment - 1; if ((alignment & mask) == 0) { // power of two? return ((sz + mask) & ~mask); } @@ -399,12 +399,27 @@ static inline uintptr_t _mi_align_up(uintptr_t sz, size_t alignment) { } } - // Align a pointer upwards static inline void* _mi_align_up_ptr(const void* p, size_t alignment) { return (void*)_mi_align_up((uintptr_t)p, alignment); } +// Align down +static inline uintptr_t _mi_align_down(uintptr_t sz, size_t alignment) { + mi_assert_internal(alignment != 0); + const uintptr_t mask = alignment - 1; + if ((alignment & mask) == 0) { // power of two? + return (sz & ~mask); + } + else { + return ((sz/alignment)*alignment); + } +} + +// Align a pointer downwards +static inline void* _mi_align_down_ptr(const void* p, size_t alignment) { + return (void*)_mi_align_down((uintptr_t)p, alignment); +} // Divide upwards: `s <= _mi_divide_up(s,d)*d < s+d`. static inline uintptr_t _mi_divide_up(uintptr_t size, size_t divider) { diff --git a/src/alloc-aligned.c b/src/alloc-aligned.c index 3924a7dc2..2f3d6f334 100644 --- a/src/alloc-aligned.c +++ b/src/alloc-aligned.c @@ -301,17 +301,18 @@ static void* mi_heap_realloc_zero_aligned_at(mi_heap_t* heap, void* p, size_t ne return p; // reallocation still fits, is aligned and not more than 50% waste } else { - // note: we don't zero allocate upfront so we only zero initialize the expanded part + // note: we don't zero allocate upfront so we only zero initialize the expanded part (at the cost of calling mi_usable_size) void* const newp = mi_heap_malloc_aligned_at(heap,newsize,alignment,offset); if (newp != NULL) { - const size_t usable = mi_usable_size(newp); + const size_t copy_size = (newsize > size ? size : newsize); + const size_t zero_start = (copy_size >= sizeof(intptr_t) ? copy_size - sizeof(intptr_t) : 0); // also set last word in the previous allocation to zero to ensure any padding is zero-initialized + const size_t usable = mi_usable_size(newp); mi_assert_internal(usable >= newsize); // use usable for zero'ing, issue #763 - if (zero && usable > size) { + if (zero && usable > zero_start) { // also set last word in the previous allocation to zero to ensure any padding is zero-initialized - const size_t start = (size >= sizeof(intptr_t) ? size - sizeof(intptr_t) : 0); - _mi_memzero((uint8_t*)newp + start, usable - start); + _mi_memzero((uint8_t*)newp + zero_start, usable - zero_start); } - _mi_memcpy(newp, p, (newsize > size ? size : newsize)); // cannot be aligned due to abitrary offset... (todo: require offset to be a multiple of sizeof(void*)?) + _mi_memcpy(newp, p, copy_size); // cannot be aligned due to abitrary offset... (todo: require offset to be a multiple of sizeof(void*)?) mi_free(p); // only free if successful } return newp; diff --git a/src/alloc.c b/src/alloc.c index 9b32dc3b9..654d69597 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -310,23 +310,26 @@ void* _mi_heap_realloc_zero(mi_heap_t* heap, void* p, size_t newsize, bool zero, if (usable_post!=NULL) { *usable_post = mi_page_usable_block_size(page); } return p; // reallocation still fits and not more than 50% waste } - // note: we don't zero allocate upfront so we only zero initialize the expanded part - void* const newp = mi_heap_umalloc(heap,newsize,usable_post); + // note: we don't zero allocate upfront so we only zero initialize the expanded part + size_t usable; // use usable for zero-ing, issue #763 + void* const newp = mi_heap_umalloc(heap,newsize,&usable); + if (usable_post!=NULL) { *usable_post = usable; } if mi_likely(newp != NULL) { - const size_t usable = mi_usable_size(newp); - mi_assert_internal(usable >= newsize); // use usable for zero-ing, issue #763 - if (zero && usable > size) { - // also set last word in the previous allocation to zero to ensure any padding is zero-initialized - const size_t start = (size >= sizeof(intptr_t) ? size - sizeof(intptr_t) : 0); - _mi_memzero((uint8_t*)newp + start, usable - start); + const size_t copy_size = (newsize > size ? size : newsize); + const size_t zero_start = _mi_align_down( (copy_size >= sizeof(intptr_t) ? copy_size - sizeof(intptr_t) : 0), sizeof(intptr_t)); // also set last word in the previous allocation to zero to ensure any padding is zero-initialized + #if MI_PADDING + usable = mi_usable_size(newp); // avoid zero'ing padding + #endif + mi_assert_internal(usable >= newsize); + if (zero && usable > zero_start) { + _mi_memzero_aligned((uint8_t*)newp + zero_start, usable - zero_start); } else if (newsize == 0) { ((uint8_t*)newp)[0] = 0; // work around for applications that expect zero-reallocation to be zero initialized (issue #725) } if mi_likely(p != NULL) { - const size_t copysize = (newsize > size ? size : newsize); - mi_track_mem_defined(p,copysize); // _mi_useable_size may be too large for byte precise memory tracking.. - _mi_memcpy(newp, p, copysize); + mi_track_mem_defined(p,copy_size); // _mi_useable_size may be too large for byte precise memory tracking.. + _mi_memcpy_aligned(newp, p, copy_size); mi_free(p); // only free the original pointer if successful } } diff --git a/src/os.c b/src/os.c index cfc1d8868..b12b7cc47 100644 --- a/src/os.c +++ b/src/os.c @@ -91,22 +91,6 @@ void _mi_os_init(void) { bool _mi_os_decommit(void* addr, size_t size); bool _mi_os_commit(void* addr, size_t size, bool* is_zero); -static inline uintptr_t _mi_align_down(uintptr_t sz, size_t alignment) { - mi_assert_internal(alignment != 0); - uintptr_t mask = alignment - 1; - if ((alignment & mask) == 0) { // power of two? - return (sz & ~mask); - } - else { - return ((sz / alignment) * alignment); - } -} - -static void* _mi_align_down_ptr(void* p, size_t alignment) { - return (void*)_mi_align_down((uintptr_t)p, alignment); -} - - /* ----------------------------------------------------------- aligned hinting -------------------------------------------------------------- */ From 36f2efec3aaf0eb1ddde3a2f1c4dc23badd35b76 Mon Sep 17 00:00:00 2001 From: daanx Date: Mon, 3 Aug 2026 16:35:10 -0700 Subject: [PATCH 09/27] fix signature of mem_is_zero --- test/test-api.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/test-api.c b/test/test-api.c index fd13da1bd..16dc8e184 100644 --- a/test/test-api.c +++ b/test/test-api.c @@ -54,15 +54,15 @@ bool test_stl_heap_allocator4(void); static bool test_zero_aligned_first(void); -bool mem_has_vals(uint8_t* p, size_t size, uint8_t val) { +static bool mem_has_vals(const uint8_t* p, size_t size, uint8_t val) { if (p==NULL) return false; for (size_t i = 0; i < size; ++i) { if (p[i] != val) return false; } return true; } -bool mem_is_zero(uint8_t* p, size_t size) { - return mem_has_vals(p,size,0); +static bool mem_is_zero(const void* p, size_t size) { + return mem_has_vals((const uint8_t*)p,size,0); } // --------------------------------------------------------------------------- From 3b798c21de7a9a3f7a7a40cdfe732d7c7bf9dd7f Mon Sep 17 00:00:00 2001 From: daanx Date: Mon, 3 Aug 2026 16:37:17 -0700 Subject: [PATCH 10/27] fix signature of mem_is_zero --- test/test-api.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test-api.c b/test/test-api.c index 16dc8e184..5814c2059 100644 --- a/test/test-api.c +++ b/test/test-api.c @@ -346,10 +346,10 @@ int main(void) { memset(ptr,123,n/2); ptr = mi_rezalloc_aligned(ptr, n/2, alignment); - assert(mem_has_vals(ptr,n/2,123)); + assert(mem_has_vals((uint8_t*)ptr,n/2,123)); ptr = mi_rezalloc_aligned(ptr, n, alignment); - assert(mem_has_vals(ptr,n/2,123)); + assert(mem_has_vals((uint8_t*)ptr,n/2,123)); result = mem_is_zero((uint8_t*)ptr + n/2, n/2); } // --------------------------------------------------- From 8810b2351eb31e8052a554367fa8e1b1a1c0fd79 Mon Sep 17 00:00:00 2001 From: daanx Date: Mon, 3 Aug 2026 16:44:10 -0700 Subject: [PATCH 11/27] add AIX support, issue #764 --- src/prim/unix/prim.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/prim/unix/prim.c b/src/prim/unix/prim.c index 5a2f54f6a..8ba935808 100644 --- a/src/prim/unix/prim.c +++ b/src/prim/unix/prim.c @@ -231,7 +231,7 @@ int _mi_prim_free(void* addr, size_t size ) { // return errno on failure static int unix_madvise(void* addr, size_t size, int advice) { - #if defined(__sun) + #if defined(__sun) || defined(_AIX) const int res = madvise((caddr_t)addr, size, advice); // Solaris needs cast (issue #520) return (res==0 ? 0 : errno); #elif defined(__QNX__) From 55f12a93f4eb95d08b481372bd11fb4f1140c4e5 Mon Sep 17 00:00:00 2001 From: daanx Date: Mon, 3 Aug 2026 20:33:37 -0700 Subject: [PATCH 12/27] set usable parameter in guarded allocation --- src/alloc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/alloc.c b/src/alloc.c index 654d69597..3c0070f9d 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -758,7 +758,8 @@ mi_decl_restrict void* _mi_heap_malloc_guarded(mi_heap_t* heap, size_t size, boo if (block==NULL) return NULL; size_t usable_size; void* const p = mi_block_ptr_set_guarded(block, obj_size, &usable_size); - if (p == NULL) return NULL; + if (p == NULL) return NULL; + if (usable!=NULL) { *usable = usable_size; } if (zero) { _mi_memzero(p,obj_size); // we have to zero afterwards as padding might have written inside the block (if the `blocksize > reqsize + os_page_size`) } From 86b4bb9361feb20a4be0aedadea1338b4476aa37 Mon Sep 17 00:00:00 2001 From: daanx Date: Mon, 3 Aug 2026 20:43:27 -0700 Subject: [PATCH 13/27] refine useable size in mi_heap_realloc_zero --- src/alloc.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/alloc.c b/src/alloc.c index 3c0070f9d..7f3ecbd7d 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -313,13 +313,11 @@ void* _mi_heap_realloc_zero(mi_heap_t* heap, void* p, size_t newsize, bool zero, // note: we don't zero allocate upfront so we only zero initialize the expanded part size_t usable; // use usable for zero-ing, issue #763 void* const newp = mi_heap_umalloc(heap,newsize,&usable); - if (usable_post!=NULL) { *usable_post = usable; } if mi_likely(newp != NULL) { + if (usable_post!=NULL) { *usable_post = usable; } const size_t copy_size = (newsize > size ? size : newsize); const size_t zero_start = _mi_align_down( (copy_size >= sizeof(intptr_t) ? copy_size - sizeof(intptr_t) : 0), sizeof(intptr_t)); // also set last word in the previous allocation to zero to ensure any padding is zero-initialized - #if MI_PADDING - usable = mi_usable_size(newp); // avoid zero'ing padding - #endif + mi_assert_internal(usable == mi_usable_size(newp)); mi_assert_internal(usable >= newsize); if (zero && usable > zero_start) { _mi_memzero_aligned((uint8_t*)newp + zero_start, usable - zero_start); @@ -756,8 +754,8 @@ mi_decl_restrict void* _mi_heap_malloc_guarded(mi_heap_t* heap, size_t size, boo const size_t req_size = _mi_align_up(bsize + os_page_size, os_page_size); mi_block_t* const block = (mi_block_t*)_mi_malloc_generic(heap, req_size, false /* don't zero */, 0 /* huge_alignment */, usable); if (block==NULL) return NULL; - size_t usable_size; - void* const p = mi_block_ptr_set_guarded(block, obj_size, &usable_size); + size_t usable_size = 0; + void* const p = mi_block_ptr_set_guarded(block, obj_size, &usable_size); if (p == NULL) return NULL; if (usable!=NULL) { *usable = usable_size; } if (zero) { From 7413a8bc3ff6c029406472a1cb91b026c7f1d7bb Mon Sep 17 00:00:00 2001 From: daanx Date: Mon, 3 Aug 2026 21:00:15 -0700 Subject: [PATCH 14/27] for realloc, use mi_usable_size if padding is enabled --- src/alloc.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/alloc.c b/src/alloc.c index 7f3ecbd7d..3177ac4a7 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -317,8 +317,12 @@ void* _mi_heap_realloc_zero(mi_heap_t* heap, void* p, size_t newsize, bool zero, if (usable_post!=NULL) { *usable_post = usable; } const size_t copy_size = (newsize > size ? size : newsize); const size_t zero_start = _mi_align_down( (copy_size >= sizeof(intptr_t) ? copy_size - sizeof(intptr_t) : 0), sizeof(intptr_t)); // also set last word in the previous allocation to zero to ensure any padding is zero-initialized - mi_assert_internal(usable == mi_usable_size(newp)); - mi_assert_internal(usable >= newsize); + #if MI_PADDING + usable = mi_usable_size(newp); + #else + mi_assert_internal(usable == mi_usable_size(newp)); + #endif + mi_assert_internal(usable >= newsize); if (zero && usable > zero_start) { _mi_memzero_aligned((uint8_t*)newp + zero_start, usable - zero_start); } From 65dd38823164060bcc43fbed172c483fa4cede43 Mon Sep 17 00:00:00 2001 From: daanx Date: Mon, 3 Aug 2026 22:51:37 -0700 Subject: [PATCH 15/27] fix usable return value for guarded allocations --- src/alloc.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/alloc.c b/src/alloc.c index 3177ac4a7..054f8d0cf 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -761,7 +761,6 @@ mi_decl_restrict void* _mi_heap_malloc_guarded(mi_heap_t* heap, size_t size, boo size_t usable_size = 0; void* const p = mi_block_ptr_set_guarded(block, obj_size, &usable_size); if (p == NULL) return NULL; - if (usable!=NULL) { *usable = usable_size; } if (zero) { _mi_memzero(p,obj_size); // we have to zero afterwards as padding might have written inside the block (if the `blocksize > reqsize + os_page_size`) } From 6d545f76425eac8b8553257142af3dbc82c5956a Mon Sep 17 00:00:00 2001 From: daanx Date: Mon, 3 Aug 2026 23:07:01 -0700 Subject: [PATCH 16/27] fix heap_realloc with guarded pages --- src/alloc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/alloc.c b/src/alloc.c index 054f8d0cf..419842f65 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -317,7 +317,7 @@ void* _mi_heap_realloc_zero(mi_heap_t* heap, void* p, size_t newsize, bool zero, if (usable_post!=NULL) { *usable_post = usable; } const size_t copy_size = (newsize > size ? size : newsize); const size_t zero_start = _mi_align_down( (copy_size >= sizeof(intptr_t) ? copy_size - sizeof(intptr_t) : 0), sizeof(intptr_t)); // also set last word in the previous allocation to zero to ensure any padding is zero-initialized - #if MI_PADDING + #if MI_PADDING || MI_GUARDED usable = mi_usable_size(newp); #else mi_assert_internal(usable == mi_usable_size(newp)); From c9b0b8d27ce868b06f82d82a19b0da9bfd9f97fc Mon Sep 17 00:00:00 2001 From: daanx Date: Tue, 4 Aug 2026 09:15:37 -0700 Subject: [PATCH 17/27] return block_size from the umalloc api's (instead of usable) --- src/alloc-aligned.c | 44 +++++++++++++------------- src/alloc.c | 75 +++++++++++++++++++++++---------------------- src/free.c | 10 +++--- 3 files changed, 65 insertions(+), 64 deletions(-) diff --git a/src/alloc-aligned.c b/src/alloc-aligned.c index 2f3d6f334..7a4665999 100644 --- a/src/alloc-aligned.c +++ b/src/alloc-aligned.c @@ -25,7 +25,7 @@ static bool mi_malloc_is_naturally_aligned( size_t size, size_t alignment ) { } #if MI_GUARDED -static mi_decl_noinline mi_decl_restrict void* mi_heap_malloc_guarded_aligned(mi_heap_t* heap, size_t size, size_t alignment, bool zero, size_t* usable) mi_attr_noexcept { +static mi_decl_noinline mi_decl_restrict void* mi_heap_malloc_guarded_aligned(mi_heap_t* heap, size_t size, size_t alignment, bool zero, size_t* pblock_size) mi_attr_noexcept { // use over allocation for guarded blocksl mi_assert_internal(alignment > 0 && alignment < MI_BLOCK_ALIGNMENT_MAX); if mi_unlikely(alignment >= MI_BLOCK_ALIGNMENT_MAX || size > (MI_MAX_ALLOC_SIZE - MI_PADDING_SIZE - alignment)) { @@ -33,7 +33,7 @@ static mi_decl_noinline mi_decl_restrict void* mi_heap_malloc_guarded_aligned(mi return NULL; } const size_t oversize = size + alignment - 1; - void* const base = _mi_heap_malloc_guarded(heap, oversize, zero, usable); + void* const base = _mi_heap_malloc_guarded(heap, oversize, zero, pblock_size); if (base==NULL) return NULL; void* const p = _mi_align_up_ptr(base, alignment); mi_track_align(base, p, (uint8_t*)p - (uint8_t*)base, size); @@ -42,22 +42,22 @@ static mi_decl_noinline mi_decl_restrict void* mi_heap_malloc_guarded_aligned(mi return p; } -static void* mi_heap_malloc_zero_no_guarded(mi_heap_t* heap, size_t size, bool zero, size_t* usable) { +static void* mi_heap_malloc_zero_no_guarded(mi_heap_t* heap, size_t size, bool zero, size_t* pblock_size) { const size_t rate = heap->guarded_sample_rate; // only write if `rate!=0` so we don't write to the constant `_mi_heap_empty` if (rate != 0) { heap->guarded_sample_rate = 0; } - void* p = _mi_heap_malloc_zero_ex(heap, size, zero, 0, usable); + void* p = _mi_heap_malloc_zero_ex(heap, size, zero, 0, pblock_size); if (rate != 0) { heap->guarded_sample_rate = rate; } return p; } #else -static void* mi_heap_malloc_zero_no_guarded(mi_heap_t* heap, size_t size, bool zero, size_t* usable) { - return _mi_heap_malloc_zero_ex(heap, size, zero, 0, usable); +static void* mi_heap_malloc_zero_no_guarded(mi_heap_t* heap, size_t size, bool zero, size_t* pblock_size) { + return _mi_heap_malloc_zero_ex(heap, size, zero, 0, pblock_size); } #endif // Fallback aligned allocation that over-allocates -- split out for better codegen -static mi_decl_noinline void* mi_heap_malloc_zero_aligned_at_overalloc(mi_heap_t* const heap, const size_t size, const size_t alignment, const size_t offset, const bool zero, size_t* usable) mi_attr_noexcept +static mi_decl_noinline void* mi_heap_malloc_zero_aligned_at_overalloc(mi_heap_t* const heap, const size_t size, const size_t alignment, const size_t offset, const bool zero, size_t* pblock_size) mi_attr_noexcept { mi_assert_internal(size <= (MI_MAX_ALLOC_SIZE - MI_PADDING_SIZE)); mi_assert_internal(mi_alignment_is_valid(alignment)); @@ -75,7 +75,7 @@ static mi_decl_noinline void* mi_heap_malloc_zero_aligned_at_overalloc(mi_heap_t } oversize = (size <= MI_SMALL_SIZE_MAX ? MI_SMALL_SIZE_MAX + 1 /* ensure we use generic malloc path */ : size); // note: no guarded as alignment > 0 - p = _mi_heap_malloc_zero_ex(heap, oversize, false, alignment, usable); // the page block size should be large enough to align in the single huge page block + p = _mi_heap_malloc_zero_ex(heap, oversize, false, alignment, pblock_size); // the page block size should be large enough to align in the single huge page block // zero afterwards as only the area from the aligned_p may be committed! if (p == NULL) return NULL; } @@ -84,10 +84,10 @@ static mi_decl_noinline void* mi_heap_malloc_zero_aligned_at_overalloc(mi_heap_t mi_assert_internal(size <= (MI_MAX_ALLOC_SIZE - MI_PADDING_SIZE) && alignment <= MI_BLOCK_ALIGNMENT_MAX); mi_assert_internal(size < SIZE_MAX - alignment); // `oversize` cannot overflow oversize = (size < MI_MAX_ALIGN_SIZE ? MI_MAX_ALIGN_SIZE : size) + alignment - 1; // adjust for size <= 16; with size 0 and alignment 64k, we would allocate a 64k block and pointing just beyond that. - p = mi_heap_malloc_zero_no_guarded(heap, oversize, zero, usable); + p = mi_heap_malloc_zero_no_guarded(heap, oversize, zero, pblock_size); if (p == NULL) return NULL; } - mi_page_t* page = _mi_ptr_page(p); + mi_page_t* page = _mi_ptr_page(p); // todo: change low level allocation api to return the page? (instead of pblock_size) // .. and align within the allocation const uintptr_t align_mask = alignment - 1; // for any x, `(x & align_mask) == (x % alignment)` @@ -97,11 +97,11 @@ static mi_decl_noinline void* mi_heap_malloc_zero_aligned_at_overalloc(mi_heap_t void* aligned_p = (void*)((uintptr_t)p + adjust); if (aligned_p != p) { mi_page_set_has_aligned(page, true); - if (usable!=NULL) { - mi_assert_internal(*usable > adjust); - if (*usable > adjust) { *usable = *usable - adjust; } - mi_assert_internal(*usable >= size); - } + // if (pblock_size!=NULL) { + // mi_assert_internal(*pblock_size > adjust); + // if (*pblock_size > adjust) { *pblock_size = *pblock_size - adjust; } + // mi_assert_internal(*pblock_size >= size); + // } #if MI_GUARDED // set tag to aligned so mi_usable_size works with guard pages if (adjust >= sizeof(mi_block_t)) { @@ -142,7 +142,7 @@ static mi_decl_noinline void* mi_heap_malloc_zero_aligned_at_overalloc(mi_heap_t } // Generic primitive aligned allocation -- split out for better codegen -static mi_decl_noinline void* mi_heap_malloc_zero_aligned_at_generic(mi_heap_t* const heap, const size_t size, const size_t alignment, const size_t offset, const bool zero, size_t* usable) mi_attr_noexcept +static mi_decl_noinline void* mi_heap_malloc_zero_aligned_at_generic(mi_heap_t* const heap, const size_t size, const size_t alignment, const size_t offset, const bool zero, size_t* pblock_size) mi_attr_noexcept { mi_assert_internal(mi_alignment_is_valid(alignment)); // we don't allocate more than MI_MAX_ALLOC_SIZE (see ) @@ -155,7 +155,7 @@ static mi_decl_noinline void* mi_heap_malloc_zero_aligned_at_generic(mi_heap_t* // this is important to try as the fast path in `mi_heap_malloc_zero_aligned` only works when there exist // a page with the right block size, and if we always use the over-alloc fallback that would never happen. if (offset == 0 && mi_malloc_is_naturally_aligned(size,alignment)) { - void* p = mi_heap_malloc_zero_no_guarded(heap, size, zero, usable); + void* p = mi_heap_malloc_zero_no_guarded(heap, size, zero, pblock_size); mi_assert_internal(p == NULL || ((uintptr_t)p % alignment) == 0); const bool is_aligned_or_null = (((uintptr_t)p) & (alignment-1))==0; if mi_likely(is_aligned_or_null) { @@ -169,7 +169,7 @@ static mi_decl_noinline void* mi_heap_malloc_zero_aligned_at_generic(mi_heap_t* } // fall back to over-allocation - return mi_heap_malloc_zero_aligned_at_overalloc(heap,size,alignment,offset,zero,usable); + return mi_heap_malloc_zero_aligned_at_overalloc(heap,size,alignment,offset,zero,pblock_size); } @@ -181,7 +181,7 @@ static mi_decl_cold mi_decl_noinline void* mi_error_bad_alignment(size_t size, s // Primitive aligned allocation static void* mi_heap_malloc_zero_aligned_at(mi_heap_t* const heap, const size_t size, const size_t alignment, const size_t offset, const bool zero, - size_t* usable) mi_attr_noexcept + size_t* pblock_size) mi_attr_noexcept { // note: we don't require `size > offset`, we just guarantee that the address at offset is aligned regardless of the allocated size. if mi_unlikely(!mi_alignment_is_valid(alignment)) { // require power-of-two and multiple of void* (see ) @@ -190,7 +190,7 @@ static void* mi_heap_malloc_zero_aligned_at(mi_heap_t* const heap, const size_t #if MI_GUARDED if (offset==0 && alignment < MI_BLOCK_ALIGNMENT_MAX && mi_heap_malloc_use_guarded(heap,size)) { - return mi_heap_malloc_guarded_aligned(heap, size, alignment, zero, usable); + return mi_heap_malloc_guarded_aligned(heap, size, alignment, zero, pblock_size); } #endif @@ -203,7 +203,7 @@ static void* mi_heap_malloc_zero_aligned_at(mi_heap_t* const heap, const size_t const bool is_aligned = (((uintptr_t)page->free + offset) & align_mask)==0; if mi_likely(is_aligned) { - if (usable!=NULL) { *usable = mi_page_usable_block_size(page); } + if (pblock_size!=NULL) { *pblock_size = mi_page_block_size(page); } void* p = (zero ? _mi_page_malloc_zeroed(heap,page,padsize) : _mi_page_malloc(heap,page,padsize)); // call specific page malloc for better codegen mi_assert_internal(p != NULL); mi_assert_internal(((uintptr_t)p + offset) % alignment == 0); @@ -214,7 +214,7 @@ static void* mi_heap_malloc_zero_aligned_at(mi_heap_t* const heap, const size_t } // fallback to generic aligned allocation - return mi_heap_malloc_zero_aligned_at_generic(heap, size, alignment, offset, zero, usable); + return mi_heap_malloc_zero_aligned_at_generic(heap, size, alignment, offset, zero, pblock_size); } diff --git a/src/alloc.c b/src/alloc.c index 419842f65..8cfa14816 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -28,7 +28,7 @@ terms of the MIT license. A copy of the license can be found in the file // Fast allocation in a page: just pop from the free list. // Fall back to generic allocation only if the list is empty. // Note: in release mode the (inlined) routine is about 7 instructions with a single test. -extern inline void* _mi_page_malloc_zero(mi_heap_t* heap, mi_page_t* page, size_t size, bool zero, size_t* usable) mi_attr_noexcept +extern inline void* _mi_page_malloc_zero(mi_heap_t* heap, mi_page_t* page, size_t size, bool zero, size_t* pblock_size) mi_attr_noexcept { mi_assert_internal(size >= MI_PADDING_SIZE); mi_assert_internal(page->block_size == 0 /* empty heap */ || mi_page_block_size(page) >= size); @@ -36,10 +36,10 @@ extern inline void* _mi_page_malloc_zero(mi_heap_t* heap, mi_page_t* page, size_ // check the free list mi_block_t* const block = page->free; if mi_unlikely(block == NULL) { - return _mi_malloc_generic(heap, size, zero, 0, usable); + return _mi_malloc_generic(heap, size, zero, 0, pblock_size); } mi_assert_internal(block != NULL && _mi_ptr_page(block) == page); - if (usable != NULL) { *usable = mi_page_usable_block_size(page); }; + if (pblock_size != NULL) { *pblock_size = mi_page_block_size(page); }; // pop from the free list page->free = mi_block_next(page, block); page->used++; @@ -122,7 +122,7 @@ extern void* _mi_page_malloc_zeroed(mi_heap_t* heap, mi_page_t* page, size_t siz return _mi_page_malloc_zero(heap,page,size,true,NULL); } -static inline mi_decl_restrict void* mi_heap_malloc_small_zero(mi_heap_t* heap, size_t size, bool zero, size_t* usable) mi_attr_noexcept { +static inline mi_decl_restrict void* mi_heap_malloc_small_zero(mi_heap_t* heap, size_t size, bool zero, size_t* pblock_size) mi_attr_noexcept { mi_assert(heap != NULL); mi_assert(size <= MI_SMALL_SIZE_MAX); #if MI_DEBUG @@ -134,13 +134,13 @@ static inline mi_decl_restrict void* mi_heap_malloc_small_zero(mi_heap_t* heap, #endif #if MI_GUARDED if (mi_heap_malloc_use_guarded(heap,size)) { - return _mi_heap_malloc_guarded(heap, size, zero, usable); + return _mi_heap_malloc_guarded(heap, size, zero, pblock_size); } #endif // get page in constant time, and allocate from it mi_page_t* page = _mi_heap_get_free_small_page(heap, size + MI_PADDING_SIZE); - void* const p = _mi_page_malloc_zero(heap, page, size + MI_PADDING_SIZE, zero, usable); + void* const p = _mi_page_malloc_zero(heap, page, size + MI_PADDING_SIZE, zero, pblock_size); mi_track_malloc(p,size,zero); #if MI_DEBUG>3 @@ -165,22 +165,22 @@ mi_decl_nodiscard extern inline mi_decl_restrict void* mi_malloc_small(size_t si } // The main allocation function -extern inline void* _mi_heap_malloc_zero_ex(mi_heap_t* heap, size_t size, bool zero, size_t huge_alignment, size_t* usable) mi_attr_noexcept { +extern inline void* _mi_heap_malloc_zero_ex(mi_heap_t* heap, size_t size, bool zero, size_t huge_alignment, size_t* pblock_size) mi_attr_noexcept { // fast path for small objects if mi_likely(size <= MI_SMALL_SIZE_MAX) { mi_assert_internal(huge_alignment == 0); - return mi_heap_malloc_small_zero(heap, size, zero, usable); + return mi_heap_malloc_small_zero(heap, size, zero, pblock_size); } #if MI_GUARDED else if (huge_alignment==0 && mi_heap_malloc_use_guarded(heap,size)) { - return _mi_heap_malloc_guarded(heap, size, zero, usable); + return _mi_heap_malloc_guarded(heap, size, zero, pblock_size); } #endif else { // regular allocation mi_assert(heap!=NULL); mi_assert(heap->thread_id == 0 || heap->thread_id == _mi_thread_id()); // heaps are thread local - void* const p = _mi_malloc_generic(heap, size + MI_PADDING_SIZE, zero, huge_alignment, usable); // note: size can overflow but it is detected in malloc_generic + void* const p = _mi_malloc_generic(heap, size + MI_PADDING_SIZE, zero, huge_alignment, pblock_size); // note: size can overflow but it is detected in malloc_generic mi_track_malloc(p,size,zero); #if MI_DEBUG>3 @@ -228,31 +228,31 @@ mi_decl_nodiscard mi_decl_restrict void* mi_calloc(size_t count, size_t size) mi return mi_heap_calloc(mi_prim_get_default_heap(),count,size); } -// Return usable size -mi_decl_nodiscard mi_decl_restrict void* mi_umalloc_small(size_t size, size_t* usable) mi_attr_noexcept { - return mi_heap_malloc_small_zero(mi_prim_get_default_heap(), size, false, usable); +// Return the block size +mi_decl_nodiscard mi_decl_restrict void* mi_umalloc_small(size_t size, size_t* pblock_size) mi_attr_noexcept { + return mi_heap_malloc_small_zero(mi_prim_get_default_heap(), size, false, pblock_size); } -mi_decl_nodiscard mi_decl_restrict void* mi_uzalloc_small(size_t size, size_t* usable) mi_attr_noexcept { - return mi_heap_malloc_small_zero(mi_prim_get_default_heap(), size, true, usable); +mi_decl_nodiscard mi_decl_restrict void* mi_uzalloc_small(size_t size, size_t* pblock_size) mi_attr_noexcept { + return mi_heap_malloc_small_zero(mi_prim_get_default_heap(), size, true, pblock_size); } -mi_decl_nodiscard mi_decl_restrict void* mi_heap_umalloc(mi_heap_t* heap, size_t size, size_t* usable) mi_attr_noexcept { - return _mi_heap_malloc_zero_ex(heap, size, false, 0, usable); +mi_decl_nodiscard mi_decl_restrict void* mi_heap_umalloc(mi_heap_t* heap, size_t size, size_t* pblock_size) mi_attr_noexcept { + return _mi_heap_malloc_zero_ex(heap, size, false, 0, pblock_size); } -mi_decl_nodiscard mi_decl_restrict void* mi_umalloc(size_t size, size_t* usable) mi_attr_noexcept { - return mi_heap_umalloc(mi_prim_get_default_heap(), size, usable); +mi_decl_nodiscard mi_decl_restrict void* mi_umalloc(size_t size, size_t* pblock_size) mi_attr_noexcept { + return mi_heap_umalloc(mi_prim_get_default_heap(), size, pblock_size); } -mi_decl_nodiscard mi_decl_restrict void* mi_uzalloc(size_t size, size_t* usable) mi_attr_noexcept { - return _mi_heap_malloc_zero_ex(mi_prim_get_default_heap(), size, true, 0, usable); +mi_decl_nodiscard mi_decl_restrict void* mi_uzalloc(size_t size, size_t* pblock_size) mi_attr_noexcept { + return _mi_heap_malloc_zero_ex(mi_prim_get_default_heap(), size, true, 0, pblock_size); } -mi_decl_nodiscard mi_decl_restrict void* mi_ucalloc(size_t count, size_t size, size_t* usable) mi_attr_noexcept { +mi_decl_nodiscard mi_decl_restrict void* mi_ucalloc(size_t count, size_t size, size_t* pblock_size) mi_attr_noexcept { size_t total; if (mi_count_size_overflow(count,size,&total)) return NULL; - return mi_uzalloc(total, usable); + return mi_uzalloc(total, pblock_size); } // Uninitialized `calloc` @@ -281,7 +281,7 @@ void* mi_expand(void* p, size_t newsize) mi_attr_noexcept { #endif } -void* _mi_heap_realloc_zero(mi_heap_t* heap, void* p, size_t newsize, bool zero, size_t* usable_pre, size_t* usable_post) mi_attr_noexcept { +void* _mi_heap_realloc_zero(mi_heap_t* heap, void* p, size_t newsize, bool zero, size_t* pblock_size_pre, size_t* pblock_size_post) mi_attr_noexcept { // if p == NULL then behave as malloc. // else if size == 0 then reallocate to a zero-sized block (and don't return NULL, just as mi_malloc(0)). // (this means that returning NULL always indicates an error, and `p` will not have been freed in that case.) @@ -290,36 +290,37 @@ void* _mi_heap_realloc_zero(mi_heap_t* heap, void* p, size_t newsize, bool zero, if (p==NULL) { page = NULL; size = 0; - if (usable_pre!=NULL) { *usable_pre = 0; } + if (pblock_size_pre!=NULL) { *pblock_size_pre = 0; } } else { page = mi_validate_ptr_page(p,"mi_realloc"); if mi_unlikely(page==NULL) { // invalid pointer - if (usable_pre!=NULL) { *usable_pre = 0; } - if (usable_post!=NULL) { *usable_post = 0; } + if (pblock_size_pre!=NULL) { *pblock_size_pre = 0; } + if (pblock_size_post!=NULL) { *pblock_size_post = 0; } return NULL; } size = _mi_usable_size(p,page); - if (usable_pre!=NULL) { *usable_pre = mi_page_usable_block_size(page); } + if (pblock_size_pre!=NULL) { *pblock_size_pre = mi_page_block_size(page); } } if mi_unlikely(newsize <= size && newsize >= (size / 2) && newsize > 0) { // note: newsize must be > 0 or otherwise we return NULL for realloc(NULL,0) mi_assert_internal(p!=NULL); // todo: do not track as the usable size is still the same in the free; adjust potential padding? // mi_track_resize(p,size,newsize) // if (newsize < size) { mi_track_mem_noaccess((uint8_t*)p + newsize, size - newsize); } - if (usable_post!=NULL) { *usable_post = mi_page_usable_block_size(page); } + if (pblock_size_post!=NULL) { *pblock_size_post = mi_page_block_size(page); } return p; // reallocation still fits and not more than 50% waste } // note: we don't zero allocate upfront so we only zero initialize the expanded part - size_t usable; // use usable for zero-ing, issue #763 - void* const newp = mi_heap_umalloc(heap,newsize,&usable); + size_t block_size; // use block_size for zero-ing, issue #763 + void* const newp = mi_heap_umalloc(heap,newsize,&block_size); if mi_likely(newp != NULL) { - if (usable_post!=NULL) { *usable_post = usable; } + if (pblock_size_post!=NULL) { *pblock_size_post = block_size; } const size_t copy_size = (newsize > size ? size : newsize); const size_t zero_start = _mi_align_down( (copy_size >= sizeof(intptr_t) ? copy_size - sizeof(intptr_t) : 0), sizeof(intptr_t)); // also set last word in the previous allocation to zero to ensure any padding is zero-initialized #if MI_PADDING || MI_GUARDED - usable = mi_usable_size(newp); + const size_t usable = mi_usable_size(newp); #else + const size_t usable = block_size; mi_assert_internal(usable == mi_usable_size(newp)); #endif mi_assert_internal(usable >= newsize); @@ -375,8 +376,8 @@ mi_decl_nodiscard void* mi_reallocn(void* p, size_t count, size_t size) mi_attr_ return mi_heap_reallocn(mi_prim_get_default_heap(),p,count,size); } -mi_decl_nodiscard void* mi_urealloc(void* p, size_t newsize, size_t* usable_pre, size_t* usable_post) mi_attr_noexcept { - return _mi_heap_realloc_zero(mi_prim_get_default_heap(),p,newsize, false, usable_pre, usable_post); +mi_decl_nodiscard void* mi_urealloc(void* p, size_t newsize, size_t* pblock_size_pre, size_t* pblock_size_post) mi_attr_noexcept { + return _mi_heap_realloc_zero(mi_prim_get_default_heap(),p,newsize, false, pblock_size_pre, pblock_size_post); } // Reallocate but free `p` on errors @@ -744,7 +745,7 @@ static void* mi_block_ptr_set_guarded(mi_block_t* block, size_t obj_size, size_t return p; } -mi_decl_restrict void* _mi_heap_malloc_guarded(mi_heap_t* heap, size_t size, bool zero, size_t* usable) mi_attr_noexcept +mi_decl_restrict void* _mi_heap_malloc_guarded(mi_heap_t* heap, size_t size, bool zero, size_t* pblock_size) mi_attr_noexcept { // allocate multiple of page size ending in a guard page // ensure minimal alignment requirement? @@ -756,7 +757,7 @@ mi_decl_restrict void* _mi_heap_malloc_guarded(mi_heap_t* heap, size_t size, boo const size_t obj_size = (mi_option_is_enabled(mi_option_guarded_precise) ? size : _mi_align_up(size, MI_MAX_ALIGN_SIZE)); const size_t bsize = _mi_align_up(_mi_align_up(obj_size, MI_MAX_ALIGN_SIZE) + sizeof(mi_block_t), MI_MAX_ALIGN_SIZE); const size_t req_size = _mi_align_up(bsize + os_page_size, os_page_size); - mi_block_t* const block = (mi_block_t*)_mi_malloc_generic(heap, req_size, false /* don't zero */, 0 /* huge_alignment */, usable); + mi_block_t* const block = (mi_block_t*)_mi_malloc_generic(heap, req_size, false /* don't zero */, 0 /* huge_alignment */, pblock_size); if (block==NULL) return NULL; size_t usable_size = 0; void* const p = mi_block_ptr_set_guarded(block, obj_size, &usable_size); diff --git a/src/free.c b/src/free.c index 9c778cdd2..bfba75326 100644 --- a/src/free.c +++ b/src/free.c @@ -160,17 +160,17 @@ static inline mi_segment_t* mi_checked_ptr_segment(const void* p, const char* ms // Free a block // Fast path written carefully to prevent register spilling on the stack -static inline void mi_free_ex(void* p, size_t* usable) mi_attr_noexcept +static inline void mi_free_ex(void* p, size_t* pblock_size) mi_attr_noexcept { mi_segment_t* const segment = mi_checked_ptr_segment(p,"mi_free"); if mi_unlikely(segment==NULL) { - if (usable!=NULL) { *usable = 0; } + if (pblock_size!=NULL) { *pblock_size = 0; } return; } const bool is_local = (_mi_prim_thread_id() == mi_atomic_load_relaxed(&segment->thread_id)); mi_page_t* const page = _mi_segment_page_of(segment, p); - if (usable!=NULL) { *usable = mi_page_usable_block_size(page); } + if (pblock_size!=NULL) { *pblock_size = mi_page_block_size(page); } if mi_likely(is_local) { // thread-local free? if mi_likely(page->flags.full_aligned == 0) { // and it is not a full page (full pages need to move from the full bin), nor has aligned blocks (aligned blocks need to be unaligned) @@ -193,8 +193,8 @@ void mi_free(void* p) mi_attr_noexcept { mi_free_ex(p,NULL); } -void mi_ufree(void* p, size_t* usable) mi_attr_noexcept { - mi_free_ex(p,usable); +void mi_ufree(void* p, size_t* pblock_size) mi_attr_noexcept { + mi_free_ex(p,pblock_size); } void mi_free_small(void* p) mi_attr_noexcept { From 427df0b47e29a9a6400cc966202a03248ffd9e46 Mon Sep 17 00:00:00 2001 From: daanx Date: Tue, 4 Aug 2026 10:02:56 -0700 Subject: [PATCH 18/27] improve umalloc and realloc efficiency --- include/mimalloc/internal.h | 12 +++---- include/mimalloc/types.h | 2 +- src/alloc-aligned.c | 57 ++++++++++++++++++-------------- src/alloc.c | 65 ++++++++++++++++++++++--------------- src/free.c | 7 ++-- src/page.c | 4 +-- 6 files changed, 84 insertions(+), 63 deletions(-) diff --git a/include/mimalloc/internal.h b/include/mimalloc/internal.h index d1ee49b0d..e08c55778 100644 --- a/include/mimalloc/internal.h +++ b/include/mimalloc/internal.h @@ -237,7 +237,7 @@ bool _mi_segment_attempt_reclaim(mi_heap_t* heap, mi_segment_t* segment); bool _mi_segment_visit_blocks(mi_segment_t* segment, int heap_tag, bool visit_blocks, mi_block_visit_fun* visitor, void* arg); // "page.c" -void* _mi_malloc_generic(mi_heap_t* heap, size_t size, bool zero, size_t huge_alignment, size_t* usable) mi_attr_noexcept mi_attr_malloc; +void* _mi_malloc_generic(mi_heap_t* heap, size_t size, bool zero, size_t huge_alignment, mi_page_t** ppage) mi_attr_noexcept mi_attr_malloc; void _mi_page_retire(mi_page_t* page) mi_attr_noexcept; // free the page if there are no other pages with many free blocks void _mi_page_unfull(mi_page_t* page); @@ -280,17 +280,17 @@ mi_msecs_t _mi_clock_end(mi_msecs_t start); mi_msecs_t _mi_clock_start(void); // "alloc.c" -void* _mi_page_malloc_zero(mi_heap_t* heap, mi_page_t* page, size_t size, bool zero, size_t* usable) mi_attr_noexcept; // called from `_mi_malloc_generic` +void* _mi_page_malloc_zero(mi_heap_t* heap, mi_page_t* page, size_t size, bool zero, mi_page_t** ppage) mi_attr_noexcept; // called from `_mi_malloc_generic` void* _mi_page_malloc(mi_heap_t* heap, mi_page_t* page, size_t size) mi_attr_noexcept; // called from `_mi_heap_malloc_aligned` void* _mi_page_malloc_zeroed(mi_heap_t* heap, mi_page_t* page, size_t size) mi_attr_noexcept; // called from `_mi_heap_malloc_aligned` void* _mi_heap_malloc_zero(mi_heap_t* heap, size_t size, bool zero) mi_attr_noexcept; -void* _mi_heap_malloc_zero_ex(mi_heap_t* heap, size_t size, bool zero, size_t huge_alignment, size_t* usable) mi_attr_noexcept; // called from `_mi_heap_malloc_aligned` -void* _mi_heap_realloc_zero(mi_heap_t* heap, void* p, size_t newsize, bool zero, size_t* usable_pre, size_t* usable_post) mi_attr_noexcept; +void* _mi_heap_malloc_zero_ex(mi_heap_t* heap, size_t size, bool zero, size_t huge_alignment, mi_page_t** ppage) mi_attr_noexcept; // called from `_mi_heap_malloc_aligned` +void* _mi_heap_realloc_zero(mi_heap_t* heap, void* p, size_t newsize, bool zero, size_t* pblock_size_pre, size_t* pblock_size_post) mi_attr_noexcept; mi_block_t* _mi_page_ptr_unalign(const mi_page_t* page, const void* p); bool _mi_free_delayed_block(mi_block_t* block); void _mi_free_generic(mi_segment_t* segment, mi_page_t* page, bool is_local, void* p) mi_attr_noexcept; // for runtime integration void _mi_padding_shrink(const mi_page_t* page, const mi_block_t* block, const size_t min_size); - +size_t _mi_page_usable_size(const mi_page_t* page, const void* p) mi_attr_noexcept; #if MI_DEBUG>1 bool _mi_page_is_valid(mi_page_t* page); #endif @@ -735,7 +735,7 @@ static inline bool mi_heap_malloc_use_guarded(mi_heap_t* heap, size_t size) { } } -mi_decl_restrict void* _mi_heap_malloc_guarded(mi_heap_t* heap, size_t size, bool zero, size_t* usable) mi_attr_noexcept; +mi_decl_restrict void* _mi_heap_malloc_guarded(mi_heap_t* heap, size_t size, bool zero, mi_page_t** ppage) mi_attr_noexcept; #endif diff --git a/include/mimalloc/types.h b/include/mimalloc/types.h index b52b63690..2024f5518 100644 --- a/include/mimalloc/types.h +++ b/include/mimalloc/types.h @@ -483,7 +483,7 @@ typedef struct mi_random_cxt_s { #if (MI_PADDING) typedef struct mi_padding_s { uint32_t canary; // encoded block value to check validity of the padding (in case of overflow) - uint32_t delta; // padding bytes before the block. (mi_usable_size(p) - delta == exact allocated bytes) + uint32_t delta; // padding bytes before the block. (mi_full_usable_size(p) - delta == exact allocated bytes) } mi_padding_t; #define MI_PADDING_SIZE (sizeof(mi_padding_t)) #define MI_PADDING_WSIZE ((MI_PADDING_SIZE + MI_INTPTR_SIZE - 1) / MI_INTPTR_SIZE) diff --git a/src/alloc-aligned.c b/src/alloc-aligned.c index 7a4665999..3756be836 100644 --- a/src/alloc-aligned.c +++ b/src/alloc-aligned.c @@ -25,7 +25,7 @@ static bool mi_malloc_is_naturally_aligned( size_t size, size_t alignment ) { } #if MI_GUARDED -static mi_decl_noinline mi_decl_restrict void* mi_heap_malloc_guarded_aligned(mi_heap_t* heap, size_t size, size_t alignment, bool zero, size_t* pblock_size) mi_attr_noexcept { +static mi_decl_noinline mi_decl_restrict void* mi_heap_malloc_guarded_aligned(mi_heap_t* heap, size_t size, size_t alignment, bool zero, mi_page_t** ppage) mi_attr_noexcept { // use over allocation for guarded blocksl mi_assert_internal(alignment > 0 && alignment < MI_BLOCK_ALIGNMENT_MAX); if mi_unlikely(alignment >= MI_BLOCK_ALIGNMENT_MAX || size > (MI_MAX_ALLOC_SIZE - MI_PADDING_SIZE - alignment)) { @@ -33,7 +33,7 @@ static mi_decl_noinline mi_decl_restrict void* mi_heap_malloc_guarded_aligned(mi return NULL; } const size_t oversize = size + alignment - 1; - void* const base = _mi_heap_malloc_guarded(heap, oversize, zero, pblock_size); + void* const base = _mi_heap_malloc_guarded(heap, oversize, zero, ppage); if (base==NULL) return NULL; void* const p = _mi_align_up_ptr(base, alignment); mi_track_align(base, p, (uint8_t*)p - (uint8_t*)base, size); @@ -42,28 +42,29 @@ static mi_decl_noinline mi_decl_restrict void* mi_heap_malloc_guarded_aligned(mi return p; } -static void* mi_heap_malloc_zero_no_guarded(mi_heap_t* heap, size_t size, bool zero, size_t* pblock_size) { +static void* mi_heap_malloc_zero_no_guarded(mi_heap_t* heap, size_t size, bool zero, mi_page_t** ppage) { const size_t rate = heap->guarded_sample_rate; // only write if `rate!=0` so we don't write to the constant `_mi_heap_empty` if (rate != 0) { heap->guarded_sample_rate = 0; } - void* p = _mi_heap_malloc_zero_ex(heap, size, zero, 0, pblock_size); + void* p = _mi_heap_malloc_zero_ex(heap, size, zero, 0, ppage); if (rate != 0) { heap->guarded_sample_rate = rate; } return p; } #else -static void* mi_heap_malloc_zero_no_guarded(mi_heap_t* heap, size_t size, bool zero, size_t* pblock_size) { - return _mi_heap_malloc_zero_ex(heap, size, zero, 0, pblock_size); +static void* mi_heap_malloc_zero_no_guarded(mi_heap_t* heap, size_t size, bool zero, mi_page_t** ppage) { + return _mi_heap_malloc_zero_ex(heap, size, zero, 0, ppage); } #endif // Fallback aligned allocation that over-allocates -- split out for better codegen -static mi_decl_noinline void* mi_heap_malloc_zero_aligned_at_overalloc(mi_heap_t* const heap, const size_t size, const size_t alignment, const size_t offset, const bool zero, size_t* pblock_size) mi_attr_noexcept +static mi_decl_noinline void* mi_heap_malloc_zero_aligned_at_overalloc(mi_heap_t* const heap, const size_t size, const size_t alignment, const size_t offset, const bool zero, mi_page_t** ppage) mi_attr_noexcept { mi_assert_internal(size <= (MI_MAX_ALLOC_SIZE - MI_PADDING_SIZE)); mi_assert_internal(mi_alignment_is_valid(alignment)); void* p; size_t oversize; + mi_page_t* page; if mi_unlikely(alignment > MI_BLOCK_ALIGNMENT_MAX) { // use OS allocation for very large alignment and allocate inside a huge page (dedicated segment with 1 page) // This can support alignments >= MI_SEGMENT_SIZE by ensuring the object can be aligned at a point in the @@ -75,7 +76,7 @@ static mi_decl_noinline void* mi_heap_malloc_zero_aligned_at_overalloc(mi_heap_t } oversize = (size <= MI_SMALL_SIZE_MAX ? MI_SMALL_SIZE_MAX + 1 /* ensure we use generic malloc path */ : size); // note: no guarded as alignment > 0 - p = _mi_heap_malloc_zero_ex(heap, oversize, false, alignment, pblock_size); // the page block size should be large enough to align in the single huge page block + p = _mi_heap_malloc_zero_ex(heap, oversize, false, alignment, &page); // the page block size should be large enough to align in the single huge page block // zero afterwards as only the area from the aligned_p may be committed! if (p == NULL) return NULL; } @@ -84,10 +85,11 @@ static mi_decl_noinline void* mi_heap_malloc_zero_aligned_at_overalloc(mi_heap_t mi_assert_internal(size <= (MI_MAX_ALLOC_SIZE - MI_PADDING_SIZE) && alignment <= MI_BLOCK_ALIGNMENT_MAX); mi_assert_internal(size < SIZE_MAX - alignment); // `oversize` cannot overflow oversize = (size < MI_MAX_ALIGN_SIZE ? MI_MAX_ALIGN_SIZE : size) + alignment - 1; // adjust for size <= 16; with size 0 and alignment 64k, we would allocate a 64k block and pointing just beyond that. - p = mi_heap_malloc_zero_no_guarded(heap, oversize, zero, pblock_size); + p = mi_heap_malloc_zero_no_guarded(heap, oversize, zero, &page); if (p == NULL) return NULL; } - mi_page_t* page = _mi_ptr_page(p); // todo: change low level allocation api to return the page? (instead of pblock_size) + mi_assert_internal(page == _mi_ptr_page(p)); + if (ppage!=NULL) { *ppage = page; } // .. and align within the allocation const uintptr_t align_mask = alignment - 1; // for any x, `(x & align_mask) == (x % alignment)` @@ -142,7 +144,7 @@ static mi_decl_noinline void* mi_heap_malloc_zero_aligned_at_overalloc(mi_heap_t } // Generic primitive aligned allocation -- split out for better codegen -static mi_decl_noinline void* mi_heap_malloc_zero_aligned_at_generic(mi_heap_t* const heap, const size_t size, const size_t alignment, const size_t offset, const bool zero, size_t* pblock_size) mi_attr_noexcept +static mi_decl_noinline void* mi_heap_malloc_zero_aligned_at_generic(mi_heap_t* const heap, const size_t size, const size_t alignment, const size_t offset, const bool zero, mi_page_t** ppage) mi_attr_noexcept { mi_assert_internal(mi_alignment_is_valid(alignment)); // we don't allocate more than MI_MAX_ALLOC_SIZE (see ) @@ -155,7 +157,7 @@ static mi_decl_noinline void* mi_heap_malloc_zero_aligned_at_generic(mi_heap_t* // this is important to try as the fast path in `mi_heap_malloc_zero_aligned` only works when there exist // a page with the right block size, and if we always use the over-alloc fallback that would never happen. if (offset == 0 && mi_malloc_is_naturally_aligned(size,alignment)) { - void* p = mi_heap_malloc_zero_no_guarded(heap, size, zero, pblock_size); + void* p = mi_heap_malloc_zero_no_guarded(heap, size, zero, ppage); mi_assert_internal(p == NULL || ((uintptr_t)p % alignment) == 0); const bool is_aligned_or_null = (((uintptr_t)p) & (alignment-1))==0; if mi_likely(is_aligned_or_null) { @@ -169,7 +171,7 @@ static mi_decl_noinline void* mi_heap_malloc_zero_aligned_at_generic(mi_heap_t* } // fall back to over-allocation - return mi_heap_malloc_zero_aligned_at_overalloc(heap,size,alignment,offset,zero,pblock_size); + return mi_heap_malloc_zero_aligned_at_overalloc(heap,size,alignment,offset,zero,ppage); } @@ -181,7 +183,7 @@ static mi_decl_cold mi_decl_noinline void* mi_error_bad_alignment(size_t size, s // Primitive aligned allocation static void* mi_heap_malloc_zero_aligned_at(mi_heap_t* const heap, const size_t size, const size_t alignment, const size_t offset, const bool zero, - size_t* pblock_size) mi_attr_noexcept + mi_page_t** ppage) mi_attr_noexcept { // note: we don't require `size > offset`, we just guarantee that the address at offset is aligned regardless of the allocated size. if mi_unlikely(!mi_alignment_is_valid(alignment)) { // require power-of-two and multiple of void* (see ) @@ -190,7 +192,7 @@ static void* mi_heap_malloc_zero_aligned_at(mi_heap_t* const heap, const size_t #if MI_GUARDED if (offset==0 && alignment < MI_BLOCK_ALIGNMENT_MAX && mi_heap_malloc_use_guarded(heap,size)) { - return mi_heap_malloc_guarded_aligned(heap, size, alignment, zero, pblock_size); + return mi_heap_malloc_guarded_aligned(heap, size, alignment, zero, ppage); } #endif @@ -203,7 +205,7 @@ static void* mi_heap_malloc_zero_aligned_at(mi_heap_t* const heap, const size_t const bool is_aligned = (((uintptr_t)page->free + offset) & align_mask)==0; if mi_likely(is_aligned) { - if (pblock_size!=NULL) { *pblock_size = mi_page_block_size(page); } + if (ppage!=NULL) { *ppage = page; } void* p = (zero ? _mi_page_malloc_zeroed(heap,page,padsize) : _mi_page_malloc(heap,page,padsize)); // call specific page malloc for better codegen mi_assert_internal(p != NULL); mi_assert_internal(((uintptr_t)p + offset) % alignment == 0); @@ -214,7 +216,7 @@ static void* mi_heap_malloc_zero_aligned_at(mi_heap_t* const heap, const size_t } // fallback to generic aligned allocation - return mi_heap_malloc_zero_aligned_at_generic(heap, size, alignment, offset, zero, pblock_size); + return mi_heap_malloc_zero_aligned_at_generic(heap, size, alignment, offset, zero, ppage); } @@ -260,8 +262,11 @@ mi_decl_nodiscard mi_decl_restrict void* mi_malloc_aligned(size_t size, size_t a return mi_heap_malloc_aligned(mi_prim_get_default_heap(), size, alignment); } -mi_decl_nodiscard mi_decl_restrict void* mi_umalloc_aligned(size_t size, size_t alignment, size_t* block_size) mi_attr_noexcept { - return mi_heap_malloc_zero_aligned_at(mi_prim_get_default_heap(), size, alignment, 0, false, block_size); +mi_decl_nodiscard mi_decl_restrict void* mi_umalloc_aligned(size_t size, size_t alignment, size_t* pblock_size) mi_attr_noexcept { + mi_page_t* page; + void* p = mi_heap_malloc_zero_aligned_at(mi_prim_get_default_heap(), size, alignment, 0, false, &page); + if (p!=NULL && pblock_size!=NULL) { *pblock_size = mi_page_block_size(page); } + return p; } mi_decl_nodiscard mi_decl_restrict void* mi_zalloc_aligned_at(size_t size, size_t alignment, size_t offset) mi_attr_noexcept { @@ -272,8 +277,11 @@ mi_decl_nodiscard mi_decl_restrict void* mi_zalloc_aligned(size_t size, size_t a return mi_heap_zalloc_aligned(mi_prim_get_default_heap(), size, alignment); } -mi_decl_nodiscard mi_decl_restrict void* mi_uzalloc_aligned(size_t size, size_t alignment, size_t* block_size) mi_attr_noexcept { - return mi_heap_malloc_zero_aligned_at(mi_prim_get_default_heap(), size, alignment, 0, true, block_size); +mi_decl_nodiscard mi_decl_restrict void* mi_uzalloc_aligned(size_t size, size_t alignment, size_t* pblock_size) mi_attr_noexcept { + mi_page_t* page; + void* p = mi_heap_malloc_zero_aligned_at(mi_prim_get_default_heap(), size, alignment, 0, true, &page); + if (p!=NULL && pblock_size!=NULL) { *pblock_size = mi_page_block_size(page); } + return p; } mi_decl_nodiscard mi_decl_restrict void* mi_calloc_aligned_at(size_t count, size_t size, size_t alignment, size_t offset) mi_attr_noexcept { @@ -302,11 +310,12 @@ static void* mi_heap_realloc_zero_aligned_at(mi_heap_t* heap, void* p, size_t ne } else { // note: we don't zero allocate upfront so we only zero initialize the expanded part (at the cost of calling mi_usable_size) - void* const newp = mi_heap_malloc_aligned_at(heap,newsize,alignment,offset); - if (newp != NULL) { + mi_page_t* newpage; + void* const newp = mi_heap_malloc_zero_aligned_at(heap,newsize,alignment,offset,false /* no zero*/, &newpage); + if (newp != NULL) { const size_t copy_size = (newsize > size ? size : newsize); const size_t zero_start = (copy_size >= sizeof(intptr_t) ? copy_size - sizeof(intptr_t) : 0); // also set last word in the previous allocation to zero to ensure any padding is zero-initialized - const size_t usable = mi_usable_size(newp); + const size_t usable = _mi_page_usable_size(newpage,newp); mi_assert_internal(usable >= newsize); // use usable for zero'ing, issue #763 if (zero && usable > zero_start) { // also set last word in the previous allocation to zero to ensure any padding is zero-initialized diff --git a/src/alloc.c b/src/alloc.c index 8cfa14816..0c65ab42f 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -28,7 +28,7 @@ terms of the MIT license. A copy of the license can be found in the file // Fast allocation in a page: just pop from the free list. // Fall back to generic allocation only if the list is empty. // Note: in release mode the (inlined) routine is about 7 instructions with a single test. -extern inline void* _mi_page_malloc_zero(mi_heap_t* heap, mi_page_t* page, size_t size, bool zero, size_t* pblock_size) mi_attr_noexcept +extern inline void* _mi_page_malloc_zero(mi_heap_t* heap, mi_page_t* page, size_t size, bool zero, mi_page_t** ppage) mi_attr_noexcept { mi_assert_internal(size >= MI_PADDING_SIZE); mi_assert_internal(page->block_size == 0 /* empty heap */ || mi_page_block_size(page) >= size); @@ -36,10 +36,10 @@ extern inline void* _mi_page_malloc_zero(mi_heap_t* heap, mi_page_t* page, size_ // check the free list mi_block_t* const block = page->free; if mi_unlikely(block == NULL) { - return _mi_malloc_generic(heap, size, zero, 0, pblock_size); + return _mi_malloc_generic(heap, size, zero, 0, ppage); } mi_assert_internal(block != NULL && _mi_ptr_page(block) == page); - if (pblock_size != NULL) { *pblock_size = mi_page_block_size(page); }; + if (ppage != NULL) { *ppage = page; }; // pop from the free list page->free = mi_block_next(page, block); page->used++; @@ -122,7 +122,7 @@ extern void* _mi_page_malloc_zeroed(mi_heap_t* heap, mi_page_t* page, size_t siz return _mi_page_malloc_zero(heap,page,size,true,NULL); } -static inline mi_decl_restrict void* mi_heap_malloc_small_zero(mi_heap_t* heap, size_t size, bool zero, size_t* pblock_size) mi_attr_noexcept { +static inline mi_decl_restrict void* mi_heap_malloc_small_zero(mi_heap_t* heap, size_t size, bool zero, mi_page_t** ppage) mi_attr_noexcept { mi_assert(heap != NULL); mi_assert(size <= MI_SMALL_SIZE_MAX); #if MI_DEBUG @@ -134,13 +134,13 @@ static inline mi_decl_restrict void* mi_heap_malloc_small_zero(mi_heap_t* heap, #endif #if MI_GUARDED if (mi_heap_malloc_use_guarded(heap,size)) { - return _mi_heap_malloc_guarded(heap, size, zero, pblock_size); + return _mi_heap_malloc_guarded(heap, size, zero, ppage); } #endif // get page in constant time, and allocate from it mi_page_t* page = _mi_heap_get_free_small_page(heap, size + MI_PADDING_SIZE); - void* const p = _mi_page_malloc_zero(heap, page, size + MI_PADDING_SIZE, zero, pblock_size); + void* const p = _mi_page_malloc_zero(heap, page, size + MI_PADDING_SIZE, zero, ppage); mi_track_malloc(p,size,zero); #if MI_DEBUG>3 @@ -165,22 +165,22 @@ mi_decl_nodiscard extern inline mi_decl_restrict void* mi_malloc_small(size_t si } // The main allocation function -extern inline void* _mi_heap_malloc_zero_ex(mi_heap_t* heap, size_t size, bool zero, size_t huge_alignment, size_t* pblock_size) mi_attr_noexcept { +extern inline void* _mi_heap_malloc_zero_ex(mi_heap_t* heap, size_t size, bool zero, size_t huge_alignment, mi_page_t** ppage) mi_attr_noexcept { // fast path for small objects if mi_likely(size <= MI_SMALL_SIZE_MAX) { mi_assert_internal(huge_alignment == 0); - return mi_heap_malloc_small_zero(heap, size, zero, pblock_size); + return mi_heap_malloc_small_zero(heap, size, zero, ppage); } #if MI_GUARDED else if (huge_alignment==0 && mi_heap_malloc_use_guarded(heap,size)) { - return _mi_heap_malloc_guarded(heap, size, zero, pblock_size); + return _mi_heap_malloc_guarded(heap, size, zero, ppage); } #endif else { // regular allocation mi_assert(heap!=NULL); mi_assert(heap->thread_id == 0 || heap->thread_id == _mi_thread_id()); // heaps are thread local - void* const p = _mi_malloc_generic(heap, size + MI_PADDING_SIZE, zero, huge_alignment, pblock_size); // note: size can overflow but it is detected in malloc_generic + void* const p = _mi_malloc_generic(heap, size + MI_PADDING_SIZE, zero, huge_alignment, ppage); // note: size can overflow but it is detected in malloc_generic mi_track_malloc(p,size,zero); #if MI_DEBUG>3 @@ -228,17 +228,31 @@ mi_decl_nodiscard mi_decl_restrict void* mi_calloc(size_t count, size_t size) mi return mi_heap_calloc(mi_prim_get_default_heap(),count,size); } +static void* mi_ublock_size( void* p, mi_page_t* page, size_t* pblock_size ) { + mi_assert_internal(page == _mi_ptr_page(p)); + if (pblock_size!=NULL) { + if (p!=NULL) { *pblock_size = mi_page_block_size(page); } + } + return p; +} + // Return the block size mi_decl_nodiscard mi_decl_restrict void* mi_umalloc_small(size_t size, size_t* pblock_size) mi_attr_noexcept { - return mi_heap_malloc_small_zero(mi_prim_get_default_heap(), size, false, pblock_size); + mi_page_t* page; + void* p = mi_heap_malloc_small_zero(mi_prim_get_default_heap(), size, false, &page); + return mi_ublock_size(p,page,pblock_size); } mi_decl_nodiscard mi_decl_restrict void* mi_uzalloc_small(size_t size, size_t* pblock_size) mi_attr_noexcept { - return mi_heap_malloc_small_zero(mi_prim_get_default_heap(), size, true, pblock_size); + mi_page_t* page; + void* p = mi_heap_malloc_small_zero(mi_prim_get_default_heap(), size, true, &page); + return mi_ublock_size(p,page,pblock_size); } mi_decl_nodiscard mi_decl_restrict void* mi_heap_umalloc(mi_heap_t* heap, size_t size, size_t* pblock_size) mi_attr_noexcept { - return _mi_heap_malloc_zero_ex(heap, size, false, 0, pblock_size); + mi_page_t* page; + void* p = _mi_heap_malloc_zero_ex(heap, size, false, 0, &page); + return mi_ublock_size(p,page,pblock_size); } mi_decl_nodiscard mi_decl_restrict void* mi_umalloc(size_t size, size_t* pblock_size) mi_attr_noexcept { @@ -246,7 +260,9 @@ mi_decl_nodiscard mi_decl_restrict void* mi_umalloc(size_t size, size_t* pblock_ } mi_decl_nodiscard mi_decl_restrict void* mi_uzalloc(size_t size, size_t* pblock_size) mi_attr_noexcept { - return _mi_heap_malloc_zero_ex(mi_prim_get_default_heap(), size, true, 0, pblock_size); + mi_page_t* page; + void* p = _mi_heap_malloc_zero_ex(mi_prim_get_default_heap(), size, true, 0, &page); + return mi_ublock_size(p,page,pblock_size); } mi_decl_nodiscard mi_decl_restrict void* mi_ucalloc(size_t count, size_t size, size_t* pblock_size) mi_attr_noexcept { @@ -275,7 +291,7 @@ void* mi_expand(void* p, size_t newsize) mi_attr_noexcept { #else if (p == NULL) return NULL; const mi_page_t* const page = mi_validate_ptr_page(p,"mi_expand"); - const size_t size = _mi_usable_size(p,page); + const size_t size = _mi_page_usable_size(page,p); if (newsize > size) return NULL; return p; // it fits #endif @@ -299,7 +315,7 @@ void* _mi_heap_realloc_zero(mi_heap_t* heap, void* p, size_t newsize, bool zero, if (pblock_size_post!=NULL) { *pblock_size_post = 0; } return NULL; } - size = _mi_usable_size(p,page); + size = _mi_page_usable_size(page,p); if (pblock_size_pre!=NULL) { *pblock_size_pre = mi_page_block_size(page); } } if mi_unlikely(newsize <= size && newsize >= (size / 2) && newsize > 0) { // note: newsize must be > 0 or otherwise we return NULL for realloc(NULL,0) @@ -311,18 +327,13 @@ void* _mi_heap_realloc_zero(mi_heap_t* heap, void* p, size_t newsize, bool zero, return p; // reallocation still fits and not more than 50% waste } // note: we don't zero allocate upfront so we only zero initialize the expanded part - size_t block_size; // use block_size for zero-ing, issue #763 - void* const newp = mi_heap_umalloc(heap,newsize,&block_size); + mi_page_t* newpage; // use block_size for zero-ing, issue #763 + void* const newp = _mi_heap_malloc_zero_ex(heap,newsize,false /* no zero */,0,&newpage); if mi_likely(newp != NULL) { - if (pblock_size_post!=NULL) { *pblock_size_post = block_size; } + if (pblock_size_post!=NULL) { *pblock_size_post = mi_page_block_size(newpage); } const size_t copy_size = (newsize > size ? size : newsize); const size_t zero_start = _mi_align_down( (copy_size >= sizeof(intptr_t) ? copy_size - sizeof(intptr_t) : 0), sizeof(intptr_t)); // also set last word in the previous allocation to zero to ensure any padding is zero-initialized - #if MI_PADDING || MI_GUARDED - const size_t usable = mi_usable_size(newp); - #else - const size_t usable = block_size; - mi_assert_internal(usable == mi_usable_size(newp)); - #endif + const size_t usable = _mi_page_usable_size(newpage,newp); mi_assert_internal(usable >= newsize); if (zero && usable > zero_start) { _mi_memzero_aligned((uint8_t*)newp + zero_start, usable - zero_start); @@ -745,7 +756,7 @@ static void* mi_block_ptr_set_guarded(mi_block_t* block, size_t obj_size, size_t return p; } -mi_decl_restrict void* _mi_heap_malloc_guarded(mi_heap_t* heap, size_t size, bool zero, size_t* pblock_size) mi_attr_noexcept +mi_decl_restrict void* _mi_heap_malloc_guarded(mi_heap_t* heap, size_t size, bool zero, mi_page_t** ppage) mi_attr_noexcept { // allocate multiple of page size ending in a guard page // ensure minimal alignment requirement? @@ -757,7 +768,7 @@ mi_decl_restrict void* _mi_heap_malloc_guarded(mi_heap_t* heap, size_t size, boo const size_t obj_size = (mi_option_is_enabled(mi_option_guarded_precise) ? size : _mi_align_up(size, MI_MAX_ALIGN_SIZE)); const size_t bsize = _mi_align_up(_mi_align_up(obj_size, MI_MAX_ALIGN_SIZE) + sizeof(mi_block_t), MI_MAX_ALIGN_SIZE); const size_t req_size = _mi_align_up(bsize + os_page_size, os_page_size); - mi_block_t* const block = (mi_block_t*)_mi_malloc_generic(heap, req_size, false /* don't zero */, 0 /* huge_alignment */, pblock_size); + mi_block_t* const block = (mi_block_t*)_mi_malloc_generic(heap, req_size, false /* don't zero */, 0 /* huge_alignment */, ppage); if (block==NULL) return NULL; size_t usable_size = 0; void* const p = mi_block_ptr_set_guarded(block, obj_size, &usable_size); diff --git a/src/free.c b/src/free.c index bfba75326..c2b2e1c66 100644 --- a/src/free.c +++ b/src/free.c @@ -359,8 +359,9 @@ static inline mi_page_t* mi_validate_ptr_page(const void* p, const char* msg) { return page; } -static inline size_t _mi_usable_size(const void* p, const mi_page_t* page) mi_attr_noexcept { +size_t _mi_page_usable_size(const mi_page_t* page, const void* p) mi_attr_noexcept { if mi_unlikely(page==NULL) return 0; + mi_assert_internal(mi_validate_ptr_page(p,"_mi_page_usable_size") == page); if mi_likely(!mi_page_has_aligned(page)) { const mi_block_t* block = mi_validate_block_from_ptr(page,p); return mi_page_usable_size_of(page, block, false /* is guarded */); @@ -373,7 +374,7 @@ static inline size_t _mi_usable_size(const void* p, const mi_page_t* page) mi_at mi_decl_nodiscard size_t mi_usable_size(const void* p) mi_attr_noexcept { const mi_page_t* const page = mi_validate_ptr_page(p,"mi_usable_size"); - return _mi_usable_size(p,page); + return _mi_page_usable_size(page,p); } @@ -385,7 +386,7 @@ void mi_free_size(void* p, size_t size) mi_attr_noexcept { MI_UNUSED_RELEASE(size); #if MI_DEBUG const mi_page_t* const page = mi_validate_ptr_page(p,"mi_free_size"); - const size_t available = _mi_usable_size(p,page); + const size_t available = _mi_page_usable_size(page,p); mi_assert(p == NULL || size <= available || available == 0 /* invalid pointer */ ); #endif mi_free(p); diff --git a/src/page.c b/src/page.c index 046b62086..8ffaf8813 100644 --- a/src/page.c +++ b/src/page.c @@ -978,7 +978,7 @@ static mi_page_t* mi_find_page(mi_heap_t* heap, size_t size, size_t huge_alignme // Note: in debug mode the size includes MI_PADDING_SIZE and might have overflowed. // The `huge_alignment` is normally 0 but is set to a multiple of MI_SLICE_SIZE for // very large requested alignments in which case we use a huge singleton page. -void* _mi_malloc_generic(mi_heap_t* heap, size_t size, bool zero, size_t huge_alignment, size_t* usable) mi_attr_noexcept +void* _mi_malloc_generic(mi_heap_t* heap, size_t size, bool zero, size_t huge_alignment, mi_page_t** ppage) mi_attr_noexcept { mi_assert_internal(heap != NULL); @@ -1024,7 +1024,7 @@ void* _mi_malloc_generic(mi_heap_t* heap, size_t size, bool zero, size_t huge_al mi_assert_internal(mi_page_block_size(page) >= size); // and try again, this time succeeding! (i.e. this should never recurse through _mi_page_malloc) - void* const p = _mi_page_malloc_zero(heap, page, size, zero, usable); + void* const p = _mi_page_malloc_zero(heap, page, size, zero, ppage); mi_assert_internal(p != NULL); // move singleton pages to the full queue From e721902660937f5fd5bd9c39ced2f381f28a4b78 Mon Sep 17 00:00:00 2001 From: daanx Date: Tue, 4 Aug 2026 12:48:37 -0700 Subject: [PATCH 19/27] compile without errors on mingw with ucrt64 --- include/mimalloc/internal.h | 20 ++++++++++++++++---- src/prim/windows/prim.c | 27 ++++++++++++++------------- 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/include/mimalloc/internal.h b/include/mimalloc/internal.h index e08c55778..02a94e490 100644 --- a/include/mimalloc/internal.h +++ b/include/mimalloc/internal.h @@ -45,7 +45,11 @@ terms of the MIT license. A copy of the license can be found in the file #define mi_decl_align(a) __attribute__((aligned(a))) #define mi_decl_noreturn __attribute__((noreturn)) #define mi_decl_weak __attribute__((weak)) +#if defined(__MINGW32__) +#define mi_decl_hidden +#else #define mi_decl_hidden __attribute__((visibility("hidden"))) +#endif #if (__GNUC__ >= 4) || defined(__clang__) #define mi_decl_cold __attribute__((cold)) #else @@ -80,6 +84,14 @@ terms of the MIT license. A copy of the license can be found in the file #define mi_likely(x) (x) #endif +#if (defined(__GNUC__) && (__GNUC__ >= 7)) || defined(__clang__) // includes clang and icc +#define mi_decl_maybe_unused __attribute__((unused)) +#elif __cplusplus >= 201703L // c++17 +#define mi_decl_maybe_unused [[maybe_unused]] +#else +#define mi_decl_maybe_unused +#endif + #ifndef __has_builtin #define __has_builtin(x) 0 #endif @@ -379,7 +391,7 @@ static inline bool _mi_is_power_of_two(uintptr_t x) { // valid alignment values are as posix memalign: static inline bool mi_alignment_is_valid(size_t alignment) { - return ((alignment!=0) && _mi_is_power_of_two(alignment)); + return ((alignment!=0) && _mi_is_power_of_two(alignment)); } // Is a pointer aligned? @@ -715,15 +727,15 @@ static inline bool mi_heap_malloc_use_guarded(mi_heap_t* heap, size_t size) { // no sample heap->guarded_sample_count = count; return false; - } - else { + } + else { // count == 0 const size_t rate = heap->guarded_sample_rate; if (rate == 0) { return false; // don't write to an empty theap } else if (size >= heap->guarded_size_min && size <= heap->guarded_size_max) { - // use guarded allocation + // use guarded allocation heap->guarded_sample_count = rate; // reset return true; } diff --git a/src/prim/windows/prim.c b/src/prim/windows/prim.c index 8fffd4475..9c968431c 100644 --- a/src/prim/windows/prim.c +++ b/src/prim/windows/prim.c @@ -79,7 +79,7 @@ static PGetLargePageMinimum pGetLargePageMinimum = NULL; typedef BOOL (__stdcall *PGetPhysicallyInstalledSystemMemory)( PULONGLONG TotalMemoryInKilobytes ); // Load a library -static HMODULE mi_win_loadlibrary(const TCHAR* library) { +static HMODULE mi_win_loadlibrary(const TCHAR* library) { #if MI_WIN_DESKTOP return LoadLibrary(library); #else @@ -91,7 +91,7 @@ static HMODULE mi_win_loadlibrary(const TCHAR* library) { static HMODULE mi_win_getlibrary(const TCHAR* library, bool* should_free) { #if MI_WIN_DESKTOP // avoid calling LoadLibrary for "kernel32", "ntdll", and "kernelbase" (also to avoid hitting the loader lock) - HMODULE mod = GetModuleHandle(library); + HMODULE mod = GetModuleHandle(library); if (mod!=NULL) { *should_free = false; return mod; @@ -102,7 +102,7 @@ static HMODULE mi_win_getlibrary(const TCHAR* library, bool* should_free) { } static void mi_win_freelibrary(HMODULE mod, bool should_free) { - if (should_free) { + if (should_free) { FreeLibrary(mod); } } @@ -622,7 +622,7 @@ void _mi_prim_out_stderr( const char* msg ) if (hcon==NULL) { AttachConsole(ATTACH_PARENT_PROCESS); // if started from a parent console, try to attach to that hcon = GetStdHandle(STD_ERROR_HANDLE); - } + } CONSOLE_SCREEN_BUFFER_INFO sbi; hconIsConsole = ((hcon != NULL && hcon != INVALID_HANDLE_VALUE) && GetConsoleScreenBufferInfo(hcon, &sbi)); #endif @@ -631,11 +631,11 @@ void _mi_prim_out_stderr( const char* msg ) if (len > 0 && len < UINT32_MAX) { DWORD written = 0; if (hcon != NULL && hcon != INVALID_HANDLE_VALUE) { - #if MI_WIN_DESKTOP + #if MI_WIN_DESKTOP if (hconIsConsole) { WriteConsoleA(hcon, msg, (DWORD)len, &written, NULL); } - else + else #endif { // use direct write in case stderr was redirected @@ -744,6 +744,7 @@ static void mi_win_tls_init(DWORD reason) { } } +mi_decl_maybe_unused static void NTAPI mi_win_main(PVOID module, DWORD reason, LPVOID reserved) { MI_UNUSED(reserved); MI_UNUSED(module); @@ -766,10 +767,10 @@ static void NTAPI mi_win_main(PVOID module, DWORD reason, LPVOID reserved) { both static and dynamic linkage (`MI_WIN_INIT_USE_CRT_TLS`). ------------------------------------------------------------------------- */ #if !defined(MI_WIN_INIT_USE_CRT_TLS) && !defined(MI_WIN_INIT_USE_RAW_DLLMAIN) && !defined(MI_WIN_INIT_USE_TLS_DLLMAIN) && !defined(MI_WIN_INIT_USE_FLS) - #if defined(__GNUC__) && !defined(_MSC_VER) /* mingw */ + #if defined(__MINGW32__) && (MI_MALLOC_VERSION < 30000L) /* mingw on v1/v2*/ #define MI_WIN_INIT_USE_FLS 1 /* needed for v1/v2, see */ #elif defined(__INTEL_LLVM_COMPILER) || defined(__INTEL_COMPILER) - #define MI_WIN_INIT_USE_TLS_DLLMAIN 1 /* needed for Intel ICX, see issue #1268 */ + #define MI_WIN_INIT_USE_TLS_DLLMAIN 1 /* needed for Intel ICX, see issue #1268 */ #else #define MI_WIN_INIT_USE_CRT_TLS 1 /* default */ #endif @@ -886,7 +887,7 @@ static void NTAPI mi_win_main(PVOID module, DWORD reason, LPVOID reserved) { __attribute__((used)) static const void* const mi_tls_used_ref = &_tls_used; // pull in the CRT tls __attribute__((used, section(".CRT$XLB"))) PIMAGE_TLS_CALLBACK _mi_tls_callback_pre = &mi_tls_attach; __attribute__((used, section(".CRT$XLY"))) PIMAGE_TLS_CALLBACK _mi_tls_callback_post = &mi_tls_detach; - __attribute__((used, section(".CRT$XIB"))) mi_crt_callback_t _mi_crt_callback_init = &mi_crt_init; + __attribute__((used, section(".CRT$XIB"))) mi_crt_callback_t _mi_crt_callback_init = &mi_crt_init; #endif #if defined(__cplusplus) @@ -982,7 +983,7 @@ static void NTAPI mi_win_main(PVOID module, DWORD reason, LPVOID reserved) { extern const IMAGE_TLS_DIRECTORY _tls_used; __attribute__((used)) static const void* const mi_tls_used_ref = &_tls_used; // pull in the CRT tls __attribute__((used, section(".CRT$XLB"))) PIMAGE_TLS_CALLBACK _mi_tls_callback_pre = &mi_tls_attach; - __attribute__((used, section(".CRT$XLY"))) PIMAGE_TLS_CALLBACK _mi_tls_callback_post = &mi_tls_detach; + __attribute__((used, section(".CRT$XLY"))) PIMAGE_TLS_CALLBACK _mi_tls_callback_post = &mi_tls_detach; #endif #if defined(__cplusplus) @@ -1056,7 +1057,7 @@ static void NTAPI mi_win_main(PVOID module, DWORD reason, LPVOID reserved) { extern const IMAGE_TLS_DIRECTORY _tls_used; __attribute__((used)) static const void* const mi_tls_used_ref = &_tls_used; // pull in the CRT tls __attribute__((used, section(".CRT$XLB"))) PIMAGE_TLS_CALLBACK _mi_tls_callback_pre = &mi_tls_attach; - __attribute__((used, section(".CRT$XLY"))) PIMAGE_TLS_CALLBACK _mi_tls_callback_post = &mi_tls_detach; + __attribute__((used, section(".CRT$XLY"))) PIMAGE_TLS_CALLBACK _mi_tls_callback_post = &mi_tls_detach; #endif #if defined(__cplusplus) @@ -1097,8 +1098,8 @@ static void NTAPI mi_win_main(PVOID module, DWORD reason, LPVOID reserved) { #pragma comment(linker, "/INCLUDE:__mi_crt_callback_init") #pragma data_seg(".CRT$XIU") mi_crt_callback_t _mi_crt_callback_init[] = { &mi_crt_init }; - #pragma data_seg() - #endif + #pragma data_seg() + #endif #if defined(__cplusplus) } #endif From 32943b8beb5bbc441c77c975aadb8555a7178ef4 Mon Sep 17 00:00:00 2001 From: daanx Date: Tue, 4 Aug 2026 13:50:56 -0700 Subject: [PATCH 20/27] add minject step for dynamic redirect tests on mingw --- CMakeLists.txt | 23 ++++++++++++++++++----- test/test-stress.c | 2 +- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2294ad9de..0fe6a833c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -563,22 +563,27 @@ else() set(mi_install_cmakedir "${CMAKE_INSTALL_LIBDIR}/cmake/mimalloc-${mi_version}") # for cmake package info endif() -set(mi_libname "mimalloc") +set(mi_libsuffix "") if(MI_SECURE) - set(mi_libname "${mi_libname}-secure") + set(mi_libsuffix "${mi_libsuffix}-secure") endif() if(MI_TRACK_VALGRIND) - set(mi_libname "${mi_libname}-valgrind") + set(mi_libsuffix "${mi_libsuffix}-valgrind") endif() if(MI_TRACK_ASAN) - set(mi_libname "${mi_libname}-asan") + set(mi_libsuffix "${mi_libsuffix}-asan") endif() string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_LC) list(APPEND mi_defines "MI_CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE_LC}") #todo: multi-config project needs $ ? if(CMAKE_BUILD_TYPE_LC MATCHES "^(release|relwithdebinfo|minsizerel|none)$") list(APPEND mi_defines MI_BUILD_RELEASE) else() - set(mi_libname "${mi_libname}-${CMAKE_BUILD_TYPE_LC}") #append build type (e.g. -debug) if not a release version + set(mi_libsuffix "${mi_libsuffix}-${CMAKE_BUILD_TYPE_LC}") #append build type (e.g. -debug) if not a release version +endif() + +set(mi_libname "mimalloc") +if(NOT mi_libsuffix STREQUAL "") +set(mi_libname "${mi_libname}-${mi_libsuffix}") endif() if(MI_BUILD_SHARED) @@ -643,16 +648,20 @@ if(MI_BUILD_SHARED) # On windows, link and copy the mimalloc redirection dll too. if(CMAKE_GENERATOR_PLATFORM STREQUAL "arm64ec") set(MIMALLOC_REDIRECT_SUFFIX "-arm64ec") + set(MINJECT_SUFFIX "") elseif(MI_ARCH STREQUAL "x64") set(MIMALLOC_REDIRECT_SUFFIX "") + set(MINJECT_SUFFIX "") if(CMAKE_SYSTEM_PROCESSOR STREQUAL "ARM64") message(STATUS "Note: x64 code emulated on Windows for arm64 should use an arm64ec build of 'mimalloc.dll'") message(STATUS " together with 'mimalloc-redirect-arm64ec.dll'. See the 'bin\\readme.md' for more information.") endif() elseif(MI_ARCH STREQUAL "x86") set(MIMALLOC_REDIRECT_SUFFIX "32") + set(MINJECT_SUFFIX "32") else() set(MIMALLOC_REDIRECT_SUFFIX "-${MI_ARCH}") # -arm64 etc. + set(MINJECT_SUFFIX "-${MI_ARCH}") endif() target_link_libraries(mimalloc PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/bin/mimalloc-redirect${MIMALLOC_REDIRECT_SUFFIX}.lib) # the DLL import library @@ -774,6 +783,10 @@ if (MI_BUILD_TESTS) if(WIN32) target_compile_definitions(mimalloc-test-stress-dynamic PRIVATE "MI_LINK_VERSION=1") # link mi_version target_link_libraries(mimalloc-test-stress-dynamic PRIVATE mimalloc ${mi_libraries}) # link mi_version + if(MINGW) + # use minject to make sure mimalloc is the first dll loaded + add_custom_command(TARGET mimalloc-test-stress-dynamic POST_BUILD COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/bin/minject${MINJECT_SUFFIX} --postfix=${mi_libsuffix} -f -i $) + endif() add_test(NAME test-stress-dynamic COMMAND ${CMAKE_COMMAND} -E env MIMALLOC_VERBOSE=1 $) else() target_link_libraries(mimalloc-test-stress-dynamic PRIVATE ${mi_libraries}) # pthreads, issue 1158 diff --git a/test/test-stress.c b/test/test-stress.c index 1abe56d2c..185945dc8 100644 --- a/test/test-stress.c +++ b/test/test-stress.c @@ -338,7 +338,7 @@ int main(int argc, char** argv) { } #endif mi_collect(true); - mi_stats_print(NULL); + mi_stats_print(NULL); #endif //bench_end_program(); return 0; From 1cf8869195a94ebd2f4f2609813b1ba2e6d55265 Mon Sep 17 00:00:00 2001 From: daanx Date: Tue, 4 Aug 2026 14:31:02 -0700 Subject: [PATCH 21/27] use __MINGW32__ to detect mingw (instead of __GNUC__) -- use default win init (instead of MI_WIN_INIT_USE_FLS) on mingw (see also PR #1349) --- CMakeLists.txt | 5 +++-- src/prim/windows/prim.c | 10 ++++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0fe6a833c..11c774d3f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -784,8 +784,9 @@ if (MI_BUILD_TESTS) target_compile_definitions(mimalloc-test-stress-dynamic PRIVATE "MI_LINK_VERSION=1") # link mi_version target_link_libraries(mimalloc-test-stress-dynamic PRIVATE mimalloc ${mi_libraries}) # link mi_version if(MINGW) - # use minject to make sure mimalloc is the first dll loaded - add_custom_command(TARGET mimalloc-test-stress-dynamic POST_BUILD COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/bin/minject${MINJECT_SUFFIX} --postfix=${mi_libsuffix} -f -i $) + # mingw always links mimalloc after system libraries. + # post-process with `minject` to make sure mimalloc is the first dll in the load order. + add_custom_command(TARGET mimalloc-test-stress-dynamic POST_BUILD COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/bin/minject${MINJECT_SUFFIX} --postfix=${mi_libsuffix} -f -i $) endif() add_test(NAME test-stress-dynamic COMMAND ${CMAKE_COMMAND} -E env MIMALLOC_VERBOSE=1 $) else() diff --git a/src/prim/windows/prim.c b/src/prim/windows/prim.c index 9c968431c..d9e5243b7 100644 --- a/src/prim/windows/prim.c +++ b/src/prim/windows/prim.c @@ -767,9 +767,7 @@ static void NTAPI mi_win_main(PVOID module, DWORD reason, LPVOID reserved) { both static and dynamic linkage (`MI_WIN_INIT_USE_CRT_TLS`). ------------------------------------------------------------------------- */ #if !defined(MI_WIN_INIT_USE_CRT_TLS) && !defined(MI_WIN_INIT_USE_RAW_DLLMAIN) && !defined(MI_WIN_INIT_USE_TLS_DLLMAIN) && !defined(MI_WIN_INIT_USE_FLS) - #if defined(__MINGW32__) && (MI_MALLOC_VERSION < 30000L) /* mingw on v1/v2*/ - #define MI_WIN_INIT_USE_FLS 1 /* needed for v1/v2, see */ - #elif defined(__INTEL_LLVM_COMPILER) || defined(__INTEL_COMPILER) + #if defined(__INTEL_LLVM_COMPILER) || defined(__INTEL_COMPILER) #define MI_WIN_INIT_USE_TLS_DLLMAIN 1 /* needed for Intel ICX, see issue #1268 */ #else #define MI_WIN_INIT_USE_CRT_TLS 1 /* default */ @@ -882,7 +880,7 @@ static void NTAPI mi_win_main(PVOID module, DWORD reason, LPVOID reserved) { #pragma data_seg(".CRT$XIB") mi_crt_callback_t _mi_crt_callback_init[] = { &mi_crt_init }; #pragma data_seg() - #elif defined(__GNUC__) // mingw + #elif defined(__MINGW32__) extern const IMAGE_TLS_DIRECTORY _tls_used; __attribute__((used)) static const void* const mi_tls_used_ref = &_tls_used; // pull in the CRT tls __attribute__((used, section(".CRT$XLB"))) PIMAGE_TLS_CALLBACK _mi_tls_callback_pre = &mi_tls_attach; @@ -979,7 +977,7 @@ static void NTAPI mi_win_main(PVOID module, DWORD reason, LPVOID reserved) { #pragma data_seg(".CRT$XLY") PIMAGE_TLS_CALLBACK _mi_tls_callback_post[] = { &mi_tls_detach }; #pragma data_seg() - #elif defined(__GNUC__) // mingw + #elif defined(__MINGW32__) extern const IMAGE_TLS_DIRECTORY _tls_used; __attribute__((used)) static const void* const mi_tls_used_ref = &_tls_used; // pull in the CRT tls __attribute__((used, section(".CRT$XLB"))) PIMAGE_TLS_CALLBACK _mi_tls_callback_pre = &mi_tls_attach; @@ -1053,7 +1051,7 @@ static void NTAPI mi_win_main(PVOID module, DWORD reason, LPVOID reserved) { #pragma data_seg(".CRT$XLY") PIMAGE_TLS_CALLBACK _mi_tls_callback_post[] = { &mi_win_main_detach }; #pragma data_seg() - #elif defined(__GNUC__) // mingw + #elif defined(__MINGW32__) extern const IMAGE_TLS_DIRECTORY _tls_used; __attribute__((used)) static const void* const mi_tls_used_ref = &_tls_used; // pull in the CRT tls __attribute__((used, section(".CRT$XLB"))) PIMAGE_TLS_CALLBACK _mi_tls_callback_pre = &mi_tls_attach; From e08587607a3d61aaa3e58b90ae0c1e651e1a1ad5 Mon Sep 17 00:00:00 2001 From: daanx Date: Tue, 4 Aug 2026 14:50:25 -0700 Subject: [PATCH 22/27] add mingw UCRT64 detection (see also PR #1349) --- CMakeLists.txt | 8 +++++++- src/prim/windows/prim.c | 5 ++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 11c774d3f..b9b78df64 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -406,6 +406,12 @@ if(MI_LIBC_MUSL) list(APPEND mi_defines MI_LIBC_MUSL=1) endif() +if(ENV{MSYSTEM} STREQUAL "UCRT64") + message(STATUS "Detected MSYS2 UCRT64 environment") + set(MI_MINGW_UCRT64 "ON") + list(APPEND mi_defines MI_WINGW_UCRT64=1) +endif() + if(MI_WIN_USE_FIXED_TLS) message(STATUS "Use fixed TLS slot on Windows to avoid extra tests in the malloc fast path (MI_WIN_USE_FIXED_TLS=ON)") list(APPEND mi_defines MI_WIN_USE_FIXED_TLS=1) @@ -783,7 +789,7 @@ if (MI_BUILD_TESTS) if(WIN32) target_compile_definitions(mimalloc-test-stress-dynamic PRIVATE "MI_LINK_VERSION=1") # link mi_version target_link_libraries(mimalloc-test-stress-dynamic PRIVATE mimalloc ${mi_libraries}) # link mi_version - if(MINGW) + if(MINGW AND MI_MINGW_UCRT64) # mingw always links mimalloc after system libraries. # post-process with `minject` to make sure mimalloc is the first dll in the load order. add_custom_command(TARGET mimalloc-test-stress-dynamic POST_BUILD COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/bin/minject${MINJECT_SUFFIX} --postfix=${mi_libsuffix} -f -i $) diff --git a/src/prim/windows/prim.c b/src/prim/windows/prim.c index d9e5243b7..d63412cd1 100644 --- a/src/prim/windows/prim.c +++ b/src/prim/windows/prim.c @@ -775,7 +775,10 @@ static void NTAPI mi_win_main(PVOID module, DWORD reason, LPVOID reserved) { #endif #if defined(MI_WIN_INIT_USE_CRT_TLS) - #define MI_PRIM_HAS_PROCESS_ATTACH 1 + #if !defined(__MINGW32__) || !defined(MI_MINGW_UCRT64) // on mingw without UCRT use the constructor attribute (in `src/prim/prim.c`) + #define MI_PRIM_HAS_PROCESS_ATTACH 1 + #endif + // nothing to do since `_mi_thread_done` is handled through the DLL_THREAD_DETACH event. void _mi_prim_thread_init_auto_done(void) {} void _mi_prim_thread_done_auto_done(void) {} From e68ba5a04495de89cb719cf21dfa56737c133cbf Mon Sep 17 00:00:00 2001 From: daanx Date: Tue, 4 Aug 2026 15:59:10 -0700 Subject: [PATCH 23/27] fix msystem ucrt64 detection --- CMakeLists.txt | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b9b78df64..b2a65b407 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -406,7 +406,7 @@ if(MI_LIBC_MUSL) list(APPEND mi_defines MI_LIBC_MUSL=1) endif() -if(ENV{MSYSTEM} STREQUAL "UCRT64") +if("$ENV{MSYSTEM}" STREQUAL "UCRT64") message(STATUS "Detected MSYS2 UCRT64 environment") set(MI_MINGW_UCRT64 "ON") list(APPEND mi_defines MI_WINGW_UCRT64=1) @@ -569,23 +569,24 @@ else() set(mi_install_cmakedir "${CMAKE_INSTALL_LIBDIR}/cmake/mimalloc-${mi_version}") # for cmake package info endif() -set(mi_libsuffix "") +set(mi_libname "mimalloc") if(MI_SECURE) - set(mi_libsuffix "${mi_libsuffix}-secure") + set(mi_libname "${mi_libname}-secure") endif() if(MI_TRACK_VALGRIND) - set(mi_libsuffix "${mi_libsuffix}-valgrind") + set(mi_libname "${mi_libname}-valgrind") endif() if(MI_TRACK_ASAN) - set(mi_libsuffix "${mi_libsuffix}-asan") + set(mi_libname "${mi_libname}-asan") endif() string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_LC) list(APPEND mi_defines "MI_CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE_LC}") #todo: multi-config project needs $ ? if(CMAKE_BUILD_TYPE_LC MATCHES "^(release|relwithdebinfo|minsizerel|none)$") list(APPEND mi_defines MI_BUILD_RELEASE) else() - set(mi_libsuffix "${mi_libsuffix}-${CMAKE_BUILD_TYPE_LC}") #append build type (e.g. -debug) if not a release version + set(mi_libname "${mi_libname}-${CMAKE_BUILD_TYPE_LC}") #append build type (e.g. -debug) if not a release version endif() +string(REGEX REPLACE "^mimalloc[-]?" "" mi_libsuffix "${mi_libname}") set(mi_libname "mimalloc") if(NOT mi_libsuffix STREQUAL "") From 66141063edff47a86433b321d8af06daff166c63 Mon Sep 17 00:00:00 2001 From: daanx Date: Tue, 4 Aug 2026 16:00:44 -0700 Subject: [PATCH 24/27] add msys2 mingw ucrt64 test --- .github/workflows/test.yaml | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 58fd50bd5..24e2afbc7 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -22,6 +22,9 @@ jobs: exclude: - os: macos-14 tests: extra + include: + - os: windows-latest + tests: mingw-ucrt64 steps: # Checkout: https://github.com/actions/checkout @@ -161,3 +164,33 @@ jobs: ctest --test-dir out/release-guarded --verbose --timeout 240 -C Release env: MIMALLOC_GUARDED_SAMPLE_RATE: 100 + + # MSYS2 mingw64 UCRT64 + - name: mingw-ucrt64 x64, Setup + if: matrix.tests == 'mingw-ucrt64' + uses: msys2/setup-msys2@v2 + with: + msystem: UCRT64 + install: | + mingw-w64-ucrt-x86_64-gcc + mingw-w64-ucrt-x86_64-git + mingw-w64-ucrt-x86_64-make + mingw-w64-ucrt-x86_64-cmake + + - name: mingw-ucrt64 x64, Debug + if: matrix.tests == 'mingw-ucrt64' + shell: msys2 {0} + run: | + env + cmake . -B out/debug-mingw-ucrt64-x64 -DCMAKE_BUILD_TYPE=Debug -DMI_DEBUG_FULL=ON + cmake --build out/debug-mingw-ucrt64-x64 --parallel 4 --config Debug + ctest --test-dir out/debug-mingw-ucrt64-x64 --verbose --timeout 240 -C Debug + + - name: mingw-ucrt64 x64, Release + if: matrix.tests == 'mingw-ucrt64' + shell: msys2 {0} + run: | + cmake . -B out/release-mingw-ucrt64-x64 -DCMAKE_BUILD_TYPE=Release -DMI_OPT_ARCH=ON + cmake --build out/release-mingw-ucrt64-x64 --parallel 4 --config Release + ctest --test-dir out/release-mingw-ucrt64-x64 --verbose --timeout 240 -C Release + From d6c11355827ae6ec76bbb2e783f5e496ee39f0ae Mon Sep 17 00:00:00 2001 From: daanx Date: Wed, 5 Aug 2026 10:34:34 -0700 Subject: [PATCH 25/27] bump version to v1.9.15 --- .github/workflows/release.yaml | 4 ++-- cmake/mimalloc-config-version.cmake | 2 +- include/mimalloc.h | 2 +- readme.md | 9 ++++++--- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 3855e6281..dd60b12e3 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -9,7 +9,7 @@ permissions: contents: write env: - RELEASE: Release v3.4.4 + RELEASE: Release v3.4.5 FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true name: Release @@ -19,7 +19,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - branch: [v3.4.4, v2.4.4, v1.9.14] # [dev,dev2,dev3] + branch: [v3.4.5, v2.4.5, v1.9.15] # [dev,dev2,dev3] # we build on the oldest ubuntu version for better binary compatibility. os: [windows-latest, macOS-latest, macos-15-intel, ubuntu-22.04, ubuntu-22.04-arm] diff --git a/cmake/mimalloc-config-version.cmake b/cmake/mimalloc-config-version.cmake index 0d9f94b50..e153b575a 100644 --- a/cmake/mimalloc-config-version.cmake +++ b/cmake/mimalloc-config-version.cmake @@ -1,6 +1,6 @@ set(mi_version_major 1) set(mi_version_minor 9) -set(mi_version_patch 14) +set(mi_version_patch 15) set(mi_version ${mi_version_major}.${mi_version_minor}) set(PACKAGE_VERSION ${mi_version}) diff --git a/include/mimalloc.h b/include/mimalloc.h index 8bab05b1b..964d33e1d 100644 --- a/include/mimalloc.h +++ b/include/mimalloc.h @@ -8,7 +8,7 @@ terms of the MIT license. A copy of the license can be found in the file #ifndef MIMALLOC_H #define MIMALLOC_H -#define MI_MALLOC_VERSION 10914 // major + 2 digits minor + 2 digits patch +#define MI_MALLOC_VERSION 10915 // major + 2 digits minor + 2 digits patch // ------------------------------------------------------ // Compiler specific attributes diff --git a/readme.md b/readme.md index 5173b0018..a53a2d37f 100644 --- a/readme.md +++ b/readme.md @@ -15,9 +15,9 @@ is a general purpose allocator with excellent [performance](#performance) charac Initially developed by Daan Leijen for the runtime systems of the [Koka](https://koka-lang.github.io) and [Lean](https://github.com/leanprover/lean) languages. -Latest release : `v3.4.4` (2026-08-01) recommended. -Latest v2 release: `v2.4.4` (2026-08-01) stable. -Latest v1 release: `v1.9.14` (2026-08-01) legacy. +Latest release : `v3.4.5` (2026-08-05) recommended. +Latest v2 release: `v2.4.5` (2026-08-05) stable. +Latest v1 release: `v1.9.15` (2026-08-05) legacy. mimalloc is a drop-in replacement for `malloc` and can be used in other programs without code changes, for example, on dynamically linked ELF-based systems (Linux, BSD, etc.) you can use it as: @@ -89,6 +89,9 @@ New development is mostly on v3, while v1 and v2 are maintained with security an __Send PR's against this version if possible.__ ### Releases +* 2026-08-05, `v1.9.15`, `v2.4.5`, `v3.4.5`: Fix compilation with xmalloc (#1353), make the build deterministic (#1355), + mi_zalloc_aligned fix (#763), improve support for mingw (ucrt64), fix fputs fallback on windows (#1354), (v3): use proper + lock backoff for first-class heap deletion. * 2026-08-01, `v1.9.14`, `v2.4.4`, `v3.4.4`: various bug and security fixes through Opus 5 LLM audit (issue #1271, by @Zoxc). (v3): use pthreads by default on macOS (issue #1333, issue #1327), fix glibc 2.44 crash (issue #1341), fix alignment check for realloc_aligned. (v1,v2,v3): Add initial mingw support, set `errno` on allocation errors, improved double-free checks and size checks in secure mode, enable From 6c84ab60d06e1e448fab8f2633765d787cc280dd Mon Sep 17 00:00:00 2001 From: daanx Date: Wed, 5 Aug 2026 10:40:35 -0700 Subject: [PATCH 26/27] update readme --- readme.md | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/readme.md b/readme.md index a53a2d37f..f67a9448b 100644 --- a/readme.md +++ b/readme.md @@ -89,6 +89,7 @@ New development is mostly on v3, while v1 and v2 are maintained with security an __Send PR's against this version if possible.__ ### Releases + * 2026-08-05, `v1.9.15`, `v2.4.5`, `v3.4.5`: Fix compilation with xmalloc (#1353), make the build deterministic (#1355), mi_zalloc_aligned fix (#763), improve support for mingw (ucrt64), fix fputs fallback on windows (#1354), (v3): use proper lock backoff for first-class heap deletion. @@ -130,24 +131,6 @@ New development is mostly on v3, while v1 and v2 are maintained with security an v3 uses faster TLS access on Windows, and has improved performance for `mi_calloc` and aligned allocations. Fixed rare race condition on older v3, fixed potential buffer overflow in debug statistics, add API for returning allocated sizes on allocation and free. -* 2025-06-09, `v1.9.4`, `v2.2.4`, `v3.1.4` (beta) : Some important bug fixes, including a case where OS memory - was not always fully released. Improved v3 performance, build on XBox, fix build on Android, support interpose - for older macOS versions, use MADV_FREE_REUSABLE on macOS, always check commit success, better support for Windows - fixed TLS offset, etc. -* 2025-03-28, `v1.9.3`, `v2.2.3`, `v3.0.3` (beta) : Various small bug and build fixes, including: - fix arm32 pre v7 builds, fix mingw build, get runtime statistics, improve statistic commit counts, - fix execution on non BMI1 x64 systems. -* 2025-03-06, `v1.9.2`, `v2.2.2`, `v3.0.2-beta`: Various small bug and build fixes. - Add `mi_options_print`, `mi_arenas_print`, and the experimental `mi_stat_get` and `mi_stat_get_json`. - Add `mi_thread_set_in_threadpool` and `mi_heap_set_numa_affinity` (v3 only). Add vcpkg portfile. - Upgrade mimalloc-redirect to v1.3.2. `MI_OPT_ARCH` is off by default now but still assumes armv8.1-a on arm64 - for fast atomic operations. Add QNX support. -* 2025-01-03, `v1.8.9`, `v2.1.9`, `v3.0.1-alpha`: Interim release. Support Windows arm64. New [guarded](#guarded) build that can place OS - guard pages behind objects to catch buffer overflows as they occur. - Many small fixes: build on Windows arm64, cygwin, riscV, and dragonfly; fix Windows static library initialization to account for - thread local destructors (in Rust/C++); macOS tag change; macOS TLS slot fix; improve stats; - consistent `mimalloc.dll` on Windows (instead of `mimalloc-override.dll`); fix mimalloc-redirect on Win11 H2; - add 0-byte to canary; upstream CPython fixes; reduce .bss size; allow fixed TLS slot on Windows for improved performance. * [Older release notes](#older-release-notes) @@ -931,6 +914,25 @@ provided by the bot. You will only need to do this once across all repos using o # Older Release Notes +* 2025-06-09, `v1.9.4`, `v2.2.4`, `v3.1.4` (beta) : Some important bug fixes, including a case where OS memory + was not always fully released. Improved v3 performance, build on XBox, fix build on Android, support interpose + for older macOS versions, use MADV_FREE_REUSABLE on macOS, always check commit success, better support for Windows + fixed TLS offset, etc. +* 2025-03-28, `v1.9.3`, `v2.2.3`, `v3.0.3` (beta) : Various small bug and build fixes, including: + fix arm32 pre v7 builds, fix mingw build, get runtime statistics, improve statistic commit counts, + fix execution on non BMI1 x64 systems. +* 2025-03-06, `v1.9.2`, `v2.2.2`, `v3.0.2-beta`: Various small bug and build fixes. + Add `mi_options_print`, `mi_arenas_print`, and the experimental `mi_stat_get` and `mi_stat_get_json`. + Add `mi_thread_set_in_threadpool` and `mi_heap_set_numa_affinity` (v3 only). Add vcpkg portfile. + Upgrade mimalloc-redirect to v1.3.2. `MI_OPT_ARCH` is off by default now but still assumes armv8.1-a on arm64 + for fast atomic operations. Add QNX support. +* 2025-01-03, `v1.8.9`, `v2.1.9`, `v3.0.1-alpha`: Interim release. Support Windows arm64. New [guarded](#guarded) build that can place OS + guard pages behind objects to catch buffer overflows as they occur. + Many small fixes: build on Windows arm64, cygwin, riscV, and dragonfly; fix Windows static library initialization to account for + thread local destructors (in Rust/C++); macOS tag change; macOS TLS slot fix; improve stats; + consistent `mimalloc.dll` on Windows (instead of `mimalloc-override.dll`); fix mimalloc-redirect on Win11 H2; + add 0-byte to canary; upstream CPython fixes; reduce .bss size; allow fixed TLS slot on Windows for improved performance. + * 2024-05-21, `v1.8.7`, `v2.1.7`: Fix build issues on less common platforms. Started upstreaming patches from the CPython [integration](https://github.com/python/cpython/issues/113141#issuecomment-2119255217). Upstream `vcpkg` patches. * 2024-05-13, `v1.8.6`, `v2.1.6`: Fix build errors on various (older) platforms. Refactored aligned allocation. From e6d8e904b22714f9c41a9b5b1106c94dcf900eb2 Mon Sep 17 00:00:00 2001 From: daanx Date: Wed, 5 Aug 2026 10:55:53 -0700 Subject: [PATCH 27/27] update readme --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index f67a9448b..2395b2c4e 100644 --- a/readme.md +++ b/readme.md @@ -92,7 +92,7 @@ New development is mostly on v3, while v1 and v2 are maintained with security an * 2026-08-05, `v1.9.15`, `v2.4.5`, `v3.4.5`: Fix compilation with xmalloc (#1353), make the build deterministic (#1355), mi_zalloc_aligned fix (#763), improve support for mingw (ucrt64), fix fputs fallback on windows (#1354), (v3): use proper - lock backoff for first-class heap deletion. + lock backoff for first-class heap deletion, improved riscv64 codegen. * 2026-08-01, `v1.9.14`, `v2.4.4`, `v3.4.4`: various bug and security fixes through Opus 5 LLM audit (issue #1271, by @Zoxc). (v3): use pthreads by default on macOS (issue #1333, issue #1327), fix glibc 2.44 crash (issue #1341), fix alignment check for realloc_aligned. (v1,v2,v3): Add initial mingw support, set `errno` on allocation errors, improved double-free checks and size checks in secure mode, enable