>From 8ce76ed581d7ad000c8d72d9fe759929d653f171 Mon Sep 17 00:00:00 2001 From: Bruno Haible Date: Tue, 21 Jul 2020 10:03:42 +0200 Subject: [PATCH 1/2] aligned-malloc: New module. * lib/aligned-malloc.h: New file. * m4/malloc-align.m4: New file. * modules/aligned-malloc: New file. * doc/posix-functions/posix_memalign.texi: Mention the new module. * doc/glibc-functions/memalign.texi: Likewise. --- ChangeLog | 9 ++ doc/glibc-functions/memalign.texi | 3 + doc/posix-functions/posix_memalign.texi | 7 +- lib/aligned-malloc.h | 140 ++++++++++++++++++++++++++++++++ m4/malloc-align.m4 | 140 ++++++++++++++++++++++++++++++++ modules/aligned-malloc | 25 ++++++ 6 files changed, 322 insertions(+), 2 deletions(-) create mode 100644 lib/aligned-malloc.h create mode 100644 m4/malloc-align.m4 create mode 100644 modules/aligned-malloc diff --git a/ChangeLog b/ChangeLog index 654ad7c..768b209 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,14 @@ 2020-07-21 Bruno Haible + aligned-malloc: New module. + * lib/aligned-malloc.h: New file. + * m4/malloc-align.m4: New file. + * modules/aligned-malloc: New file. + * doc/posix-functions/posix_memalign.texi: Mention the new module. + * doc/glibc-functions/memalign.texi: Likewise. + +2020-07-21 Bruno Haible + inttypes: Fix PRI*PTR and SCN*PTR on 64-bit native Windows. * m4/inttypes.m4 (gl_INTTYPES_PRI_SCN): On 64-bit native Windows, make sure PRIPTR_PREFIX is defined to "ll", not "l". diff --git a/doc/glibc-functions/memalign.texi b/doc/glibc-functions/memalign.texi index a7e9715..0740f73 100644 --- a/doc/glibc-functions/memalign.texi +++ b/doc/glibc-functions/memalign.texi @@ -27,3 +27,6 @@ Portability problems not fixed by Gnulib: This function is missing on some platforms: Mac OS X 10.5, FreeBSD 6.0, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 5.1, HP-UX 11.00, mingw, MSVC 14. @end itemize + +The Gnulib module @code{aligned-malloc} provides functions for +allocating and freeing blocks of suitably aligned memory. diff --git a/doc/posix-functions/posix_memalign.texi b/doc/posix-functions/posix_memalign.texi index 53abc86..084841b 100644 --- a/doc/posix-functions/posix_memalign.texi +++ b/doc/posix-functions/posix_memalign.texi @@ -17,5 +17,8 @@ This function is missing on some platforms: Mac OS X 10.5, FreeBSD 6.0, NetBSD 3.0, OpenBSD 3.8, Minix 3.1.8, AIX 5.1, HP-UX 11, IRIX 6.5, Solaris 10, Cygwin 1.5.x, mingw, MSVC 14, Android 4.1. @end itemize -The Gnulib module @code{pagealign_alloc} provides a similar API -that returns memory aligned on a system page boundary. +The Gnulib module @code{aligned-malloc} provides functions for +allocating and freeing blocks of suitably aligned memory. + +The Gnulib module @code{pagealign_alloc} provides a similar API for +allocating and freeing blocks of memory aligned on a system page boundary. diff --git a/lib/aligned-malloc.h b/lib/aligned-malloc.h new file mode 100644 index 0000000..86382fd --- /dev/null +++ b/lib/aligned-malloc.h @@ -0,0 +1,140 @@ +/* Allocate memory with indefinite extent and specified alignment. + + Copyright (C) 2020 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* Written by Bruno Haible , 2020. */ + +/* Before including this file, you need to define the following macro: + + ALIGNMENT A constant expression that evaluates to the desired alignment + (a power of 2). + + And you also need to #include and . */ + +/* aligned_malloc allocates a block of memory of SIZE bytes, aligned on a + boundary of ALIGNMENT bytes. + The block can be freed through aligned_free(), NOT through free(). + Upon failure, it returns NULL. */ + +/* This module exists instead of a posix_memalign() or memalign() emulation, + because we can't reasonably emulate posix_memalign() or memalign(): + If malloc() returned p, only free (p) is allowed, not free (p + 1), + free (p + 2), free (p + 4), free (p + 8), or similar. + + We can use posix_memalign(). On older systems, we can alternatively use + memalign() instead. In the Solaris documentation of memalign() it is not + specified how a memory block returned by memalign() can be freed, but + it actually can be freed with free(). */ + +#if ((ALIGNMENT) <= MALLOC_ALIGNMENT) || HAVE_POSIX_MEMALIGN || HAVE_MEMALIGN + +# if (ALIGNMENT) <= MALLOC_ALIGNMENT +/* Simply use malloc. */ + +# ifdef aligned_malloc + /* The caller wants an inline function, not a macro. */ +static inline void * +aligned_malloc (size_t size) +{ + return malloc (size); +} +# else +# define aligned_malloc malloc +# endif + +# elif HAVE_POSIX_MEMALIGN +/* Use posix_memalign. + This is OK since ALIGNMENT > MALLOC_ALIGNMENT >= sizeof (void *). */ + +static inline void * +aligned_malloc (size_t size) +{ + void *p; + int ret = posix_memalign (&p, (ALIGNMENT), size); + if (ret == 0) + return p; + else + return NULL; +} + +# elif HAVE_MEMALIGN /* HP-UX, IRIX, Solaris <= 10 */ +/* Use memalign. */ + +static inline void * +aligned_malloc (size_t size) +{ + return memalign ((ALIGNMENT), size); +} + +# endif + +# ifdef aligned_free + /* The caller wants an inline function, not a macro. */ +static inline void +aligned_free (void *q) +{ + free (q); +} +# else +# define aligned_free free +# endif + +#else +/* Use malloc and waste a bit of memory. */ + +static inline void * +aligned_malloc (size_t size) +{ + size += (ALIGNMENT); + if (size >= (ALIGNMENT)) /* no overflow? */ + { + void *p = malloc (size); + if (p != NULL) + { + /* Go to the next multiple of ALIGNMENT. */ + void *q = + (void *) (((uintptr_t) p + (ALIGNMENT)) & -(intptr_t)(ALIGNMENT)); + /* Now q - p <= ALIGNMENT and + q - p >= MALLOC_ALIGNMENT >= sizeof (void *). + This is enough to store a back pointer to p. */ + ((void **) q)[-1] = p; + return q; + } + } + return NULL; +} + +static inline void +aligned_free (void *q) +{ + if (q != NULL) + { + if ((uintptr_t) q & ((ALIGNMENT) - 1)) + /* Argument not aligned as expected. */ + abort (); + else + { + void *p = ((void **) q)[-1]; + if (!((uintptr_t) p <= (uintptr_t) q + && (uintptr_t) q - (uintptr_t) p >= MALLOC_ALIGNMENT + && (uintptr_t) q - (uintptr_t) p <= (ALIGNMENT))) + abort (); + free (p); + } + } +} + +#endif diff --git a/m4/malloc-align.m4 b/m4/malloc-align.m4 new file mode 100644 index 0000000..7ee8343 --- /dev/null +++ b/m4/malloc-align.m4 @@ -0,0 +1,140 @@ +# malloc-align.m4 serial 1 +dnl Copyright (C) 2020 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl Defines a C macro MALLOC_ALIGNMENT, whose value is a numeric constant, +dnl a power of 2, with the property that +dnl (uintptr_t) malloc (N) +dnl is always guaranteed to be a multiple of MALLOC_ALIGNMENT. + +AC_DEFUN([gl_MALLOC_ALIGNMENT], +[ + AC_REQUIRE([AC_CANONICAL_HOST]) + AC_CACHE_CHECK([for the malloc() alignment], + [gl_cv_malloc_alignment], + [gl_cv_malloc_alignment= + AC_RUN_IFELSE( + [AC_LANG_PROGRAM( + [[#include + #include + #if defined _WIN32 && !defined __CYGWIN__ + # include + /* uintptr_t is equivalent to 'unsigned long long' if _WIN64, + or to 'unsigned long' otherwise. */ + #else + # undef uintptr_t + # define uintptr_t unsigned long + #endif + ]], + [[FILE *fp = fopen ("conftest.out", "w"); + if (fp == NULL) + return 1; + { + uintptr_t bits = 0; + bits |= (uintptr_t) malloc (1); + bits |= (uintptr_t) malloc (1); + bits |= (uintptr_t) malloc (1); + bits |= (uintptr_t) malloc (2); + bits |= (uintptr_t) malloc (2); + bits |= (uintptr_t) malloc (2); + bits |= (uintptr_t) malloc (3); + bits |= (uintptr_t) malloc (3); + bits |= (uintptr_t) malloc (3); + bits |= (uintptr_t) malloc (5); + bits |= (uintptr_t) malloc (8); + bits |= (uintptr_t) malloc (8); + bits |= (uintptr_t) malloc (13); + bits |= (uintptr_t) malloc (13); + bits |= (uintptr_t) malloc (19); + bits |= (uintptr_t) malloc (19); + bits |= (uintptr_t) malloc (28); + bits |= (uintptr_t) malloc (28); + bits |= (uintptr_t) malloc (37); + bits |= (uintptr_t) malloc (37); + bits |= (uintptr_t) malloc (73); + bits |= (uintptr_t) malloc (73); + bits |= (uintptr_t) malloc (117); + bits |= (uintptr_t) malloc (117); + bits |= (uintptr_t) malloc (351); + bits |= (uintptr_t) malloc (351); + bits |= (uintptr_t) malloc (914); + bits |= (uintptr_t) malloc (914); + bits |= (uintptr_t) malloc (1712); + bits |= (uintptr_t) malloc (1712); + bits |= (uintptr_t) malloc (4021); + bits |= (uintptr_t) malloc (4021); + bits |= (uintptr_t) malloc (7641); + bits |= (uintptr_t) malloc (7641); + bits |= (uintptr_t) malloc (17027); + bits |= (uintptr_t) malloc (17027); + bits |= (uintptr_t) malloc (81231); + bits |= (uintptr_t) malloc (81231); + fprintf (fp, "%u\n", (unsigned int) (((bits ^ (bits - 1)) + 1) >> 1)); + } + if (fclose (fp) != 0) + return 2; + return 0; + ]]) + ], + [gl_cv_malloc_alignment=`cat conftest.out`], + [gl_cv_malloc_alignment="unknown"], + [dnl When cross-compiling, guess a value. Note that it's OK to return + dnl a smaller value (e.g. 4 instead of 8 or 16). + gl_cv_malloc_alignment="unknown" + case "$host_os" in + linux* | mingw*) + dnl On Linux: + dnl - It's 8 on most 32-bit platforms, except 16 on x86_64-x32 and + dnl (with newer versions of glibc) on i386 and powerpc. 8 is a + dnl safe guess. + dnl - It's 16 on all 64-bit platforms. + dnl On Windows: It's 8 on 32-bit Windows, 16 on 64-bit Windows. + for nn in 4 8 16 32; do + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[ + #define MALLOC_ALIGN (2 * sizeof (void *)) + int test [MALLOC_ALIGN <= $nn ? 1 : -1]; + ]]) + ], + [gl_cv_malloc_alignment="guessing $nn" + break + ], + [:]) + done + ;; + *) + dnl If we don't know, assume the worst. + dnl This minimum is e.g. reached on NetBSD/i386 and NetBSD/sparc. + for nn in 4 8 16; do + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[ + #define MALLOC_ALIGN (sizeof (void *)) + int test [MALLOC_ALIGN <= $nn ? 1 : -1]; + ]]) + ], + [gl_cv_malloc_alignment="guessing $nn" + break + ], + [:]) + done + ;; + esac + ]) + ]) + case "$gl_cv_malloc_alignment" in + "unknown") + dnl Assume the worst. + value=4 + ;; + "guessing "*) + value=`echo "$gl_cv_malloc_alignment" | sed -e 's/guessing //'` + ;; + *) + value="$gl_cv_malloc_alignment" + ;; + esac + AC_DEFINE_UNQUOTED([MALLOC_ALIGNMENT], [$value], + [Define to the guaranteed alignment of malloc() return values.]) +]) diff --git a/modules/aligned-malloc b/modules/aligned-malloc new file mode 100644 index 0000000..29483d8 --- /dev/null +++ b/modules/aligned-malloc @@ -0,0 +1,25 @@ +Description: +Allocate memory with indefinite extent and specified alignment. + +Files: +lib/aligned-malloc.h +m4/malloc-align.m4 + +Depends-on: +stdint + +configure.ac: +gl_MALLOC_ALIGNMENT +AC_REQUIRE([AC_C_INLINE]) +AC_CHECK_FUNCS([posix_memalign memalign]) + +Makefile.am: + +Include: +"aligned-malloc.h" + +License: +LGPLv2+ + +Maintainer: +all -- 2.7.4