]> granicus.if.org Git - php/commitdiff
Remove dead live ranges and FREE instructions
authorDmitry Stogov <dmitry@zend.com>
Wed, 19 Jul 2017 10:07:33 +0000 (13:07 +0300)
committerDmitry Stogov <dmitry@zend.com>
Wed, 19 Jul 2017 10:07:33 +0000 (13:07 +0300)
ext/opcache/Optimizer/dce.c

index cf75f0ad4ae5a86ea2fbcfcb31228bffa334c93b..db9a8a54bc91e1d673ea25e3592567d02db6dcd1 100644 (file)
@@ -350,25 +350,31 @@ static zend_bool dce_instr(context *ctx, zend_op *opline, zend_ssa_op *ssa_op) {
        }
 
        /* We mark FREEs as dead, but they're only really dead if the destroyed var is dead */
-       if (opline->opcode == ZEND_FREE && !is_var_dead(ctx, ssa_op->op1_use)) {
+       if (opline->opcode == ZEND_FREE
+                       && (ssa->var_info[ssa_op->op1_use].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))
+                       && !is_var_dead(ctx, ssa_op->op1_use)) {
                return 0;
        }
 
-       if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) && !is_var_dead(ctx, ssa_op->op1_use)) {
+       if ((opline->op1_type & (IS_VAR|IS_TMP_VAR))&& !is_var_dead(ctx, ssa_op->op1_use)) {
                if (!try_remove_var_def(ctx, ssa_op->op1_use, ssa_op->op1_use_chain, opline)) {
-                       free_var = ssa_op->op1_use;
-                       free_var_type = opline->op1_type;
+                       if (ssa->var_info[ssa_op->op1_use].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) {
+                               free_var = ssa_op->op1_use;
+                               free_var_type = opline->op1_type;
+                       }
                }
        }
        if ((opline->op2_type & (IS_VAR|IS_TMP_VAR)) && !is_var_dead(ctx, ssa_op->op2_use)) {
                if (!try_remove_var_def(ctx, ssa_op->op2_use, ssa_op->op2_use_chain, opline)) {
-                       if (free_var >= 0) {
-                               // TODO: We can't free two vars. Keep instruction alive.
-                               zend_bitset_excl(ctx->instr_dead, opline - ctx->op_array->opcodes);
-                               return 0;
+                       if (ssa->var_info[ssa_op->op2_use].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) {
+                               if (free_var >= 0) {
+                                       // TODO: We can't free two vars. Keep instruction alive.
+                                       zend_bitset_excl(ctx->instr_dead, opline - ctx->op_array->opcodes);
+                                       return 0;
+                               }
+                               free_var = ssa_op->op2_use;
+                               free_var_type = opline->op2_type;
                        }
-                       free_var = ssa_op->op2_use;
-                       free_var_type = opline->op2_type;
                }
        }
 
@@ -544,6 +550,69 @@ static inline zend_bool may_break_varargs(const zend_op_array *op_array, const z
        return 0;
 }
 
+static void dce_live_ranges(context *ctx, zend_op_array *op_array, zend_ssa *ssa)
+{
+       int i = 0;
+       int j = 0;
+       zend_live_range *live_range = op_array->live_range;
+
+       while (i < op_array->last_live_range) {
+               if ((live_range->var & ZEND_LIVE_MASK) != ZEND_LIVE_TMPVAR) {
+                       /* keep */
+                       j++;
+               } else {
+                       uint32_t var = live_range->var & ~ZEND_LIVE_MASK;
+                       uint32_t def = live_range->start - 1;
+
+                       if (op_array->opcodes[def].result_type == IS_UNUSED) {
+                               if (op_array->opcodes[def].opcode == ZEND_DO_FCALL) {
+                                       /* constructor call */
+                                       do {
+                                               def--;
+                                               if ((op_array->opcodes[def].result_type & (IS_TMP_VAR|IS_VAR))
+                                                               && op_array->opcodes[def].result.var == var) {
+                                                       ZEND_ASSERT(op_array->opcodes[def].opcode == ZEND_NEW);
+                                                       break;
+                                               }
+                                       } while (def > 0);
+                               } else if (op_array->opcodes[def].opcode == ZEND_OP_DATA) {
+                                       def--;
+                               }
+                       }
+
+#if ZEND_DEBUG
+                       ZEND_ASSERT(op_array->opcodes[def].result_type & (IS_TMP_VAR|IS_VAR));
+                       ZEND_ASSERT(op_array->opcodes[def].result.var == var);
+                       ZEND_ASSERT(ssa->ops[def].result_def >= 0);
+#else
+                       if (!(op_array->opcodes[def].result_type & (IS_TMP_VAR|IS_VAR))
+                                       || op_array->opcodes[def].result.var != var
+                                       || ssa->ops[def].result_def < 0) {
+                               /* TODO: Some wrong live-range? keep it. */
+                               j++;
+                               live_range++;
+                               i++;
+                               continue;
+                       }
+#endif
+
+                       var = ssa->ops[def].result_def;
+
+                       if ((ssa->var_info[var].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))
+                                       && !is_var_dead(ctx, var)) {
+                               /* keep */
+                               j++;
+                       } else if (i != j) {
+                               op_array->live_range[j] = *live_range;
+                       }
+               }
+
+               live_range++;
+               i++;
+       }
+       op_array->last_live_range = j;
+}
+
 int dce_optimize_op_array(zend_op_array *op_array, zend_ssa *ssa, zend_bool reorder_dtor_effects) {
        int i;
        zend_ssa_phi *phi;
@@ -613,6 +682,10 @@ int dce_optimize_op_array(zend_op_array *op_array, zend_ssa *ssa, zend_bool reor
                }
        }
 
+       if (op_array->live_range) {
+               dce_live_ranges(&ctx, op_array, ssa);
+       }
+
        /* Eliminate dead instructions */
        ZEND_BITSET_FOREACH(ctx.instr_dead, ctx.instr_worklist_len, i) {
                removed_ops += dce_instr(&ctx, &op_array->opcodes[i], &ssa->ops[i]);