diff --git a/Zend/Optimizer/compact_literals.c b/Zend/Optimizer/compact_literals.c index b0ad30e6b90c..34f6d5500cf7 100644 --- a/Zend/Optimizer/compact_literals.c +++ b/Zend/Optimizer/compact_literals.c @@ -634,7 +634,7 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx opline->result.num = func_slot[opline->op2.constant]; } else { opline->result.num = cache_size; - cache_size += sizeof(void *); + cache_size += opline->opcode == ZEND_INIT_FCALL_BY_NAME ? 2 * sizeof(void *) : sizeof(void *); func_slot[opline->op2.constant] = opline->result.num; } break; diff --git a/Zend/Optimizer/zend_optimizer.c b/Zend/Optimizer/zend_optimizer.c index ebc76017c602..59b719466707 100644 --- a/Zend/Optimizer/zend_optimizer.c +++ b/Zend/Optimizer/zend_optimizer.c @@ -369,7 +369,7 @@ bool zend_optimizer_update_op2_const(zend_op_array *op_array, drop_leading_backslash(val); opline->op2.constant = zend_optimizer_add_literal(op_array, val); zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val))); - opline->result.num = alloc_cache_slots(op_array, 1); + opline->result.num = alloc_cache_slots(op_array, 2); break; case ZEND_ASSIGN_STATIC_PROP: case ZEND_ASSIGN_STATIC_PROP_REF: @@ -422,7 +422,7 @@ bool zend_optimizer_update_op2_const(zend_op_array *op_array, drop_leading_backslash(val); opline->op2.constant = zend_optimizer_add_literal(op_array, val); zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val))); - opline->result.num = alloc_cache_slots(op_array, 1); + opline->result.num = alloc_cache_slots(op_array, 2); } else { opline->op2.constant = zend_optimizer_add_literal(op_array, val); } diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 039e95913f6d..6d0cd22bfda3 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -3706,7 +3706,7 @@ static void zend_compile_dynamic_call(znode *result, znode *name_node, zend_ast opline->opcode = ZEND_INIT_FCALL_BY_NAME; opline->op2_type = IS_CONST; opline->op2.constant = zend_add_func_name_literal(str); - opline->result.num = zend_alloc_cache_slot(); + opline->result.num = zend_alloc_cache_slots(2); } } else { zend_emit_op(NULL, ZEND_INIT_DYNAMIC_CALL, NULL, name_node); diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 934860c33012..9939ffa7eada 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -3749,7 +3749,8 @@ ZEND_VM_HOT_HANDLER(59, ZEND_INIT_FCALL_BY_NAME, ANY, CONST, NUM|CACHE_SLOT) zval *function_name, *func; zend_execute_data *call; - fbc = CACHED_PTR(opline->result.num); + int num = opline->result.num; + fbc = CACHED_PTR(num); if (UNEXPECTED(fbc == NULL)) { function_name = (zval*)RT_CONSTANT(opline, opline->op2); func = zend_hash_find_known_hash(EG(function_table), Z_STR_P(function_name+1)); @@ -3760,9 +3761,13 @@ ZEND_VM_HOT_HANDLER(59, ZEND_INIT_FCALL_BY_NAME, ANY, CONST, NUM|CACHE_SLOT) if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array); } - CACHE_PTR(opline->result.num, fbc); + CACHE_PTR(num, fbc); + CACHE_PTR(num + sizeof(void*), (void*)(uintptr_t)zend_vm_calc_used_stack(opline->extended_value, fbc)); } - call = _zend_vm_stack_push_call_frame(ZEND_CALL_NESTED_FUNCTION, + /* Because zend_vm_calc_used_stack depends only on the number of arguments and the function definition fbc, + * it can be cached in the run time cache. */ + call = _zend_vm_stack_push_call_frame_ex( + (uint32_t)(uintptr_t)CACHED_PTR(num + sizeof(void*)), ZEND_CALL_NESTED_FUNCTION, fbc, opline->extended_value, NULL); call->prev_execute_data = EX(call); EX(call) = call; diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 37c2d1ee653e..4b893ad89cc1 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -3574,7 +3574,8 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_FCALL_BY_NAME zval *function_name, *func; zend_execute_data *call; - fbc = CACHED_PTR(opline->result.num); + int num = opline->result.num; + fbc = CACHED_PTR(num); if (UNEXPECTED(fbc == NULL)) { function_name = (zval*)RT_CONSTANT(opline, opline->op2); func = zend_hash_find_known_hash(EG(function_table), Z_STR_P(function_name+1)); @@ -3585,9 +3586,13 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_FCALL_BY_NAME if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array); } - CACHE_PTR(opline->result.num, fbc); + CACHE_PTR(num, fbc); + CACHE_PTR(num + sizeof(void*), (void*)(uintptr_t)zend_vm_calc_used_stack(opline->extended_value, fbc)); } - call = _zend_vm_stack_push_call_frame(ZEND_CALL_NESTED_FUNCTION, + /* Because zend_vm_calc_used_stack depends only on the number of arguments and the function definition fbc, + * it can be cached in the run time cache. */ + call = _zend_vm_stack_push_call_frame_ex( + (uint32_t)(uintptr_t)CACHED_PTR(num + sizeof(void*)), ZEND_CALL_NESTED_FUNCTION, fbc, opline->extended_value, NULL); call->prev_execute_data = EX(call); EX(call) = call; diff --git a/ext/opcache/jit/zend_jit_arm64.dasc b/ext/opcache/jit/zend_jit_arm64.dasc index 5a3f8e0b86c6..a62e58fe394b 100644 --- a/ext/opcache/jit/zend_jit_arm64.dasc +++ b/ext/opcache/jit/zend_jit_arm64.dasc @@ -8765,9 +8765,11 @@ static int zend_jit_init_fcall(dasm_State **Dst, const zend_op *opline, uint32_t | ADD_SUB_64_WITH_CONST_32 add, FCARG2x, REG2, opline->result.num, TMP1 | EXT_CALL zend_jit_find_func_helper, REG0 } else if (opline->opcode == ZEND_INIT_FCALL_BY_NAME) { + /* TODO: Actually use the precomputed stack size in the JIT For ZEND_INIT_FCALL_BY_NAME */ | LOAD_ADDR FCARG1x, Z_STR_P(zv + 1); | ADD_SUB_64_WITH_CONST_32 add, FCARG2x, REG2, opline->result.num, TMP1 - | EXT_CALL zend_jit_find_func_helper, REG0 + | LOAD_ADDR CARG3, opline + | EXT_CALL zend_jit_find_func_by_name_helper, REG0 } else if (opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME) { | LOAD_ADDR FCARG1x, zv; | ADD_SUB_64_WITH_CONST_32 add, FCARG2x, REG2, opline->result.num, TMP1 diff --git a/ext/opcache/jit/zend_jit_helpers.c b/ext/opcache/jit/zend_jit_helpers.c index 27585385046c..ed7ccb5fc6cc 100644 --- a/ext/opcache/jit/zend_jit_helpers.c +++ b/ext/opcache/jit/zend_jit_helpers.c @@ -77,6 +77,24 @@ static zend_function* ZEND_FASTCALL zend_jit_find_func_helper(zend_string *name, return fbc; } +static zend_function* ZEND_FASTCALL zend_jit_find_func_by_name_helper(zend_string *name, void **cache_slot, zend_op *opline) +{ + zval *func = zend_hash_find_known_hash(EG(function_table), name); + zend_function *fbc; + + if (UNEXPECTED(func == NULL)) { + /* When opcache.preload is used, func can be null when compiling - ext/opcache/tests/jit/bug81256.phpt */ + return NULL; + } + fbc = Z_FUNC_P(func); + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { + fbc = _zend_jit_init_func_run_time_cache(&fbc->op_array); + } + cache_slot[0] = fbc; + cache_slot[1] = (void*)(uintptr_t)zend_vm_calc_used_stack(opline->extended_value, fbc); + return fbc; +} + static zend_function* ZEND_FASTCALL zend_jit_find_ns_func_helper(zval *func_name, void **cache_slot) { zval *func = zend_hash_find_known_hash(EG(function_table), Z_STR_P(func_name + 1)); diff --git a/ext/opcache/jit/zend_jit_x86.dasc b/ext/opcache/jit/zend_jit_x86.dasc index 9ea1bbf17878..64193cbaffd2 100644 --- a/ext/opcache/jit/zend_jit_x86.dasc +++ b/ext/opcache/jit/zend_jit_x86.dasc @@ -9350,7 +9350,16 @@ static int zend_jit_init_fcall(dasm_State **Dst, const zend_op *opline, uint32_t } else if (opline->opcode == ZEND_INIT_FCALL_BY_NAME) { | LOAD_ADDR FCARG1a, Z_STR_P(zv + 1); | lea FCARG2a, aword [r2 + opline->result.num] - | EXT_CALL zend_jit_find_func_helper, r0 + |.if X64 + | LOAD_ADDR CARG3, opline + |.else + | sub r4, 12 + | push opline + |.endif + | EXT_CALL zend_jit_find_func_by_name_helper, r0 + |.if not(X64) + | add r4, 12 + |.endif } else if (opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME) { | LOAD_ADDR FCARG1a, zv; | lea FCARG2a, aword [r2 + opline->result.num]