From d94b9545d639dfddfacdd7fec75c7580dddda676 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Wed, 27 Apr 2016 17:04:25 +0200 Subject: [PATCH] Support known static/method calls in call graph For this purpose extract the function lookup call into a helper zend_optimizer_get_called_func(). --- ext/opcache/Optimizer/optimize_func_calls.c | 46 +------------ ext/opcache/Optimizer/zend_call_graph.c | 33 ++++----- ext/opcache/Optimizer/zend_optimizer.c | 67 ++++++++++++++++++- .../Optimizer/zend_optimizer_internal.h | 2 + 4 files changed, 83 insertions(+), 65 deletions(-) diff --git a/ext/opcache/Optimizer/optimize_func_calls.c b/ext/opcache/Optimizer/optimize_func_calls.c index c3c519ab4c..b37e0fee57 100644 --- a/ext/opcache/Optimizer/optimize_func_calls.c +++ b/ext/opcache/Optimizer/optimize_func_calls.c @@ -59,52 +59,10 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx) switch (opline->opcode) { case ZEND_INIT_FCALL_BY_NAME: case ZEND_INIT_NS_FCALL_BY_NAME: - if (ZEND_OP2_IS_CONST_STRING(opline)) { - zend_function *func; - zval *function_name = &ZEND_OP2_LITERAL(opline) + 1; - if ((func = zend_hash_find_ptr(&ctx->script->function_table, - Z_STR_P(function_name))) != NULL) { - call_stack[call].func = func; - } - } - call_stack[call].opline = opline; - call++; - break; case ZEND_INIT_STATIC_METHOD_CALL: - if (ZEND_OP2_IS_CONST_STRING(opline)) { - zend_class_entry *ce = NULL; - if (ZEND_OP1_IS_CONST_STRING(opline)) { - zend_string *class_name = Z_STR_P(&ZEND_OP1_LITERAL(opline) + 1); - ce = zend_hash_find_ptr(&ctx->script->class_table, class_name); - } else if (opline->op1_type == IS_UNUSED && op_array->scope - && !(op_array->scope->ce_flags & ZEND_ACC_TRAIT) - && (opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) { - ce = op_array->scope; - } - if (ce) { - zend_string *func_name = Z_STR_P(&ZEND_OP2_LITERAL(opline) + 1); - call_stack[call].func = zend_hash_find_ptr(&ce->function_table, func_name); - } - } - call_stack[call].opline = opline; - call++; - break; case ZEND_INIT_METHOD_CALL: - if (opline->op1_type == IS_UNUSED && ZEND_OP2_IS_CONST_STRING(opline) - && op_array->scope && !(op_array->scope->ce_flags & ZEND_ACC_TRAIT)) { - zend_string *method_name = Z_STR_P(&ZEND_OP2_LITERAL(opline) + 1); - zend_function *fbc = zend_hash_find_ptr( - &op_array->scope->function_table, method_name); - if (fbc) { - zend_bool is_private = (fbc->common.fn_flags & ZEND_ACC_PRIVATE) != 0; - zend_bool is_final = (fbc->common.fn_flags & ZEND_ACC_FINAL) != 0; - zend_bool same_scope = fbc->common.scope == op_array->scope; - if ((is_private && same_scope) - || (is_final && (!is_private || same_scope))) { - call_stack[call].func = fbc; - } - } - } + call_stack[call].func = zend_optimizer_get_called_func( + ctx->script, op_array, opline, 0); /* break missing intentionally */ case ZEND_NEW: case ZEND_INIT_DYNAMIC_CALL: diff --git a/ext/opcache/Optimizer/zend_call_graph.c b/ext/opcache/Optimizer/zend_call_graph.c index ef586e12a7..f245d14eaa 100644 --- a/ext/opcache/Optimizer/zend_call_graph.c +++ b/ext/opcache/Optimizer/zend_call_graph.c @@ -21,6 +21,8 @@ #include "php.h" #include "zend_compile.h" #include "zend_extensions.h" +#include "Optimizer/zend_optimizer.h" +#include "zend_optimizer_internal.h" #include "zend_inference.h" #include "zend_call_graph.h" #include "zend_func_info.h" @@ -150,39 +152,32 @@ static int zend_analyze_calls(zend_arena **arena, zend_script *script, uint32_t call_info = NULL; switch (opline->opcode) { case ZEND_INIT_FCALL: - if ((func = zend_hash_find_ptr(&script->function_table, Z_STR_P(CRT_CONSTANT(opline->op2)))) != NULL) { - zend_func_info *callee_func_info = ZEND_FUNC_INFO(&func->op_array); - if (callee_func_info) { - call_info = zend_arena_calloc(arena, 1, sizeof(zend_call_info) + (sizeof(zend_send_arg_info) * ((int)opline->extended_value - 1))); - call_info->caller_op_array = op_array; - call_info->caller_init_opline = opline; - call_info->caller_call_opline = NULL; - call_info->callee_func = func; - call_info->num_args = opline->extended_value; - call_info->next_caller = callee_func_info->caller_info; - callee_func_info->caller_info = call_info; - call_info->next_callee = func_info->callee_info; - func_info->callee_info = call_info; - } - } else if ((func = zend_hash_find_ptr(EG(function_table), Z_STR_P(CRT_CONSTANT(opline->op2)))) != NULL && - func->type == ZEND_INTERNAL_FUNCTION) { + case ZEND_INIT_METHOD_CALL: + case ZEND_INIT_STATIC_METHOD_CALL: + func = zend_optimizer_get_called_func( + script, op_array, opline, (build_flags & ZEND_RT_CONSTANTS) != 0); + if (func) { call_info = zend_arena_calloc(arena, 1, sizeof(zend_call_info) + (sizeof(zend_send_arg_info) * ((int)opline->extended_value - 1))); call_info->caller_op_array = op_array; call_info->caller_init_opline = opline; call_info->caller_call_opline = NULL; call_info->callee_func = func; call_info->num_args = opline->extended_value; - call_info->next_caller = NULL; call_info->next_callee = func_info->callee_info; func_info->callee_info = call_info; + + if (func->type == ZEND_INTERNAL_FUNCTION) { + call_info->next_caller = NULL; + } else { + zend_func_info *callee_func_info = ZEND_FUNC_INFO(&func->op_array); + call_info->next_caller = callee_func_info->caller_info; + } } /* break missing intentionally */ case ZEND_INIT_FCALL_BY_NAME: case ZEND_INIT_NS_FCALL_BY_NAME: case ZEND_INIT_DYNAMIC_CALL: case ZEND_NEW: - case ZEND_INIT_METHOD_CALL: - case ZEND_INIT_STATIC_METHOD_CALL: case ZEND_INIT_USER_CALL: call_stack[call] = call_info; call++; diff --git a/ext/opcache/Optimizer/zend_optimizer.c b/ext/opcache/Optimizer/zend_optimizer.c index a18f79a843..1e4e092512 100644 --- a/ext/opcache/Optimizer/zend_optimizer.c +++ b/ext/opcache/Optimizer/zend_optimizer.c @@ -527,6 +527,70 @@ int zend_optimizer_replace_by_const(zend_op_array *op_array, return 1; } +zend_function *zend_optimizer_get_called_func( + zend_script *script, zend_op_array *op_array, zend_op *opline, zend_bool rt_constants) +{ +#define GET_OP(op) CRT_CONSTANT_EX(op_array, opline->op, rt_constants) + switch (opline->opcode) { + case ZEND_INIT_FCALL: + { + zend_string *function_name = Z_STR_P(GET_OP(op2)); + zend_function *func; + if ((func = zend_hash_find_ptr(&script->function_table, function_name)) != NULL) { + return func; + } else if ((func = zend_hash_find_ptr(EG(function_table), function_name)) != NULL) { + ZEND_ASSERT(func->type == ZEND_INTERNAL_FUNCTION); + return func; + } + break; + } + case ZEND_INIT_FCALL_BY_NAME: + case ZEND_INIT_NS_FCALL_BY_NAME: + if (opline->op2_type == IS_CONST && Z_TYPE_P(GET_OP(op2)) == IS_STRING) { + zval *function_name = GET_OP(op2) + 1; + return zend_hash_find_ptr(&script->function_table, Z_STR_P(function_name)); + } + break; + case ZEND_INIT_STATIC_METHOD_CALL: + if (opline->op2_type == IS_CONST && Z_TYPE_P(GET_OP(op2)) == IS_STRING) { + zend_class_entry *ce = NULL; + if (opline->op1_type == IS_CONST && Z_TYPE_P(GET_OP(op1)) == IS_STRING) { + zend_string *class_name = Z_STR_P(GET_OP(op1) + 1); + ce = zend_hash_find_ptr(&script->class_table, class_name); + } else if (opline->op1_type == IS_UNUSED && op_array->scope + && !(op_array->scope->ce_flags & ZEND_ACC_TRAIT) + && (opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) { + ce = op_array->scope; + } + if (ce) { + zend_string *func_name = Z_STR_P(GET_OP(op2) + 1); + return zend_hash_find_ptr(&ce->function_table, func_name); + } + } + break; + case ZEND_INIT_METHOD_CALL: + if (opline->op1_type == IS_UNUSED + && opline->op2_type == IS_CONST && Z_TYPE_P(GET_OP(op2)) == IS_STRING + && op_array->scope && !(op_array->scope->ce_flags & ZEND_ACC_TRAIT)) { + zend_string *method_name = Z_STR_P(GET_OP(op2) + 1); + zend_function *fbc = zend_hash_find_ptr( + &op_array->scope->function_table, method_name); + if (fbc) { + zend_bool is_private = (fbc->common.fn_flags & ZEND_ACC_PRIVATE) != 0; + zend_bool is_final = (fbc->common.fn_flags & ZEND_ACC_FINAL) != 0; + zend_bool same_scope = fbc->common.scope == op_array->scope; + if ((is_private && same_scope) + || (is_final && (!is_private || same_scope))) { + return fbc; + } + } + } + break; + } + return NULL; +#undef GET_OP +} + static void zend_optimize(zend_op_array *op_array, zend_optimizer_ctx *ctx) { @@ -751,8 +815,7 @@ static void zend_adjust_fcall_stack_size_graph(zend_op_array *op_array) while (call_info) { zend_op *opline = call_info->caller_init_opline; - if (opline && call_info->callee_func) { - ZEND_ASSERT(opline->opcode == ZEND_INIT_FCALL); + if (opline && call_info->callee_func && opline->opcode == ZEND_INIT_FCALL) { opline->op1.num = zend_vm_calc_used_stack(opline->extended_value, call_info->callee_func); } call_info = call_info->next_callee; diff --git a/ext/opcache/Optimizer/zend_optimizer_internal.h b/ext/opcache/Optimizer/zend_optimizer_internal.h index 220a00d6c4..1d51b57634 100644 --- a/ext/opcache/Optimizer/zend_optimizer_internal.h +++ b/ext/opcache/Optimizer/zend_optimizer_internal.h @@ -106,5 +106,7 @@ void zend_optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_c void zend_optimizer_nop_removal(zend_op_array *op_array); void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx *ctx); int zend_optimizer_is_disabled_func(const char *name, size_t len); +zend_function *zend_optimizer_get_called_func( + zend_script *script, zend_op_array *op_array, zend_op *opline, zend_bool rt_constants); #endif -- 2.40.0