switch to a common calloc implementation
authorRich Felker <dalias@aerifal.cx>
Wed, 10 Jun 2020 23:41:27 +0000 (19:41 -0400)
committerRich Felker <dalias@aerifal.cx>
Thu, 11 Jun 2020 00:34:34 +0000 (20:34 -0400)
abstractly, calloc is completely malloc-implementation-independent;
it's malloc followed by memset, or as we do it, a "conditional memset"
that avoids touching fresh zero pages.

previously, calloc was kept separate for the bump allocator, which can
always skip memset, and the version of calloc provided with the full
malloc conditionally skipped the clearing for large direct-mmapped
allocations. the latter is a moderately attractive optimization, and
can be added back if needed. however, further consideration to make it
correct under malloc replacement would be needed.

commit b4b1e10364c8737a632be61582e05a8d3acf5690 documented the
contract for malloc replacement as allowing omission of calloc, and
indeed that worked for dynamic linking, but for static linking it was
possible to get the non-clearing definition from the bump allocator;
if not for that, it would have been a link error trying to pull in
malloc.o.

the conditional-clearing code for the new common calloc is taken from
mal0_clear in oldmalloc, but drops the need to access actual page size
and just uses a fixed value of 4096. this avoids potentially needing
access to global data for the sake of an optimization that at best
marginally helps archs with offensively-large page sizes.

src/malloc/calloc.c [new file with mode: 0644]
src/malloc/lite_malloc.c
src/malloc/oldmalloc/malloc.c

diff --git a/src/malloc/calloc.c b/src/malloc/calloc.c
new file mode 100644 (file)
index 0000000..322193c
--- /dev/null
@@ -0,0 +1,37 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+
+static size_t mal0_clear(char *p, size_t n)
+{
+       const size_t pagesz = 4096; /* arbitrary */
+       if (n < pagesz) return n;
+#ifdef __GNUC__
+       typedef uint64_t __attribute__((__may_alias__)) T;
+#else
+       typedef unsigned char T;
+#endif
+       char *pp = p + n;
+       size_t i = (uintptr_t)pp & (pagesz - 1);
+       for (;;) {
+               pp = memset(pp - i, 0, i);
+               if (pp - p < pagesz) return pp - p;
+               for (i = pagesz; i; i -= 2*sizeof(T), pp -= 2*sizeof(T))
+                       if (((T *)pp)[-1] | ((T *)pp)[-2])
+                               break;
+       }
+}
+
+void *calloc(size_t m, size_t n)
+{
+       if (n && m > (size_t)-1/n) {
+               errno = ENOMEM;
+               return 0;
+       }
+       n *= m;
+       void *p = malloc(n);
+       if (!p) return p;
+       n = mal0_clear(p, n);
+       return memset(p, 0, n);
+}
index c3f0c129acbcb88e58a59a9231c0650f602753c3..f8931ba597dcadcd56a00e8858c89cbe43a6b8fb 100644 (file)
@@ -101,14 +101,3 @@ static void *__simple_malloc(size_t n)
 }
 
 weak_alias(__simple_malloc, malloc);
-
-static void *__simple_calloc(size_t m, size_t n)
-{
-       if (n && m > (size_t)-1/n) {
-               errno = ENOMEM;
-               return 0;
-       }
-       return __simple_malloc(n * m);
-}
-
-weak_alias(__simple_calloc, calloc);
index df3ea1becd3d5eb81ab68cb8e493b3ba27e81b6c..afa75722b8f716c51d40eebc4079a83d46f13016 100644 (file)
@@ -341,42 +341,6 @@ void *malloc(size_t n)
        return CHUNK_TO_MEM(c);
 }
 
-static size_t mal0_clear(char *p, size_t pagesz, size_t n)
-{
-#ifdef __GNUC__
-       typedef uint64_t __attribute__((__may_alias__)) T;
-#else
-       typedef unsigned char T;
-#endif
-       char *pp = p + n;
-       size_t i = (uintptr_t)pp & (pagesz - 1);
-       for (;;) {
-               pp = memset(pp - i, 0, i);
-               if (pp - p < pagesz) return pp - p;
-               for (i = pagesz; i; i -= 2*sizeof(T), pp -= 2*sizeof(T))
-                       if (((T *)pp)[-1] | ((T *)pp)[-2])
-                               break;
-       }
-}
-
-void *calloc(size_t m, size_t n)
-{
-       if (n && m > (size_t)-1/n) {
-               errno = ENOMEM;
-               return 0;
-       }
-       n *= m;
-       void *p = malloc(n);
-       if (!p) return p;
-       if (!__malloc_replaced) {
-               if (IS_MMAPPED(MEM_TO_CHUNK(p)))
-                       return p;
-               if (n >= PAGE_SIZE)
-                       n = mal0_clear(p, PAGE_SIZE, n);
-       }
-       return memset(p, 0, n);
-}
-
 void *realloc(void *p, size_t n)
 {
        struct chunk *self, *next;