summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Stogov <dmitry@zend.com>2017-07-19 13:07:33 +0300
committerDmitry Stogov <dmitry@zend.com>2017-07-19 13:07:33 +0300
commit8447b86eef4ee7fe67757672972e9409fc6e14f3 (patch)
tree9b0049892b772ce71583bd8b7b137a2768356e5c
parent5274cefb4d0875dc0fa7228892fa290a8f64ccf5 (diff)
downloadphp-git-8447b86eef4ee7fe67757672972e9409fc6e14f3.tar.gz
Remove dead live ranges and FREE instructions
-rw-r--r--ext/opcache/Optimizer/dce.c93
1 files changed, 83 insertions, 10 deletions
diff --git a/ext/opcache/Optimizer/dce.c b/ext/opcache/Optimizer/dce.c
index cf75f0ad4a..db9a8a54bc 100644
--- a/ext/opcache/Optimizer/dce.c
+++ b/ext/opcache/Optimizer/dce.c
@@ -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]);