}
if (ssa->var_info) {
- int i;
+ int op_1;
+ int v;
int remove_nops = 0;
+ zend_op *opline;
+ zval tmp;
- // 1: #1.T = OP_Y | #3.CV = OP_Y
- // 2: ASSIGN #2.CV [undef,scalar] -> #3.CV, #1.T | NOP
- // --
- // 2: ASSIGN #2.CV [undef,scalar] -> #3.CV, X | 3.CV = QM_ASSIGN X
+ for (v = op_array->last_var; v < ssa->vars_count; v++) {
- for (i = 0; i < ssa->vars_count; i++) {
- int op2 = ssa->vars[i].definition;
+ op_1 = ssa->vars[v].definition;
- if (op2 < 0) {
+ if (op_1 < 0) {
continue;
}
- if (op_array->opcodes[op2].opcode == ZEND_ASSIGN
- && op_array->opcodes[op2].op1_type == IS_CV
- && !RETURN_VALUE_USED(&op_array->opcodes[op2])
+ opline = op_array->opcodes + op_1;
+
+ /* Convert LONG constants to DOUBLE */
+ if (ssa->var_info[v].use_as_double) {
+ if (opline->opcode == ZEND_ASSIGN
+ && opline->op2_type == IS_CONST
+ && ssa->ops[op_1].op1_def == v
+ && !RETURN_VALUE_USED(opline)
+ ) {
+
+// op_1: ASSIGN ? -> #v [use_as_double], long(?) => ASSIGN ? -> #v, double(?)
+
+ zval *zv = CT_CONSTANT_EX(op_array, opline->op2.constant);
+ ZEND_ASSERT(Z_TYPE_INFO_P(zv) == IS_LONG);
+ ZVAL_DOUBLE(&tmp, zval_get_double(zv));
+ opline->op2.constant = zend_optimizer_add_literal(op_array, &tmp);
+
+ } else if (opline->opcode == ZEND_QM_ASSIGN
+ && opline->op1_type == IS_CONST
+ ) {
+
+// op_1: QM_ASSIGN #v [use_as_double], long(?) => QM_ASSIGN #v, double(?)
+
+ zval *zv = CT_CONSTANT_EX(op_array, opline->op1.constant);
+ ZEND_ASSERT(Z_TYPE_INFO_P(zv) == IS_LONG);
+ ZVAL_DOUBLE(&tmp, zval_get_double(zv));
+ opline->op1.constant = zend_optimizer_add_literal(op_array, &tmp);
+ }
+
+ } else {
+ if (opline->opcode == ZEND_ADD
+ || opline->opcode == ZEND_SUB
+ || opline->opcode == ZEND_MUL
+ || opline->opcode == ZEND_IS_EQUAL
+ || opline->opcode == ZEND_IS_NOT_EQUAL
+ || opline->opcode == ZEND_IS_SMALLER
+ || opline->opcode == ZEND_IS_SMALLER_OR_EQUAL
+ ) {
+
+ if (opline->op1_type == IS_CONST
+ && opline->op2_type != IS_CONST
+ && (OP2_INFO() & MAY_BE_ANY) == MAY_BE_DOUBLE
+ && Z_TYPE_INFO_P(CT_CONSTANT_EX(op_array, opline->op1.constant)) == IS_LONG
+ ) {
+
+// op_1: #v.? = ADD long(?), #?.? [double] => #v.? = ADD double(?), #?.? [double]
+
+ zval *zv = CT_CONSTANT_EX(op_array, opline->op1.constant);
+ ZVAL_DOUBLE(&tmp, zval_get_double(zv));
+ opline->op1.constant = zend_optimizer_add_literal(op_array, &tmp);
+
+ } else if (opline->op1_type != IS_CONST
+ && opline->op2_type == IS_CONST
+ && (OP1_INFO() & MAY_BE_ANY) == MAY_BE_DOUBLE
+ && Z_TYPE_INFO_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == IS_LONG
+ ) {
+
+// op_1: #v.? = ADD #?.? [double], long(?) => #v.? = ADD #?.? [double], double(?)
+
+ zval *zv = CT_CONSTANT_EX(op_array, opline->op2.constant);
+ ZVAL_DOUBLE(&tmp, zval_get_double(zv));
+ opline->op2.constant = zend_optimizer_add_literal(op_array, &tmp);
+ }
+ }
+ }
+
+ if (ssa->vars[v].var >= op_array->last_var) {
+ /* skip TMP and VAR */
+ continue;
+ }
+
+ if (opline->opcode == ZEND_ASSIGN
+ && ssa->ops[op_1].op1_def == v
+ && !RETURN_VALUE_USED(opline)
) {
- int var2 = ssa->ops[op2].op1_use;
+ int orig_var = ssa->ops[op_1].op1_use;
- if (var2 >= 0
- && !(ssa->var_info[var2].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))
+ if (orig_var >= 0
+ && !(ssa->var_info[orig_var].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))
) {
- if ((op_array->opcodes[op2].op2_type & (IS_TMP_VAR|IS_VAR))
- && ssa->ops[op2].op2_use >= 0
- && !(ssa->var_info[ssa->ops[op2].op2_use].type & MAY_BE_REF)
- && ssa->vars[ssa->ops[op2].op2_use].definition >= 0
- && ssa->ops[ssa->vars[ssa->ops[op2].op2_use].definition].result_def == ssa->ops[op2].op2_use
- && ssa->ops[ssa->vars[ssa->ops[op2].op2_use].definition].result_use < 0
- && ssa->vars[ssa->ops[op2].op2_use].use_chain == op2
- && ssa->ops[op2].op2_use_chain < 0
- && !ssa->vars[ssa->ops[op2].op2_use].phi_use_chain
- && !ssa->vars[ssa->ops[op2].op2_use].sym_use_chain
+ int src_var = ssa->ops[op_1].op2_use;
+
+ if ((opline->op2_type & (IS_TMP_VAR|IS_VAR))
+ && src_var >= 0
+ && !(ssa->var_info[src_var].type & MAY_BE_REF)
+ && ssa->vars[src_var].definition >= 0
+ && ssa->ops[ssa->vars[src_var].definition].result_def == src_var
+ && ssa->ops[ssa->vars[src_var].definition].result_use < 0
+ && ssa->vars[src_var].use_chain == op_1
+ && ssa->ops[op_1].op2_use_chain < 0
+ && !ssa->vars[src_var].phi_use_chain
+ && !ssa->vars[src_var].sym_use_chain
/* see Zend/tests/generators/aborted_yield_during_new.phpt */
- && op_array->opcodes[ssa->vars[ssa->ops[op2].op2_use].definition].opcode != ZEND_NEW
+ && op_array->opcodes[ssa->vars[src_var].definition].opcode != ZEND_NEW
) {
- int var1 = ssa->ops[op2].op2_use;
- int op1 = ssa->vars[var1].definition;
- int var3 = i;
- if (zend_ssa_unlink_use_chain(ssa, op2, var2)) {
+ int op_2 = ssa->vars[src_var].definition;
+
+// op_2: #src_var.T = OP ... => #var0.CV = OP ...
+// op_1: ASSIGN #orig_var.CV [undef,scalar] -> #var0.CV, #src_var.T NOP
+
+ if (zend_ssa_unlink_use_chain(ssa, op_1, orig_var)) {
/* Reconstruct SSA */
- ssa->vars[var3].definition = op1;
- ssa->ops[op1].result_def = var3;
+ ssa->vars[v].definition = op_2;
+ ssa->ops[op_2].result_def = v;
- ssa->vars[var1].definition = -1;
- ssa->vars[var1].use_chain = -1;
+ ssa->vars[src_var].definition = -1;
+ ssa->vars[src_var].use_chain = -1;
- ssa->ops[op2].op1_use = -1;
- ssa->ops[op2].op2_use = -1;
- ssa->ops[op2].op1_def = -1;
- ssa->ops[op2].op1_use_chain = -1;
+ ssa->ops[op_1].op1_use = -1;
+ ssa->ops[op_1].op2_use = -1;
+ ssa->ops[op_1].op1_def = -1;
+ ssa->ops[op_1].op1_use_chain = -1;
/* Update opcodes */
- op_array->opcodes[op1].result_type = op_array->opcodes[op2].op1_type;
- op_array->opcodes[op1].result.var = op_array->opcodes[op2].op1.var;
- MAKE_NOP(&op_array->opcodes[op2]);
+ op_array->opcodes[op_2].result_type = opline->op1_type;
+ op_array->opcodes[op_2].result.var = opline->op1.var;
+ MAKE_NOP(opline);
remove_nops = 1;
}
- } else if (op_array->opcodes[op2].op2_type == IS_CONST
- || ((op_array->opcodes[op2].op2_type & (IS_TMP_VAR|IS_VAR|IS_CV))
- && ssa->ops[op2].op2_use >= 0
- && ssa->ops[op2].op2_def < 0)
+ } else if (opline->op2_type == IS_CONST
+ || ((opline->op2_type & (IS_TMP_VAR|IS_VAR|IS_CV))
+ && ssa->ops[op_1].op2_use >= 0
+ && ssa->ops[op_1].op2_def < 0)
) {
- int var3 = i;
- if (zend_ssa_unlink_use_chain(ssa, op2, var2)) {
+// op_1: ASSIGN #orig_var.CV [undef,scalar] -> #var0.CV, CONST|TMPVAR => QM_ASSIGN v.CV, CONST|TMPVAR
+
+ if (zend_ssa_unlink_use_chain(ssa, op_1, orig_var)) {
/* Reconstruct SSA */
- ssa->ops[op2].result_def = var3;
- ssa->ops[op2].op1_def = -1;
- ssa->ops[op2].op1_use = ssa->ops[op2].op2_use;
- ssa->ops[op2].op1_use_chain = ssa->ops[op2].op2_use_chain;
- ssa->ops[op2].op2_use = -1;
- ssa->ops[op2].op2_use_chain = -1;
+ ssa->ops[op_1].result_def = v;
+ ssa->ops[op_1].op1_def = -1;
+ ssa->ops[op_1].op1_use = ssa->ops[op_1].op2_use;
+ ssa->ops[op_1].op1_use_chain = ssa->ops[op_1].op2_use_chain;
+ ssa->ops[op_1].op2_use = -1;
+ ssa->ops[op_1].op2_use_chain = -1;
/* Update opcode */
- op_array->opcodes[op2].result_type = op_array->opcodes[op2].op1_type;
- op_array->opcodes[op2].result.var = op_array->opcodes[op2].op1.var;
- op_array->opcodes[op2].op1_type = op_array->opcodes[op2].op2_type;
- op_array->opcodes[op2].op1.var = op_array->opcodes[op2].op2.var;
- op_array->opcodes[op2].op2_type = IS_UNUSED;
- op_array->opcodes[op2].op2.var = 0;
- op_array->opcodes[op2].opcode = ZEND_QM_ASSIGN;
+ opline->result_type = opline->op1_type;
+ opline->result.var = opline->op1.var;
+ opline->op1_type = opline->op2_type;
+ opline->op1.var = opline->op2.var;
+ opline->op2_type = IS_UNUSED;
+ opline->op2.var = 0;
+ opline->opcode = ZEND_QM_ASSIGN;
}
}
}
- } else if (op_array->opcodes[op2].opcode == ZEND_ASSIGN_ADD
- && op_array->opcodes[op2].extended_value == 0
- && ssa->ops[op2].op1_def == i
- && op_array->opcodes[op2].op2_type == IS_CONST
- && Z_TYPE_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op2].op2.constant)) == IS_LONG
- && Z_LVAL_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op2].op2.constant)) == 1
- && ssa->ops[op2].op1_use >= 0
- && !(ssa->var_info[ssa->ops[op2].op1_use].type & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
-
- op_array->opcodes[op2].opcode = ZEND_PRE_INC;
- SET_UNUSED(op_array->opcodes[op2].op2);
-
- } else if (op_array->opcodes[op2].opcode == ZEND_ASSIGN_SUB
- && op_array->opcodes[op2].extended_value == 0
- && ssa->ops[op2].op1_def == i
- && op_array->opcodes[op2].op2_type == IS_CONST
- && Z_TYPE_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op2].op2.constant)) == IS_LONG
- && Z_LVAL_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op2].op2.constant)) == 1
- && ssa->ops[op2].op1_use >= 0
- && !(ssa->var_info[ssa->ops[op2].op1_use].type & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
-
- op_array->opcodes[op2].opcode = ZEND_PRE_DEC;
- SET_UNUSED(op_array->opcodes[op2].op2);
-
- } else if (op_array->opcodes[op2].opcode == ZEND_VERIFY_RETURN_TYPE
- && ssa->ops[op2].op1_def == i
- && ssa->ops[op2].op1_use >= 0
- && ssa->ops[op2].op1_use_chain == -1
- && ssa->vars[i].use_chain >= 0
- && (ssa->var_info[ssa->ops[op2].op1_use].type & (MAY_BE_ANY|MAY_BE_UNDEF)) == (ssa->var_info[ssa->ops[op2].op1_def].type & MAY_BE_ANY)) {
- /* remove useless type check */
- int var1 = ssa->ops[op2].op1_use;
- int ret = ssa->vars[i].use_chain;
-
- ssa->vars[var1].use_chain = ret;
- ssa->ops[ret].op1_use = var1;
-
- ssa->vars[i].definition = -1;
- ssa->vars[i].use_chain = -1;
-
- ssa->ops[op2].op1_def = -1;
- ssa->ops[op2].op1_use = -1;
-
- MAKE_NOP(&op_array->opcodes[op2]);
+ } else if (opline->opcode == ZEND_ASSIGN_ADD
+ && opline->extended_value == 0
+ && ssa->ops[op_1].op1_def == v
+ && opline->op2_type == IS_CONST
+ && Z_TYPE_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == IS_LONG
+ && Z_LVAL_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == 1
+ && ssa->ops[op_1].op1_use >= 0
+ && !(ssa->var_info[ssa->ops[op_1].op1_use].type & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
+
+// op_1: ASSIGN_ADD #?.CV [undef,null,int,foat] ->#var0.CV, int(1) => PRE_INC #?.CV ->#var0.CV
+
+ opline->opcode = ZEND_PRE_INC;
+ SET_UNUSED(opline->op2);
+
+ } else if (opline->opcode == ZEND_ASSIGN_SUB
+ && opline->extended_value == 0
+ && ssa->ops[op_1].op1_def == v
+ && opline->op2_type == IS_CONST
+ && Z_TYPE_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == IS_LONG
+ && Z_LVAL_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == 1
+ && ssa->ops[op_1].op1_use >= 0
+ && !(ssa->var_info[ssa->ops[op_1].op1_use].type & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
+
+// op_1: ASSIGN_SUB #?.CV [undef,null,int,foat] -> #var0.CV, int(1) => PRE_DEC #?.CV ->#var0.CV
+
+ opline->opcode = ZEND_PRE_DEC;
+ SET_UNUSED(opline->op2);
+
+ } else if (opline->opcode == ZEND_VERIFY_RETURN_TYPE
+ && ssa->ops[op_1].op1_def == v
+ && ssa->ops[op_1].op1_use >= 0
+ && ssa->ops[op_1].op1_use_chain == -1
+ && ssa->vars[v].use_chain >= 0
+ && (ssa->var_info[ssa->ops[op_1].op1_use].type & (MAY_BE_ANY|MAY_BE_UNDEF)) == (ssa->var_info[ssa->ops[op_1].op1_def].type & MAY_BE_ANY)) {
+
+// op_1: VERIFY_RETURN_TYPE #orig_var.CV [T] -> #var0.CV [T] => NOP
+
+ int orig_var = ssa->ops[op_1].op1_use;
+ int ret = ssa->vars[v].use_chain;
+
+ ssa->vars[orig_var].use_chain = ret;
+ ssa->ops[ret].op1_use = orig_var;
+
+ ssa->vars[v].definition = -1;
+ ssa->vars[v].use_chain = -1;
+
+ ssa->ops[op_1].op1_def = -1;
+ ssa->ops[op_1].op1_use = -1;
+
+ MAKE_NOP(opline);
remove_nops = 1;
- } else if (ssa->ops[op2].op1_def == i
- && !RETURN_VALUE_USED(&op_array->opcodes[op2])
- && ssa->ops[op2].op1_use >= 0
- && !(ssa->var_info[ssa->ops[op2].op1_use].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))
- && (op_array->opcodes[op2].opcode == ZEND_ASSIGN_ADD
- || op_array->opcodes[op2].opcode == ZEND_ASSIGN_SUB
- || op_array->opcodes[op2].opcode == ZEND_ASSIGN_MUL
- || op_array->opcodes[op2].opcode == ZEND_ASSIGN_DIV
- || op_array->opcodes[op2].opcode == ZEND_ASSIGN_MOD
- || op_array->opcodes[op2].opcode == ZEND_ASSIGN_SL
- || op_array->opcodes[op2].opcode == ZEND_ASSIGN_SR
- || op_array->opcodes[op2].opcode == ZEND_ASSIGN_BW_OR
- || op_array->opcodes[op2].opcode == ZEND_ASSIGN_BW_AND
- || op_array->opcodes[op2].opcode == ZEND_ASSIGN_BW_XOR)
- && op_array->opcodes[op2].extended_value == 0) {
+ } else if (ssa->ops[op_1].op1_def == v
+ && !RETURN_VALUE_USED(opline)
+ && ssa->ops[op_1].op1_use >= 0
+ && !(ssa->var_info[ssa->ops[op_1].op1_use].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))
+ && (opline->opcode == ZEND_ASSIGN_ADD
+ || opline->opcode == ZEND_ASSIGN_SUB
+ || opline->opcode == ZEND_ASSIGN_MUL
+ || opline->opcode == ZEND_ASSIGN_DIV
+ || opline->opcode == ZEND_ASSIGN_MOD
+ || opline->opcode == ZEND_ASSIGN_SL
+ || opline->opcode == ZEND_ASSIGN_SR
+ || opline->opcode == ZEND_ASSIGN_BW_OR
+ || opline->opcode == ZEND_ASSIGN_BW_AND
+ || opline->opcode == ZEND_ASSIGN_BW_XOR)
+ && opline->extended_value == 0) {
+
+// op_1: ASSIGN_ADD #orig_var.CV [undef,null,bool,int,double] -> #var0.CV, ? => #var0.CV = ADD #orig_var.CV, ?
/* Reconstruct SSA */
- ssa->ops[op2].result_def = ssa->ops[op2].op1_def;
- ssa->ops[op2].op1_def = -1;
+ ssa->ops[op_1].result_def = ssa->ops[op_1].op1_def;
+ ssa->ops[op_1].op1_def = -1;
/* Update opcode */
- op_array->opcodes[op2].opcode -= (ZEND_ASSIGN_ADD - ZEND_ADD);
- op_array->opcodes[op2].result_type = op_array->opcodes[op2].op1_type;
- op_array->opcodes[op2].result.var = op_array->opcodes[op2].op1.var;
+ opline->opcode -= (ZEND_ASSIGN_ADD - ZEND_ADD);
+ opline->result_type = opline->op1_type;
+ opline->result.var = opline->op1.var;
}
}
+
if (remove_nops) {
zend_ssa_remove_nops(op_array, ssa);
}
}
- if (ssa->var_info) {
- int i;
- zend_op *opline;
- zval tmp;
-
- /* Convert LONG constants to DOUBLE */
- for (i = 0; i < ssa->vars_count; i++) {
- if (ssa->vars[i].definition >= 0) {
- int op = ssa->vars[i].definition;
-
- opline = op_array->opcodes + op;
- if (ssa->var_info[i].use_as_double) {
- if (opline->opcode == ZEND_ASSIGN &&
- opline->op2_type == IS_CONST) {
- zval *zv = CT_CONSTANT_EX(op_array, opline->op2.constant);
- ZEND_ASSERT(Z_TYPE_INFO_P(zv) == IS_LONG);
- ZVAL_DOUBLE(&tmp, zval_get_double(zv));
- opline->op2.constant = zend_optimizer_add_literal(op_array, &tmp);
- } else if (opline->opcode == ZEND_QM_ASSIGN &&
- opline->op1_type == IS_CONST) {
- zval *zv = CT_CONSTANT_EX(op_array, opline->op1.constant);
- ZEND_ASSERT(Z_TYPE_INFO_P(zv) == IS_LONG);
- ZVAL_DOUBLE(&tmp, zval_get_double(zv));
- opline->op1.constant = zend_optimizer_add_literal(op_array, &tmp);
- }
- } else {
- if (opline->opcode == ZEND_ADD ||
- opline->opcode == ZEND_SUB ||
- opline->opcode == ZEND_MUL ||
- opline->opcode == ZEND_IS_EQUAL ||
- opline->opcode == ZEND_IS_NOT_EQUAL ||
- opline->opcode == ZEND_IS_SMALLER ||
- opline->opcode == ZEND_IS_SMALLER_OR_EQUAL) {
- if (opline->op1_type == IS_CONST &&
- opline->op2_type != IS_CONST &&
- (OP2_INFO() & MAY_BE_ANY) == MAY_BE_DOUBLE &&
- Z_TYPE_INFO_P(CT_CONSTANT_EX(op_array, opline->op1.constant)) == IS_LONG) {
- zval *zv = CT_CONSTANT_EX(op_array, opline->op1.constant);
- ZVAL_DOUBLE(&tmp, zval_get_double(zv));
- opline->op1.constant = zend_optimizer_add_literal(op_array, &tmp);
- } else if (opline->op1_type != IS_CONST &&
- opline->op2_type == IS_CONST &&
- (OP1_INFO() & MAY_BE_ANY) == MAY_BE_DOUBLE &&
- Z_TYPE_INFO_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == IS_LONG) {
- zval *zv = CT_CONSTANT_EX(op_array, opline->op2.constant);
- ZVAL_DOUBLE(&tmp, zval_get_double(zv));
- opline->op2.constant = zend_optimizer_add_literal(op_array, &tmp);
- }
-
- }
- }
- }
- }
- }
-
if (ctx->debug_level & ZEND_DUMP_AFTER_DFA_PASS) {
zend_dump_op_array(op_array, ZEND_DUMP_SSA, "after dfa pass", ssa);
}