From: Nikita Popov Date: Fri, 9 Sep 2016 14:02:48 +0000 (+0200) Subject: Merge branch 'PHP-7.0' into PHP-7.1 X-Git-Tag: php-7.1.0RC2~40 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=766264f8d2c97f2343ee20b6c050fe336696e9bc;p=php Merge branch 'PHP-7.0' into PHP-7.1 --- 766264f8d2c97f2343ee20b6c050fe336696e9bc diff --cc ext/opcache/Optimizer/block_pass.c index 3490a73e03,710d7bc9b2..e36c533d37 --- a/ext/opcache/Optimizer/block_pass.c +++ b/ext/opcache/Optimizer/block_pass.c @@@ -344,449 -790,377 +344,449 @@@ static void zend_optimize_block(zend_ba } #endif - /* IS_EQ(TRUE, X) => BOOL(X) - * IS_EQ(FALSE, X) => BOOL_NOT(X) - * IS_NOT_EQ(TRUE, X) => BOOL_NOT(X) - * IS_NOT_EQ(FALSE, X) => BOOL(X) - * CASE(TRUE, X) => BOOL(X) - * CASE(FALSE, X) => BOOL_NOT(X) - */ - if (opline->opcode == ZEND_IS_EQUAL || - opline->opcode == ZEND_IS_NOT_EQUAL || - /* CASE variable will be deleted later by FREE, so we can't optimize it */ - (opline->opcode == ZEND_CASE && (ZEND_OP1_TYPE(opline) & (IS_CONST|IS_CV)))) { - if (ZEND_OP1_TYPE(opline) == IS_CONST && - (Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_FALSE || - Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_TRUE)) { - /* T = IS_EQUAL(TRUE, X) => T = BOOL(X) */ - /* T = IS_EQUAL(FALSE, X) => T = BOOL_NOT(X) */ - /* T = IS_NOT_EQUAL(TRUE, X) => T = BOOL_NOT(X) */ - /* T = IS_NOT_EQUAL(FALSE, X) => T = BOOL(X) */ - /* Optimization of comparison with "null" is not safe, - * because ("0" == null) is not equal to !("0") - */ - opline->opcode = - ((opline->opcode != ZEND_IS_NOT_EQUAL) == ((Z_TYPE(ZEND_OP1_LITERAL(opline))) == IS_TRUE)) ? - ZEND_BOOL : ZEND_BOOL_NOT; - COPY_NODE(opline->op1, opline->op2); - SET_UNUSED(opline->op2); - } else if (ZEND_OP2_TYPE(opline) == IS_CONST && - (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_FALSE || - Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_TRUE)) { - /* T = IS_EQUAL(X, TRUE) => T = BOOL(X) */ - /* T = IS_EQUAL(X, FALSE) => T = BOOL_NOT(X) */ - /* T = IS_NOT_EQUAL(X, TRUE) => T = BOOL_NOT(X) */ - /* T = IS_NOT_EQUAL(X, FALSE) => T = BOOL(X) */ - /* Optimization of comparison with "null" is not safe, - * because ("0" == null) is not equal to !("0") - */ - opline->opcode = - ((opline->opcode != ZEND_IS_NOT_EQUAL) == ((Z_TYPE(ZEND_OP2_LITERAL(opline))) == IS_TRUE)) ? - ZEND_BOOL : ZEND_BOOL_NOT; - SET_UNUSED(opline->op2); - } - } + case ZEND_FETCH_LIST: + if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) { + /* LIST variable will be deleted later by FREE */ + Tsource[VAR_NUM(opline->op1.var)] = NULL; + } + break; - if ((opline->opcode == ZEND_BOOL || - opline->opcode == ZEND_BOOL_NOT || - opline->opcode == ZEND_JMPZ || - opline->opcode == ZEND_JMPNZ || - opline->opcode == ZEND_JMPZNZ) && - ZEND_OP1_TYPE(opline) == IS_TMP_VAR && - VAR_SOURCE(opline->op1) != NULL && - !zend_bitset_in(used_ext, VAR_NUM(ZEND_OP1(opline).var)) && - VAR_SOURCE(opline->op1)->opcode == ZEND_BOOL_NOT) { - /* T = BOOL_NOT(X) + JMPZ(T) -> NOP, JMPNZ(X) */ - zend_op *src = VAR_SOURCE(opline->op1); - - COPY_NODE(opline->op1, src->op1); - - switch (opline->opcode) { - case ZEND_BOOL: - /* T = BOOL_NOT(X) + BOOL(T) -> NOP, BOOL_NOT(X) */ - opline->opcode = ZEND_BOOL_NOT; - break; - case ZEND_BOOL_NOT: - /* T = BOOL_NOT(X) + BOOL_BOOL(T) -> NOP, BOOL(X) */ - opline->opcode = ZEND_BOOL; + case ZEND_CASE: + if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) { + /* CASE variable will be deleted later by FREE, so we can't optimize it */ + Tsource[VAR_NUM(opline->op1.var)] = NULL; break; - case ZEND_JMPZ: - /* T = BOOL_NOT(X) + JMPZ(T,L) -> NOP, JMPNZ(X,L) */ - opline->opcode = ZEND_JMPNZ; - break; - case ZEND_JMPNZ: - /* T = BOOL_NOT(X) + JMPNZ(T,L) -> NOP, JMPZ(X,L) */ - opline->opcode = ZEND_JMPZ; + } + if (opline->op1_type == IS_CONST && + opline->op2_type == IS_CONST) { break; - case ZEND_JMPZNZ: - { - /* T = BOOL_NOT(X) + JMPZNZ(T,L1,L2) -> NOP, JMPZNZ(X,L2,L1) */ - int op_t; - zend_code_block *op_b; - - op_t = opline->extended_value; - opline->extended_value = ZEND_OP2(opline).opline_num; - ZEND_OP2(opline).opline_num = op_t; - - op_b = block->ext_to; - block->ext_to = block->op2_to; - block->op2_to = op_b; + } + /* break missing intentionally */ + + case ZEND_IS_EQUAL: + case ZEND_IS_NOT_EQUAL: + if (opline->op1_type == IS_CONST && + opline->op2_type == IS_CONST) { + goto optimize_constant_binary_op; + } + /* IS_EQ(TRUE, X) => BOOL(X) + * IS_EQ(FALSE, X) => BOOL_NOT(X) + * IS_NOT_EQ(TRUE, X) => BOOL_NOT(X) + * IS_NOT_EQ(FALSE, X) => BOOL(X) + * CASE(TRUE, X) => BOOL(X) + * CASE(FALSE, X) => BOOL_NOT(X) + */ + if (opline->op1_type == IS_CONST && + (Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_FALSE || + Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_TRUE)) { + /* Optimization of comparison with "null" is not safe, + * because ("0" == null) is not equal to !("0") + */ + opline->opcode = + ((opline->opcode != ZEND_IS_NOT_EQUAL) == ((Z_TYPE(ZEND_OP1_LITERAL(opline))) == IS_TRUE)) ? + ZEND_BOOL : ZEND_BOOL_NOT; + COPY_NODE(opline->op1, opline->op2); + SET_UNUSED(opline->op2); + goto optimize_bool; + } else if (opline->op2_type == IS_CONST && + (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_FALSE || + Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_TRUE)) { + /* Optimization of comparison with "null" is not safe, + * because ("0" == null) is not equal to !("0") + */ + opline->opcode = + ((opline->opcode != ZEND_IS_NOT_EQUAL) == ((Z_TYPE(ZEND_OP2_LITERAL(opline))) == IS_TRUE)) ? + ZEND_BOOL : ZEND_BOOL_NOT; + SET_UNUSED(opline->op2); + goto optimize_bool; } break; - } - VAR_UNSET(opline->op1); - MAKE_NOP(src); - continue; - } else + case ZEND_BOOL: + case ZEND_BOOL_NOT: + optimize_bool: + if (opline->op1_type == IS_CONST) { + goto optimize_const_unary_op; + } + if (opline->op1_type == IS_TMP_VAR && + !zend_bitset_in(used_ext, VAR_NUM(opline->op1.var))) { + src = VAR_SOURCE(opline->op1); + if (src) { + switch (src->opcode) { + case ZEND_BOOL_NOT: + /* T = BOOL_NOT(X) + BOOL(T) -> NOP, BOOL_NOT(X) */ + VAR_SOURCE(opline->op1) = NULL; + COPY_NODE(opline->op1, src->op1); + opline->opcode = (opline->opcode == ZEND_BOOL) ? ZEND_BOOL_NOT : ZEND_BOOL; + MAKE_NOP(src); + goto optimize_bool; + case ZEND_BOOL: + /* T = BOOL(X) + BOOL(T) -> NOP, BOOL(X) */ + VAR_SOURCE(opline->op1) = NULL; + COPY_NODE(opline->op1, src->op1); + MAKE_NOP(src); + goto optimize_bool; + case ZEND_IS_EQUAL: + if (opline->opcode == ZEND_BOOL_NOT) { + src->opcode = ZEND_IS_NOT_EQUAL; + } + COPY_NODE(src->result, opline->result); + SET_VAR_SOURCE(src); + MAKE_NOP(opline); + break; + case ZEND_IS_NOT_EQUAL: + if (opline->opcode == ZEND_BOOL_NOT) { + src->opcode = ZEND_IS_EQUAL; + } + COPY_NODE(src->result, opline->result); + SET_VAR_SOURCE(src); + MAKE_NOP(opline); + break; + case ZEND_IS_IDENTICAL: + if (opline->opcode == ZEND_BOOL_NOT) { + src->opcode = ZEND_IS_NOT_IDENTICAL; + } + COPY_NODE(src->result, opline->result); + SET_VAR_SOURCE(src); + MAKE_NOP(opline); + break; + case ZEND_IS_NOT_IDENTICAL: + if (opline->opcode == ZEND_BOOL_NOT) { + src->opcode = ZEND_IS_IDENTICAL; + } + COPY_NODE(src->result, opline->result); + SET_VAR_SOURCE(src); + MAKE_NOP(opline); + break; + case ZEND_IS_SMALLER: + if (opline->opcode == ZEND_BOOL_NOT) { + zend_uchar tmp_type; + uint32_t tmp; + + src->opcode = ZEND_IS_SMALLER_OR_EQUAL; + tmp_type = src->op1_type; + src->op1_type = src->op2_type; + src->op2_type = tmp_type; + tmp = src->op1.num; + src->op1.num = src->op2.num; + src->op2.num = tmp; + } + COPY_NODE(src->result, opline->result); + SET_VAR_SOURCE(src); + MAKE_NOP(opline); + break; + case ZEND_IS_SMALLER_OR_EQUAL: + if (opline->opcode == ZEND_BOOL_NOT) { + zend_uchar tmp_type; + uint32_t tmp; + + src->opcode = ZEND_IS_SMALLER; + tmp_type = src->op1_type; + src->op1_type = src->op2_type; + src->op2_type = tmp_type; + tmp = src->op1.num; + src->op1.num = src->op2.num; + src->op2.num = tmp; + } + COPY_NODE(src->result, opline->result); + SET_VAR_SOURCE(src); + MAKE_NOP(opline); + break; + case ZEND_ISSET_ISEMPTY_VAR: + case ZEND_ISSET_ISEMPTY_DIM_OBJ: + case ZEND_ISSET_ISEMPTY_PROP_OBJ: + case ZEND_ISSET_ISEMPTY_STATIC_PROP: + case ZEND_INSTANCEOF: + case ZEND_TYPE_CHECK: + case ZEND_DEFINED: + if (opline->opcode == ZEND_BOOL_NOT) { + break; + } + COPY_NODE(src->result, opline->result); + SET_VAR_SOURCE(src); + MAKE_NOP(opline); + break; + } + } + } + break; + + case ZEND_JMPZ: + case ZEND_JMPNZ: + case ZEND_JMPZ_EX: + case ZEND_JMPNZ_EX: + case ZEND_JMPZNZ: + optimize_jmpznz: + if (opline->op1_type == IS_TMP_VAR && + (!zend_bitset_in(used_ext, VAR_NUM(opline->op1.var)) || + (opline->result_type == opline->op1_type && + opline->result.var == opline->op1.var))) { + src = VAR_SOURCE(opline->op1); + if (src) { + if (src->opcode == ZEND_BOOL_NOT && + opline->opcode != ZEND_JMPZ_EX && + opline->opcode != ZEND_JMPNZ_EX) { + VAR_SOURCE(opline->op1) = NULL; + COPY_NODE(opline->op1, src->op1); + if (opline->opcode == ZEND_JMPZ) { + /* T = BOOL_NOT(X) + JMPZ(T) -> NOP, JMPNZ(X) */ + opline->opcode = ZEND_JMPNZ; + } else if (opline->opcode == ZEND_JMPNZ) { + /* T = BOOL_NOT(X) + JMPNZ(T) -> NOP, JMPZ(X) */ + opline->opcode = ZEND_JMPZ; #if 0 - /* T = BOOL_NOT(X) + T = JMPZ_EX(T, X) -> T = BOOL_NOT(X), JMPNZ(X) */ - if(0 && (opline->opcode == ZEND_JMPZ_EX || - opline->opcode == ZEND_JMPNZ_EX) && - ZEND_OP1_TYPE(opline) == IS_TMP_VAR && - VAR_SOURCE(opline->op1) != NULL && - VAR_SOURCE(opline->op1)->opcode == ZEND_BOOL_NOT && - ZEND_OP1(opline).var == ZEND_RESULT(opline).var - ) { - zend_op *src = VAR_SOURCE(opline->op1); - if(opline->opcode == ZEND_JMPZ_EX) { - opline->opcode = ZEND_JMPNZ; - } else { - opline->opcode = ZEND_JMPZ; - } - COPY_NODE(opline->op1, src->op1); - SET_UNUSED(opline->result); - continue; - } else + } else if (opline->opcode == ZEND_JMPZ_EX) { + /* T = BOOL_NOT(X) + JMPZ_EX(T) -> NOP, JMPNZ_EX(X) */ + opline->opcode = ZEND_JMPNZ_EX; + } else if (opline->opcode == ZEND_JMPNZ_EX) { + /* T = BOOL_NOT(X) + JMPNZ_EX(T) -> NOP, JMPZ_EX(X) */ + opline->opcode = ZEND_JMPZ; #endif - /* T = BOOL(X) + JMPZ(T) -> NOP, JMPZ(X) */ - if ((opline->opcode == ZEND_BOOL || - opline->opcode == ZEND_BOOL_NOT || - opline->opcode == ZEND_JMPZ || - opline->opcode == ZEND_JMPZ_EX || - opline->opcode == ZEND_JMPNZ_EX || - opline->opcode == ZEND_JMPNZ || - opline->opcode == ZEND_JMPZNZ) && - (ZEND_OP1_TYPE(opline) & (IS_TMP_VAR|IS_VAR)) && - VAR_SOURCE(opline->op1) != NULL && - (!zend_bitset_in(used_ext, VAR_NUM(ZEND_OP1(opline).var)) || - ((ZEND_RESULT_TYPE(opline) & (IS_TMP_VAR|IS_VAR)) && - ZEND_RESULT(opline).var == ZEND_OP1(opline).var)) && - (VAR_SOURCE(opline->op1)->opcode == ZEND_BOOL || - VAR_SOURCE(opline->op1)->opcode == ZEND_QM_ASSIGN)) { - zend_op *src = VAR_SOURCE(opline->op1); - COPY_NODE(opline->op1, src->op1); - - VAR_UNSET(opline->op1); - MAKE_NOP(src); - continue; - } else if (last_op && opline->opcode == ZEND_ECHO && - last_op->opcode == ZEND_ECHO && - ZEND_OP1_TYPE(opline) == IS_CONST && - Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_DOUBLE && - ZEND_OP1_TYPE(last_op) == IS_CONST && - Z_TYPE(ZEND_OP1_LITERAL(last_op)) != IS_DOUBLE) { - /* compress consecutive ECHO's. - * Float to string conversion may be affected by current - * locale setting. - */ - int l, old_len; - - if (Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_STRING) { - convert_to_string_safe(&ZEND_OP1_LITERAL(opline)); - } - if (Z_TYPE(ZEND_OP1_LITERAL(last_op)) != IS_STRING) { - convert_to_string_safe(&ZEND_OP1_LITERAL(last_op)); - } - old_len = Z_STRLEN(ZEND_OP1_LITERAL(last_op)); - l = old_len + Z_STRLEN(ZEND_OP1_LITERAL(opline)); - if (!Z_REFCOUNTED(ZEND_OP1_LITERAL(last_op))) { - zend_string *tmp = zend_string_alloc(l, 0); - memcpy(ZSTR_VAL(tmp), Z_STRVAL(ZEND_OP1_LITERAL(last_op)), old_len); - Z_STR(ZEND_OP1_LITERAL(last_op)) = tmp; - } else { - Z_STR(ZEND_OP1_LITERAL(last_op)) = zend_string_extend(Z_STR(ZEND_OP1_LITERAL(last_op)), l, 0); - } - Z_TYPE_INFO(ZEND_OP1_LITERAL(last_op)) = IS_STRING_EX; - memcpy(Z_STRVAL(ZEND_OP1_LITERAL(last_op)) + old_len, Z_STRVAL(ZEND_OP1_LITERAL(opline)), Z_STRLEN(ZEND_OP1_LITERAL(opline))); - Z_STRVAL(ZEND_OP1_LITERAL(last_op))[l] = '\0'; - zval_dtor(&ZEND_OP1_LITERAL(opline)); - ZVAL_STR(&ZEND_OP1_LITERAL(opline), zend_new_interned_string(Z_STR(ZEND_OP1_LITERAL(last_op)))); - ZVAL_NULL(&ZEND_OP1_LITERAL(last_op)); - MAKE_NOP(last_op); - } else if ((opline->opcode == ZEND_CONCAT) && - ZEND_OP2_TYPE(opline) == IS_CONST && - ZEND_OP1_TYPE(opline) == IS_TMP_VAR && - VAR_SOURCE(opline->op1) && - (VAR_SOURCE(opline->op1)->opcode == ZEND_CONCAT || - VAR_SOURCE(opline->op1)->opcode == ZEND_FAST_CONCAT) && - ZEND_OP2_TYPE(VAR_SOURCE(opline->op1)) == IS_CONST && - ZEND_RESULT(VAR_SOURCE(opline->op1)).var == ZEND_OP1(opline).var) { - /* compress consecutive CONCAT/ADD_STRING/ADD_CHARs */ - zend_op *src = VAR_SOURCE(opline->op1); - int l, old_len; - - if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_STRING) { - convert_to_string_safe(&ZEND_OP2_LITERAL(opline)); - } - if (Z_TYPE(ZEND_OP2_LITERAL(src)) != IS_STRING) { - convert_to_string_safe(&ZEND_OP2_LITERAL(src)); - } + } else { + /* T = BOOL_NOT(X) + JMPZNZ(T,L1,L2) -> NOP, JMPZNZ(X,L2,L1) */ + uint32_t tmp; + + ZEND_ASSERT(opline->opcode == ZEND_JMPZNZ); + tmp = block->successors[0]; + block->successors[0] = block->successors[1]; + block->successors[1] = tmp; + } + MAKE_NOP(src); + goto optimize_jmpznz; + } else if (src->opcode == ZEND_BOOL || + src->opcode == ZEND_QM_ASSIGN) { + VAR_SOURCE(opline->op1) = NULL; + COPY_NODE(opline->op1, src->op1); + MAKE_NOP(src); + goto optimize_jmpznz; + } + } + } + break; - VAR_UNSET(opline->op1); - COPY_NODE(opline->op1, src->op1); - old_len = Z_STRLEN(ZEND_OP2_LITERAL(src)); - l = old_len + Z_STRLEN(ZEND_OP2_LITERAL(opline)); - if (!Z_REFCOUNTED(ZEND_OP2_LITERAL(src))) { - zend_string *tmp = zend_string_alloc(l, 0); - memcpy(ZSTR_VAL(tmp), Z_STRVAL(ZEND_OP2_LITERAL(src)), old_len); - Z_STR(ZEND_OP2_LITERAL(last_op)) = tmp; - } else { - Z_STR(ZEND_OP2_LITERAL(src)) = zend_string_extend(Z_STR(ZEND_OP2_LITERAL(src)), l, 0); - } - Z_TYPE_INFO(ZEND_OP2_LITERAL(last_op)) = IS_STRING_EX; - memcpy(Z_STRVAL(ZEND_OP2_LITERAL(src)) + old_len, Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline))); - Z_STRVAL(ZEND_OP2_LITERAL(src))[l] = '\0'; - zend_string_release(Z_STR(ZEND_OP2_LITERAL(opline))); - ZVAL_STR(&ZEND_OP2_LITERAL(opline), zend_new_interned_string(Z_STR(ZEND_OP2_LITERAL(src)))); - ZVAL_NULL(&ZEND_OP2_LITERAL(src)); - MAKE_NOP(src); - } else if ((opline->opcode == ZEND_ADD || - opline->opcode == ZEND_SUB || - opline->opcode == ZEND_MUL || - opline->opcode == ZEND_DIV || - opline->opcode == ZEND_MOD || - opline->opcode == ZEND_SL || - opline->opcode == ZEND_SR || - opline->opcode == ZEND_CONCAT || - opline->opcode == ZEND_FAST_CONCAT || - opline->opcode == ZEND_IS_EQUAL || - opline->opcode == ZEND_IS_NOT_EQUAL || - opline->opcode == ZEND_IS_SMALLER || - opline->opcode == ZEND_IS_SMALLER_OR_EQUAL || - opline->opcode == ZEND_IS_IDENTICAL || - opline->opcode == ZEND_IS_NOT_IDENTICAL || - opline->opcode == ZEND_BOOL_XOR || - opline->opcode == ZEND_BW_OR || - opline->opcode == ZEND_BW_AND || - opline->opcode == ZEND_BW_XOR) && - ZEND_OP1_TYPE(opline)==IS_CONST && - ZEND_OP2_TYPE(opline)==IS_CONST) { - /* evaluate constant expressions */ - binary_op_type binary_op = get_binary_op(opline->opcode); - zval result; - int er; - - if ((opline->opcode == ZEND_DIV || opline->opcode == ZEND_MOD) && - zval_get_long(&ZEND_OP2_LITERAL(opline)) == 0) { - if (RESULT_USED(opline)) { - SET_VAR_SOURCE(opline); + case ZEND_CONCAT: + case ZEND_FAST_CONCAT: + if (opline->op1_type == IS_CONST && + opline->op2_type == IS_CONST) { + goto optimize_constant_binary_op; } - opline++; - continue; - } else if ((opline->opcode == ZEND_SL || opline->opcode == ZEND_SR) && - zval_get_long(&ZEND_OP2_LITERAL(opline)) < 0) { - if (RESULT_USED(opline)) { - SET_VAR_SOURCE(opline); + + if (opline->op2_type == IS_CONST && + opline->op1_type == IS_TMP_VAR) { + + src = VAR_SOURCE(opline->op1); + if (src && + (src->opcode == ZEND_CONCAT || + src->opcode == ZEND_FAST_CONCAT) && + src->op2_type == IS_CONST) { + /* compress consecutive CONCATs */ + int l, old_len; + + if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_STRING) { + convert_to_string_safe(&ZEND_OP2_LITERAL(opline)); + } + if (Z_TYPE(ZEND_OP2_LITERAL(src)) != IS_STRING) { + convert_to_string_safe(&ZEND_OP2_LITERAL(src)); + } + + VAR_SOURCE(opline->op1) = NULL; + COPY_NODE(opline->op1, src->op1); + old_len = Z_STRLEN(ZEND_OP2_LITERAL(src)); + l = old_len + Z_STRLEN(ZEND_OP2_LITERAL(opline)); + if (!Z_REFCOUNTED(ZEND_OP2_LITERAL(src))) { + zend_string *tmp = zend_string_alloc(l, 0); + memcpy(ZSTR_VAL(tmp), Z_STRVAL(ZEND_OP2_LITERAL(src)), old_len); + Z_STR(ZEND_OP2_LITERAL(src)) = tmp; + } else { + Z_STR(ZEND_OP2_LITERAL(src)) = zend_string_extend(Z_STR(ZEND_OP2_LITERAL(src)), l, 0); + } + Z_TYPE_INFO(ZEND_OP2_LITERAL(src)) = IS_STRING_EX; + memcpy(Z_STRVAL(ZEND_OP2_LITERAL(src)) + old_len, Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline))); + Z_STRVAL(ZEND_OP2_LITERAL(src))[l] = '\0'; + zend_string_release(Z_STR(ZEND_OP2_LITERAL(opline))); + ZVAL_STR(&ZEND_OP2_LITERAL(opline), zend_new_interned_string(Z_STR(ZEND_OP2_LITERAL(src)))); + ZVAL_NULL(&ZEND_OP2_LITERAL(src)); + MAKE_NOP(src); + } } - opline++; - continue; - } - er = EG(error_reporting); - EG(error_reporting) = 0; - if (binary_op(&result, &ZEND_OP1_LITERAL(opline), &ZEND_OP2_LITERAL(opline)) == SUCCESS) { - literal_dtor(&ZEND_OP1_LITERAL(opline)); - literal_dtor(&ZEND_OP2_LITERAL(opline)); - opline->opcode = ZEND_QM_ASSIGN; - SET_UNUSED(opline->op2); - zend_optimizer_update_op1_const(op_array, opline, &result); - } - EG(error_reporting) = er; - } else if ((opline->opcode == ZEND_BOOL || - opline->opcode == ZEND_BOOL_NOT || - opline->opcode == ZEND_BW_NOT) && ZEND_OP1_TYPE(opline) == IS_CONST) { - /* evaluate constant unary ops */ - unary_op_type unary_op = get_unary_op(opline->opcode); - zval result; - - if (unary_op) { - unary_op(&result, &ZEND_OP1_LITERAL(opline)); - literal_dtor(&ZEND_OP1_LITERAL(opline)); - } else { - /* BOOL */ - ZVAL_COPY_VALUE(&result, &ZEND_OP1_LITERAL(opline)); - convert_to_boolean(&result); - ZVAL_NULL(&ZEND_OP1_LITERAL(opline)); - } - opline->opcode = ZEND_QM_ASSIGN; - zend_optimizer_update_op1_const(op_array, opline, &result); - } else if ((opline->opcode == ZEND_RETURN || opline->opcode == ZEND_EXIT) && - (ZEND_OP1_TYPE(opline) & (IS_TMP_VAR|IS_VAR)) && - VAR_SOURCE(opline->op1) && - VAR_SOURCE(opline->op1)->opcode == ZEND_QM_ASSIGN) { - /* T = QM_ASSIGN(X), RETURN(T) to RETURN(X) */ - zend_op *src = VAR_SOURCE(opline->op1); - VAR_UNSET(opline->op1); - COPY_NODE(opline->op1, src->op1); - MAKE_NOP(src); - } else if (opline->opcode == ZEND_CONCAT || opline->opcode == ZEND_FAST_CONCAT) { - if ((ZEND_OP1_TYPE(opline) & (IS_TMP_VAR|IS_VAR)) && - VAR_SOURCE(opline->op1) && - VAR_SOURCE(opline->op1)->opcode == ZEND_CAST && - VAR_SOURCE(opline->op1)->extended_value == IS_STRING) { - /* convert T1 = CAST(STRING, X), T2 = CONCAT(T1, Y) to T2 = CONCAT(X,Y) */ - zend_op *src = VAR_SOURCE(opline->op1); - VAR_UNSET(opline->op1); - COPY_NODE(opline->op1, src->op1); - MAKE_NOP(src); - } - if ((ZEND_OP2_TYPE(opline) & (IS_TMP_VAR|IS_VAR)) && - VAR_SOURCE(opline->op2) && - VAR_SOURCE(opline->op2)->opcode == ZEND_CAST && - VAR_SOURCE(opline->op2)->extended_value == IS_STRING) { - /* convert T1 = CAST(STRING, X), T2 = CONCAT(Y, T1) to T2 = CONCAT(Y,X) */ - zend_op *src = VAR_SOURCE(opline->op2); - VAR_UNSET(opline->op2); - COPY_NODE(opline->op2, src->op1); - MAKE_NOP(src); - } - if (ZEND_OP1_TYPE(opline) == IS_CONST && - Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING && - Z_STRLEN(ZEND_OP1_LITERAL(opline)) == 0) { - /* convert CONCAT('', X) => CAST(STRING, X) */ - literal_dtor(&ZEND_OP1_LITERAL(opline)); - opline->opcode = ZEND_CAST; - opline->extended_value = IS_STRING; - COPY_NODE(opline->op1, opline->op2); - opline->op2_type = IS_UNUSED; - opline->op2.var = 0; - } else if (ZEND_OP2_TYPE(opline) == IS_CONST && + + if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) { + src = VAR_SOURCE(opline->op1); + if (src && + src->opcode == ZEND_CAST && + src->extended_value == IS_STRING) { + /* convert T1 = CAST(STRING, X), T2 = CONCAT(T1, Y) to T2 = CONCAT(X,Y) */ + VAR_SOURCE(opline->op1) = NULL; + COPY_NODE(opline->op1, src->op1); + MAKE_NOP(src); + } + } + if (opline->op2_type & (IS_TMP_VAR|IS_VAR)) { + src = VAR_SOURCE(opline->op2); + if (src && + src->opcode == ZEND_CAST && + src->extended_value == IS_STRING) { + /* convert T1 = CAST(STRING, X), T2 = CONCAT(Y, T1) to T2 = CONCAT(Y,X) */ + zend_op *src = VAR_SOURCE(opline->op2); + VAR_SOURCE(opline->op2) = NULL; + COPY_NODE(opline->op2, src->op1); + MAKE_NOP(src); + } + } + if (opline->op1_type == IS_CONST && + Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING && + Z_STRLEN(ZEND_OP1_LITERAL(opline)) == 0) { + /* convert CONCAT('', X) => CAST(STRING, X) */ + literal_dtor(&ZEND_OP1_LITERAL(opline)); + opline->opcode = ZEND_CAST; + opline->extended_value = IS_STRING; + COPY_NODE(opline->op1, opline->op2); + opline->op2_type = IS_UNUSED; + opline->op2.var = 0; + } else if (opline->op2_type == IS_CONST && Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING && Z_STRLEN(ZEND_OP2_LITERAL(opline)) == 0) { - /* convert CONCAT(X, '') => CAST(STRING, X) */ - literal_dtor(&ZEND_OP2_LITERAL(opline)); - opline->opcode = ZEND_CAST; - opline->extended_value = IS_STRING; - opline->op2_type = IS_UNUSED; - opline->op2.var = 0; - } else if (opline->opcode == ZEND_CONCAT && - (opline->op1_type == IS_CONST || - (opline->op1_type == IS_TMP_VAR && - VAR_SOURCE(opline->op1) && - (VAR_SOURCE(opline->op1)->opcode == ZEND_FAST_CONCAT || - VAR_SOURCE(opline->op1)->opcode == ZEND_ROPE_END || - VAR_SOURCE(opline->op1)->opcode == ZEND_FETCH_CONSTANT))) && - (opline->op2_type == IS_CONST || - (opline->op2_type == IS_TMP_VAR && - VAR_SOURCE(opline->op2) && - (VAR_SOURCE(opline->op2)->opcode == ZEND_FAST_CONCAT || - VAR_SOURCE(opline->op2)->opcode == ZEND_ROPE_END || - VAR_SOURCE(opline->op2)->opcode == ZEND_FETCH_CONSTANT)))) { - opline->opcode = ZEND_FAST_CONCAT; - } - } else if (opline->opcode == ZEND_QM_ASSIGN && - ZEND_OP1_TYPE(opline) == ZEND_RESULT_TYPE(opline) && - ZEND_OP1(opline).var == ZEND_RESULT(opline).var) { - /* strip T = QM_ASSIGN(T) */ - MAKE_NOP(opline); - } else if (opline->opcode == ZEND_BOOL && - ZEND_OP1_TYPE(opline) == IS_TMP_VAR && - VAR_SOURCE(opline->op1) && - (VAR_SOURCE(opline->op1)->opcode == ZEND_IS_EQUAL || - VAR_SOURCE(opline->op1)->opcode == ZEND_IS_NOT_EQUAL || - VAR_SOURCE(opline->op1)->opcode == ZEND_IS_SMALLER || - VAR_SOURCE(opline->op1)->opcode == ZEND_IS_SMALLER_OR_EQUAL || - VAR_SOURCE(opline->op1)->opcode == ZEND_BOOL || - VAR_SOURCE(opline->op1)->opcode == ZEND_IS_IDENTICAL || - VAR_SOURCE(opline->op1)->opcode == ZEND_IS_NOT_IDENTICAL || - VAR_SOURCE(opline->op1)->opcode == ZEND_ISSET_ISEMPTY_VAR || - VAR_SOURCE(opline->op1)->opcode == ZEND_ISSET_ISEMPTY_DIM_OBJ) && - !zend_bitset_in(used_ext, VAR_NUM(ZEND_OP1(opline).var))) { - /* T = IS_SMALLER(X, Y), T1 = BOOL(T) => T = IS_SMALLER(X, Y), T1 = QM_ASSIGN(T) */ - zend_op *src = VAR_SOURCE(opline->op1); - COPY_NODE(src->result, opline->result); - SET_VAR_SOURCE(src); - MAKE_NOP(opline); + /* convert CONCAT(X, '') => CAST(STRING, X) */ + literal_dtor(&ZEND_OP2_LITERAL(opline)); + opline->opcode = ZEND_CAST; + opline->extended_value = IS_STRING; + opline->op2_type = IS_UNUSED; + opline->op2.var = 0; + } else if (opline->opcode == ZEND_CONCAT && + (opline->op1_type == IS_CONST || + (opline->op1_type == IS_TMP_VAR && + VAR_SOURCE(opline->op1) && + (VAR_SOURCE(opline->op1)->opcode == ZEND_FAST_CONCAT || + VAR_SOURCE(opline->op1)->opcode == ZEND_ROPE_END || + VAR_SOURCE(opline->op1)->opcode == ZEND_FETCH_CONSTANT || + VAR_SOURCE(opline->op1)->opcode == ZEND_FETCH_CLASS_CONSTANT))) && + (opline->op2_type == IS_CONST || + (opline->op2_type == IS_TMP_VAR && + VAR_SOURCE(opline->op2) && + (VAR_SOURCE(opline->op2)->opcode == ZEND_FAST_CONCAT || + VAR_SOURCE(opline->op2)->opcode == ZEND_ROPE_END || + VAR_SOURCE(opline->op2)->opcode == ZEND_FETCH_CONSTANT || + VAR_SOURCE(opline->op2)->opcode == ZEND_FETCH_CLASS_CONSTANT)))) { + opline->opcode = ZEND_FAST_CONCAT; + } + break; + + case ZEND_ADD: + case ZEND_SUB: + case ZEND_MUL: + case ZEND_DIV: + case ZEND_MOD: + case ZEND_SL: + case ZEND_SR: + case ZEND_IS_SMALLER: + case ZEND_IS_SMALLER_OR_EQUAL: + case ZEND_IS_IDENTICAL: + case ZEND_IS_NOT_IDENTICAL: + case ZEND_BOOL_XOR: + case ZEND_BW_OR: + case ZEND_BW_AND: + case ZEND_BW_XOR: + if (opline->op1_type == IS_CONST && + opline->op2_type == IS_CONST) { + /* evaluate constant expressions */ + binary_op_type binary_op; + zval result; + int er; + +optimize_constant_binary_op: + binary_op = get_binary_op(opline->opcode); + if ((opline->opcode == ZEND_DIV || opline->opcode == ZEND_MOD) && + zval_get_long(&ZEND_OP2_LITERAL(opline)) == 0) { + SET_VAR_SOURCE(opline); + opline++; + continue; + } else if ((opline->opcode == ZEND_SL || opline->opcode == ZEND_SR) && + zval_get_long(&ZEND_OP2_LITERAL(opline)) < 0) { + SET_VAR_SOURCE(opline); + opline++; + continue; + } else if (zend_binary_op_produces_numeric_string_error(opline->opcode, &ZEND_OP1_LITERAL(opline), &ZEND_OP2_LITERAL(opline))) { + SET_VAR_SOURCE(opline); + opline++; + continue; + } + er = EG(error_reporting); + EG(error_reporting) = 0; + if (binary_op(&result, &ZEND_OP1_LITERAL(opline), &ZEND_OP2_LITERAL(opline)) == SUCCESS) { + literal_dtor(&ZEND_OP1_LITERAL(opline)); + literal_dtor(&ZEND_OP2_LITERAL(opline)); + opline->opcode = ZEND_QM_ASSIGN; + SET_UNUSED(opline->op2); + zend_optimizer_update_op1_const(op_array, opline, &result); + } + EG(error_reporting) = er; + } + break; + + case ZEND_BW_NOT: + if (opline->op1_type == IS_CONST) { + /* evaluate constant unary ops */ + unary_op_type unary_op; + zval result; + +optimize_const_unary_op: + unary_op = get_unary_op(opline->opcode); + if (unary_op) { + unary_op(&result, &ZEND_OP1_LITERAL(opline)); + literal_dtor(&ZEND_OP1_LITERAL(opline)); + } else { + /* BOOL */ - result = ZEND_OP1_LITERAL(opline); ++ ZVAL_COPY_VALUE(&result, &ZEND_OP1_LITERAL(opline)); + convert_to_boolean(&result); + ZVAL_NULL(&ZEND_OP1_LITERAL(opline)); + } + opline->opcode = ZEND_QM_ASSIGN; + zend_optimizer_update_op1_const(op_array, opline, &result); + } + break; + + case ZEND_RETURN: + case ZEND_EXIT: + if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) { + src = VAR_SOURCE(opline->op1); + if (src && src->opcode == ZEND_QM_ASSIGN) { + zend_op *op = src + 1; + zend_bool optimize = 1; + + while (op < opline) { + if ((op->op1_type == opline->op1_type + && op->op1.var == opline->op1.var) + || (op->op2_type == opline->op1_type + && op->op2.var == opline->op1.var)) { + optimize = 0; + break; + } + op++; + } + + if (optimize) { + /* T = QM_ASSIGN(X), RETURN(T) to NOP, RETURN(X) */ + VAR_SOURCE(opline->op1) = NULL; + COPY_NODE(opline->op1, src->op1); + MAKE_NOP(src); + } + } + } + break; + + case ZEND_QM_ASSIGN: + if (opline->op1_type == opline->result_type && + opline->op1.var == opline->result.var) { + /* strip T = QM_ASSIGN(T) */ + MAKE_NOP(opline); + } + break; } + /* get variable source */ - if (RESULT_USED(opline)) { + if (opline->result_type & (IS_VAR|IS_TMP_VAR)) { SET_VAR_SOURCE(opline); } - if (opline->opcode != ZEND_NOP) { - last_op = opline; - } opline++; } @@@ -1603,87 -1886,55 +1603,88 @@@ static void zend_t_usage(zend_cfg *cfg continue; } - zend_bitset_copy(usage, used_ext, bitset_len); + end = op_array->opcodes + block->start; + opline = end + block->len - 1; + if (!next_block || + !(next_block->flags & ZEND_BB_FOLLOW) || + (next_block->flags & ZEND_BB_TARGET)) { + /* Skip continuation of "extended" BB */ + zend_bitset_copy(usage, used_ext, bitset_len); + } else if (block->successors[1] != -1) { + zend_bitset_union(usage, used_ext, bitset_len); + } + next_block = block; - while (opline >= block->start_opline) { + while (opline >= end) { /* usage checks */ - if (RES_NEVER_USED(opline)) { - switch (opline->opcode) { - case ZEND_ASSIGN_ADD: - case ZEND_ASSIGN_SUB: - case ZEND_ASSIGN_MUL: - case ZEND_ASSIGN_DIV: - case ZEND_ASSIGN_POW: - case ZEND_ASSIGN_MOD: - case ZEND_ASSIGN_SL: - case ZEND_ASSIGN_SR: - case ZEND_ASSIGN_CONCAT: - case ZEND_ASSIGN_BW_OR: - case ZEND_ASSIGN_BW_AND: - case ZEND_ASSIGN_BW_XOR: - case ZEND_PRE_INC: - case ZEND_PRE_DEC: - case ZEND_POST_INC: - case ZEND_POST_DEC: - case ZEND_ASSIGN: - case ZEND_ASSIGN_REF: - case ZEND_DO_FCALL: - case ZEND_DO_ICALL: - case ZEND_DO_UCALL: - case ZEND_DO_FCALL_BY_NAME: - if (ZEND_RESULT_TYPE(opline) == IS_VAR) { - ZEND_RESULT_TYPE(opline) |= EXT_TYPE_UNUSED; - } - break; - case ZEND_QM_ASSIGN: - case ZEND_BOOL: - case ZEND_BOOL_NOT: - if (ZEND_OP1_TYPE(opline) == IS_TMP_VAR) { - opline->opcode = ZEND_FREE; - } else { - if (ZEND_OP1_TYPE(opline) == IS_CONST) { - literal_dtor(&ZEND_OP1_LITERAL(opline)); + if (opline->result_type == IS_VAR) { + if (!zend_bitset_in(usage, VAR_NUM(opline->result.var))) { + switch (opline->opcode) { + case ZEND_ASSIGN_ADD: + case ZEND_ASSIGN_SUB: + case ZEND_ASSIGN_MUL: + case ZEND_ASSIGN_DIV: + case ZEND_ASSIGN_POW: + case ZEND_ASSIGN_MOD: + case ZEND_ASSIGN_SL: + case ZEND_ASSIGN_SR: + case ZEND_ASSIGN_CONCAT: + case ZEND_ASSIGN_BW_OR: + case ZEND_ASSIGN_BW_AND: + case ZEND_ASSIGN_BW_XOR: + case ZEND_PRE_INC: + case ZEND_PRE_DEC: + case ZEND_ASSIGN: + case ZEND_ASSIGN_REF: + case ZEND_DO_FCALL: + case ZEND_DO_ICALL: + case ZEND_DO_UCALL: + case ZEND_DO_FCALL_BY_NAME: + opline->result_type = IS_UNUSED; + break; + } + } else { + zend_bitset_excl(usage, VAR_NUM(opline->result.var)); + } + } else if (opline->result_type == IS_TMP_VAR) { + if (!zend_bitset_in(usage, VAR_NUM(opline->result.var))) { + switch (opline->opcode) { + case ZEND_POST_INC: + case ZEND_POST_DEC: + opline->opcode -= 2; + opline->result_type = IS_UNUSED; + break; + case ZEND_QM_ASSIGN: + case ZEND_BOOL: + case ZEND_BOOL_NOT: - if (ZEND_OP1_TYPE(opline) == IS_CONST) { - literal_dtor(&ZEND_OP1_LITERAL(opline)); - } else if (ZEND_OP1_TYPE(opline) == IS_TMP_VAR) { ++ if (ZEND_OP1_TYPE(opline) == IS_TMP_VAR) { + opline->opcode = ZEND_FREE; + } else { ++ if (ZEND_OP1_TYPE(opline) == IS_CONST) { ++ literal_dtor(&ZEND_OP1_LITERAL(opline)); ++ } + MAKE_NOP(opline); } - MAKE_NOP(opline); - } - break; - case ZEND_JMPZ_EX: - case ZEND_JMPNZ_EX: - opline->opcode -= 3; - SET_UNUSED(opline->result); - break; + break; + case ZEND_JMPZ_EX: + case ZEND_JMPNZ_EX: + opline->opcode -= 3; + SET_UNUSED(opline->result); + break; + case ZEND_ADD_ARRAY_ELEMENT: + case ZEND_ROPE_ADD: + zend_bitset_incl(usage, VAR_NUM(opline->result.var)); + break; + } + } else { + switch (opline->opcode) { + case ZEND_ADD_ARRAY_ELEMENT: + case ZEND_ROPE_ADD: + break; + default: + zend_bitset_excl(usage, VAR_NUM(opline->result.var)); + break; + } } }