diff libcxxabi/src/fallback_malloc.cpp @ 236:c4bab56944e8 llvm-original

LLVM 16
author kono
date Wed, 09 Nov 2022 17:45:10 +0900
parents 79ff65ed7e25
children 1f2b6ac9f198
line wrap: on
line diff
--- a/libcxxabi/src/fallback_malloc.cpp	Wed Jul 21 10:27:27 2021 +0900
+++ b/libcxxabi/src/fallback_malloc.cpp	Wed Nov 09 17:45:10 2022 +0900
@@ -1,4 +1,4 @@
-//===------------------------ fallback_malloc.cpp -------------------------===//
+//===----------------------------------------------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -15,6 +15,7 @@
 #endif
 #endif
 
+#include <assert.h>
 #include <stdlib.h> // for malloc, calloc, free
 #include <string.h> // for memset
 #include <new> // for std::__libcpp_aligned_{alloc,free}
@@ -33,10 +34,9 @@
 
 // When POSIX threads are not available, make the mutex operations a nop
 #ifndef _LIBCXXABI_HAS_NO_THREADS
-_LIBCPP_SAFE_STATIC
-static std::__libcpp_mutex_t heap_mutex = _LIBCPP_MUTEX_INITIALIZER;
+static _LIBCPP_CONSTINIT std::__libcpp_mutex_t heap_mutex = _LIBCPP_MUTEX_INITIALIZER;
 #else
-static void* heap_mutex = 0;
+static _LIBCPP_CONSTINIT void* heap_mutex = 0;
 #endif
 
 class mutexor {
@@ -64,11 +64,28 @@
 typedef unsigned short heap_offset;
 typedef unsigned short heap_size;
 
+// On both 64 and 32 bit targets heap_node should have the following properties
+// Size: 4
+// Alignment: 2
 struct heap_node {
   heap_offset next_node; // offset into heap
   heap_size len;         // size in units of "sizeof(heap_node)"
 };
 
+// All pointers returned by fallback_malloc must be at least aligned
+// as RequiredAligned. Note that RequiredAlignment can be greater than
+// alignof(std::max_align_t) on 64 bit systems compiling 32 bit code.
+struct FallbackMaxAlignType {
+} __attribute__((aligned));
+const size_t RequiredAlignment = alignof(FallbackMaxAlignType);
+
+static_assert(alignof(FallbackMaxAlignType) % sizeof(heap_node) == 0,
+              "The required alignment must be evenly divisible by the sizeof(heap_node)");
+
+// The number of heap_node's that can fit in a chunk of memory with the size
+// of the RequiredAlignment. On 64 bit targets NodesPerAlignment should be 4.
+const size_t NodesPerAlignment = alignof(FallbackMaxAlignType) / sizeof(heap_node);
+
 static const heap_node* list_end =
     (heap_node*)(&heap[HEAP_SIZE]); // one past the end of the heap
 static heap_node* freelist = NULL;
@@ -83,10 +100,23 @@
       sizeof(heap_node));
 }
 
+// Return a pointer to the first address, 'A', in `heap` that can actually be
+// used to represent a heap_node. 'A' must be aligned so that
+// '(A + sizeof(heap_node)) % RequiredAlignment == 0'. On 64 bit systems this
+// address should be 12 bytes after the first 16 byte boundary.
+heap_node* getFirstAlignedNodeInHeap() {
+  heap_node* node = (heap_node*)heap;
+  const size_t alignNBytesAfterBoundary = RequiredAlignment - sizeof(heap_node);
+  size_t boundaryOffset = reinterpret_cast<size_t>(node) % RequiredAlignment;
+  size_t requiredOffset = alignNBytesAfterBoundary - boundaryOffset;
+  size_t NElemOffset = requiredOffset / sizeof(heap_node);
+  return node + NElemOffset;
+}
+
 void init_heap() {
-  freelist = (heap_node*)heap;
+  freelist = getFirstAlignedNodeInHeap();
   freelist->next_node = offset_from_node(list_end);
-  freelist->len = HEAP_SIZE / sizeof(heap_node);
+  freelist->len = static_cast<heap_size>(list_end - freelist);
 }
 
 //  How big a chunk we allocate
@@ -110,23 +140,44 @@
   for (p = freelist, prev = 0; p && p != list_end;
        prev = p, p = node_from_offset(p->next_node)) {
 
-    if (p->len > nelems) { //  chunk is larger, shorten, and return the tail
+    // Check the invariant that all heap_nodes pointers 'p' are aligned
+    // so that 'p + 1' has an alignment of at least RequiredAlignment
+    assert(reinterpret_cast<size_t>(p + 1) % RequiredAlignment == 0);
+
+    // Calculate the number of extra padding elements needed in order
+    // to split 'p' and create a properly aligned heap_node from the tail
+    // of 'p'. We calculate aligned_nelems such that 'p->len - aligned_nelems'
+    // will be a multiple of NodesPerAlignment.
+    size_t aligned_nelems = nelems;
+    if (p->len > nelems) {
+      heap_size remaining_len = static_cast<heap_size>(p->len - nelems);
+      aligned_nelems += remaining_len % NodesPerAlignment;
+    }
+
+    // chunk is larger and we can create a properly aligned heap_node
+    // from the tail. In this case we shorten 'p' and return the tail.
+    if (p->len > aligned_nelems) {
       heap_node* q;
-
-      p->len = static_cast<heap_size>(p->len - nelems);
+      p->len = static_cast<heap_size>(p->len - aligned_nelems);
       q = p + p->len;
       q->next_node = 0;
-      q->len = static_cast<heap_size>(nelems);
-      return (void*)(q + 1);
+      q->len = static_cast<heap_size>(aligned_nelems);
+      void* ptr = q + 1;
+      assert(reinterpret_cast<size_t>(ptr) % RequiredAlignment == 0);
+      return ptr;
     }
 
-    if (p->len == nelems) { // exact size match
+    // The chunk is the exact size or the chunk is larger but not large
+    // enough to split due to alignment constraints.
+    if (p->len >= nelems) {
       if (prev == 0)
         freelist = node_from_offset(p->next_node);
       else
         prev->next_node = p->next_node;
       p->next_node = 0;
-      return (void*)(p + 1);
+      void* ptr = p + 1;
+      assert(reinterpret_cast<size_t>(ptr) % RequiredAlignment == 0);
+      return ptr;
     }
   }
   return NULL; // couldn't find a spot big enough