]> granicus.if.org Git - php/commitdiff
Live ranges construction optimization
authorDmitry Stogov <dmitry@zend.com>
Mon, 21 Jan 2019 14:25:24 +0000 (17:25 +0300)
committerDmitry Stogov <dmitry@zend.com>
Mon, 21 Jan 2019 14:25:24 +0000 (17:25 +0300)
Zend/zend_opcode.c
ext/opcache/Optimizer/zend_optimizer.c

index dac934115eae4b2c3bddf77987996515677a41a2..e415035fe6da651c28e9364ab10b3a05dcd06b64 100644 (file)
@@ -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;
index 00866026f0261b2b29ca40c3aa78318c7177b65a..4a7d68acda4c652849d432a34d29a6faa498c9fc 100644 (file)
@@ -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);
+                               }
                        }
                }