@@ -106,7 +106,7 @@ static zend_always_inline void php_parallel_cache_type(zend_type *type) { /* {{{
106106 if (ZEND_TYPE_USES_ARENA (* type )) {
107107 ZEND_TYPE_FULL_MASK (* type ) &= ~_ZEND_TYPE_ARENA_BIT ;
108108 }
109-
109+
110110 ZEND_TYPE_SET_PTR (* type , list );
111111 }
112112
@@ -125,6 +125,9 @@ static zend_always_inline void php_parallel_cache_type(zend_type *type) { /* {{{
125125/* {{{ */
126126static zend_op_array * php_parallel_cache_create (const zend_function * source , bool statics ) {
127127 zend_op_array * cached = php_parallel_cache_copy_mem ((void * ) source , sizeof (zend_op_array ));
128+ uint32_t * literal_map = NULL ;
129+ uint32_t * offset_map = NULL ;
130+ uint32_t new_last_literal = cached -> last_literal ;
128131
129132 cached -> fn_flags |= ZEND_ACC_IMMUTABLE ;
130133
@@ -144,13 +147,13 @@ static zend_op_array* php_parallel_cache_create(const zend_function *source, boo
144147#if PHP_VERSION_ID >= 80100
145148 if (cached -> num_dynamic_func_defs ) {
146149 uint32_t it = 0 ;
147-
150+
148151 cached -> dynamic_func_defs = php_parallel_cache_copy_mem (
149152 cached -> dynamic_func_defs ,
150153 sizeof (zend_op_array * ) * cached -> num_dynamic_func_defs );
151-
154+
152155 while (it < cached -> num_dynamic_func_defs ) {
153- cached -> dynamic_func_defs [it ] =
156+ cached -> dynamic_func_defs [it ] =
154157 (zend_op_array * ) php_parallel_cache_create (
155158 (zend_function * ) cached -> dynamic_func_defs [it ], statics );
156159 it ++ ;
@@ -165,15 +168,51 @@ static zend_op_array* php_parallel_cache_create(const zend_function *source, boo
165168 cached -> refcount = NULL ;
166169
167170 if (cached -> last_literal ) {
168- zval * literal = cached -> literals ,
169- * end = literal + cached -> last_literal ;
170- zval * slot = php_parallel_cache_copy_mem (
171- cached -> literals ,
172- sizeof (zval ) * cached -> last_literal );
171+ zend_op * src_opline = source -> op_array .opcodes ;
172+ zend_op * src_end = src_opline + source -> op_array .last ;
173+
174+ // A map to keep track of which literals are referenced by the
175+ // `ZEND_INIT_FCALL` opcodes we found so that we can expand those later
176+ literal_map = emalloc (sizeof (uint32_t ) * cached -> last_literal );
177+ memset (literal_map , 0 , sizeof (uint32_t ) * cached -> last_literal );
178+
179+ // Search for `ZEND_INIT_FCALL` opcodes and remember the indexes for the
180+ // literals, as we are rewriting them later to `ZEND_INIT_FCALL_BY_NAME`
181+ // which requires a second, lower cased literal just in the next literal
182+ // slot.
183+ while (src_opline < src_end ) {
184+ if (src_opline -> opcode == ZEND_INIT_FCALL && src_opline -> op2_type == IS_CONST ) {
185+ uint32_t idx ;
186+ #if ZEND_USE_ABS_CONST_ADDR
187+ idx = (zval * )src_opline -> op2 .zv - source -> op_array .literals ;
188+ #else
189+ idx = ((zval * )((char * )src_opline + src_opline -> op2 .constant ) - source -> op_array .literals );
190+ #endif
191+ if (idx < cached -> last_literal ) {
192+ if (literal_map [idx ] == 0 ) {
193+ literal_map [idx ] = 1 ;
194+ new_last_literal ++ ;
195+ }
196+ }
197+ }
198+ src_opline ++ ;
199+ }
200+ }
201+
202+ if (new_last_literal ) {
203+ zval * literal = source -> op_array .literals ;
204+ zval * slot = php_parallel_cache_alloc (sizeof (zval ) * new_last_literal );
205+ uint32_t idx = 0 ;
206+
207+ offset_map = emalloc (sizeof (uint32_t ) * cached -> last_literal );
173208
174209 cached -> literals = slot ;
175210
176- while (literal < end ) {
211+ for (uint32_t i = 0 ; i < cached -> last_literal ; i ++ ) {
212+ /* Record the mapping from old literal index (i) to new literal index (idx)
213+ so we can update opcode operands later. */
214+ offset_map [i ] = idx ;
215+
177216 if (Z_TYPE_P (literal ) == IS_ARRAY ) {
178217 ZVAL_ARR (slot ,
179218 php_parallel_copy_hash_persistent (
@@ -183,12 +222,27 @@ static zend_op_array* php_parallel_cache_create(const zend_function *source, boo
183222 } else if (Z_TYPE_P (literal ) == IS_STRING ) {
184223 ZVAL_STR (slot ,
185224 php_parallel_copy_string_interned (Z_STR_P (literal )));
225+ } else {
226+ * slot = * literal ;
227+ }
228+
229+ Z_TYPE_FLAGS_P (slot ) &= ~(IS_TYPE_REFCOUNTED |IS_TYPE_COLLECTABLE );
230+
231+ /* If this literal was used by INIT_FCALL, insert its lowercased version next. */
232+ if (literal_map [i ]) {
233+ zend_string * lower = zend_string_tolower (Z_STR_P (slot ));
234+ slot ++ ;
235+ idx ++ ;
236+ ZVAL_STR (slot , php_parallel_copy_string_interned (lower ));
237+ zend_string_release (lower );
238+ Z_TYPE_FLAGS_P (slot ) &= ~(IS_TYPE_REFCOUNTED |IS_TYPE_COLLECTABLE );
186239 }
187240
188- Z_TYPE_FLAGS_P (slot ) &= ~(IS_TYPE_REFCOUNTED |IS_TYPE_COLLECTABLE );
189241 literal ++ ;
190242 slot ++ ;
243+ idx ++ ;
191244 }
245+ cached -> last_literal = new_last_literal ;
192246 }
193247
194248 if (cached -> last_var ) {
@@ -211,15 +265,27 @@ static zend_op_array* php_parallel_cache_create(const zend_function *source, boo
211265 * end = opline + cached -> last ;
212266
213267 while (opline < end ) {
268+ /* Replace ZEND_INIT_FCALL with ZEND_INIT_FCALL_BY_NAME.
269+ We must clear op1_type (IS_UNUSED) and op1.var (0) to invalidate the
270+ original thread's cache slot. */
271+ if (opline -> opcode == ZEND_INIT_FCALL ) {
272+ opline -> opcode = ZEND_INIT_FCALL_BY_NAME ;
273+ opline -> op1_type = IS_UNUSED ;
274+ opline -> op1 .var = 0 ;
275+ ZEND_VM_SET_OPCODE_HANDLER (opline );
276+ }
277+
278+ /* Remap IS_CONST operands to their new locations in the expanded literal table
279+ using the offset_map we built earlier. */
214280 if (opline -> op1_type == IS_CONST ) {
281+ uint32_t idx ;
282+ zend_op * src_opline = source -> op_array .opcodes + (opline - opcodes );
215283#if ZEND_USE_ABS_CONST_ADDR
216- opline -> op1 .zv = (zval * )((char * )opline -> op1 .zv + ((char * )cached -> literals - (char * )source -> op_array .literals ));
284+ idx = (zval * )src_opline -> op1 .zv - source -> op_array .literals ;
285+ opline -> op1 .zv = & cached -> literals [offset_map [idx ]];
217286#else
218- opline -> op1 .constant =
219- (char * )(cached -> literals +
220- ((zval * )((char * )(source -> op_array .opcodes + (opline - opcodes )) +
221- (int32_t )opline -> op1 .constant ) - source -> op_array .literals )) -
222- (char * )opline ;
287+ idx = ((zval * )((char * )src_opline + src_opline -> op1 .constant ) - source -> op_array .literals );
288+ opline -> op1 .constant = (char * )& cached -> literals [offset_map [idx ]] - (char * )opline ;
223289#endif
224290 if (opline -> opcode == ZEND_SEND_VAL
225291 || opline -> opcode == ZEND_SEND_VAL_EX
@@ -228,14 +294,14 @@ static zend_op_array* php_parallel_cache_create(const zend_function *source, boo
228294 }
229295 }
230296 if (opline -> op2_type == IS_CONST ) {
297+ uint32_t idx ;
298+ zend_op * src_opline = source -> op_array .opcodes + (opline - opcodes );
231299#if ZEND_USE_ABS_CONST_ADDR
232- opline -> op2 .zv = (zval * )((char * )opline -> op2 .zv + ((char * )cached -> literals - (char * )source -> op_array .literals ));
300+ idx = (zval * )src_opline -> op2 .zv - source -> op_array .literals ;
301+ opline -> op2 .zv = & cached -> literals [offset_map [idx ]];
233302#else
234- opline -> op2 .constant =
235- (char * )(cached -> literals +
236- ((zval * )((char * )(source -> op_array .opcodes + (opline - opcodes )) +
237- (int32_t )opline -> op2 .constant ) - source -> op_array .literals )) -
238- (char * )opline ;
303+ idx = ((zval * )((char * )src_opline + src_opline -> op2 .constant ) - source -> op_array .literals );
304+ opline -> op2 .constant = (char * )& cached -> literals [offset_map [idx ]] - (char * )opline ;
239305#endif
240306 }
241307#if ZEND_USE_ABS_JMP_ADDR
@@ -272,6 +338,14 @@ static zend_op_array* php_parallel_cache_create(const zend_function *source, boo
272338 cached -> opcodes = opcodes ;
273339 }
274340
341+ if (literal_map ) {
342+ efree (literal_map );
343+ }
344+
345+ if (offset_map ) {
346+ efree (offset_map );
347+ }
348+
275349 if (cached -> arg_info ) {
276350 zend_arg_info * it = cached -> arg_info ,
277351 * end = it + cached -> num_args ,
@@ -291,7 +365,7 @@ static zend_op_array* php_parallel_cache_create(const zend_function *source, boo
291365 info -> name =
292366 php_parallel_copy_string_interned (it -> name );
293367 }
294-
368+
295369 php_parallel_cache_type (& info -> type );
296370
297371 info ++ ;
@@ -335,7 +409,7 @@ static zend_op_array* php_parallel_cache_create(const zend_function *source, boo
335409/* {{{ */
336410static zend_always_inline zend_function * php_parallel_cache_function_ex (const zend_function * source , bool statics ) {
337411 zend_op_array * cached ;
338-
412+
339413 pthread_mutex_lock (& PCG (mutex ));
340414
341415 if ((cached = zend_hash_index_find_ptr (& PCG (table ), (zend_ulong ) source -> op_array .opcodes ))) {
@@ -346,7 +420,7 @@ static zend_always_inline zend_function* php_parallel_cache_function_ex(const ze
346420
347421 zend_hash_index_add_ptr (
348422 & PCG (table ),
349- (zend_ulong ) source -> op_array .opcodes ,
423+ (zend_ulong ) source -> op_array .opcodes ,
350424 cached );
351425
352426_php_parallel_cached_function_return :
0 commit comments