Commit 49f8de7ae5b for php

commit 49f8de7ae5bf7be9119e81bd081c823a1cc8580b
Author: Marc Bennewitz <marc-mabe@users.noreply.github.com>
Date:   Mon Sep 28 19:51:21 2026 +0200

    zend_alloc: use uint32_t for small bin numbers (#23960)

    zend_mm_alloc_small() and zend_mm_free_small() took the bin number as a
    signed int, while zend_mm_alloc_small_slow() and the other bin helpers
    take uint32_t. The bin number indexes bin_data_size[] several times per
    call, and with the signed type GCC failed to see that the indexes are the
    same, so it loaded bin_data_size[bin_num] twice on the emalloc() and
    efree() fast paths and more often in zend_mm_realloc_heap().

    Take the bin number as uint32_t in both, return it as such from
    zend_mm_small_size_to_bin(), and declare old_bin_num in
    zend_mm_realloc_heap() accordingly.

diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c
index 4fc8926c0fc..c652cbd39ce 100644
--- a/Zend/zend_alloc.c
+++ b/Zend/zend_alloc.c
@@ -1269,7 +1269,7 @@ static zend_always_inline void zend_mm_free_large(zend_mm_heap *heap, zend_mm_ch
 /**************/

 /* higher set bit number (0->N/A, 1->1, 2->2, 4->3, 8->4, 127->7, 128->8 etc) */
-static zend_always_inline int zend_mm_small_size_to_bit(int size)
+static zend_always_inline int zend_mm_small_size_to_bit(uint32_t size)
 {
 #if (defined(__GNUC__) || __has_builtin(__builtin_clz))  && defined(PHP_HAVE_BUILTIN_CLZ)
 	return (__builtin_clz(size) ^ 0x1f) + 1;
@@ -1300,19 +1300,19 @@ static zend_always_inline int zend_mm_small_size_to_bit(int size)
 # define MIN(a, b) (((a) < (b)) ? (a) : (b))
 #endif

-static zend_always_inline int zend_mm_small_size_to_bin(size_t size)
+static zend_always_inline uint32_t zend_mm_small_size_to_bin(size_t size)
 {
 #if 0
 	int n;
-                            /*0,  1,  2,  3,  4,  5,  6,  7,  8,  9  10, 11, 12*/
-	static const int f1[] = { 3,  3,  3,  3,  3,  3,  3,  4,  5,  6,  7,  8,  9};
-	static const int f2[] = { 0,  0,  0,  0,  0,  0,  0,  4,  8, 12, 16, 20, 24};
+                                 /*0,  1,  2,  3,  4,  5,  6,  7,  8,  9  10, 11, 12*/
+	static const uint32_t f1[] = { 3,  3,  3,  3,  3,  3,  3,  4,  5,  6,  7,  8,  9};
+	static const uint32_t f2[] = { 0,  0,  0,  0,  0,  0,  0,  4,  8, 12, 16, 20, 24};

 	if (UNEXPECTED(size <= 2)) return 0;
 	n = zend_mm_small_size_to_bit(size - 1);
 	return ((size-1) >> f1[n]) + f2[n];
 #else
-	unsigned int t1, t2;
+	uint32_t t1, t2;

 	if (size <= 64) {
 		/* we need to support size == 0 ... */
@@ -1323,7 +1323,7 @@ static zend_always_inline int zend_mm_small_size_to_bin(size_t size)
 		t1 = t1 >> t2;
 		t2 = t2 - 3;
 		t2 = t2 << 2;
-		return (int)(t1 + t2);
+		return t1 + t2;
 	}
 #endif
 }
@@ -1457,7 +1457,7 @@ static zend_never_inline void *zend_mm_alloc_small_slow(zend_mm_heap *heap, uint
 	return bin;
 }

-static zend_always_inline void *zend_mm_alloc_small(zend_mm_heap *heap, int bin_num ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
+static zend_always_inline void *zend_mm_alloc_small(zend_mm_heap *heap, uint32_t bin_num ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
 {
 	ZEND_ASSERT(bin_data_size[bin_num] >= ZEND_MM_MIN_USEABLE_BIN_SIZE);

@@ -1479,7 +1479,7 @@ static zend_always_inline void *zend_mm_alloc_small(zend_mm_heap *heap, int bin_
 	}
 }

-static zend_always_inline void zend_mm_free_small(zend_mm_heap *heap, void *ptr, int bin_num)
+static zend_always_inline void zend_mm_free_small(zend_mm_heap *heap, void *ptr, uint32_t bin_num)
 {
 	ZEND_ASSERT(bin_data_size[bin_num] >= ZEND_MM_MIN_USEABLE_BIN_SIZE);

@@ -1525,7 +1525,7 @@ static zend_always_inline zend_mm_debug_info *zend_mm_get_debug_info(zend_mm_hea
 	info = chunk->map[page_num];
 	ZEND_MM_CHECK(chunk->heap == heap, "zend_mm_heap corrupted");
 	if (EXPECTED(info & ZEND_MM_IS_SRUN)) {
-		int bin_num = ZEND_MM_SRUN_BIN_NUM(info);
+		uint32_t bin_num = ZEND_MM_SRUN_BIN_NUM(info);
 		return (zend_mm_debug_info*)((char*)ptr + bin_data_size[bin_num] - ZEND_MM_ALIGNED_SIZE(sizeof(zend_mm_debug_info)));
 	} else /* if (info & ZEND_MM_IS_LRUN) */ {
 		int pages_count = ZEND_MM_LRUN_PAGES(info);
@@ -1779,7 +1779,7 @@ static zend_always_inline void *zend_mm_realloc_heap(zend_mm_heap *heap, void *p

 		ZEND_MM_CHECK(chunk->heap == heap, "zend_mm_heap corrupted");
 		if (info & ZEND_MM_IS_SRUN) {
-			int old_bin_num = ZEND_MM_SRUN_BIN_NUM(info);
+			uint32_t old_bin_num = ZEND_MM_SRUN_BIN_NUM(info);

 			do {
 				old_size = bin_data_size[old_bin_num];
@@ -2124,7 +2124,7 @@ ZEND_API void zend_mm_refresh_key_child(zend_mm_heap *heap)
 	zend_mm_init_key(heap);

 	/* Update shadow pointers with new key */
-	for (int i = 0; i < ZEND_MM_BINS; i++) {
+	for (uint32_t i = 0; i < ZEND_MM_BINS; i++) {
 		zend_mm_free_slot *slot = heap->free_slot[i];
 		if (!slot) {
 			continue;
@@ -2299,7 +2299,7 @@ ZEND_API size_t zend_mm_gc(zend_mm_heap *heap)
 			if (zend_mm_bitset_is_set(chunk->free_map, i)) {
 				info = chunk->map[i];
 				if (info & ZEND_MM_IS_SRUN) {
-					int bin_num = ZEND_MM_SRUN_BIN_NUM(info);
+					uint32_t bin_num = ZEND_MM_SRUN_BIN_NUM(info);
 					int pages_count = bin_pages[bin_num];

 					if (ZEND_MM_SRUN_FREE_COUNTER(info) == bin_elements[bin_num]) {
@@ -2340,7 +2340,7 @@ static zend_long zend_mm_find_leaks_small(zend_mm_chunk *p, uint32_t i, uint32_t
 {
 	bool empty = true;
 	zend_long count = 0;
-	int bin_num = ZEND_MM_SRUN_BIN_NUM(p->map[i]);
+	uint32_t bin_num = ZEND_MM_SRUN_BIN_NUM(p->map[i]);
 	zend_mm_debug_info *dbg = (zend_mm_debug_info*)((char*)p + ZEND_MM_PAGE_SIZE * i + bin_data_size[bin_num] * (j + 1) - ZEND_MM_ALIGNED_SIZE(sizeof(zend_mm_debug_info)));

 	while (j < bin_elements[bin_num]) {
@@ -2371,7 +2371,7 @@ static zend_long zend_mm_find_leaks(zend_mm_heap *heap, zend_mm_chunk *p, uint32
 		while (i < p->free_tail) {
 			if (zend_mm_bitset_is_set(p->free_map, i)) {
 				if (p->map[i] & ZEND_MM_IS_SRUN) {
-					int bin_num = ZEND_MM_SRUN_BIN_NUM(p->map[i]);
+					uint32_t bin_num = ZEND_MM_SRUN_BIN_NUM(p->map[i]);
 					count += zend_mm_find_leaks_small(p, i, 0, leak);
 					i += bin_pages[bin_num];
 				} else /* if (p->map[i] & ZEND_MM_IS_LRUN) */ {
@@ -2462,7 +2462,7 @@ static void zend_mm_check_leaks(zend_mm_heap *heap)
 		while (i < p->free_tail) {
 			if (zend_mm_bitset_is_set(p->free_map, i)) {
 				if (p->map[i] & ZEND_MM_IS_SRUN) {
-					int bin_num = ZEND_MM_SRUN_BIN_NUM(p->map[i]);
+					uint32_t bin_num = ZEND_MM_SRUN_BIN_NUM(p->map[i]);
 					zend_mm_debug_info *dbg = (zend_mm_debug_info*)((char*)p + ZEND_MM_PAGE_SIZE * i + bin_data_size[bin_num] - ZEND_MM_ALIGNED_SIZE(sizeof(zend_mm_debug_info)));

 					j = 0;