diff --git a/ext/opcache/Optimizer/block_pass.c b/ext/opcache/Optimizer/block_pass.c index 3f57183ed37..555c2778cfb 100644 --- a/ext/opcache/Optimizer/block_pass.c +++ b/ext/opcache/Optimizer/block_pass.c @@ -940,11 +940,8 @@ static void zend_optimize_block(zend_code_block *block, zend_op_array *op_array, } VAR_UNSET(opline->op1); - if (ZEND_OP1_TYPE(src) == IS_UNUSED) { - /* 5.3 may use IS_UNUSED as first argument to ZEND_ADD_... */ + if (opline->opcode != ZEND_CONCAT) { opline->opcode = ZEND_ADD_STRING; - } else { - opline->opcode = ZEND_CONCAT; } COPY_NODE(opline->op1, src->op1); old_len = Z_STRLEN(ZEND_OP2_LITERAL(src)); @@ -1039,45 +1036,47 @@ static void zend_optimize_block(zend_code_block *block, zend_op_array *op_array, VAR_UNSET(opline->op1); COPY_NODE(opline->op1, src->op1); MAKE_NOP(src); - } else if ((opline->opcode == ZEND_ADD_STRING || - opline->opcode == ZEND_ADD_CHAR || - opline->opcode == ZEND_ADD_VAR || - opline->opcode == ZEND_CONCAT) && - ZEND_OP1_TYPE(opline) == IS_TMP_VAR && - VAR_SOURCE(opline->op1) && - VAR_SOURCE(opline->op1)->opcode == ZEND_CONCAT && - ZEND_OP2_TYPE(VAR_SOURCE(opline->op1)) == IS_CONST && - Z_TYPE(ZEND_OP2_LITERAL(VAR_SOURCE(opline->op1))) == IS_STRING && - Z_STRLEN(ZEND_OP2_LITERAL(VAR_SOURCE(opline->op1))) == 0) { - /* convert T = CONCAT(X,''), T = ADD_STRING(T, Y) to T = CONCAT(X,Y) */ - zend_op *src = VAR_SOURCE(opline->op1); - VAR_UNSET(opline->op1); - COPY_NODE(opline->op1, src->op1); - if (opline->opcode == ZEND_ADD_CHAR) { - char c = (char)Z_LVAL(ZEND_OP2_LITERAL(opline)); - ZVAL_STRINGL(&ZEND_OP2_LITERAL(opline), &c, 1); + } else if (opline->opcode == ZEND_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); } - opline->opcode = ZEND_CONCAT; - literal_dtor(&ZEND_OP2_LITERAL(src)); /* will take care of empty_string too */ - MAKE_NOP(src); - } else if ((opline->opcode == ZEND_ADD_STRING || - opline->opcode == ZEND_ADD_CHAR || - opline->opcode == ZEND_ADD_VAR || - opline->opcode == ZEND_CONCAT) && - (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); - if (opline->opcode == ZEND_ADD_CHAR) { - char c = (char)Z_LVAL(ZEND_OP2_LITERAL(opline)); - ZVAL_STRINGL(&ZEND_OP2_LITERAL(opline), &c, 1); + 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 && + 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; } - opline->opcode = ZEND_CONCAT; - MAKE_NOP(src); } else if (opline->opcode == ZEND_QM_ASSIGN && ZEND_OP1_TYPE(opline) == ZEND_RESULT_TYPE(opline) && ZEND_OP1(opline).var == ZEND_RESULT(opline).var) { diff --git a/ext/opcache/Optimizer/pass1_5.c b/ext/opcache/Optimizer/pass1_5.c index f37084ed79c..031e0e3b375 100644 --- a/ext/opcache/Optimizer/pass1_5.c +++ b/ext/opcache/Optimizer/pass1_5.c @@ -87,11 +87,12 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx) break; } EG(error_reporting) = er; - literal_dtor(&ZEND_OP1_LITERAL(opline)); - literal_dtor(&ZEND_OP2_LITERAL(opline)); - MAKE_NOP(opline); - zend_optimizer_replace_by_const(op_array, opline + 1, IS_TMP_VAR, tv, &result); + if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_TMP_VAR, tv, &result)) { + literal_dtor(&ZEND_OP1_LITERAL(opline)); + literal_dtor(&ZEND_OP2_LITERAL(opline)); + MAKE_NOP(opline); + } } break; @@ -124,10 +125,10 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx) break; } - literal_dtor(&ZEND_OP1_LITERAL(opline)); - MAKE_NOP(opline); - - zend_optimizer_replace_by_const(op_array, opline + 1, type, tv, &res); + if (zend_optimizer_replace_by_const(op_array, opline + 1, type, tv, &res)) { + literal_dtor(&ZEND_OP1_LITERAL(opline)); + MAKE_NOP(opline); + } } else if (opline->extended_value == _IS_BOOL) { /* T = CAST(X, IS_BOOL) => T = BOOL(X) */ opline->opcode = ZEND_BOOL; @@ -151,10 +152,11 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx) break; } EG(error_reporting) = er; - literal_dtor(&ZEND_OP1_LITERAL(opline)); - MAKE_NOP(opline); - zend_optimizer_replace_by_const(op_array, opline + 1, IS_TMP_VAR, tv, &result); + if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_TMP_VAR, tv, &result)) { + literal_dtor(&ZEND_OP1_LITERAL(opline)); + MAKE_NOP(opline); + } } break; @@ -190,6 +192,7 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx) last_op = next_op; final_length += (requires_conversion? 1 : Z_STRLEN(ZEND_OP2_LITERAL(opline))); str = zend_string_alloc(final_length, 0); + str->len = final_length; ptr = str->val; ptr[final_length] = '\0'; if (requires_conversion) { /* ZEND_ADD_CHAR */ @@ -201,11 +204,10 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx) ptr++; } else { /* ZEND_ADD_STRING */ memcpy(ptr, Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline))); - zend_string_release(Z_STR(ZEND_OP2_LITERAL(opline))); - Z_STR(ZEND_OP2_LITERAL(opline)) = str; ptr += Z_STRLEN(ZEND_OP2_LITERAL(opline)); + zend_string_release(Z_STR(ZEND_OP2_LITERAL(opline))); + ZVAL_NEW_STR(&ZEND_OP2_LITERAL(opline), str); } - Z_STRLEN(ZEND_OP2_LITERAL(opline)) = final_length; next_op = opline + 1; while (next_op < last_op) { if (next_op->opcode == ZEND_ADD_STRING) { @@ -246,9 +248,10 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx) if ((offset = zend_get_constant_str("__COMPILER_HALT_OFFSET__", sizeof("__COMPILER_HALT_OFFSET__") - 1)) != NULL) { uint32_t tv = ZEND_RESULT(opline).var; - literal_dtor(&ZEND_OP2_LITERAL(opline)); - MAKE_NOP(opline); - zend_optimizer_replace_by_const(op_array, opline, IS_TMP_VAR, tv, offset); + if (zend_optimizer_replace_by_const(op_array, opline, IS_TMP_VAR, tv, offset)) { + literal_dtor(&ZEND_OP2_LITERAL(opline)); + MAKE_NOP(opline); + } } EG(current_execute_data) = orig_execute_data; break; @@ -269,9 +272,10 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx) if (Z_TYPE(c) == IS_CONSTANT_AST) { break; } - literal_dtor(&ZEND_OP2_LITERAL(opline)); - MAKE_NOP(opline); - zend_optimizer_replace_by_const(op_array, opline, IS_TMP_VAR, tv, &c); + if (zend_optimizer_replace_by_const(op_array, opline, IS_TMP_VAR, tv, &c)) { + literal_dtor(&ZEND_OP2_LITERAL(opline)); + MAKE_NOP(opline); + } } /* class constant */ @@ -333,9 +337,10 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx) } else { MAKE_NOP((opline - 1)); } - literal_dtor(&ZEND_OP2_LITERAL(opline)); - MAKE_NOP(opline); - zend_optimizer_replace_by_const(op_array, opline, IS_TMP_VAR, tv, &t); + if (zend_optimizer_replace_by_const(op_array, opline, IS_TMP_VAR, tv, &t)) { + literal_dtor(&ZEND_OP2_LITERAL(opline)); + MAKE_NOP(opline); + } } } } @@ -559,9 +564,10 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx) zval t; ZVAL_LONG(&t, Z_STRLEN(ZEND_OP1_LITERAL(opline))); - zend_optimizer_replace_by_const(op_array, opline + 1, IS_TMP_VAR, ZEND_RESULT(opline).var, &t); - literal_dtor(&ZEND_OP1_LITERAL(opline)); - MAKE_NOP(opline); + if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_TMP_VAR, ZEND_RESULT(opline).var, &t)) { + literal_dtor(&ZEND_OP1_LITERAL(opline)); + MAKE_NOP(opline); + } } break; case ZEND_DEFINED: @@ -572,9 +578,10 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx) break; } ZVAL_TRUE(&c); - zend_optimizer_replace_by_const(op_array, opline, IS_TMP_VAR, tv, &c); - literal_dtor(&ZEND_OP1_LITERAL(opline)); - MAKE_NOP(opline); + if (zend_optimizer_replace_by_const(op_array, opline, IS_TMP_VAR, tv, &c)) { + literal_dtor(&ZEND_OP1_LITERAL(opline)); + MAKE_NOP(opline); + } } break; case ZEND_DECLARE_CONST: diff --git a/ext/opcache/Optimizer/zend_optimizer.c b/ext/opcache/Optimizer/zend_optimizer.c index 7be076cb7f5..d0d67a57dad 100644 --- a/ext/opcache/Optimizer/zend_optimizer.c +++ b/ext/opcache/Optimizer/zend_optimizer.c @@ -158,6 +158,9 @@ void zend_optimizer_update_op2_const(zend_op_array *op_array, Z_CACHE_SLOT(op_array->literals[opline->op2.constant]) = op_array->cache_size; op_array->cache_size += sizeof(void*); return; + } else if (opline->opcode == ZEND_ADD_VAR) { + convert_to_string(val); + opline->opcode = ZEND_ADD_STRING; } opline->op2.constant = zend_optimizer_add_literal(op_array, val); if (Z_TYPE_P(val) == IS_STRING) { @@ -306,6 +309,7 @@ int zend_optimizer_replace_by_const(zend_op_array *op_array, case ZEND_SEND_VAR_NO_REF: if (opline->extended_value & ZEND_ARG_COMPILE_TIME_BOUND) { if (opline->extended_value & ZEND_ARG_SEND_BY_REF) { + zval_dtor(val); return 0; } opline->extended_value = 0; @@ -374,6 +378,7 @@ int zend_optimizer_replace_by_const(zend_op_array *op_array, ZEND_OP2(opline).var == var) { switch (opline->opcode) { case ZEND_ASSIGN_REF: + zval_dtor(val); return 0; default: break;