From 2602e5890e6813a7072e8ca9f98e21588d8915ac Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 3 Oct 2024 22:28:29 +0300 Subject: [PATCH 1/4] JIT for INIT_STATIC_METHOD_CALL --- Zend/zend_execute.c | 2 +- Zend/zend_execute.h | 1 + ext/opcache/jit/zend_jit.c | 13 ++ ext/opcache/jit/zend_jit_helpers.c | 86 ++++++++++++ ext/opcache/jit/zend_jit_ir.c | 208 +++++++++++++++++++++++++++++ ext/opcache/jit/zend_jit_trace.c | 17 ++- 6 files changed, 325 insertions(+), 2 deletions(-) diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 23907d1f5491b..71186a8061008 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -2454,7 +2454,7 @@ static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_invalid_method_call(z Z_STRVAL_P(function_name), zend_zval_value_name(object)); } -static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_non_static_method_call(const zend_function *fbc) +ZEND_API void ZEND_FASTCALL zend_non_static_method_call(const zend_function *fbc) { zend_throw_error( zend_ce_error, diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h index 225a6d5d232ed..03056243aa884 100644 --- a/Zend/zend_execute.h +++ b/Zend/zend_execute.h @@ -436,6 +436,7 @@ ZEND_API void zend_cleanup_unfinished_execution(zend_execute_data *execute_data, ZEND_API ZEND_ATTRIBUTE_DEPRECATED HashTable *zend_unfinished_execution_gc(zend_execute_data *execute_data, zend_execute_data *call, zend_get_gc_buffer *gc_buffer); ZEND_API HashTable *zend_unfinished_execution_gc_ex(zend_execute_data *execute_data, zend_execute_data *call, zend_get_gc_buffer *gc_buffer, bool suspended_by_yield); ZEND_API zval* ZEND_FASTCALL zend_fetch_static_property(zend_execute_data *ex, int fetch_type); +ZEND_API void ZEND_FASTCALL zend_non_static_method_call(const zend_function *fbc); ZEND_API void zend_frameless_observed_call(zend_execute_data *execute_data); diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index d39eaea9fb36a..3e77d36aad8ac 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -2520,6 +2520,19 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op goto jit_failure; } goto done; + case ZEND_INIT_STATIC_METHOD_CALL: + if (!(opline->op2_type == IS_CONST + && (opline->op1_type == IS_CONST + || (opline->op1_type == IS_UNUSED + && ((opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF + || (opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_PARENT))))) { + break; + } + if (!zend_jit_init_static_method_call(&ctx, opline, b, op_array, ssa, ssa_op, call_level, + NULL, 0)) { + goto jit_failure; + } + goto done; case ZEND_ROPE_INIT: case ZEND_ROPE_ADD: case ZEND_ROPE_END: diff --git a/ext/opcache/jit/zend_jit_helpers.c b/ext/opcache/jit/zend_jit_helpers.c index 63f4863cf4e6c..58eade6a55aeb 100644 --- a/ext/opcache/jit/zend_jit_helpers.c +++ b/ext/opcache/jit/zend_jit_helpers.c @@ -189,6 +189,92 @@ static zend_function* ZEND_FASTCALL zend_jit_find_method_tmp_helper(zend_object return fbc; } + +static zend_class_entry* ZEND_FASTCALL zend_jit_find_class_helper(zend_execute_data *execute_data) +{ + const zend_op *opline = EX(opline); + zend_class_entry *ce; + + if (opline->op1_type == IS_CONST) { + /* no function found. try a static method in class */ + ce = CACHED_PTR(opline->result.num); + if (UNEXPECTED(ce == NULL)) { + ce = zend_fetch_class_by_name(Z_STR_P(RT_CONSTANT(opline, opline->op1)), Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); + } + } else if (opline->op1_type == IS_UNUSED) { + ce = zend_fetch_class(NULL, opline->op1.num); + } else { + ce = Z_CE_P(EX_VAR(opline->op1.var)); + } + return ce; +} + +static zend_function* ZEND_FASTCALL zend_jit_find_static_method_helper(zend_execute_data *execute_data, zend_class_entry *ce) +{ + const zend_op *opline = EX(opline); + zend_function *fbc; + + ZEND_ASSERT(opline->op2_type == IS_CONST); + + if (opline->op1_type == IS_CONST && + EXPECTED((fbc = CACHED_PTR(opline->result.num + sizeof(void*))) != NULL)) { + /* nothing to do */ + } else if (opline->op1_type != IS_CONST && + EXPECTED(CACHED_PTR(opline->result.num) == ce)) { + fbc = CACHED_PTR(opline->result.num + sizeof(void*)); + } else if (opline->op2_type != IS_UNUSED) { + zval *function_name = RT_CONSTANT(opline, opline->op2); + + ZEND_ASSERT(Z_TYPE_P(function_name) == IS_STRING); + if (ce->get_static_method) { + fbc = ce->get_static_method(ce, Z_STR_P(function_name)); + } else { + fbc = zend_std_get_static_method(ce, Z_STR_P(function_name), RT_CONSTANT(opline, opline->op2) + 1); + } + if (UNEXPECTED(fbc == NULL)) { + if (EXPECTED(!EG(exception))) { + zend_undefined_method(ce, Z_STR_P(function_name)); + } + return NULL; + } + if (EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE))) && + EXPECTED(!(fbc->common.scope->ce_flags & ZEND_ACC_TRAIT))) { + CACHE_POLYMORPHIC_PTR(opline->result.num, ce, fbc); + } + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { + zend_init_func_run_time_cache(&fbc->op_array); + } + } else { + if (UNEXPECTED(ce->constructor == NULL)) { + zend_throw_error(NULL, "Cannot call constructor"); + return NULL; + } + if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJ(EX(This))->ce != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) { + zend_throw_error(NULL, "Cannot call private %s::__construct()", ZSTR_VAL(ce->name)); + return NULL;; + } + fbc = ce->constructor; + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { + zend_init_func_run_time_cache(&fbc->op_array); + } + } + + return fbc; +} + +static zend_execute_data* ZEND_FASTCALL zend_jit_push_this_metod_call_frame(zend_class_entry *scope, zend_function *fbc, uint32_t num_args) +{ + zend_execute_data *execute_data = EG(current_execute_data); + + if (Z_TYPE(EX(This)) != IS_OBJECT || !instanceof_function(Z_OBJCE(EX(This)), scope)) { + zend_non_static_method_call(fbc); + return NULL; + } + + scope = (zend_class_entry*)Z_OBJ(EX(This)); + return zend_vm_stack_push_call_frame(ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_HAS_THIS, fbc, num_args, scope); +} + static zend_execute_data* ZEND_FASTCALL zend_jit_push_static_metod_call_frame(zend_object *obj, zend_function *fbc, uint32_t num_args) { zend_class_entry *scope = obj->ce; diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 4f1663bc0c053..a698f1b0c1c88 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -3020,6 +3020,9 @@ static void zend_jit_setup_disasm(void) REGISTER_HELPER(zend_jit_find_method_tmp_helper); REGISTER_HELPER(zend_jit_push_static_metod_call_frame); REGISTER_HELPER(zend_jit_push_static_metod_call_frame_tmp); + REGISTER_HELPER(zend_jit_find_class_helper); + REGISTER_HELPER(zend_jit_find_static_method_helper); + REGISTER_HELPER(zend_jit_push_this_metod_call_frame); REGISTER_HELPER(zend_jit_free_trampoline_helper); REGISTER_HELPER(zend_jit_verify_return_slow); REGISTER_HELPER(zend_jit_deprecated_helper); @@ -8542,6 +8545,9 @@ static int zend_jit_push_call_frame(zend_jit_ctx *jit, const zend_op *opline, co ir_CONST_U32(ZEND_CALL_HAS_THIS | ZEND_CALL_RELEASE_THIS))); } } + } else if (opline->opcode == ZEND_INIT_STATIC_METHOD_CALL) { + // JIT: Z_CE(call->This) = called_scope; + ir_STORE(jit_CALL(rx, This), this_ref); } else if (!is_closure) { // JIT: Z_CE(call->This) = called_scope; ir_STORE(jit_CALL(rx, This), IR_NULL); @@ -9061,6 +9067,208 @@ static int zend_jit_init_method_call(zend_jit_ctx *jit, return 1; } +static int zend_jit_init_static_method_call(zend_jit_ctx *jit, + const zend_op *opline, + uint32_t b, + const zend_op_array *op_array, + zend_ssa *ssa, + const zend_ssa_op *ssa_op, + int call_level, + zend_jit_trace_rec *trace, + int checked_stack) +{ + zend_func_info *info = ZEND_FUNC_INFO(op_array); + zend_call_info *call_info = NULL; + zend_class_entry *ce = NULL; + zend_function *func = NULL; + ir_ref func_ref, func_ref2, scope_ref, scope_ref2, if_cached, cold_path, ref; + ir_ref if_static = IR_UNUSED; + + if (info) { + call_info = info->callee_info; + while (call_info && call_info->caller_init_opline != opline) { + call_info = call_info->next_callee; + } + if (call_info && call_info->callee_func && !call_info->is_prototype) { + func = call_info->callee_func; + } + } + + if (opline->op1_type == IS_CONST) { + zval *zv = RT_CONSTANT(opline, opline->op1); + zend_string *class_name; + + ZEND_ASSERT(Z_TYPE_P(zv) == IS_STRING); + class_name = Z_STR_P(zv); + ce = zend_lookup_class_ex(class_name, NULL, ZEND_FETCH_CLASS_NO_AUTOLOAD); + if (ce && (ce->type == ZEND_INTERNAL_CLASS || ce->info.user.filename != op_array->filename)) { + ce = NULL; + } + } else { + ZEND_ASSERT(opline->op1_type == IS_UNUSED); + if ((opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) { + ce = op_array->scope; + } else { + ZEND_ASSERT((opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_PARENT); + ce = op_array->scope; + if (ce) { + if (ce->parent) { + ce = ce->parent; + if (ce->type == ZEND_INTERNAL_CLASS || ce->info.user.filename != op_array->filename) { + ce = NULL; + } + } else { + ce = NULL; + } + } + } + } + + if (!func && ce) { + zval *zv = RT_CONSTANT(opline, opline->op2); + zend_string *method_name; + + ZEND_ASSERT(Z_TYPE_P(zv) == IS_STRING); + method_name = Z_STR_P(zv); + zv = zend_hash_find(&ce->function_table, method_name); + if (zv) { + zend_function *fn = Z_PTR_P(zv); + + if (fn->common.scope == op_array->scope + || (fn->common.fn_flags & ZEND_ACC_PUBLIC) + || ((fn->common.fn_flags & ZEND_ACC_PROTECTED) + && instanceof_function_slow(op_array->scope, fn->common.scope))) { + func = fn; + } + } + } + + if (jit->delayed_call_level) { + if (!zend_jit_save_call_chain(jit, jit->delayed_call_level)) { + return 0; + } + } + + // JIT: fbc = CACHED_PTR(opline->result.num + sizeof(void*)); + func_ref = ir_LOAD_A(ir_ADD_OFFSET(ir_LOAD_A(jit_EX(run_time_cache)), opline->result.num + sizeof(void*))); + + // JIT: if (fbc) + if_cached = ir_IF(func_ref); + ir_IF_FALSE_cold(if_cached); + + jit_SET_EX_OPLINE(jit, opline); + scope_ref2 = ir_CALL_1(IR_ADDR, ir_CONST_FC_FUNC(zend_jit_find_class_helper), jit_FP(jit)); + ir_GUARD(scope_ref2, jit_STUB_ADDR(jit, jit_stub_exception_handler)); + + func_ref2 = ir_CALL_2(IR_ADDR, ir_CONST_FC_FUNC(zend_jit_find_static_method_helper), jit_FP(jit), scope_ref2); + ir_GUARD(func_ref2, jit_STUB_ADDR(jit, jit_stub_exception_handler)); + + cold_path = ir_END(); + + ir_IF_TRUE(if_cached); + if (ce && (ce->ce_flags & ZEND_ACC_IMMUTABLE) && (ce->ce_flags & ZEND_ACC_LINKED)) { + scope_ref = ir_CONST_ADDR(ce); + } else { + scope_ref = ir_LOAD_A(ir_ADD_OFFSET(ir_LOAD_A(jit_EX(run_time_cache)), opline->result.num)); + } + + ir_MERGE_2(cold_path, ir_END()); + func_ref = ir_PHI_2(IR_ADDR, func_ref2, func_ref); + scope_ref = ir_PHI_2(IR_ADDR, scope_ref2, scope_ref); + + if ((!func || zend_jit_may_be_modified(func, op_array)) + && trace + && trace->op == ZEND_JIT_TRACE_INIT_CALL + && trace->func +#ifdef _WIN32 + && trace->func->type != ZEND_INTERNAL_FUNCTION +#endif + ) { + int32_t exit_point; + const void *exit_addr; + + exit_point = zend_jit_trace_get_exit_point(opline, func ? ZEND_JIT_EXIT_INVALIDATE : 0); + exit_addr = zend_jit_trace_get_exit_addr(exit_point); + if (!exit_addr) { + return 0; + } + +// jit->trace->exit_info[exit_point].poly_func_ref = func_ref; +// jit->trace->exit_info[exit_point].poly_this_ref = scope_ref; + + func = (zend_function*)trace->func; + + if (func->type == ZEND_USER_FUNCTION && + (!(func->common.fn_flags & ZEND_ACC_IMMUTABLE) || + (func->common.fn_flags & ZEND_ACC_CLOSURE) || + !func->common.function_name)) { + const zend_op *opcodes = func->op_array.opcodes; + + ir_GUARD( + ir_EQ( + ir_LOAD_A(ir_ADD_OFFSET(func_ref, offsetof(zend_op_array, opcodes))), + ir_CONST_ADDR(opcodes)), + ir_CONST_ADDR(exit_addr)); + } else { + ir_GUARD(ir_EQ(func_ref, ir_CONST_ADDR(func)), ir_CONST_ADDR(exit_addr)); + } + } + + if (!func || !(func->common.fn_flags & ZEND_ACC_STATIC)) { + if (!func) { + // JIT: if (fbc->common.fn_flags & ZEND_ACC_STATIC) { + if_static = ir_IF(ir_AND_U32( + ir_LOAD_U32(ir_ADD_OFFSET(func_ref, offsetof(zend_function, common.fn_flags))), + ir_CONST_U32(ZEND_ACC_STATIC))); + ir_IF_FALSE_cold(if_static); + } + + ref = ir_CALL_3(IR_ADDR, ir_CONST_FC_FUNC(zend_jit_push_this_metod_call_frame), + scope_ref, + func_ref, + ir_CONST_U32(opline->extended_value)); + ir_GUARD(ref, jit_STUB_ADDR(jit, jit_stub_exception_handler)); + jit_STORE_IP(jit, ref); + + if (!func) { + cold_path = ir_END(); + ir_IF_TRUE(if_static); + } + } + + if (!func || (func->common.fn_flags & ZEND_ACC_STATIC)) { + if (opline->op1_type == IS_UNUSED + && ((opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_PARENT || + (opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF)) { + if (op_array->fn_flags & ZEND_ACC_STATIC) { + scope_ref = ir_LOAD_A(jit_EX(This.value.ref)); + } else { + scope_ref = ir_LOAD_A(ir_ADD_OFFSET(jit_EX(This.value.ref), offsetof(zend_object, ce))); + } + } + if (!zend_jit_push_call_frame(jit, opline, op_array, func, 0, 0, checked_stack, func_ref, scope_ref)) { + return 0; + } + + if (!func) { + ir_MERGE_2(cold_path, ir_END()); + } + } + + zend_jit_start_reuse_ip(jit); + if (zend_jit_needs_call_chain(call_info, b, op_array, ssa, ssa_op, opline, call_level, trace)) { + if (!zend_jit_save_call_chain(jit, call_level)) { + return 0; + } + } else { + ZEND_ASSERT(call_level > 0); + jit->delayed_call_level = call_level; + delayed_call_chain = 1; + } + + return 1; +} + static int zend_jit_init_closure_call(zend_jit_ctx *jit, const zend_op *opline, uint32_t b, diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 0edc6fe65e63d..7fd1c3fa6e3db 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -2602,7 +2602,7 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin case ZEND_INIT_NS_FCALL_BY_NAME: case ZEND_INIT_METHOD_CALL: case ZEND_INIT_DYNAMIC_CALL: - //case ZEND_INIT_STATIC_METHOD_CALL: + case ZEND_INIT_STATIC_METHOD_CALL: //case ZEND_INIT_PARENT_PROPERTY_HOOK_CALL: //case ZEND_INIT_USER_CALL: //case ZEND_NEW: @@ -6365,6 +6365,21 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par goto jit_failure; } goto done; + case ZEND_INIT_STATIC_METHOD_CALL: + if (!(opline->op2_type == IS_CONST + && (opline->op1_type == IS_CONST + || (opline->op1_type == IS_UNUSED + && ((opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF + || (opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_PARENT))))) { + break; + } + if (!zend_jit_init_static_method_call(&ctx, opline, + op_array_ssa->cfg.map ? op_array_ssa->cfg.map[opline - op_array->opcodes] : -1, + op_array, ssa, ssa_op, frame->call_level, + p + 1, peek_checked_stack - checked_stack)) { + goto jit_failure; + } + goto done; case ZEND_INIT_DYNAMIC_CALL: if (orig_op2_type != IS_OBJECT || op2_ce != zend_ce_closure) { break; From 3a73b2b9eebc048b5875e0dfeb03698634d55984 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Mon, 7 Oct 2024 12:15:05 +0300 Subject: [PATCH 2/4] Fixed typo --- ext/opcache/jit/zend_jit_helpers.c | 6 +++--- ext/opcache/jit/zend_jit_ir.c | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ext/opcache/jit/zend_jit_helpers.c b/ext/opcache/jit/zend_jit_helpers.c index 58eade6a55aeb..140f69dee062c 100644 --- a/ext/opcache/jit/zend_jit_helpers.c +++ b/ext/opcache/jit/zend_jit_helpers.c @@ -262,7 +262,7 @@ static zend_function* ZEND_FASTCALL zend_jit_find_static_method_helper(zend_exec return fbc; } -static zend_execute_data* ZEND_FASTCALL zend_jit_push_this_metod_call_frame(zend_class_entry *scope, zend_function *fbc, uint32_t num_args) +static zend_execute_data* ZEND_FASTCALL zend_jit_push_this_method_call_frame(zend_class_entry *scope, zend_function *fbc, uint32_t num_args) { zend_execute_data *execute_data = EG(current_execute_data); @@ -275,14 +275,14 @@ static zend_execute_data* ZEND_FASTCALL zend_jit_push_this_metod_call_frame(zend return zend_vm_stack_push_call_frame(ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_HAS_THIS, fbc, num_args, scope); } -static zend_execute_data* ZEND_FASTCALL zend_jit_push_static_metod_call_frame(zend_object *obj, zend_function *fbc, uint32_t num_args) +static zend_execute_data* ZEND_FASTCALL zend_jit_push_static_method_call_frame(zend_object *obj, zend_function *fbc, uint32_t num_args) { zend_class_entry *scope = obj->ce; return zend_vm_stack_push_call_frame(ZEND_CALL_NESTED_FUNCTION, fbc, num_args, scope); } -static zend_execute_data* ZEND_FASTCALL zend_jit_push_static_metod_call_frame_tmp(zend_object *obj, zend_function *fbc, uint32_t num_args) +static zend_execute_data* ZEND_FASTCALL zend_jit_push_static_method_call_frame_tmp(zend_object *obj, zend_function *fbc, uint32_t num_args) { zend_class_entry *scope = obj->ce; diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index a698f1b0c1c88..e2870e9fffa64 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -3018,11 +3018,11 @@ static void zend_jit_setup_disasm(void) REGISTER_HELPER(zend_jit_invalid_method_call_tmp); REGISTER_HELPER(zend_jit_find_method_helper); REGISTER_HELPER(zend_jit_find_method_tmp_helper); - REGISTER_HELPER(zend_jit_push_static_metod_call_frame); - REGISTER_HELPER(zend_jit_push_static_metod_call_frame_tmp); + REGISTER_HELPER(zend_jit_push_static_method_call_frame); + REGISTER_HELPER(zend_jit_push_static_method_call_frame_tmp); REGISTER_HELPER(zend_jit_find_class_helper); REGISTER_HELPER(zend_jit_find_static_method_helper); - REGISTER_HELPER(zend_jit_push_this_metod_call_frame); + REGISTER_HELPER(zend_jit_push_this_method_call_frame); REGISTER_HELPER(zend_jit_free_trampoline_helper); REGISTER_HELPER(zend_jit_verify_return_slow); REGISTER_HELPER(zend_jit_deprecated_helper); @@ -9021,12 +9021,12 @@ static int zend_jit_init_method_call(zend_jit_ctx *jit, ir_ref ret; if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) && !delayed_fetch_this) { - ret = ir_CALL_3(IR_ADDR, ir_CONST_FC_FUNC(zend_jit_push_static_metod_call_frame_tmp), + ret = ir_CALL_3(IR_ADDR, ir_CONST_FC_FUNC(zend_jit_push_static_method_call_frame_tmp), this_ref, func_ref, ir_CONST_U32(opline->extended_value)); } else { - ret = ir_CALL_3(IR_ADDR, ir_CONST_FC_FUNC(zend_jit_push_static_metod_call_frame), + ret = ir_CALL_3(IR_ADDR, ir_CONST_FC_FUNC(zend_jit_push_static_method_call_frame), this_ref, func_ref, ir_CONST_U32(opline->extended_value)); @@ -9223,7 +9223,7 @@ static int zend_jit_init_static_method_call(zend_jit_ctx *jit, ir_IF_FALSE_cold(if_static); } - ref = ir_CALL_3(IR_ADDR, ir_CONST_FC_FUNC(zend_jit_push_this_metod_call_frame), + ref = ir_CALL_3(IR_ADDR, ir_CONST_FC_FUNC(zend_jit_push_this_method_call_frame), scope_ref, func_ref, ir_CONST_U32(opline->extended_value)); From dae72b0dae30191e29c2d2dfef0193fb7ffaa455 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Mon, 7 Oct 2024 12:26:37 +0300 Subject: [PATCH 3/4] Fix missing LOAD --- ext/opcache/jit/zend_jit_ir.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index e2870e9fffa64..31c599ed67a60 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -9243,7 +9243,7 @@ static int zend_jit_init_static_method_call(zend_jit_ctx *jit, if (op_array->fn_flags & ZEND_ACC_STATIC) { scope_ref = ir_LOAD_A(jit_EX(This.value.ref)); } else { - scope_ref = ir_LOAD_A(ir_ADD_OFFSET(jit_EX(This.value.ref), offsetof(zend_object, ce))); + scope_ref = ir_LOAD_A(ir_ADD_OFFSET(ir_LOAD_A(jit_EX(This.value.ref)), offsetof(zend_object, ce))); } } if (!zend_jit_push_call_frame(jit, opline, op_array, func, 0, 0, checked_stack, func_ref, scope_ref)) { From ece11002a4173942582224501134c12c755aedd4 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Mon, 7 Oct 2024 12:43:21 +0300 Subject: [PATCH 4/4] Separate zend_get_known_class() --- ext/opcache/jit/zend_jit.c | 37 ++++++++++++++++++++ ext/opcache/jit/zend_jit_ir.c | 66 +++-------------------------------- 2 files changed, 41 insertions(+), 62 deletions(-) diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index 3e77d36aad8ac..2357bb4d1002c 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -558,6 +558,43 @@ static bool zend_jit_is_persistent_constant(zval *key, uint32_t flags) return c && (ZEND_CONSTANT_FLAGS(c) & CONST_PERSISTENT); } +static zend_class_entry* zend_get_known_class(const zend_op_array *op_array, const zend_op *opline, uint8_t op_type, znode_op op) +{ + zend_class_entry *ce = NULL; + + if (op_type == IS_CONST) { + zval *zv = RT_CONSTANT(opline, op); + zend_string *class_name; + + ZEND_ASSERT(Z_TYPE_P(zv) == IS_STRING); + class_name = Z_STR_P(zv); + ce = zend_lookup_class_ex(class_name, NULL, ZEND_FETCH_CLASS_NO_AUTOLOAD); + if (ce && (ce->type == ZEND_INTERNAL_CLASS || ce->info.user.filename != op_array->filename)) { + ce = NULL; + } + } else { + ZEND_ASSERT(op_type == IS_UNUSED); + if ((op.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) { + ce = op_array->scope; + } else { + ZEND_ASSERT((op.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_PARENT); + ce = op_array->scope; + if (ce) { + if (ce->parent) { + ce = ce->parent; + if (ce->type == ZEND_INTERNAL_CLASS || ce->info.user.filename != op_array->filename) { + ce = NULL; + } + } else { + ce = NULL; + } + } + } + } + + return ce; +} + static zend_property_info* zend_get_known_property_info(const zend_op_array *op_array, zend_class_entry *ce, zend_string *member, bool on_this, zend_string *filename) { zend_property_info *info = NULL; diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 31c599ed67a60..73b416f959905 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -9079,7 +9079,7 @@ static int zend_jit_init_static_method_call(zend_jit_ctx *jit, { zend_func_info *info = ZEND_FUNC_INFO(op_array); zend_call_info *call_info = NULL; - zend_class_entry *ce = NULL; + zend_class_entry *ce; zend_function *func = NULL; ir_ref func_ref, func_ref2, scope_ref, scope_ref2, if_cached, cold_path, ref; ir_ref if_static = IR_UNUSED; @@ -9094,36 +9094,7 @@ static int zend_jit_init_static_method_call(zend_jit_ctx *jit, } } - if (opline->op1_type == IS_CONST) { - zval *zv = RT_CONSTANT(opline, opline->op1); - zend_string *class_name; - - ZEND_ASSERT(Z_TYPE_P(zv) == IS_STRING); - class_name = Z_STR_P(zv); - ce = zend_lookup_class_ex(class_name, NULL, ZEND_FETCH_CLASS_NO_AUTOLOAD); - if (ce && (ce->type == ZEND_INTERNAL_CLASS || ce->info.user.filename != op_array->filename)) { - ce = NULL; - } - } else { - ZEND_ASSERT(opline->op1_type == IS_UNUSED); - if ((opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) { - ce = op_array->scope; - } else { - ZEND_ASSERT((opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_PARENT); - ce = op_array->scope; - if (ce) { - if (ce->parent) { - ce = ce->parent; - if (ce->type == ZEND_INTERNAL_CLASS || ce->info.user.filename != op_array->filename) { - ce = NULL; - } - } else { - ce = NULL; - } - } - } - } - + ce = zend_get_known_class(op_array, opline, opline->op1_type, opline->op1); if (!func && ce) { zval *zv = RT_CONSTANT(opline, opline->op2); zend_string *method_name; @@ -15826,38 +15797,9 @@ static int zend_jit_fetch_static_prop(zend_jit_ctx *jit, const zend_op *opline, ir_ref ref, ref2, if_cached, fast_path, cold_path, prop_info_ref, if_typed, if_def; int fetch_type; zend_property_info *known_prop_info = NULL; - zend_class_entry *ce = NULL; - - if (opline->op2_type == IS_CONST) { - zval *zv = RT_CONSTANT(opline, opline->op2); - zend_string *class_name; - - ZEND_ASSERT(Z_TYPE_P(zv) == IS_STRING); - class_name = Z_STR_P(zv); - ce = zend_lookup_class_ex(class_name, NULL, ZEND_FETCH_CLASS_NO_AUTOLOAD); - if (ce && (ce->type == ZEND_INTERNAL_CLASS || ce->info.user.filename != op_array->filename)) { - ce = NULL; - } - } else { - ZEND_ASSERT(opline->op2_type == IS_UNUSED); - if (opline->op2.num == ZEND_FETCH_CLASS_SELF) { - ce = op_array->scope; - } else { - ZEND_ASSERT(opline->op2.num == ZEND_FETCH_CLASS_PARENT); - ce = op_array->scope; - if (ce) { - if (ce->parent) { - ce = ce->parent; - if (ce->type == ZEND_INTERNAL_CLASS || ce->info.user.filename != op_array->filename) { - ce = NULL; - } - } else { - ce = NULL; - } - } - } - } + zend_class_entry *ce; + ce = zend_get_known_class(op_array, opline, opline->op2_type, opline->op2); if (ce) { zval *zv = RT_CONSTANT(opline, opline->op1); zend_string *prop_name;