From: Nikita Popov Date: Thu, 16 Mar 2017 00:26:27 +0000 (+0100) Subject: Use call_map to avoid linear call lookup X-Git-Tag: php-7.1.4RC1~33 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=9331be7d6af4ba9ab9945ac1d72812603fdd821c;p=php Use call_map to avoid linear call lookup --- diff --git a/ext/opcache/Optimizer/zend_call_graph.c b/ext/opcache/Optimizer/zend_call_graph.c index f022bfc892..5800a220bc 100644 --- a/ext/opcache/Optimizer/zend_call_graph.c +++ b/ext/opcache/Optimizer/zend_call_graph.c @@ -266,6 +266,29 @@ int zend_build_call_graph(zend_arena **arena, zend_script *script, uint32_t buil } /* }}} */ +zend_call_info **zend_build_call_map(zend_arena **arena, zend_func_info *info, zend_op_array *op_array) /* {{{ */ +{ + zend_call_info **map, *call; + if (!info->callee_info) { + /* Don't build call map if function contains no calls */ + return NULL; + } + + map = zend_arena_calloc(arena, sizeof(zend_call_info *), op_array->last); + for (call = info->callee_info; call; call = call->next_callee) { + int i; + map[call->caller_init_opline - op_array->opcodes] = call; + map[call->caller_call_opline - op_array->opcodes] = call; + for (i = 0; i < call->num_args; i++) { + if (call->arg_info[i].opline) { + map[call->arg_info[i].opline - op_array->opcodes] = call; + } + } + } + return map; +} +/* }}} */ + /* * Local variables: * tab-width: 4 diff --git a/ext/opcache/Optimizer/zend_call_graph.h b/ext/opcache/Optimizer/zend_call_graph.h index a59f894472..49c7217c40 100644 --- a/ext/opcache/Optimizer/zend_call_graph.h +++ b/ext/opcache/Optimizer/zend_call_graph.h @@ -51,6 +51,7 @@ struct _zend_func_info { zend_ssa ssa; /* Static Single Assignmnt Form */ zend_call_info *caller_info; /* where this function is called from */ zend_call_info *callee_info; /* which functions are called from this one */ + zend_call_info **call_map; /* Call info associated with init/call/send opnum */ int num_args; /* (-1 - unknown) */ zend_recv_arg_info *arg_info; zend_ssa_var_info return_info; @@ -69,6 +70,7 @@ typedef struct _zend_call_graph { BEGIN_EXTERN_C() int zend_build_call_graph(zend_arena **arena, zend_script *script, uint32_t build_flags, zend_call_graph *call_graph); +zend_call_info **zend_build_call_map(zend_arena **arena, zend_func_info *info, zend_op_array *op_array); int zend_analyze_calls(zend_arena **arena, zend_script *script, uint32_t build_flags, zend_op_array *op_array, zend_func_info *func_info); END_EXTERN_C() diff --git a/ext/opcache/Optimizer/zend_inference.c b/ext/opcache/Optimizer/zend_inference.c index 3bdc0310e6..752108a7fd 100644 --- a/ext/opcache/Optimizer/zend_inference.c +++ b/ext/opcache/Optimizer/zend_inference.c @@ -1357,21 +1357,24 @@ int zend_inference_calc_range(const zend_op_array *op_array, zend_ssa *ssa, int case ZEND_DO_ICALL: case ZEND_DO_UCALL: case ZEND_DO_FCALL_BY_NAME: - if (ssa->ops[line].result_def == var && ZEND_FUNC_INFO(op_array)) { + if (ssa->ops[line].result_def == var) { zend_func_info *func_info = ZEND_FUNC_INFO(op_array); - zend_call_info *call_info = func_info->callee_info; + zend_call_info *call_info; + if (!func_info || !func_info->call_map) { + break; + } - while (call_info && call_info->caller_call_opline != opline) { - call_info = call_info->next_callee; + call_info = func_info->call_map[opline - op_array->opcodes]; + if (!call_info) { + break; } - if (call_info) { - if (call_info->callee_func->type == ZEND_USER_FUNCTION) { - func_info = ZEND_FUNC_INFO(&call_info->callee_func->op_array); - if (func_info && func_info->return_info.has_range) { - *tmp = func_info->return_info.range; - return 1; - } + if (call_info->callee_func->type == ZEND_USER_FUNCTION) { + func_info = ZEND_FUNC_INFO(&call_info->callee_func->op_array); + if (func_info && func_info->return_info.has_range) { + *tmp = func_info->return_info.range; + return 1; } + } //TODO: we can't use type inference for internal functions at this point ??? #if 0 uint32_t type; @@ -1394,7 +1397,6 @@ int zend_inference_calc_range(const zend_op_array *op_array, zend_ssa *ssa, int return 1; } #endif - } } break; // FIXME: support for more opcodes @@ -3126,13 +3128,10 @@ static void zend_update_type_info(const zend_op_array *op_array, zend_func_info *func_info = ZEND_FUNC_INFO(op_array); zend_call_info *call_info; - if (!func_info) { + if (!func_info || !func_info->call_map) { goto unknown_opcode; } - call_info = func_info->callee_info; - while (call_info && call_info->caller_call_opline != opline) { - call_info = call_info->next_callee; - } + call_info = func_info->call_map[opline - op_array->opcodes]; if (!call_info) { goto unknown_opcode; } @@ -3554,18 +3553,14 @@ static int is_recursive_tail_call(const zend_op_array *op_array, { zend_func_info *info = ZEND_FUNC_INFO(op_array); - if (info->ssa.ops && info->ssa.vars && + if (info->ssa.ops && info->ssa.vars && info->call_map && info->ssa.ops[opline - op_array->opcodes].op1_use >= 0 && info->ssa.vars[info->ssa.ops[opline - op_array->opcodes].op1_use].definition >= 0) { zend_op *op = op_array->opcodes + info->ssa.vars[info->ssa.ops[opline - op_array->opcodes].op1_use].definition; if (op->opcode == ZEND_DO_UCALL) { - zend_call_info *call_info = info->callee_info; - - while (call_info && call_info->caller_call_opline != op) { - call_info = call_info->next_callee; - } + zend_call_info *call_info = info->call_map[op - op_array->opcodes]; if (call_info && op_array == &call_info->callee_func->op_array) { return 1; } diff --git a/ext/opcache/Optimizer/zend_optimizer.c b/ext/opcache/Optimizer/zend_optimizer.c index fbcb3a2e16..08ac084713 100644 --- a/ext/opcache/Optimizer/zend_optimizer.c +++ b/ext/opcache/Optimizer/zend_optimizer.c @@ -978,9 +978,10 @@ int zend_optimize_script(zend_script *script, zend_long optimization_level, zend } for (i = 0; i < call_graph.op_arrays_count; i++) { - if (call_graph.op_arrays[i]->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) { - func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]); - if (func_info) { + func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]); + if (func_info) { + func_info->call_map = zend_build_call_map(&ctx.arena, func_info, call_graph.op_arrays[i]); + if (call_graph.op_arrays[i]->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) { zend_init_func_return_info(call_graph.op_arrays[i], script, &func_info->return_info); } }