From 00648411ea102da271534a4cf15d3953386719e5 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Mon, 15 Apr 2024 19:11:06 +0200 Subject: [PATCH] MEM: support overaligned types in MEM_cnew_array This is similar to the recent change for `MEM_cnew`: 60a3d85b888c70330b7f9e691d8e82d66561d0f1. A new function called `MEM_calloc_arrayN_aligned` is added that is used by both `cnew` functions now. Pull Request: https://projects.blender.org/blender/blender/pulls/120632 --- intern/guardedalloc/MEM_guardedalloc.h | 22 ++++++++------ intern/guardedalloc/intern/mallocn.cc | 6 ++++ .../intern/mallocn_guarded_impl.cc | 30 +++++++++++++++++++ intern/guardedalloc/intern/mallocn_intern.h | 7 +++++ .../intern/mallocn_lockfree_impl.cc | 30 +++++++++++++++++++ 5 files changed, 86 insertions(+), 9 deletions(-) diff --git a/intern/guardedalloc/MEM_guardedalloc.h b/intern/guardedalloc/MEM_guardedalloc.h index 6fb9021fac1..08c187c55fa 100644 --- a/intern/guardedalloc/MEM_guardedalloc.h +++ b/intern/guardedalloc/MEM_guardedalloc.h @@ -134,6 +134,16 @@ extern void *(*MEM_mallocN_aligned)(size_t len, const char *str) /* ATTR_MALLOC */ ATTR_WARN_UNUSED_RESULT ATTR_ALLOC_SIZE(1) ATTR_NONNULL(3); +/** + * Allocate an aligned block of memory that is initialized with zeros. + */ +extern void *(*MEM_calloc_arrayN_aligned)( + size_t len, + size_t size, + size_t alignment, + const char *str) /* ATTR_MALLOC */ ATTR_WARN_UNUSED_RESULT ATTR_ALLOC_SIZE(1, 2) + ATTR_NONNULL(4); + /** * Print a list of the names and sizes of all allocated memory * blocks. as a python dict for easy investigation. @@ -311,14 +321,7 @@ template inline void MEM_delete(const T *ptr) template inline T *MEM_cnew(const char *allocation_name) { static_assert(std::is_trivial_v, "For non-trivial types, MEM_new should be used."); - if (alignof(T) <= MEM_MIN_CPP_ALIGNMENT) { - return static_cast(MEM_callocN(sizeof(T), allocation_name)); - } - void *ptr = MEM_mallocN_aligned(sizeof(T), alignof(T), allocation_name); - if (ptr) { - memset(ptr, 0, sizeof(T)); - } - return static_cast(ptr); + return static_cast(MEM_calloc_arrayN_aligned(1, sizeof(T), alignof(T), allocation_name)); } /** @@ -327,7 +330,8 @@ template inline T *MEM_cnew(const char *allocation_name) template inline T *MEM_cnew_array(const size_t length, const char *allocation_name) { static_assert(std::is_trivial_v, "For non-trivial types, MEM_new should be used."); - return static_cast(MEM_calloc_arrayN(length, sizeof(T), allocation_name)); + return static_cast( + MEM_calloc_arrayN_aligned(length, sizeof(T), alignof(T), allocation_name)); } /** diff --git a/intern/guardedalloc/intern/mallocn.cc b/intern/guardedalloc/intern/mallocn.cc index 256d78e21db..81afeb88845 100644 --- a/intern/guardedalloc/intern/mallocn.cc +++ b/intern/guardedalloc/intern/mallocn.cc @@ -38,6 +38,10 @@ void *(*MEM_malloc_arrayN)(size_t len, size_t size, const char *str) = MEM_lockf void *(*MEM_mallocN_aligned)(size_t len, size_t alignment, const char *str) = MEM_lockfree_mallocN_aligned; +void *(*MEM_calloc_arrayN_aligned)(size_t len, + size_t size, + size_t alignment, + const char *str) = MEM_lockfree_calloc_arrayN_aligned; void (*MEM_printmemlist_pydict)(void) = MEM_lockfree_printmemlist_pydict; void (*MEM_printmemlist)(void) = MEM_lockfree_printmemlist; void (*MEM_callbackmemlist)(void (*func)(void *)) = MEM_lockfree_callbackmemlist; @@ -120,6 +124,7 @@ void MEM_use_lockfree_allocator(void) MEM_mallocN = MEM_lockfree_mallocN; MEM_malloc_arrayN = MEM_lockfree_malloc_arrayN; MEM_mallocN_aligned = MEM_lockfree_mallocN_aligned; + MEM_calloc_arrayN_aligned = MEM_lockfree_calloc_arrayN_aligned; MEM_printmemlist_pydict = MEM_lockfree_printmemlist_pydict; MEM_printmemlist = MEM_lockfree_printmemlist; MEM_callbackmemlist = MEM_lockfree_callbackmemlist; @@ -154,6 +159,7 @@ void MEM_use_guarded_allocator(void) MEM_mallocN = MEM_guarded_mallocN; MEM_malloc_arrayN = MEM_guarded_malloc_arrayN; MEM_mallocN_aligned = MEM_guarded_mallocN_aligned; + MEM_calloc_arrayN_aligned = MEM_guarded_calloc_arrayN_aligned; MEM_printmemlist_pydict = MEM_guarded_printmemlist_pydict; MEM_printmemlist = MEM_guarded_printmemlist; MEM_callbackmemlist = MEM_guarded_callbackmemlist; diff --git a/intern/guardedalloc/intern/mallocn_guarded_impl.cc b/intern/guardedalloc/intern/mallocn_guarded_impl.cc index 593fb3718fe..0256675b840 100644 --- a/intern/guardedalloc/intern/mallocn_guarded_impl.cc +++ b/intern/guardedalloc/intern/mallocn_guarded_impl.cc @@ -619,6 +619,36 @@ void *MEM_guarded_calloc_arrayN(size_t len, size_t size, const char *str) return MEM_guarded_callocN(total_size, str); } +void *MEM_guarded_calloc_arrayN_aligned(const size_t len, + const size_t size, + const size_t alignment, + const char *str) +{ + size_t bytes_num; + if (UNLIKELY(!MEM_size_safe_multiply(len, size, &bytes_num))) { + print_error( + "Calloc array aborted due to integer overflow: " + "len=" SIZET_FORMAT "x" SIZET_FORMAT " in %s, total " SIZET_FORMAT "\n", + SIZET_ARG(len), + SIZET_ARG(size), + str, + mem_in_use); + abort(); + return nullptr; + } + if (alignment <= MEM_MIN_CPP_ALIGNMENT) { + return MEM_callocN(bytes_num, str); + } + /* There is no lower level #calloc with an alignment parameter, so we have to fallback to using + * #memset unfortunately. */ + void *ptr = MEM_mallocN_aligned(bytes_num, alignment, str); + if (!ptr) { + return nullptr; + } + memset(ptr, 0, bytes_num); + return ptr; +} + /* Memory statistics print */ typedef struct MemPrintBlock { const char *name; diff --git a/intern/guardedalloc/intern/mallocn_intern.h b/intern/guardedalloc/intern/mallocn_intern.h index 0f98fad1351..32e31aafb29 100644 --- a/intern/guardedalloc/intern/mallocn_intern.h +++ b/intern/guardedalloc/intern/mallocn_intern.h @@ -155,6 +155,11 @@ void *MEM_lockfree_mallocN_aligned(size_t len, size_t alignment, const char *str) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_ALLOC_SIZE(1) ATTR_NONNULL(3); +void *MEM_lockfree_calloc_arrayN_aligned(size_t len, + size_t size, + size_t alignment, + const char *str) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT + ATTR_ALLOC_SIZE(1, 2) ATTR_NONNULL(4); void MEM_lockfree_printmemlist_pydict(void); void MEM_lockfree_printmemlist(void); void MEM_lockfree_callbackmemlist(void (*func)(void *)); @@ -202,6 +207,8 @@ void *MEM_guarded_mallocN_aligned(size_t len, size_t alignment, const char *str) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_ALLOC_SIZE(1) ATTR_NONNULL(3); +void *MEM_guarded_calloc_arrayN_aligned(size_t len, size_t size, size_t alignment, const char *str) + ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_ALLOC_SIZE(1, 2) ATTR_NONNULL(4); void MEM_guarded_printmemlist_pydict(void); void MEM_guarded_printmemlist(void); void MEM_guarded_callbackmemlist(void (*func)(void *)); diff --git a/intern/guardedalloc/intern/mallocn_lockfree_impl.cc b/intern/guardedalloc/intern/mallocn_lockfree_impl.cc index ba7ca885d19..571d08c4f97 100644 --- a/intern/guardedalloc/intern/mallocn_lockfree_impl.cc +++ b/intern/guardedalloc/intern/mallocn_lockfree_impl.cc @@ -371,6 +371,36 @@ void *MEM_lockfree_mallocN_aligned(size_t len, size_t alignment, const char *str return nullptr; } +void *MEM_lockfree_calloc_arrayN_aligned(const size_t len, + const size_t size, + const size_t alignment, + const char *str) +{ + size_t bytes_num; + if (UNLIKELY(!MEM_size_safe_multiply(len, size, &bytes_num))) { + print_error( + "Calloc array aborted due to integer overflow: " + "len=" SIZET_FORMAT "x" SIZET_FORMAT " in %s, total " SIZET_FORMAT "\n", + SIZET_ARG(len), + SIZET_ARG(size), + str, + memory_usage_current()); + abort(); + return nullptr; + } + if (alignment <= MEM_MIN_CPP_ALIGNMENT) { + return MEM_callocN(bytes_num, str); + } + /* There is no lower level #calloc with an alignment parameter, so we have to fallback to using + * #memset unfortunately. */ + void *ptr = MEM_mallocN_aligned(bytes_num, alignment, str); + if (!ptr) { + return nullptr; + } + memset(ptr, 0, bytes_num); + return ptr; +} + void MEM_lockfree_printmemlist_pydict() {} void MEM_lockfree_printmemlist() {}