From: Dmitry Stogov Date: Mon, 21 Jan 2019 14:25:24 +0000 (+0300) Subject: Live ranges construction optimization X-Git-Tag: php-7.4.0alpha1~1186 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=97ccafd4c6a565df73199212c9736ea5b659157b;p=php Live ranges construction optimization --- diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index dac934115e..e415035fe6 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -553,7 +553,7 @@ static void emit_live_range( zend_op *def_opline = &op_array->opcodes[start], *orig_def_opline = def_opline; zend_op *use_opline = &op_array->opcodes[end]; zend_live_range *range; - uint32_t kind = ZEND_LIVE_TMPVAR; + uint32_t kind; switch (def_opline->opcode) { /* These should never be the first def. */ @@ -575,6 +575,7 @@ static void emit_live_range( return; case ZEND_BEGIN_SILENCE: kind = ZEND_LIVE_SILENCE; + start++; break; case ZEND_ROPE_INIT: kind = ZEND_LIVE_ROPE; @@ -584,6 +585,7 @@ static void emit_live_range( case ZEND_FE_RESET_R: case ZEND_FE_RESET_RW: kind = ZEND_LIVE_LOOP; + start++; break; /* Objects created via ZEND_NEW are only fully initialized * after the DO_FCALL (constructor call). */ @@ -591,6 +593,7 @@ static void emit_live_range( int level = 0; while (def_opline + 1 < use_opline) { def_opline++; + start++; if (def_opline->opcode == ZEND_DO_FCALL) { if (level == 0) { break; @@ -616,16 +619,18 @@ static void emit_live_range( } } } + if (start + 1 == end) { + /* Trivial live-range, no need to store it. */ + return; + } + /* break missing intentionally */ + default: + start++; + kind = ZEND_LIVE_TMPVAR; break; } } - start = def_opline + 1 - op_array->opcodes; - if (start == end) { - /* Trivial live-range, no need to store it. */ - return; - } - /* Check hook to determine whether a live range is necessary, e.g. based on type info. */ if (needs_live_range && !needs_live_range(op_array, orig_def_opline)) { return; @@ -669,28 +674,31 @@ static int cmp_live_range(const zend_live_range *a, const zend_live_range *b) { return a->start - b->start; } static void swap_live_range(zend_live_range *a, zend_live_range *b) { - zend_live_range tmp = *a; - *a = *b; - *b = tmp; + uint32_t tmp; + tmp = a->var; + a->var = b->var; + b->var = tmp; + tmp = a->start; + a->start = b->start; + b->start = tmp; + tmp = a->end; + a->end = b->end; + b->end = tmp; } static void zend_calc_live_ranges( zend_op_array *op_array, zend_needs_live_range_cb needs_live_range) { - zend_op *opline = &op_array->opcodes[op_array->last - 1]; - zend_op *begin = op_array->opcodes; + uint32_t opnum = op_array->last; + zend_op *opline = &op_array->opcodes[opnum]; ALLOCA_FLAG(use_heap) uint32_t var_offset = op_array->last_var; uint32_t *last_use = do_alloca(sizeof(uint32_t) * op_array->T, use_heap); memset(last_use, -1, sizeof(uint32_t) * op_array->T); ZEND_ASSERT(!op_array->live_range); - for (; opline >= begin; opline--) { - uint32_t opnum = opline - begin; - - if (opline->opcode == ZEND_OP_DATA) { - /* OP_DATA is really part of the previous opcode. */ - opnum--; - } + while (opnum > 0) { + opnum--; + opline--; if ((opline->result_type & (IS_TMP_VAR|IS_VAR)) && !is_fake_def(opline)) { uint32_t var_num = EX_VAR_TO_NUM(opline->result.var) - var_offset; @@ -700,7 +708,20 @@ static void zend_calc_live_ranges( * which case the last one starts the live range. As such, we can simply ignore * missing uses here. */ if (last_use[var_num] != (uint32_t) -1) { - emit_live_range(op_array, var_num, opnum, last_use[var_num], needs_live_range); + /* Skip trivial live-range */ + if (opnum + 1 != last_use[var_num]) { + uint32_t num; + +#if 1 + /* OP_DATA uses only op1 operand */ + ZEND_ASSERT(opline->opcode != ZEND_OP_DATA); + num = opnum; +#else + /* OP_DATA is really part of the previous opcode. */ + num = opnum - (opline->opcode == ZEND_OP_DATA); +#endif + emit_live_range(op_array, var_num, num, last_use[var_num], needs_live_range); + } last_use[var_num] = (uint32_t) -1; } } @@ -708,20 +729,46 @@ static void zend_calc_live_ranges( if ((opline->op1_type & (IS_TMP_VAR|IS_VAR)) && !keeps_op1_alive(opline)) { uint32_t var_num = EX_VAR_TO_NUM(opline->op1.var) - var_offset; if (last_use[var_num] == (uint32_t) -1) { - last_use[var_num] = opnum; + /* OP_DATA is really part of the previous opcode. */ + last_use[var_num] = opnum - (opline->opcode == ZEND_OP_DATA); } } if (opline->op2_type & (IS_TMP_VAR|IS_VAR)) { uint32_t var_num = EX_VAR_TO_NUM(opline->op2.var) - var_offset; if (last_use[var_num] == (uint32_t) -1) { +#if 1 + /* OP_DATA uses only op1 operand */ + ZEND_ASSERT(opline->opcode != ZEND_OP_DATA); last_use[var_num] = opnum; +#else + /* OP_DATA is really part of the previous opcode. */ + last_use[var_num] = opnum - (opline->opcode == ZEND_OP_DATA); +#endif } } } - if (op_array->live_range) { - zend_sort(op_array->live_range, op_array->last_live_range, sizeof(zend_live_range), - (compare_func_t) cmp_live_range, (swap_func_t) swap_live_range); + if (op_array->last_live_range > 1) { + zend_live_range *r1 = op_array->live_range; + zend_live_range *r2 = r1 + op_array->last_live_range - 1; + + /* In most cases we need just revert the array */ + while (r1 < r2) { + swap_live_range(r1, r2); + r1++; + r2--; + } + + r1 = op_array->live_range; + r2 = r1 + op_array->last_live_range - 1; + while (r1 < r2) { + if (r1->start > (r1+1)->start) { + zend_sort(r1, r2 - r1 + 1, sizeof(zend_live_range), + (compare_func_t) cmp_live_range, (swap_func_t) swap_live_range); + break; + } + r1++; + } } free_alloca(last_use, use_heap); @@ -730,10 +777,7 @@ static void zend_calc_live_ranges( ZEND_API void zend_recalc_live_ranges( zend_op_array *op_array, zend_needs_live_range_cb needs_live_range) { /* We assume that we never create live-ranges where there were none before. */ - if (!op_array->live_range) { - return; - } - + ZEND_ASSERT(op_array->live_range); efree(op_array->live_range); op_array->live_range = NULL; op_array->last_live_range = 0; diff --git a/ext/opcache/Optimizer/zend_optimizer.c b/ext/opcache/Optimizer/zend_optimizer.c index 00866026f0..4a7d68acda 100644 --- a/ext/opcache/Optimizer/zend_optimizer.c +++ b/ext/opcache/Optimizer/zend_optimizer.c @@ -1327,7 +1327,9 @@ static void zend_optimize_op_array(zend_op_array *op_array, /* Redo pass_two() */ zend_redo_pass_two(op_array); - zend_recalc_live_ranges(op_array, NULL); + if (op_array->live_range) { + zend_recalc_live_ranges(op_array, NULL); + } } static void zend_adjust_fcall_stack_size(zend_op_array *op_array, zend_optimizer_ctx *ctx) @@ -1487,13 +1489,18 @@ int zend_optimize_script(zend_script *script, zend_long optimization_level, zend } for (i = 0; i < call_graph.op_arrays_count; i++) { - func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]); + op_array = call_graph.op_arrays[i]; + func_info = ZEND_FUNC_INFO(op_array); if (func_info && func_info->ssa.var_info) { - zend_redo_pass_two_ex(call_graph.op_arrays[i], &func_info->ssa); - zend_recalc_live_ranges(call_graph.op_arrays[i], needs_live_range); + zend_redo_pass_two_ex(op_array, &func_info->ssa); + if (op_array->live_range) { + zend_recalc_live_ranges(op_array, needs_live_range); + } } else { - zend_redo_pass_two(call_graph.op_arrays[i]); - zend_recalc_live_ranges(call_graph.op_arrays[i], NULL); + zend_redo_pass_two(op_array); + if (op_array->live_range) { + zend_recalc_live_ranges(op_array, NULL); + } } }