From 4b0e0ad2032bddd492ce161937c42be463a7202f Mon Sep 17 00:00:00 2001 From: henderkes Date: Fri, 12 Jun 2026 16:03:43 +0700 Subject: [PATCH 1/5] TSRM: make CG, EG, SCNG and AG compile-time offsets saves an offset load every time they're accessed --- TSRM/TSRM.c | 39 ++++++++++++++++++++++++++++++++++++++ TSRM/TSRM.h | 7 +++++++ Zend/zend.c | 9 ++++++--- Zend/zend_alloc.c | 5 +++-- Zend/zend_globals.h | 9 +++++++++ Zend/zend_globals_macros.h | 6 +++--- Zend/zend_portability.h | 2 +- main/main.c | 6 ++++++ 8 files changed, 74 insertions(+), 9 deletions(-) diff --git a/TSRM/TSRM.c b/TSRM/TSRM.c index e99993204b6f..7cf7ae7732d5 100644 --- a/TSRM/TSRM.c +++ b/TSRM/TSRM.c @@ -36,6 +36,15 @@ struct _tsrm_tls_entry { tsrm_tls_entry *next; }; +#if defined(__cplusplus) || defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 202311L) +# define TSRM_STATIC_ASSERT(c, m) static_assert((c), m) +#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) /* C11 */ +# define TSRM_STATIC_ASSERT(c, m) _Static_assert((c), m) +#else +# define TSRM_STATIC_ASSERT(c, m) +#endif +TSRM_STATIC_ASSERT(TSRM_ALIGNED_SIZE(sizeof(tsrm_tls_entry)) == TSRM_FAST_RESERVED_BASE, + "TSRM_FAST_RESERVED_BASE must equal the tsrm_tls_entry header size"); typedef struct { size_t size; @@ -319,6 +328,14 @@ TSRM_API void tsrm_reserve(size_t size) }/*}}}*/ +/* Reserve the front of the fast space for fixed-offset fast resources, so the + * bump-allocated ones (ts_allocate_fast_id) are placed after it. */ +TSRM_API void tsrm_reserve_fast_front(size_t size) +{/*{{{*/ + tsrm_reserved_pos = TSRM_ALIGNED_SIZE(size); +}/*}}}*/ + + /* allocates a new fast thread-safe-resource id */ TSRM_API ts_rsrc_id ts_allocate_fast_id(ts_rsrc_id *rsrc_id, size_t *offset, size_t size, ts_allocate_ctor ctor, ts_allocate_dtor dtor) {/*{{{*/ @@ -368,6 +385,28 @@ TSRM_API ts_rsrc_id ts_allocate_fast_id(ts_rsrc_id *rsrc_id, size_t *offset, siz return *rsrc_id; }/*}}}*/ + +/* Allocate a fast resource id at a fixed, compile-time-constant offset. + * Like tsrm_reserve(), assumes single-threaded startup. */ +TSRM_API ts_rsrc_id ts_allocate_fast_id_at(ts_rsrc_id *rsrc_id, size_t *offset, size_t fixed_offset, size_t size, ts_allocate_ctor ctor, ts_allocate_dtor dtor) +{/*{{{*/ + size_t saved_pos = tsrm_reserved_pos; + + if (fixed_offset - TSRM_FAST_RESERVED_BASE > tsrm_reserved_size) { + TSRM_ERROR((TSRM_ERROR_LEVEL_ERROR, "Unable to allocate space for fixed fast resource")); + *rsrc_id = 0; + *offset = 0; + return 0; + } + tsrm_reserved_pos = fixed_offset - TSRM_FAST_RESERVED_BASE; + ts_allocate_fast_id(rsrc_id, offset, size, ctor, dtor); + /* keep ordinary (bump-allocated) fast resources past the high-water mark */ + if (tsrm_reserved_pos < saved_pos) { + tsrm_reserved_pos = saved_pos; + } + return *rsrc_id; +}/*}}}*/ + static void set_thread_local_storage_resource_to(tsrm_tls_entry *thread_resource) { tsrm_tls_set(thread_resource); diff --git a/TSRM/TSRM.h b/TSRM/TSRM.h index ea13552c8374..fa7f56904073 100644 --- a/TSRM/TSRM.h +++ b/TSRM/TSRM.h @@ -75,6 +75,8 @@ typedef void (*ts_allocate_dtor)(void *); #define THREAD_HASH_OF(thr,ts) (unsigned long)thr%(unsigned long)ts +#define TSRM_FAST_RESERVED_BASE TSRM_ALIGNED_SIZE(4 * sizeof(void *)) + #ifdef __cplusplus extern "C" { #endif @@ -94,6 +96,11 @@ TSRM_API ts_rsrc_id ts_allocate_id(ts_rsrc_id *rsrc_id, size_t size, ts_allocate TSRM_API void tsrm_reserve(size_t size); TSRM_API ts_rsrc_id ts_allocate_fast_id(ts_rsrc_id *rsrc_id, size_t *offset, size_t size, ts_allocate_ctor ctor, ts_allocate_dtor dtor); +/* Fast resources at caller-chosen, compile-time-constant offsets. The fixed + * prefix must be reserved after tsrm_reserve() and before any fast id. */ +TSRM_API void tsrm_reserve_fast_front(size_t size); +TSRM_API ts_rsrc_id ts_allocate_fast_id_at(ts_rsrc_id *rsrc_id, size_t *offset, size_t fixed_offset, size_t size, ts_allocate_ctor ctor, ts_allocate_dtor dtor); + /* fetches the requested resource for the current thread */ TSRM_API void *ts_resource_ex(ts_rsrc_id id, THREAD_T *th_id); #define ts_resource(id) ts_resource_ex(id, NULL) diff --git a/Zend/zend.c b/Zend/zend.c index f16b1a30dbbc..9411b92a2018 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -1019,9 +1019,12 @@ void zend_startup(zend_utility_functions *utility_functions) /* {{{ */ zend_init_rsrc_list_dtors(); #ifdef ZTS - ts_allocate_fast_id(&compiler_globals_id, &compiler_globals_offset, sizeof(zend_compiler_globals), (ts_allocate_ctor) compiler_globals_ctor, (ts_allocate_dtor) compiler_globals_dtor); - ts_allocate_fast_id(&executor_globals_id, &executor_globals_offset, sizeof(zend_executor_globals), (ts_allocate_ctor) executor_globals_ctor, (ts_allocate_dtor) executor_globals_dtor); - ts_allocate_fast_id(&language_scanner_globals_id, &language_scanner_globals_offset, sizeof(zend_php_scanner_globals), (ts_allocate_ctor) php_scanner_globals_ctor, NULL); + ts_allocate_fast_id_at(&compiler_globals_id, &compiler_globals_offset, ZEND_CG_OFFSET, sizeof(zend_compiler_globals), (ts_allocate_ctor) compiler_globals_ctor, (ts_allocate_dtor) compiler_globals_dtor); + ts_allocate_fast_id_at(&executor_globals_id, &executor_globals_offset, ZEND_EG_OFFSET, sizeof(zend_executor_globals), (ts_allocate_ctor) executor_globals_ctor, (ts_allocate_dtor) executor_globals_dtor); + ts_allocate_fast_id_at(&language_scanner_globals_id, &language_scanner_globals_offset, ZEND_SCNG_OFFSET, sizeof(zend_php_scanner_globals), (ts_allocate_ctor) php_scanner_globals_ctor, NULL); + ZEND_ASSERT(compiler_globals_offset == ZEND_CG_OFFSET); + ZEND_ASSERT(executor_globals_offset == ZEND_EG_OFFSET); + ZEND_ASSERT(language_scanner_globals_offset == ZEND_SCNG_OFFSET); ts_allocate_fast_id(&ini_scanner_globals_id, &ini_scanner_globals_offset, sizeof(zend_ini_scanner_globals), (ts_allocate_ctor) ini_scanner_globals_ctor, NULL); compiler_globals = ts_resource(compiler_globals_id); executor_globals = ts_resource(executor_globals_id); diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index 942a8b8e1309..7a4469826054 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -2614,7 +2614,7 @@ typedef struct _zend_alloc_globals { #ifdef ZTS static int alloc_globals_id; static size_t alloc_globals_offset; -# define AG(v) ZEND_TSRMG_FAST(alloc_globals_offset, zend_alloc_globals *, v) +# define AG(v) ZEND_TSRMG_FAST(ZEND_AG_OFFSET, zend_alloc_globals *, v) #else # define AG(v) (alloc_globals.v) static zend_alloc_globals alloc_globals; @@ -3335,7 +3335,8 @@ ZEND_API void start_memory_manager(void) # endif #endif #ifdef ZTS - ts_allocate_fast_id(&alloc_globals_id, &alloc_globals_offset, sizeof(zend_alloc_globals), (ts_allocate_ctor) alloc_globals_ctor, (ts_allocate_dtor) alloc_globals_dtor); + ts_allocate_fast_id_at(&alloc_globals_id, &alloc_globals_offset, ZEND_AG_OFFSET, sizeof(zend_alloc_globals), (ts_allocate_ctor) alloc_globals_ctor, (ts_allocate_dtor) alloc_globals_dtor); + ZEND_ASSERT(alloc_globals_offset == ZEND_AG_OFFSET); #else alloc_globals_ctor(&alloc_globals); #endif diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h index 8257df32e831..5ab6df774386 100644 --- a/Zend/zend_globals.h +++ b/Zend/zend_globals.h @@ -329,6 +329,15 @@ struct _zend_executor_globals { void *reserved[ZEND_MAX_RESERVED_RESOURCES]; }; +#ifdef ZTS +/* Fixed compile-time offsets of the hot globals in the TSRM fast space. + * AG is last, sizeof(zend_alloc_globals) isn't header-visible. */ +# define ZEND_CG_OFFSET (TSRM_FAST_RESERVED_BASE) +# define ZEND_EG_OFFSET (ZEND_CG_OFFSET + TSRM_ALIGNED_SIZE(sizeof(zend_compiler_globals))) +# define ZEND_SCNG_OFFSET (ZEND_EG_OFFSET + TSRM_ALIGNED_SIZE(sizeof(zend_executor_globals))) +# define ZEND_AG_OFFSET (ZEND_SCNG_OFFSET + TSRM_ALIGNED_SIZE(sizeof(zend_php_scanner_globals))) +#endif + #define EG_FLAGS_INITIAL (0) #define EG_FLAGS_IN_SHUTDOWN (1<<0) #define EG_FLAGS_OBJECT_STORE_NO_REUSE (1<<1) diff --git a/Zend/zend_globals_macros.h b/Zend/zend_globals_macros.h index bde10a0989d1..2d2948e50a86 100644 --- a/Zend/zend_globals_macros.h +++ b/Zend/zend_globals_macros.h @@ -30,7 +30,7 @@ BEGIN_EXTERN_C() /* Compiler */ #ifdef ZTS -# define CG(v) ZEND_TSRMG_FAST(compiler_globals_offset, zend_compiler_globals *, v) +# define CG(v) ZEND_TSRMG_FAST(ZEND_CG_OFFSET, zend_compiler_globals *, v) #else # define CG(v) (compiler_globals.v) extern ZEND_API struct _zend_compiler_globals compiler_globals; @@ -40,7 +40,7 @@ ZEND_API int zendparse(void); /* Executor */ #ifdef ZTS -# define EG(v) ZEND_TSRMG_FAST(executor_globals_offset, zend_executor_globals *, v) +# define EG(v) ZEND_TSRMG_FAST(ZEND_EG_OFFSET, zend_executor_globals *, v) #else # define EG(v) (executor_globals.v) extern ZEND_API zend_executor_globals executor_globals; @@ -48,7 +48,7 @@ extern ZEND_API zend_executor_globals executor_globals; /* Language Scanner */ #ifdef ZTS -# define LANG_SCNG(v) ZEND_TSRMG_FAST(language_scanner_globals_offset, zend_php_scanner_globals *, v) +# define LANG_SCNG(v) ZEND_TSRMG_FAST(ZEND_SCNG_OFFSET, zend_php_scanner_globals *, v) extern ZEND_API ts_rsrc_id language_scanner_globals_id; extern ZEND_API size_t language_scanner_globals_offset; #else diff --git a/Zend/zend_portability.h b/Zend/zend_portability.h index ccad24682fdb..33ea4230b6a4 100644 --- a/Zend/zend_portability.h +++ b/Zend/zend_portability.h @@ -862,7 +862,7 @@ extern "C++" { /** @deprecated */ #define ZEND_CGG_DIAGNOSTIC_IGNORED_END ZEND_DIAGNOSTIC_IGNORED_END -#if defined(__cplusplus) +#if defined(__cplusplus) || defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 202311L) # define ZEND_STATIC_ASSERT(c, m) static_assert((c), m) #elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) /* C11 */ # define ZEND_STATIC_ASSERT(c, m) _Static_assert((c), m) diff --git a/main/main.c b/main/main.c index 6bda55ac8746..c1a09b5b3e6a 100644 --- a/main/main.c +++ b/main/main.c @@ -2842,6 +2842,12 @@ PHPAPI bool php_tsrm_startup_ex(int expected_threads) { bool ret = tsrm_startup(expected_threads, 1, 0, NULL); php_reserve_tsrm_memory(); + /* Must reserve exactly the prefix laid out by ZEND_*_OFFSET (zend_globals.h). */ + tsrm_reserve_fast_front( + TSRM_ALIGNED_SIZE(sizeof(zend_compiler_globals)) + + TSRM_ALIGNED_SIZE(sizeof(zend_executor_globals)) + + TSRM_ALIGNED_SIZE(sizeof(zend_php_scanner_globals)) + + TSRM_ALIGNED_SIZE(zend_mm_globals_size())); // AG size, exposed through function call (void)ts_resource(0); return ret; } From eacb8786adae43d6f6b96ac559b76c9fbdf9e94c Mon Sep 17 00:00:00 2001 From: henderkes Date: Thu, 25 Jun 2026 19:03:56 +0700 Subject: [PATCH 2/5] place hot globals at negative offsets before cache pointer --- TSRM/TSRM.c | 73 +++++++++++++++++++---------------------- TSRM/TSRM.h | 7 ++-- Zend/zend_alloc.c | 1 + Zend/zend_globals.h | 11 +++---- Zend/zend_portability.h | 2 +- 5 files changed, 43 insertions(+), 51 deletions(-) diff --git a/TSRM/TSRM.c b/TSRM/TSRM.c index 7cf7ae7732d5..963a11b281bf 100644 --- a/TSRM/TSRM.c +++ b/TSRM/TSRM.c @@ -36,21 +36,11 @@ struct _tsrm_tls_entry { tsrm_tls_entry *next; }; -#if defined(__cplusplus) || defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 202311L) -# define TSRM_STATIC_ASSERT(c, m) static_assert((c), m) -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) /* C11 */ -# define TSRM_STATIC_ASSERT(c, m) _Static_assert((c), m) -#else -# define TSRM_STATIC_ASSERT(c, m) -#endif -TSRM_STATIC_ASSERT(TSRM_ALIGNED_SIZE(sizeof(tsrm_tls_entry)) == TSRM_FAST_RESERVED_BASE, - "TSRM_FAST_RESERVED_BASE must equal the tsrm_tls_entry header size"); - typedef struct { size_t size; ts_allocate_ctor ctor; ts_allocate_dtor dtor; - size_t fast_offset; + ptrdiff_t fast_offset; int done; } tsrm_resource_type; @@ -67,6 +57,10 @@ static int resource_types_table_size; /* Reserved space for fast globals access */ static size_t tsrm_reserved_pos = 0; static size_t tsrm_reserved_size = 0; +/* Fixed-offset front region (before the TLS entry) and the pending offset for + * the next ts_allocate_fast_id() that should land in it. */ +static size_t tsrm_reserved_front = 0; +static ptrdiff_t tsrm_fixed_offset = 0; static MUTEX_T tsmm_mutex; /* thread-safe memory manager mutex */ static MUTEX_T tsrm_env_mutex; /* tsrm environ mutex */ @@ -164,6 +158,7 @@ TSRM_API bool tsrm_startup(int expected_threads, int expected_resources, int deb tsrm_reserved_pos = 0; tsrm_reserved_size = 0; + tsrm_reserved_front = 0; tsrm_env_mutex = tsrm_mutex_alloc(); @@ -214,7 +209,7 @@ TSRM_API void tsrm_shutdown(void) } else { free(p->storage); } - free(p); + free((char *) p - tsrm_reserved_front); p = next_p; } } @@ -241,6 +236,7 @@ TSRM_API void tsrm_shutdown(void) tsrm_reserved_pos = 0; tsrm_reserved_size = 0; + tsrm_reserved_front = 0; }/*}}}*/ /* {{{ */ @@ -328,11 +324,13 @@ TSRM_API void tsrm_reserve(size_t size) }/*}}}*/ -/* Reserve the front of the fast space for fixed-offset fast resources, so the - * bump-allocated ones (ts_allocate_fast_id) are placed after it. */ +/* Carve a fixed-offset front region out of the reserved space. It is placed + * before the TLS entry, so the hot globals get compile-time-constant negative + * offsets from the cache pointer. */ TSRM_API void tsrm_reserve_fast_front(size_t size) {/*{{{*/ - tsrm_reserved_pos = TSRM_ALIGNED_SIZE(size); + tsrm_reserved_front = TSRM_ALIGNED_SIZE(size); + tsrm_reserved_size -= tsrm_reserved_front; }/*}}}*/ @@ -348,17 +346,21 @@ TSRM_API ts_rsrc_id ts_allocate_fast_id(ts_rsrc_id *rsrc_id, size_t *offset, siz TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Obtained resource id %d", *rsrc_id)); size = TSRM_ALIGNED_SIZE(size); - if (tsrm_reserved_size - tsrm_reserved_pos < size) { + if (tsrm_fixed_offset) { + /* Fixed (negative) offset into the reserved front region. */ + *offset = (size_t) tsrm_fixed_offset; + tsrm_fixed_offset = 0; + } else if (tsrm_reserved_size - tsrm_reserved_pos < size) { TSRM_ERROR((TSRM_ERROR_LEVEL_ERROR, "Unable to allocate space for fast resource")); *rsrc_id = 0; *offset = 0; tsrm_mutex_unlock(tsmm_mutex); return 0; + } else { + *offset = TSRM_ALIGNED_SIZE(sizeof(tsrm_tls_entry)) + tsrm_reserved_pos; + tsrm_reserved_pos += size; } - *offset = TSRM_ALIGNED_SIZE(sizeof(tsrm_tls_entry)) + tsrm_reserved_pos; - tsrm_reserved_pos += size; - /* store the new resource type in the resource sizes table */ if (resource_types_table_size < id_count) { tsrm_resource_type *_tmp; @@ -386,25 +388,13 @@ TSRM_API ts_rsrc_id ts_allocate_fast_id(ts_rsrc_id *rsrc_id, size_t *offset, siz }/*}}}*/ -/* Allocate a fast resource id at a fixed, compile-time-constant offset. - * Like tsrm_reserve(), assumes single-threaded startup. */ -TSRM_API ts_rsrc_id ts_allocate_fast_id_at(ts_rsrc_id *rsrc_id, size_t *offset, size_t fixed_offset, size_t size, ts_allocate_ctor ctor, ts_allocate_dtor dtor) +/* Allocate a fast resource id at a fixed, compile-time-constant offset (a + * negative offset into the reserved front region). Like tsrm_reserve(), assumes + * single-threaded startup. */ +TSRM_API ts_rsrc_id ts_allocate_fast_id_at(ts_rsrc_id *rsrc_id, size_t *offset, ptrdiff_t fixed_offset, size_t size, ts_allocate_ctor ctor, ts_allocate_dtor dtor) {/*{{{*/ - size_t saved_pos = tsrm_reserved_pos; - - if (fixed_offset - TSRM_FAST_RESERVED_BASE > tsrm_reserved_size) { - TSRM_ERROR((TSRM_ERROR_LEVEL_ERROR, "Unable to allocate space for fixed fast resource")); - *rsrc_id = 0; - *offset = 0; - return 0; - } - tsrm_reserved_pos = fixed_offset - TSRM_FAST_RESERVED_BASE; - ts_allocate_fast_id(rsrc_id, offset, size, ctor, dtor); - /* keep ordinary (bump-allocated) fast resources past the high-water mark */ - if (tsrm_reserved_pos < saved_pos) { - tsrm_reserved_pos = saved_pos; - } - return *rsrc_id; + tsrm_fixed_offset = fixed_offset; + return ts_allocate_fast_id(rsrc_id, offset, size, ctor, dtor); }/*}}}*/ static void set_thread_local_storage_resource_to(tsrm_tls_entry *thread_resource) @@ -417,7 +407,10 @@ static void set_thread_local_storage_resource_to(tsrm_tls_entry *thread_resource static void allocate_new_resource(tsrm_tls_entry **thread_resources_ptr, THREAD_T thread_id) {/*{{{*/ TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Creating data structures for thread %x", thread_id)); - (*thread_resources_ptr) = (tsrm_tls_entry *) malloc(TSRM_ALIGNED_SIZE(sizeof(tsrm_tls_entry)) + tsrm_reserved_size); + /* The entry follows the fixed-offset front region. + * hot globals live at negative offsets from the TLS cache pointer. */ + char *block = (char *) malloc(tsrm_reserved_front + TSRM_ALIGNED_SIZE(sizeof(tsrm_tls_entry)) + tsrm_reserved_size); + (*thread_resources_ptr) = (tsrm_tls_entry *) (block + tsrm_reserved_front); (*thread_resources_ptr)->storage = NULL; if (id_count > 0) { (*thread_resources_ptr)->storage = (void **) malloc(sizeof(void *)*id_count); @@ -526,7 +519,7 @@ TSRM_API void *ts_resource_ex(ts_rsrc_id id, THREAD_T *th_id) set_thread_local_storage_resource_to(thread_resources); /* Free up the old resource from the old thread instance */ ts_free_resources(thread_resources); - free(thread_resources); + free((char *) thread_resources - tsrm_reserved_front); /* Allocate a new resource at the same point in the linked list, and relink the next pointer */ allocate_new_resource(last_thread_resources, thread_id); thread_resources = *last_thread_resources; @@ -568,7 +561,7 @@ void ts_free_thread(void) tsrm_tls_table[hash_value] = thread_resources->next; } tsrm_tls_set(0); - free(thread_resources); + free((char *) thread_resources - tsrm_reserved_front); break; } if (thread_resources->next) { diff --git a/TSRM/TSRM.h b/TSRM/TSRM.h index fa7f56904073..c6c1b64d6755 100644 --- a/TSRM/TSRM.h +++ b/TSRM/TSRM.h @@ -20,6 +20,7 @@ # include
#endif +#include #include #include @@ -75,8 +76,6 @@ typedef void (*ts_allocate_dtor)(void *); #define THREAD_HASH_OF(thr,ts) (unsigned long)thr%(unsigned long)ts -#define TSRM_FAST_RESERVED_BASE TSRM_ALIGNED_SIZE(4 * sizeof(void *)) - #ifdef __cplusplus extern "C" { #endif @@ -97,9 +96,9 @@ TSRM_API void tsrm_reserve(size_t size); TSRM_API ts_rsrc_id ts_allocate_fast_id(ts_rsrc_id *rsrc_id, size_t *offset, size_t size, ts_allocate_ctor ctor, ts_allocate_dtor dtor); /* Fast resources at caller-chosen, compile-time-constant offsets. The fixed - * prefix must be reserved after tsrm_reserve() and before any fast id. */ + * front region must be reserved after tsrm_reserve() and before any fast id. */ TSRM_API void tsrm_reserve_fast_front(size_t size); -TSRM_API ts_rsrc_id ts_allocate_fast_id_at(ts_rsrc_id *rsrc_id, size_t *offset, size_t fixed_offset, size_t size, ts_allocate_ctor ctor, ts_allocate_dtor dtor); +TSRM_API ts_rsrc_id ts_allocate_fast_id_at(ts_rsrc_id *rsrc_id, size_t *offset, ptrdiff_t fixed_offset, size_t size, ts_allocate_ctor ctor, ts_allocate_dtor dtor); /* fetches the requested resource for the current thread */ TSRM_API void *ts_resource_ex(ts_rsrc_id id, THREAD_T *th_id); diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index 7a4469826054..d0f2b221b9a7 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -2614,6 +2614,7 @@ typedef struct _zend_alloc_globals { #ifdef ZTS static int alloc_globals_id; static size_t alloc_globals_offset; +# define ZEND_AG_OFFSET (ZEND_SCNG_OFFSET - (ptrdiff_t) TSRM_ALIGNED_SIZE(sizeof(zend_alloc_globals))) # define AG(v) ZEND_TSRMG_FAST(ZEND_AG_OFFSET, zend_alloc_globals *, v) #else # define AG(v) (alloc_globals.v) diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h index 5ab6df774386..61499c0cc23d 100644 --- a/Zend/zend_globals.h +++ b/Zend/zend_globals.h @@ -330,12 +330,11 @@ struct _zend_executor_globals { }; #ifdef ZTS -/* Fixed compile-time offsets of the hot globals in the TSRM fast space. - * AG is last, sizeof(zend_alloc_globals) isn't header-visible. */ -# define ZEND_CG_OFFSET (TSRM_FAST_RESERVED_BASE) -# define ZEND_EG_OFFSET (ZEND_CG_OFFSET + TSRM_ALIGNED_SIZE(sizeof(zend_compiler_globals))) -# define ZEND_SCNG_OFFSET (ZEND_EG_OFFSET + TSRM_ALIGNED_SIZE(sizeof(zend_executor_globals))) -# define ZEND_AG_OFFSET (ZEND_SCNG_OFFSET + TSRM_ALIGNED_SIZE(sizeof(zend_php_scanner_globals))) +/* Compile-time offsets of the hot globals, in a reserved region just before the + * cache pointer. ZEND_AG_OFFSET is furthest, in zend_alloc.c. */ +# define ZEND_CG_OFFSET (-(ptrdiff_t) TSRM_ALIGNED_SIZE(sizeof(zend_compiler_globals))) +# define ZEND_EG_OFFSET (ZEND_CG_OFFSET - (ptrdiff_t) TSRM_ALIGNED_SIZE(sizeof(zend_executor_globals))) +# define ZEND_SCNG_OFFSET (ZEND_EG_OFFSET - (ptrdiff_t) TSRM_ALIGNED_SIZE(sizeof(zend_php_scanner_globals))) #endif #define EG_FLAGS_INITIAL (0) diff --git a/Zend/zend_portability.h b/Zend/zend_portability.h index 33ea4230b6a4..ccad24682fdb 100644 --- a/Zend/zend_portability.h +++ b/Zend/zend_portability.h @@ -862,7 +862,7 @@ extern "C++" { /** @deprecated */ #define ZEND_CGG_DIAGNOSTIC_IGNORED_END ZEND_DIAGNOSTIC_IGNORED_END -#if defined(__cplusplus) || defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 202311L) +#if defined(__cplusplus) # define ZEND_STATIC_ASSERT(c, m) static_assert((c), m) #elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) /* C11 */ # define ZEND_STATIC_ASSERT(c, m) _Static_assert((c), m) From f5fb0e80485a640bbaf1474a05692a0258958a5b Mon Sep 17 00:00:00 2001 From: henderkes Date: Thu, 25 Jun 2026 21:22:24 +0700 Subject: [PATCH 3/5] clarify comment --- main/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/main.c b/main/main.c index c1a09b5b3e6a..9390a5644374 100644 --- a/main/main.c +++ b/main/main.c @@ -2842,7 +2842,7 @@ PHPAPI bool php_tsrm_startup_ex(int expected_threads) { bool ret = tsrm_startup(expected_threads, 1, 0, NULL); php_reserve_tsrm_memory(); - /* Must reserve exactly the prefix laid out by ZEND_*_OFFSET (zend_globals.h). */ + /* Must cover the total size of every ZEND_*_OFFSET global, or the furthest underflows the block. */ tsrm_reserve_fast_front( TSRM_ALIGNED_SIZE(sizeof(zend_compiler_globals)) + TSRM_ALIGNED_SIZE(sizeof(zend_executor_globals)) + From 8cd97918069958a74467b3f02c476379dc4df132 Mon Sep 17 00:00:00 2001 From: henderkes Date: Thu, 25 Jun 2026 21:24:10 +0700 Subject: [PATCH 4/5] apply @arnaud-lb's suggestion (ts_allocate_fast_id becomes a bump-allocating version of ts_allocate_fast_id_at) --- TSRM/TSRM.c | 49 ++++++++++++++++++++++--------------------------- 1 file changed, 22 insertions(+), 27 deletions(-) diff --git a/TSRM/TSRM.c b/TSRM/TSRM.c index 963a11b281bf..1c51fdb17d5d 100644 --- a/TSRM/TSRM.c +++ b/TSRM/TSRM.c @@ -57,10 +57,7 @@ static int resource_types_table_size; /* Reserved space for fast globals access */ static size_t tsrm_reserved_pos = 0; static size_t tsrm_reserved_size = 0; -/* Fixed-offset front region (before the TLS entry) and the pending offset for - * the next ts_allocate_fast_id() that should land in it. */ static size_t tsrm_reserved_front = 0; -static ptrdiff_t tsrm_fixed_offset = 0; static MUTEX_T tsmm_mutex; /* thread-safe memory manager mutex */ static MUTEX_T tsrm_env_mutex; /* tsrm environ mutex */ @@ -337,29 +334,37 @@ TSRM_API void tsrm_reserve_fast_front(size_t size) /* allocates a new fast thread-safe-resource id */ TSRM_API ts_rsrc_id ts_allocate_fast_id(ts_rsrc_id *rsrc_id, size_t *offset, size_t size, ts_allocate_ctor ctor, ts_allocate_dtor dtor) {/*{{{*/ - TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Obtaining a new fast resource id, %d bytes", size)); + ptrdiff_t fixed_offset; tsrm_mutex_lock(tsmm_mutex); - - /* obtain a resource id */ - *rsrc_id = TSRM_SHUFFLE_RSRC_ID(id_count++); - TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Obtained resource id %d", *rsrc_id)); - size = TSRM_ALIGNED_SIZE(size); - if (tsrm_fixed_offset) { - /* Fixed (negative) offset into the reserved front region. */ - *offset = (size_t) tsrm_fixed_offset; - tsrm_fixed_offset = 0; - } else if (tsrm_reserved_size - tsrm_reserved_pos < size) { + if (tsrm_reserved_size - tsrm_reserved_pos < size) { TSRM_ERROR((TSRM_ERROR_LEVEL_ERROR, "Unable to allocate space for fast resource")); *rsrc_id = 0; *offset = 0; tsrm_mutex_unlock(tsmm_mutex); return 0; - } else { - *offset = TSRM_ALIGNED_SIZE(sizeof(tsrm_tls_entry)) + tsrm_reserved_pos; - tsrm_reserved_pos += size; } + fixed_offset = TSRM_ALIGNED_SIZE(sizeof(tsrm_tls_entry)) + tsrm_reserved_pos; + tsrm_reserved_pos += size; + tsrm_mutex_unlock(tsmm_mutex); + + return ts_allocate_fast_id_at(rsrc_id, offset, fixed_offset, size, ctor, dtor); +}/*}}}*/ + + +TSRM_API ts_rsrc_id ts_allocate_fast_id_at(ts_rsrc_id *rsrc_id, size_t *offset, ptrdiff_t fixed_offset, size_t size, ts_allocate_ctor ctor, ts_allocate_dtor dtor) +{/*{{{*/ + TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Obtaining a new fast resource id, %d bytes", size)); + + tsrm_mutex_lock(tsmm_mutex); + + /* obtain a resource id */ + *rsrc_id = TSRM_SHUFFLE_RSRC_ID(id_count++); + TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Obtained resource id %d", *rsrc_id)); + + size = TSRM_ALIGNED_SIZE(size); + *offset = (size_t) fixed_offset; /* store the new resource type in the resource sizes table */ if (resource_types_table_size < id_count) { @@ -387,16 +392,6 @@ TSRM_API ts_rsrc_id ts_allocate_fast_id(ts_rsrc_id *rsrc_id, size_t *offset, siz return *rsrc_id; }/*}}}*/ - -/* Allocate a fast resource id at a fixed, compile-time-constant offset (a - * negative offset into the reserved front region). Like tsrm_reserve(), assumes - * single-threaded startup. */ -TSRM_API ts_rsrc_id ts_allocate_fast_id_at(ts_rsrc_id *rsrc_id, size_t *offset, ptrdiff_t fixed_offset, size_t size, ts_allocate_ctor ctor, ts_allocate_dtor dtor) -{/*{{{*/ - tsrm_fixed_offset = fixed_offset; - return ts_allocate_fast_id(rsrc_id, offset, size, ctor, dtor); -}/*}}}*/ - static void set_thread_local_storage_resource_to(tsrm_tls_entry *thread_resource) { tsrm_tls_set(thread_resource); From 80a1269d7ae74e52d0300f6274595772a2234840 Mon Sep 17 00:00:00 2001 From: henderkes Date: Sat, 27 Jun 2026 09:24:18 +0700 Subject: [PATCH 5/5] suggestions --- TSRM/TSRM.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/TSRM/TSRM.c b/TSRM/TSRM.c index 1c51fdb17d5d..4222e88755d6 100644 --- a/TSRM/TSRM.c +++ b/TSRM/TSRM.c @@ -325,17 +325,15 @@ TSRM_API void tsrm_reserve(size_t size) * before the TLS entry, so the hot globals get compile-time-constant negative * offsets from the cache pointer. */ TSRM_API void tsrm_reserve_fast_front(size_t size) -{/*{{{*/ +{ tsrm_reserved_front = TSRM_ALIGNED_SIZE(size); tsrm_reserved_size -= tsrm_reserved_front; -}/*}}}*/ +} /* allocates a new fast thread-safe-resource id */ TSRM_API ts_rsrc_id ts_allocate_fast_id(ts_rsrc_id *rsrc_id, size_t *offset, size_t size, ts_allocate_ctor ctor, ts_allocate_dtor dtor) {/*{{{*/ - ptrdiff_t fixed_offset; - tsrm_mutex_lock(tsmm_mutex); size = TSRM_ALIGNED_SIZE(size); if (tsrm_reserved_size - tsrm_reserved_pos < size) { @@ -345,7 +343,7 @@ TSRM_API ts_rsrc_id ts_allocate_fast_id(ts_rsrc_id *rsrc_id, size_t *offset, siz tsrm_mutex_unlock(tsmm_mutex); return 0; } - fixed_offset = TSRM_ALIGNED_SIZE(sizeof(tsrm_tls_entry)) + tsrm_reserved_pos; + ptrdiff_t fixed_offset = TSRM_ALIGNED_SIZE(sizeof(tsrm_tls_entry)) + tsrm_reserved_pos; tsrm_reserved_pos += size; tsrm_mutex_unlock(tsmm_mutex); @@ -354,7 +352,7 @@ TSRM_API ts_rsrc_id ts_allocate_fast_id(ts_rsrc_id *rsrc_id, size_t *offset, siz TSRM_API ts_rsrc_id ts_allocate_fast_id_at(ts_rsrc_id *rsrc_id, size_t *offset, ptrdiff_t fixed_offset, size_t size, ts_allocate_ctor ctor, ts_allocate_dtor dtor) -{/*{{{*/ +{ TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Obtaining a new fast resource id, %d bytes", size)); tsrm_mutex_lock(tsmm_mutex); @@ -390,7 +388,7 @@ TSRM_API ts_rsrc_id ts_allocate_fast_id_at(ts_rsrc_id *rsrc_id, size_t *offset, TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Successfully allocated new resource id %d", *rsrc_id)); return *rsrc_id; -}/*}}}*/ +} static void set_thread_local_storage_resource_to(tsrm_tls_entry *thread_resource) {