@@ -205,17 +205,52 @@ static size_t tsrm_ls_cache_tcb_offset = 0;
205205static size_t tsrm_tls_index = -1;
206206static size_t tsrm_tls_offset = -1;
207207
208+ # ifdef ZEND_EG_TLS
209+ /* When nonzero, &executor_globals_tls/&compiler_globals_tls equal the thread
210+ * pointer plus this offset, so the JIT forms them without a runtime call. */
211+ static size_t eg_tls_tcb_offset = 0;
212+ static size_t cg_tls_tcb_offset = 0;
213+ /* gottpoff yields the offset from the %fs-based thread pointer that ir_TLS(0)
214+ * loads. */
215+ # if defined(__ELF__) && defined(__x86_64__) && defined(__GNUC__) && !defined(TSRM_TLS_MODEL_DEFAULT)
216+ # define ZEND_JIT_TLS_TCB_OFFSET(sym) __extension__({ \
217+ size_t _off; \
218+ __asm__ ("movq " #sym "@gottpoff(%%rip),%0" : "=r" (_off)); \
219+ _off; \
220+ })
221+ # elif defined(__ELF__) && defined(__aarch64__) && !defined(__APPLE__) && \
222+ (defined(__GNUC__) || defined(__clang__))
223+ /* The TLS variable sits at a fixed offset from tpidr_el0 (the thread pointer
224+ * the JIT reads with mrs); compute it once on the main thread. Subtracting the
225+ * thread pointer is model-independent (works for both local- and initial-exec)
226+ * and matches tsrm_get_ls_cache_tcb_offset()'s tprel reasoning. */
227+ # define ZEND_JIT_TLS_TCB_OFFSET(sym) __extension__({ \
228+ char *_tp; \
229+ __asm__ ("mrs %0, tpidr_el0" : "=r" (_tp)); \
230+ (size_t)((char*)&(sym) - _tp); \
231+ })
232+ # else
233+ # define ZEND_JIT_TLS_TCB_OFFSET(sym) ((size_t)0)
234+ # endif
235+ # endif
236+
208237# define EG_TLS_OFFSET(field) \
209238 (executor_globals_offset + offsetof(zend_executor_globals, field))
210239
211240# define CG_TLS_OFFSET(field) \
212241 (compiler_globals_offset + offsetof(zend_compiler_globals, field))
213242
214- # define jit_EG(_field) \
243+ # ifdef ZEND_EG_TLS
244+ # define jit_EG(_field) \
245+ ir_ADD_OFFSET(jit_EG_base(jit), offsetof(zend_executor_globals, _field))
246+ # define jit_CG(_field) \
247+ ir_ADD_OFFSET(jit_CG_base(jit), offsetof(zend_compiler_globals, _field))
248+ # else
249+ # define jit_EG(_field) \
215250 ir_ADD_OFFSET(jit_TLS(jit), EG_TLS_OFFSET(_field))
216-
217- # define jit_CG(_field) \
251+ # define jit_CG(_field) \
218252 ir_ADD_OFFSET(jit_TLS(jit), CG_TLS_OFFSET(_field))
253+ # endif
219254
220255#else
221256
@@ -299,6 +334,11 @@ typedef struct _zend_jit_ctx {
299334 int b; /* current basic block number or -1 */
300335#ifdef ZTS
301336 ir_ref tls;
337+ # ifdef ZEND_EG_TLS
338+ ir_ref tp; /* cached thread pointer for &EG/&CG */
339+ ir_ref eg_tls; /* cached base of __thread executor_globals_tls */
340+ ir_ref cg_tls; /* cached base of __thread compiler_globals_tls */
341+ # endif
302342#endif
303343 ir_ref fp;
304344 ir_ref poly_func_ref; /* restored from parent trace snapshot */
@@ -494,7 +534,64 @@ static void * ZEND_FASTCALL zend_jit_get_tsrm_ls_cache(void)
494534 return _tsrm_ls_cache;
495535}
496536
497- static ir_ref jit_TLS(zend_jit_ctx *jit)
537+ # ifdef ZEND_EG_TLS
538+ static void * ZEND_FASTCALL zend_jit_get_eg_tls(void)
539+ {
540+ return &executor_globals_tls;
541+ }
542+ static void * ZEND_FASTCALL zend_jit_get_cg_tls(void)
543+ {
544+ return &compiler_globals_tls;
545+ }
546+
547+ /* Walk the control chain back from the current point: reuse the cached ref if we
548+ * reach it (it still dominates here), but bail at a block start or a call, since
549+ * the cached value lives in a caller-saved register that a call would clobber. */
550+ static ir_ref jit_tls_reuse(zend_jit_ctx *jit, ir_ref cached)
551+ {
552+ ir_ref ref = jit->ctx.control;
553+
554+ while (cached) {
555+ if (ref == cached) {
556+ return cached;
557+ }
558+ ir_insn *insn = &jit->ctx.ir_base[ref];
559+ if (insn->op >= IR_START || insn->op == IR_CALL) {
560+ break;
561+ }
562+ ref = insn->op1;
563+ }
564+ return IR_UNUSED;
565+ }
566+
567+ /* Thread pointer, cached per basic block, used to form &EG/&CG with an add. */
568+ static ir_ref jit_TP(zend_jit_ctx *jit)
569+ {
570+ ZEND_ASSERT(jit->ctx.control);
571+ if (!jit_tls_reuse(jit, jit->tp)) {
572+ jit->tp = ir_TLS(0, IR_NULL);
573+ }
574+ return jit->tp;
575+ }
576+
577+ /* Used where the TCB offset is unknown: resolve the base via a cached call. */
578+ static ir_ref jit_GLOBALS_TLS_call(zend_jit_ctx *jit, ir_ref *cache, const void *fn)
579+ {
580+ ZEND_ASSERT(jit->ctx.control);
581+ if (!jit_tls_reuse(jit, *cache)) {
582+ *cache = ir_CALL(IR_ADDR, ir_CONST_FC_FUNC(fn));
583+ }
584+ return *cache;
585+ }
586+ # define jit_EG_base(jit) (eg_tls_tcb_offset \
587+ ? ir_ADD_OFFSET(jit_TP(jit), eg_tls_tcb_offset) \
588+ : jit_GLOBALS_TLS_call((jit), &(jit)->eg_tls, zend_jit_get_eg_tls))
589+ # define jit_CG_base(jit) (cg_tls_tcb_offset \
590+ ? ir_ADD_OFFSET(jit_TP(jit), cg_tls_tcb_offset) \
591+ : jit_GLOBALS_TLS_call((jit), &(jit)->cg_tls, zend_jit_get_cg_tls))
592+ # endif
593+
594+ static ZEND_ATTRIBUTE_UNUSED ir_ref jit_TLS(zend_jit_ctx *jit)
498595{
499596 ZEND_ASSERT(jit->ctx.control);
500597 if (jit->tls) {
@@ -2821,6 +2918,11 @@ static void zend_jit_init_ctx(zend_jit_ctx *jit, uint32_t flags)
28212918 jit->b = -1;
28222919#ifdef ZTS
28232920 jit->tls = IR_UNUSED;
2921+ # ifdef ZEND_EG_TLS
2922+ jit->tp = IR_UNUSED;
2923+ jit->eg_tls = IR_UNUSED;
2924+ jit->cg_tls = IR_UNUSED;
2925+ # endif
28242926#endif
28252927 jit->fp = IR_UNUSED;
28262928 jit->poly_func_ref = IR_UNUSED;
@@ -3215,6 +3317,10 @@ static void zend_jit_setup_disasm(void)
32153317 REGISTER_DATA(CG(map_ptr_base));
32163318#else /* ZTS */
32173319 REGISTER_HELPER(zend_jit_get_tsrm_ls_cache);
3320+ # ifdef ZEND_EG_TLS
3321+ REGISTER_HELPER(zend_jit_get_eg_tls);
3322+ REGISTER_HELPER(zend_jit_get_cg_tls);
3323+ # endif
32183324#endif
32193325#endif
32203326}
@@ -3434,6 +3540,10 @@ static void zend_jit_setup(bool reattached)
34343540 zend_accel_error(ACCEL_LOG_INFO,
34353541 "Could not get _tsrm_ls_cache offsets, will fallback to runtime resolution");
34363542 }
3543+ # ifdef ZEND_EG_TLS
3544+ eg_tls_tcb_offset = ZEND_JIT_TLS_TCB_OFFSET(executor_globals_tls);
3545+ cg_tls_tcb_offset = ZEND_JIT_TLS_TCB_OFFSET(compiler_globals_tls);
3546+ # endif
34373547#endif
34383548
34393549#if !defined(ZEND_WIN32) && !defined(IR_TARGET_AARCH64)
0 commit comments