aboutsummaryrefslogtreecommitdiffstats
path: root/src/alloc.c
diff options
context:
space:
mode:
authorPaul Eggert2016-08-28 02:13:18 -0700
committerPaul Eggert2016-08-28 02:14:13 -0700
commit7fcce24e75b8281621a0b8816dc58cbdc05fdc91 (patch)
treef6de4c69b1d0db6811cfbf085ff896906a124b84 /src/alloc.c
parent4be1ab61f4c1784f6870a5d53185bb0a6d9c5312 (diff)
downloademacs-7fcce24e75b8281621a0b8816dc58cbdc05fdc91.tar.gz
emacs-7fcce24e75b8281621a0b8816dc58cbdc05fdc91.zip
Memory allocator alignment fixes
These changes remove some assumptions about heap allocator alignment that may not be true on unusual platforms. * src/alloc.c (POWER_OF_2): New macro. (ROUNDUP): Use it. (BLOCK_ALIGN): Verify that it is a power of 2. (aligned_alloc): Check that alignment passed to posix_memalign satisfies POSIX restrictions. (lisp_align_malloc): Check that size passed to aligned_alloc satisfies C11 restrictions. (MALLOC_IS_GC_ALIGNED): Check that GCALIGNMENT is 8, since the code has not been verified to work with other GCALIGNMENT values and the ice is thin here. On GNU/Linux, malloc can return a value that is a multiple of 8 but not 16, even though __alignof__ (max_align_t) is 16. See: https://gcc.gnu.org/ml/gcc-patches/2016-08/msg01902.html (lmalloc) [USE_ALIGNED_ALLOC]: Use aligned_alloc only if size is a multiple of alignment, since C11 says the behavior is undefined otherwise. (lmalloc, lrealloc): Don't use INT_ADD_WRAPV on size_t, as in general this macro is restricted to signed types. Remove assertion that the result is a multiple of GCALIGNMENT, as that need not be true.
Diffstat (limited to 'src/alloc.c')
-rw-r--r--src/alloc.c81
1 files changed, 48 insertions, 33 deletions
diff --git a/src/alloc.c b/src/alloc.c
index db165757e19..67187f12ea6 100644
--- a/src/alloc.c
+++ b/src/alloc.c
@@ -478,13 +478,18 @@ static int staticidx;
478 478
479static void *pure_alloc (size_t, int); 479static void *pure_alloc (size_t, int);
480 480
481/* Return X rounded to the next multiple of Y. Arguments should not 481/* True if N is a power of 2. N should be positive. */
482 have side effects, as they are evaluated more than once. Assume X
483 + Y - 1 does not overflow. Tune for Y being a power of 2. */
484 482
485#define ROUNDUP(x, y) ((y) & ((y) - 1) \ 483#define POWER_OF_2(n) (((n) & ((n) - 1)) == 0)
486 ? ((x) + (y) - 1) - ((x) + (y) - 1) % (y) \ 484
487 : ((x) + (y) - 1) & ~ ((y) - 1)) 485/* Return X rounded to the next multiple of Y. Y should be positive,
486 and Y - 1 + X should not overflow. Arguments should not have side
487 effects, as they are evaluated more than once. Tune for Y being a
488 power of 2. */
489
490#define ROUNDUP(x, y) (POWER_OF_2 (y) \
491 ? ((y) - 1 + (x)) & ~ ((y) - 1) \
492 : ((y) - 1 + (x)) - ((y) - 1 + (x)) % (y))
488 493
489/* Return PTR rounded up to the next multiple of ALIGNMENT. */ 494/* Return PTR rounded up to the next multiple of ALIGNMENT. */
490 495
@@ -639,13 +644,14 @@ buffer_memory_full (ptrdiff_t nbytes)
639#define XMALLOC_OVERRUN_CHECK_OVERHEAD \ 644#define XMALLOC_OVERRUN_CHECK_OVERHEAD \
640 (2 * XMALLOC_OVERRUN_CHECK_SIZE + XMALLOC_OVERRUN_SIZE_SIZE) 645 (2 * XMALLOC_OVERRUN_CHECK_SIZE + XMALLOC_OVERRUN_SIZE_SIZE)
641 646
642/* Define XMALLOC_OVERRUN_SIZE_SIZE so that (1) it's large enough to
643 hold a size_t value and (2) the header size is a multiple of the
644 alignment that Emacs needs for C types and for USE_LSB_TAG. */
645#define XMALLOC_BASE_ALIGNMENT alignof (max_align_t) 647#define XMALLOC_BASE_ALIGNMENT alignof (max_align_t)
646 648
647#define XMALLOC_HEADER_ALIGNMENT \ 649#define XMALLOC_HEADER_ALIGNMENT \
648 COMMON_MULTIPLE (GCALIGNMENT, XMALLOC_BASE_ALIGNMENT) 650 COMMON_MULTIPLE (GCALIGNMENT, XMALLOC_BASE_ALIGNMENT)
651
652/* Define XMALLOC_OVERRUN_SIZE_SIZE so that (1) it's large enough to
653 hold a size_t value and (2) the header size is a multiple of the
654 alignment that Emacs needs for C types and for USE_LSB_TAG. */
649#define XMALLOC_OVERRUN_SIZE_SIZE \ 655#define XMALLOC_OVERRUN_SIZE_SIZE \
650 (((XMALLOC_OVERRUN_CHECK_SIZE + sizeof (size_t) \ 656 (((XMALLOC_OVERRUN_CHECK_SIZE + sizeof (size_t) \
651 + XMALLOC_HEADER_ALIGNMENT - 1) \ 657 + XMALLOC_HEADER_ALIGNMENT - 1) \
@@ -1126,6 +1132,10 @@ lisp_free (void *block)
1126/* The entry point is lisp_align_malloc which returns blocks of at most 1132/* The entry point is lisp_align_malloc which returns blocks of at most
1127 BLOCK_BYTES and guarantees they are aligned on a BLOCK_ALIGN boundary. */ 1133 BLOCK_BYTES and guarantees they are aligned on a BLOCK_ALIGN boundary. */
1128 1134
1135/* Byte alignment of storage blocks. */
1136#define BLOCK_ALIGN (1 << 10)
1137verify (POWER_OF_2 (BLOCK_ALIGN));
1138
1129/* Use aligned_alloc if it or a simple substitute is available. 1139/* Use aligned_alloc if it or a simple substitute is available.
1130 Address sanitization breaks aligned allocation, as of gcc 4.8.2 and 1140 Address sanitization breaks aligned allocation, as of gcc 4.8.2 and
1131 clang 3.3 anyway. Aligned allocation is incompatible with 1141 clang 3.3 anyway. Aligned allocation is incompatible with
@@ -1143,15 +1153,20 @@ lisp_free (void *block)
1143static void * 1153static void *
1144aligned_alloc (size_t alignment, size_t size) 1154aligned_alloc (size_t alignment, size_t size)
1145{ 1155{
1156 /* POSIX says the alignment must be a power-of-2 multiple of sizeof (void *).
1157 Verify this for all arguments this function is given. */
1158 verify (BLOCK_ALIGN % sizeof (void *) == 0
1159 && POWER_OF_2 (BLOCK_ALIGN / sizeof (void *)));
1160 verify (GCALIGNMENT % sizeof (void *) == 0
1161 && POWER_OF_2 (GCALIGNMENT / sizeof (void *)));
1162 eassert (alignment == BLOCK_ALIGN || alignment == GCALIGNMENT);
1163
1146 void *p; 1164 void *p;
1147 return posix_memalign (&p, alignment, size) == 0 ? p : 0; 1165 return posix_memalign (&p, alignment, size) == 0 ? p : 0;
1148} 1166}
1149# endif 1167# endif
1150#endif 1168#endif
1151 1169
1152/* BLOCK_ALIGN has to be a power of 2. */
1153#define BLOCK_ALIGN (1 << 10)
1154
1155/* Padding to leave at the end of a malloc'd block. This is to give 1170/* Padding to leave at the end of a malloc'd block. This is to give
1156 malloc a chance to minimize the amount of memory wasted to alignment. 1171 malloc a chance to minimize the amount of memory wasted to alignment.
1157 It should be tuned to the particular malloc library used. 1172 It should be tuned to the particular malloc library used.
@@ -1253,6 +1268,7 @@ lisp_align_malloc (size_t nbytes, enum mem_type type)
1253#endif 1268#endif
1254 1269
1255#ifdef USE_ALIGNED_ALLOC 1270#ifdef USE_ALIGNED_ALLOC
1271 verify (ABLOCKS_BYTES % BLOCK_ALIGN == 0);
1256 abase = base = aligned_alloc (BLOCK_ALIGN, ABLOCKS_BYTES); 1272 abase = base = aligned_alloc (BLOCK_ALIGN, ABLOCKS_BYTES);
1257#else 1273#else
1258 base = malloc (ABLOCKS_BYTES); 1274 base = malloc (ABLOCKS_BYTES);
@@ -1379,15 +1395,21 @@ lisp_align_free (void *block)
1379# define __alignof__(type) alignof (type) 1395# define __alignof__(type) alignof (type)
1380#endif 1396#endif
1381 1397
1382/* True if malloc returns a multiple of GCALIGNMENT. In practice this 1398/* True if malloc (N) is known to return a multiple of GCALIGNMENT
1383 holds if __alignof__ (max_align_t) is a multiple. Use __alignof__ 1399 whenever N is also a multiple. In practice this is true if
1384 if available, as otherwise this check would fail with GCC x86. 1400 __alignof__ (max_align_t) is a multiple as well, assuming
1401 GCALIGNMENT is 8; other values of GCALIGNMENT have not been looked
1402 into. Use __alignof__ if available, as otherwise
1403 MALLOC_IS_GC_ALIGNED would be false on GCC x86 even though the
1404 alignment is OK there.
1405
1385 This is a macro, not an enum constant, for portability to HP-UX 1406 This is a macro, not an enum constant, for portability to HP-UX
1386 10.20 cc and AIX 3.2.5 xlc. */ 1407 10.20 cc and AIX 3.2.5 xlc. */
1387#define MALLOC_IS_GC_ALIGNED (__alignof__ (max_align_t) % GCALIGNMENT == 0) 1408#define MALLOC_IS_GC_ALIGNED \
1409 (GCALIGNMENT == 8 && __alignof__ (max_align_t) % GCALIGNMENT == 0)
1388 1410
1389/* True if P is suitably aligned for SIZE, where Lisp alignment may be 1411/* True if a malloc-returned pointer P is suitably aligned for SIZE,
1390 needed if SIZE is Lisp-aligned. */ 1412 where Lisp alignment may be needed if SIZE is Lisp-aligned. */
1391 1413
1392static bool 1414static bool
1393laligned (void *p, size_t size) 1415laligned (void *p, size_t size)
@@ -1416,24 +1438,20 @@ static void *
1416lmalloc (size_t size) 1438lmalloc (size_t size)
1417{ 1439{
1418#if USE_ALIGNED_ALLOC 1440#if USE_ALIGNED_ALLOC
1419 if (! MALLOC_IS_GC_ALIGNED) 1441 if (! MALLOC_IS_GC_ALIGNED && size % GCALIGNMENT == 0)
1420 return aligned_alloc (GCALIGNMENT, size); 1442 return aligned_alloc (GCALIGNMENT, size);
1421#endif 1443#endif
1422 1444
1423 void *p;
1424 while (true) 1445 while (true)
1425 { 1446 {
1426 p = malloc (size); 1447 void *p = malloc (size);
1427 if (laligned (p, size)) 1448 if (laligned (p, size))
1428 break; 1449 return p;
1429 free (p); 1450 free (p);
1430 size_t bigger; 1451 size_t bigger = size + GCALIGNMENT;
1431 if (! INT_ADD_WRAPV (size, GCALIGNMENT, &bigger)) 1452 if (size < bigger)
1432 size = bigger; 1453 size = bigger;
1433 } 1454 }
1434
1435 eassert ((intptr_t) p % GCALIGNMENT == 0);
1436 return p;
1437} 1455}
1438 1456
1439static void * 1457static void *
@@ -1443,14 +1461,11 @@ lrealloc (void *p, size_t size)
1443 { 1461 {
1444 p = realloc (p, size); 1462 p = realloc (p, size);
1445 if (laligned (p, size)) 1463 if (laligned (p, size))
1446 break; 1464 return p;
1447 size_t bigger; 1465 size_t bigger = size + GCALIGNMENT;
1448 if (! INT_ADD_WRAPV (size, GCALIGNMENT, &bigger)) 1466 if (size < bigger)
1449 size = bigger; 1467 size = bigger;
1450 } 1468 }
1451
1452 eassert ((intptr_t) p % GCALIGNMENT == 0);
1453 return p;
1454} 1469}
1455 1470
1456 1471