diff options
| author | Paul Eggert | 2025-01-15 23:32:43 -0800 |
|---|---|---|
| committer | Paul Eggert | 2025-01-17 15:56:30 -0800 |
| commit | d3a2ec5210746a942263d5e18ee3b8190b9698e1 (patch) | |
| tree | e5f9974259a48a557c12387ea1fd4e297e644354 /src/alloc.c | |
| parent | 29794c71452c58c596c58fd6148741d213d3ee7b (diff) | |
| download | emacs-d3a2ec5210746a942263d5e18ee3b8190b9698e1.tar.gz emacs-d3a2ec5210746a942263d5e18ee3b8190b9698e1.zip | |
Simplify alloc by assuming MALLOC_IS_LISP_ALIGNED
Problem reported by Hong Xu <https://bugs.gnu.org/75551#14>.
* src/alloc.c (MALLOC_IS_LISP_ALIGNED): static_assert it,
since it is true on all current Emacs platforms.
All uses simplified to assume it is true.
(xmalloc, xzalloc, xrealloc, lisp_malloc): Just use
malloc/calloc/realloc. Since we are using the malloc-gnu
and realloc-posix modules, we need not worry about whether
these functions return a null pointer for zero-size requests.
(xrealloc): Stop worrying about no-longer-existing platforms
where realloc (nullptr, ...) did not work.
(laligned, lmalloc, lrealloc): Remove. All uses removed.
Diffstat (limited to 'src/alloc.c')
| -rw-r--r-- | src/alloc.c | 128 |
1 files changed, 26 insertions, 102 deletions
diff --git a/src/alloc.c b/src/alloc.c index 8307c74c106..e7290c55f88 100644 --- a/src/alloc.c +++ b/src/alloc.c | |||
| @@ -704,21 +704,24 @@ buffer_memory_full (ptrdiff_t nbytes) | |||
| 704 | ((a) % (b) == 0 ? (a) : (b) % (a) == 0 ? (b) : (a) * (b)) | 704 | ((a) % (b) == 0 ? (a) : (b) % (a) == 0 ? (b) : (a) * (b)) |
| 705 | 705 | ||
| 706 | /* Alignment needed for memory blocks that are allocated via malloc | 706 | /* Alignment needed for memory blocks that are allocated via malloc |
| 707 | and that contain Lisp objects. On typical hosts malloc already | 707 | and that contain Lisp objects. */ |
| 708 | aligns sufficiently, but extra work is needed on oddball hosts | ||
| 709 | where Emacs would crash if malloc returned a non-GCALIGNED pointer. */ | ||
| 710 | enum { LISP_ALIGNMENT = alignof (union { union emacs_align_type x; | 708 | enum { LISP_ALIGNMENT = alignof (union { union emacs_align_type x; |
| 711 | GCALIGNED_UNION_MEMBER }) }; | 709 | GCALIGNED_UNION_MEMBER }) }; |
| 712 | static_assert (LISP_ALIGNMENT % GCALIGNMENT == 0); | 710 | static_assert (LISP_ALIGNMENT % GCALIGNMENT == 0); |
| 713 | 711 | ||
| 714 | /* True if malloc (N) is known to return storage suitably aligned for | 712 | /* Verify Emacs's assumption that malloc (N) returns storage suitably |
| 715 | Lisp objects whenever N is a multiple of LISP_ALIGNMENT. In | 713 | aligned for Lisp objects whenever N is a multiple of LISP_ALIGNMENT. |
| 716 | practice this is true whenever alignof (max_align_t) is also a | 714 | This assumption holds for current Emacs porting targets; |
| 715 | if the assumption fails on a new platform, this check should | ||
| 716 | cause compilation to fail and some porting work will need to be done. | ||
| 717 | |||
| 718 | In practice the assumption holds when alignof (max_align_t) is also a | ||
| 717 | multiple of LISP_ALIGNMENT. This works even for buggy platforms | 719 | multiple of LISP_ALIGNMENT. This works even for buggy platforms |
| 718 | like MinGW circa 2020, where alignof (max_align_t) is 16 even though | 720 | like MinGW circa 2020, where alignof (max_align_t) is 16 even though |
| 719 | the malloc alignment is only 8, and where Emacs still works because | 721 | the malloc alignment is only 8, and where Emacs still works because |
| 720 | it never does anything that requires an alignment of 16. */ | 722 | it never does anything that requires an alignment of 16. */ |
| 721 | enum { MALLOC_IS_LISP_ALIGNED = alignof (max_align_t) % LISP_ALIGNMENT == 0 }; | 723 | enum { MALLOC_IS_LISP_ALIGNED = alignof (max_align_t) % LISP_ALIGNMENT == 0 }; |
| 724 | static_assert (MALLOC_IS_LISP_ALIGNED); | ||
| 722 | 725 | ||
| 723 | /* If compiled with XMALLOC_BLOCK_INPUT_CHECK, define a symbol | 726 | /* If compiled with XMALLOC_BLOCK_INPUT_CHECK, define a symbol |
| 724 | BLOCK_INPUT_IN_MEMORY_ALLOCATORS that is visible to the debugger. | 727 | BLOCK_INPUT_IN_MEMORY_ALLOCATORS that is visible to the debugger. |
| @@ -759,9 +762,6 @@ malloc_unblock_input (void) | |||
| 759 | malloc_probe (size); \ | 762 | malloc_probe (size); \ |
| 760 | } while (0) | 763 | } while (0) |
| 761 | 764 | ||
| 762 | static void *lmalloc (size_t, bool) ATTRIBUTE_MALLOC_SIZE ((1)); | ||
| 763 | static void *lrealloc (void *, size_t); | ||
| 764 | |||
| 765 | /* Like malloc but check for no memory and block interrupt input. */ | 765 | /* Like malloc but check for no memory and block interrupt input. */ |
| 766 | 766 | ||
| 767 | void * | 767 | void * |
| @@ -770,7 +770,7 @@ xmalloc (size_t size) | |||
| 770 | void *val; | 770 | void *val; |
| 771 | 771 | ||
| 772 | MALLOC_BLOCK_INPUT; | 772 | MALLOC_BLOCK_INPUT; |
| 773 | val = lmalloc (size, false); | 773 | val = malloc (size); |
| 774 | MALLOC_UNBLOCK_INPUT; | 774 | MALLOC_UNBLOCK_INPUT; |
| 775 | 775 | ||
| 776 | if (!val) | 776 | if (!val) |
| @@ -787,7 +787,7 @@ xzalloc (size_t size) | |||
| 787 | void *val; | 787 | void *val; |
| 788 | 788 | ||
| 789 | MALLOC_BLOCK_INPUT; | 789 | MALLOC_BLOCK_INPUT; |
| 790 | val = lmalloc (size, true); | 790 | val = calloc (1, size); |
| 791 | MALLOC_UNBLOCK_INPUT; | 791 | MALLOC_UNBLOCK_INPUT; |
| 792 | 792 | ||
| 793 | if (!val) | 793 | if (!val) |
| @@ -804,12 +804,7 @@ xrealloc (void *block, size_t size) | |||
| 804 | void *val; | 804 | void *val; |
| 805 | 805 | ||
| 806 | MALLOC_BLOCK_INPUT; | 806 | MALLOC_BLOCK_INPUT; |
| 807 | /* Call lmalloc when BLOCK is null, for the benefit of long-obsolete | 807 | val = realloc (block, size); |
| 808 | platforms lacking support for realloc (NULL, size). */ | ||
| 809 | if (! block) | ||
| 810 | val = lmalloc (size, false); | ||
| 811 | else | ||
| 812 | val = lrealloc (block, size); | ||
| 813 | MALLOC_UNBLOCK_INPUT; | 808 | MALLOC_UNBLOCK_INPUT; |
| 814 | 809 | ||
| 815 | if (!val) | 810 | if (!val) |
| @@ -994,15 +989,23 @@ record_xmalloc (size_t size) | |||
| 994 | } | 989 | } |
| 995 | 990 | ||
| 996 | 991 | ||
| 997 | /* Like malloc but used for allocating Lisp data. NBYTES is the | ||
| 998 | number of bytes to allocate, TYPE describes the intended use of the | ||
| 999 | allocated memory block (for strings, for conses, ...). */ | ||
| 1000 | |||
| 1001 | #if ! USE_LSB_TAG | 992 | #if ! USE_LSB_TAG |
| 1002 | extern void *lisp_malloc_loser; | 993 | extern void *lisp_malloc_loser; |
| 1003 | void *lisp_malloc_loser EXTERNALLY_VISIBLE; | 994 | void *lisp_malloc_loser EXTERNALLY_VISIBLE; |
| 1004 | #endif | 995 | #endif |
| 1005 | 996 | ||
| 997 | /* Allocate memory for Lisp data. | ||
| 998 | NBYTES is the number of bytes to allocate; it must be Lisp-aligned. | ||
| 999 | If CLEARIT, arrange for the allocated memory to be cleared | ||
| 1000 | by using calloc, which can be faster than malloc+memset. | ||
| 1001 | TYPE describes the intended use of the allocated memory block | ||
| 1002 | (for strings, for conses, ...). | ||
| 1003 | Return a null pointer if and only if allocation failed. | ||
| 1004 | |||
| 1005 | Code allocating heap memory for Lisp should use this function to get | ||
| 1006 | a pointer P; that way, if T is an enum Lisp_Type value and | ||
| 1007 | L == make_lisp_ptr (P, T), then XPNTR (L) == P and XTYPE (L) == T. */ | ||
| 1008 | |||
| 1006 | static void * | 1009 | static void * |
| 1007 | lisp_malloc (size_t nbytes, bool clearit, enum mem_type type) | 1010 | lisp_malloc (size_t nbytes, bool clearit, enum mem_type type) |
| 1008 | { | 1011 | { |
| @@ -1014,7 +1017,7 @@ lisp_malloc (size_t nbytes, bool clearit, enum mem_type type) | |||
| 1014 | allocated_mem_type = type; | 1017 | allocated_mem_type = type; |
| 1015 | #endif | 1018 | #endif |
| 1016 | 1019 | ||
| 1017 | val = lmalloc (nbytes, clearit); | 1020 | val = clearit ? calloc (1, nbytes) : malloc (nbytes); |
| 1018 | 1021 | ||
| 1019 | #if ! USE_LSB_TAG | 1022 | #if ! USE_LSB_TAG |
| 1020 | /* If the memory just allocated cannot be addressed thru a Lisp | 1023 | /* If the memory just allocated cannot be addressed thru a Lisp |
| @@ -1098,11 +1101,7 @@ aligned_alloc (size_t alignment, size_t size) | |||
| 1098 | Verify this for all arguments this function is given. */ | 1101 | Verify this for all arguments this function is given. */ |
| 1099 | static_assert (BLOCK_ALIGN % sizeof (void *) == 0 | 1102 | static_assert (BLOCK_ALIGN % sizeof (void *) == 0 |
| 1100 | && POWER_OF_2 (BLOCK_ALIGN / sizeof (void *))); | 1103 | && POWER_OF_2 (BLOCK_ALIGN / sizeof (void *))); |
| 1101 | static_assert (MALLOC_IS_LISP_ALIGNED | 1104 | eassert (alignment == BLOCK_ALIGN); |
| 1102 | || (LISP_ALIGNMENT % sizeof (void *) == 0 | ||
| 1103 | && POWER_OF_2 (LISP_ALIGNMENT / sizeof (void *)))); | ||
| 1104 | eassert (alignment == BLOCK_ALIGN | ||
| 1105 | || (!MALLOC_IS_LISP_ALIGNED && alignment == LISP_ALIGNMENT)); | ||
| 1106 | 1105 | ||
| 1107 | void *p; | 1106 | void *p; |
| 1108 | return posix_memalign (&p, alignment, size) == 0 ? p : 0; | 1107 | return posix_memalign (&p, alignment, size) == 0 ? p : 0; |
| @@ -1350,81 +1349,6 @@ lisp_align_free (void *block) | |||
| 1350 | MALLOC_UNBLOCK_INPUT; | 1349 | MALLOC_UNBLOCK_INPUT; |
| 1351 | } | 1350 | } |
| 1352 | 1351 | ||
| 1353 | /* True if a malloc-returned pointer P is suitably aligned for SIZE, | ||
| 1354 | where Lisp object alignment may be needed if SIZE is a multiple of | ||
| 1355 | LISP_ALIGNMENT. */ | ||
| 1356 | |||
| 1357 | static bool | ||
| 1358 | laligned (void *p, size_t size) | ||
| 1359 | { | ||
| 1360 | return (MALLOC_IS_LISP_ALIGNED || (intptr_t) p % LISP_ALIGNMENT == 0 | ||
| 1361 | || size % LISP_ALIGNMENT != 0); | ||
| 1362 | } | ||
| 1363 | |||
| 1364 | /* Like malloc and realloc except return null only on failure, | ||
| 1365 | the result is Lisp-aligned if SIZE is, and lrealloc's pointer | ||
| 1366 | argument must be nonnull. Code allocating C heap memory | ||
| 1367 | for a Lisp object should use one of these functions to obtain a | ||
| 1368 | pointer P; that way, if T is an enum Lisp_Type value and L == | ||
| 1369 | make_lisp_ptr (P, T), then XPNTR (L) == P and XTYPE (L) == T. | ||
| 1370 | |||
| 1371 | If CLEARIT, arrange for the allocated memory to be cleared. | ||
| 1372 | This might use calloc, as calloc can be faster than malloc+memset. | ||
| 1373 | |||
| 1374 | On typical modern platforms these functions' loops do not iterate. | ||
| 1375 | On now-rare (and perhaps nonexistent) platforms, the code can loop, | ||
| 1376 | reallocating (typically with larger and larger sizes) until the | ||
| 1377 | allocator returns a Lisp-aligned pointer. This loop in | ||
| 1378 | theory could repeat forever. If an infinite loop is possible on a | ||
| 1379 | platform, a build would surely loop and the builder can then send | ||
| 1380 | us a bug report. Adding a counter to try to detect any such loop | ||
| 1381 | would complicate the code (and possibly introduce bugs, in code | ||
| 1382 | that's never really exercised) for little benefit. */ | ||
| 1383 | |||
| 1384 | static void * | ||
| 1385 | lmalloc (size_t size, bool clearit) | ||
| 1386 | { | ||
| 1387 | #ifdef USE_ALIGNED_ALLOC | ||
| 1388 | if (! MALLOC_IS_LISP_ALIGNED && size % LISP_ALIGNMENT == 0) | ||
| 1389 | { | ||
| 1390 | void *p = aligned_alloc (LISP_ALIGNMENT, size); | ||
| 1391 | if (p) | ||
| 1392 | { | ||
| 1393 | if (clearit) | ||
| 1394 | memclear (p, size); | ||
| 1395 | } | ||
| 1396 | else if (! (MALLOC_0_IS_NONNULL || size)) | ||
| 1397 | return aligned_alloc (LISP_ALIGNMENT, LISP_ALIGNMENT); | ||
| 1398 | return p; | ||
| 1399 | } | ||
| 1400 | #endif | ||
| 1401 | |||
| 1402 | while (true) | ||
| 1403 | { | ||
| 1404 | void *p = clearit ? calloc (1, size) : malloc (size); | ||
| 1405 | if (laligned (p, size) && (MALLOC_0_IS_NONNULL || size || p)) | ||
| 1406 | return p; | ||
| 1407 | free (p); | ||
| 1408 | size_t bigger; | ||
| 1409 | if (!ckd_add (&bigger, size, LISP_ALIGNMENT)) | ||
| 1410 | size = bigger; | ||
| 1411 | } | ||
| 1412 | } | ||
| 1413 | |||
| 1414 | static void * | ||
| 1415 | lrealloc (void *p, size_t size) | ||
| 1416 | { | ||
| 1417 | while (true) | ||
| 1418 | { | ||
| 1419 | p = realloc (p, size); | ||
| 1420 | if (laligned (p, size) && (size || p)) | ||
| 1421 | return p; | ||
| 1422 | size_t bigger; | ||
| 1423 | if (!ckd_add (&bigger, size, LISP_ALIGNMENT)) | ||
| 1424 | size = bigger; | ||
| 1425 | } | ||
| 1426 | } | ||
| 1427 | |||
| 1428 | 1352 | ||
| 1429 | /*********************************************************************** | 1353 | /*********************************************************************** |
| 1430 | Interval Allocation | 1354 | Interval Allocation |