From ef8cf76815e10db08d07897296c09b925a1511a1 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 10 Oct 2013 15:32:30 +0400 Subject: [PATCH] Fixed bug #65845 (Error when Zend Opcache Optimizer is fully enabled). --- NEWS | 2 + ext/opcache/Optimizer/pass1_5.c | 130 ++---------------------- ext/opcache/Optimizer/zend_optimizer.c | 131 +++++++++++++++++++++++++ ext/opcache/tests/bug65845.phpt | 14 +++ 4 files changed, 154 insertions(+), 123 deletions(-) create mode 100644 ext/opcache/tests/bug65845.phpt diff --git a/NEWS b/NEWS index b70bdcc71b..9436fe9883 100644 --- a/NEWS +++ b/NEWS @@ -48,6 +48,8 @@ PHP NEWS . Added support for GNU Hurd. (Svante Signell) . Added function opcache_compile_file() to load PHP scripts into cache without execution. (Julien) + . Fixed bug #65845 (Error when Zend Opcache Optimizer is fully enabled). + (Dmitry) . Fixed bug #65665 (Exception not properly caught when opcache enabled). (Laruence) . Fixed bug #65510 (5.5.2 crashes in _get_zval_ptr_ptr_var). (Dmitry) diff --git a/ext/opcache/Optimizer/pass1_5.c b/ext/opcache/Optimizer/pass1_5.c index 795b954173..70ec6d5e2e 100644 --- a/ext/opcache/Optimizer/pass1_5.c +++ b/ext/opcache/Optimizer/pass1_5.c @@ -37,7 +37,6 @@ if (ZEND_OPTIMIZER_PASS_1 & OPTIMIZATION_LEVEL) { int (*binary_op)(zval *result, zval *op1, zval *op2 TSRMLS_DC) = get_binary_op(opline->opcode); zend_uint tv = ZEND_RESULT(opline).var; /* temporary variable */ zval result; - zend_op *tmp_opline; int er; if (opline->opcode == ZEND_DIV && @@ -61,95 +60,7 @@ if (ZEND_OPTIMIZER_PASS_1 & OPTIMIZATION_LEVEL) { literal_dtor(&ZEND_OP2_LITERAL(opline)); MAKE_NOP(opline); - /* substitute the following TMP_VAR usage with constant */ - for (tmp_opline = opline + 1; tmp_opline < end; tmp_opline++) { - if (ZEND_OP1_TYPE(tmp_opline) == IS_TMP_VAR && - ZEND_OP1(tmp_opline).var == tv) { - if (tmp_opline->opcode == ZEND_FREE) { - MAKE_NOP(tmp_opline); - zval_dtor(&result); - } else { - ZEND_OP1_TYPE(tmp_opline) = IS_CONST; -#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO - tmp_opline->op1.constant = zend_optimizer_add_literal(op_array, &result TSRMLS_CC); - if (Z_TYPE(result) == IS_STRING) { - Z_HASH_P(&ZEND_OP1_LITERAL(tmp_opline)) = zend_hash_func(Z_STRVAL(ZEND_OP1_LITERAL(tmp_opline)), Z_STRLEN(ZEND_OP1_LITERAL(tmp_opline)) + 1); - if (tmp_opline->opcode == ZEND_INIT_STATIC_METHOD_CALL || - tmp_opline->opcode == ZEND_DO_FCALL || - tmp_opline->opcode == ZEND_CATCH || - tmp_opline->opcode == ZEND_FETCH_CONSTANT) { - op_array->literals[tmp_opline->op1.constant].cache_slot = op_array->last_cache_slot++; - } - } -#else - ZEND_OP1_LITERAL(tmp_opline) = result; -#endif - } - /* TMP_VAR my be used only once */ - break; - } - if (ZEND_OP2_TYPE(tmp_opline) == IS_TMP_VAR && - ZEND_OP2(tmp_opline).var == tv) { - ZEND_OP2_TYPE(tmp_opline) = IS_CONST; -#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO - tmp_opline->op2.constant = zend_optimizer_add_literal(op_array, &result TSRMLS_CC); - if (Z_TYPE(result) == IS_STRING) { - Z_HASH_P(&ZEND_OP2_LITERAL(tmp_opline)) = zend_hash_func(Z_STRVAL(ZEND_OP2_LITERAL(tmp_opline)), Z_STRLEN(ZEND_OP2_LITERAL(tmp_opline)) + 1); - if (tmp_opline->opcode == ZEND_FETCH_R || - tmp_opline->opcode == ZEND_FETCH_W || - tmp_opline->opcode == ZEND_FETCH_RW || - tmp_opline->opcode == ZEND_FETCH_IS || - tmp_opline->opcode == ZEND_FETCH_UNSET || - tmp_opline->opcode == ZEND_FETCH_FUNC_ARG || - tmp_opline->opcode == ZEND_FETCH_CLASS || - tmp_opline->opcode == ZEND_INIT_FCALL_BY_NAME || - tmp_opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME || - tmp_opline->opcode == ZEND_UNSET_VAR || - tmp_opline->opcode == ZEND_ISSET_ISEMPTY_VAR || - tmp_opline->opcode == ZEND_ADD_INTERFACE || - tmp_opline->opcode == ZEND_ADD_TRAIT) { - op_array->literals[tmp_opline->op2.constant].cache_slot = op_array->last_cache_slot++; - } else if (tmp_opline->opcode == ZEND_INIT_METHOD_CALL || - tmp_opline->opcode == ZEND_INIT_STATIC_METHOD_CALL || - tmp_opline->opcode == ZEND_FETCH_CONSTANT || - tmp_opline->opcode == ZEND_ASSIGN_OBJ || - tmp_opline->opcode == ZEND_FETCH_OBJ_R || - tmp_opline->opcode == ZEND_FETCH_OBJ_W || - tmp_opline->opcode == ZEND_FETCH_OBJ_RW || - tmp_opline->opcode == ZEND_FETCH_OBJ_IS || - tmp_opline->opcode == ZEND_FETCH_OBJ_UNSET || - tmp_opline->opcode == ZEND_FETCH_OBJ_FUNC_ARG || - tmp_opline->opcode == ZEND_UNSET_OBJ || - tmp_opline->opcode == ZEND_PRE_INC_OBJ || - tmp_opline->opcode == ZEND_PRE_DEC_OBJ || - tmp_opline->opcode == ZEND_POST_INC_OBJ || - tmp_opline->opcode == ZEND_POST_DEC_OBJ || - tmp_opline->opcode == ZEND_ISSET_ISEMPTY_PROP_OBJ) { - op_array->literals[tmp_opline->op2.constant].cache_slot = op_array->last_cache_slot; - op_array->last_cache_slot += 2; - } else if (tmp_opline->opcode == ZEND_ASSIGN_ADD || - tmp_opline->opcode == ZEND_ASSIGN_SUB || - tmp_opline->opcode == ZEND_ASSIGN_MUL || - tmp_opline->opcode == ZEND_ASSIGN_DIV || - tmp_opline->opcode == ZEND_ASSIGN_MOD || - tmp_opline->opcode == ZEND_ASSIGN_SL || - tmp_opline->opcode == ZEND_ASSIGN_SR || - tmp_opline->opcode == ZEND_ASSIGN_CONCAT || - tmp_opline->opcode == ZEND_ASSIGN_BW_OR || - tmp_opline->opcode == ZEND_ASSIGN_BW_AND || - tmp_opline->opcode == ZEND_ASSIGN_BW_XOR) { - if (tmp_opline->extended_value == ZEND_ASSIGN_OBJ) { - op_array->literals[tmp_opline->op2.constant].cache_slot = op_array->last_cache_slot; - op_array->last_cache_slot += 2; - } - } - } -#else - ZEND_OP2_LITERAL(tmp_opline) = result; -#endif - break; - } - } + replace_tmp_by_const(op_array, opline + 1, tv, &result TSRMLS_CC); } break; @@ -159,6 +70,7 @@ if (ZEND_OPTIMIZER_PASS_1 & OPTIMIZATION_LEVEL) { opline->extended_value != IS_OBJECT && opline->extended_value != IS_RESOURCE) { /* cast of constant operand */ + zend_uint tv = ZEND_RESULT(opline).var; /* temporary variable */ zval res; res = ZEND_OP1_LITERAL(opline); zval_copy_ctor(&res); @@ -179,11 +91,11 @@ if (ZEND_OPTIMIZER_PASS_1 & OPTIMIZATION_LEVEL) { convert_to_string(&res); break; } + literal_dtor(&ZEND_OP1_LITERAL(opline)); - opline->opcode = ZEND_QM_ASSIGN; - opline->extended_value = 0; - ZEND_OP1_LITERAL(opline) = res; - SET_UNUSED(opline->op2); + MAKE_NOP(opline); + + replace_tmp_by_const(op_array, opline + 1, tv, &res TSRMLS_CC); } else if (opline->extended_value == IS_BOOL) { /* T = CAST(X, IS_BOOL) => T = BOOL(X) */ opline->opcode = ZEND_BOOL; @@ -197,7 +109,6 @@ if (ZEND_OPTIMIZER_PASS_1 & OPTIMIZATION_LEVEL) { /* unary operation on constant operand */ unary_op_type unary_op = get_unary_op(opline->opcode); zval result; - zend_op *tmp_opline; zend_uint tv = ZEND_RESULT(opline).var; /* temporary variable */ int er; @@ -218,34 +129,7 @@ if (ZEND_OPTIMIZER_PASS_1 & OPTIMIZATION_LEVEL) { literal_dtor(&ZEND_OP1_LITERAL(opline)); MAKE_NOP(opline); - /* substitute the following TMP_VAR usage with constant */ - for (tmp_opline = opline + 1; tmp_opline < end; tmp_opline++) { - if (ZEND_OP1_TYPE(tmp_opline) == IS_TMP_VAR && - ZEND_OP1(tmp_opline).var == tv) { - if (tmp_opline->opcode == ZEND_FREE) { - MAKE_NOP(tmp_opline); - zval_dtor(&result); - } else { - ZEND_OP1_TYPE(tmp_opline) = IS_CONST; -#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO - tmp_opline->op1.constant = zend_optimizer_add_literal(op_array, &result TSRMLS_CC); -#else - ZEND_OP1_LITERAL(tmp_opline) = result; -#endif - } - break; - } - if (ZEND_OP2_TYPE(tmp_opline) == IS_TMP_VAR && - ZEND_OP2(tmp_opline).var == tv) { - ZEND_OP2_TYPE(tmp_opline) = IS_CONST; -#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO - tmp_opline->op2.constant = zend_optimizer_add_literal(op_array, &result TSRMLS_CC); -#else - ZEND_OP2_LITERAL(tmp_opline) = result; -#endif - break; - } - } + replace_tmp_by_const(op_array, opline + 1, tv, &result TSRMLS_CC); } break; diff --git a/ext/opcache/Optimizer/zend_optimizer.c b/ext/opcache/Optimizer/zend_optimizer.c index 1f411d5da8..28085cb441 100644 --- a/ext/opcache/Optimizer/zend_optimizer.c +++ b/ext/opcache/Optimizer/zend_optimizer.c @@ -110,6 +110,137 @@ int zend_optimizer_add_literal(zend_op_array *op_array, const zval *zv TSRMLS_DC #endif +static void replace_tmp_by_const(zend_op_array *op_array, + zend_op *opline, + zend_uint var, + zval *val + TSRMLS_DC) +{ + zend_op *end = op_array->opcodes + op_array->last; + + while (opline < end) { + if (ZEND_OP1_TYPE(opline) == IS_TMP_VAR && + ZEND_OP1(opline).var == var) { + + if (opline->opcode == ZEND_FREE) { + MAKE_NOP(opline); + zval_dtor(val); + } else { + ZEND_OP1_TYPE(opline) = IS_CONST; +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + if (Z_TYPE_P(val) == IS_STRING) { + switch (opline->opcode) { + case ZEND_INIT_STATIC_METHOD_CALL: + case ZEND_CATCH: + case ZEND_FETCH_CONSTANT: + opline->op1.constant = zend_optimizer_add_literal(op_array, val TSRMLS_CC); + Z_HASH_P(&ZEND_OP1_LITERAL(opline)) = zend_hash_func(Z_STRVAL(ZEND_OP1_LITERAL(opline)), Z_STRLEN(ZEND_OP1_LITERAL(opline)) + 1); + op_array->literals[opline->op1.constant].cache_slot = op_array->last_cache_slot++; + zend_str_tolower(Z_STRVAL_P(val), Z_STRLEN_P(val)); + zend_optimizer_add_literal(op_array, val TSRMLS_CC); + op_array->literals[opline->op1.constant+1].hash_value = zend_hash_func(Z_STRVAL(op_array->literals[opline->op1.constant+1].constant), Z_STRLEN(op_array->literals[opline->op1.constant+1].constant) + 1); + break; + case ZEND_DO_FCALL: + zend_str_tolower(Z_STRVAL_P(val), Z_STRLEN_P(val)); + opline->op1.constant = zend_optimizer_add_literal(op_array, val TSRMLS_CC); + Z_HASH_P(&ZEND_OP1_LITERAL(opline)) = zend_hash_func(Z_STRVAL(ZEND_OP1_LITERAL(opline)), Z_STRLEN(ZEND_OP1_LITERAL(opline)) + 1); + op_array->literals[opline->op1.constant].cache_slot = op_array->last_cache_slot++; + break; + default: + opline->op1.constant = zend_optimizer_add_literal(op_array, val TSRMLS_CC); + Z_HASH_P(&ZEND_OP1_LITERAL(opline)) = zend_hash_func(Z_STRVAL(ZEND_OP1_LITERAL(opline)), Z_STRLEN(ZEND_OP1_LITERAL(opline)) + 1); + break; + } + } else { + opline->op1.constant = zend_optimizer_add_literal(op_array, val TSRMLS_CC); + } +#else + ZEND_OP1_LITERAL(opline) = *val; +#endif + } + /* TMP_VAR my be used only once */ + break; + } + + if (ZEND_OP2_TYPE(opline) == IS_TMP_VAR && + ZEND_OP2(opline).var == var) { + + ZEND_OP2_TYPE(opline) = IS_CONST; +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + opline->op2.constant = zend_optimizer_add_literal(op_array, val TSRMLS_CC); + if (Z_TYPE_P(val) == IS_STRING) { + Z_HASH_P(&ZEND_OP2_LITERAL(opline)) = zend_hash_func(Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)) + 1); + switch (opline->opcode) { + case ZEND_FETCH_R: + case ZEND_FETCH_W: + case ZEND_FETCH_RW: + case ZEND_FETCH_IS: + case ZEND_FETCH_UNSET: + case ZEND_FETCH_FUNC_ARG: + case ZEND_FETCH_CLASS: + case ZEND_INIT_FCALL_BY_NAME: + /*case ZEND_INIT_NS_FCALL_BY_NAME:*/ + case ZEND_UNSET_VAR: + case ZEND_ISSET_ISEMPTY_VAR: + case ZEND_ADD_INTERFACE: + case ZEND_ADD_TRAIT: + op_array->literals[opline->op2.constant].cache_slot = op_array->last_cache_slot++; + zend_str_tolower(Z_STRVAL_P(val), Z_STRLEN_P(val)); + zend_optimizer_add_literal(op_array, val TSRMLS_CC); + op_array->literals[opline->op2.constant+1].hash_value = zend_hash_func(Z_STRVAL(op_array->literals[opline->op2.constant+1].constant), Z_STRLEN(op_array->literals[opline->op2.constant+1].constant) + 1); + break; + case ZEND_INIT_METHOD_CALL: + case ZEND_INIT_STATIC_METHOD_CALL: + zend_str_tolower(Z_STRVAL_P(val), Z_STRLEN_P(val)); + zend_optimizer_add_literal(op_array, val TSRMLS_CC); + op_array->literals[opline->op2.constant+1].hash_value = zend_hash_func(Z_STRVAL(op_array->literals[opline->op2.constant+1].constant), Z_STRLEN(op_array->literals[opline->op2.constant+1].constant) + 1); + /* break missing intentionally */ + /*case ZEND_FETCH_CONSTANT:*/ + case ZEND_ASSIGN_OBJ: + case ZEND_FETCH_OBJ_R: + case ZEND_FETCH_OBJ_W: + case ZEND_FETCH_OBJ_RW: + case ZEND_FETCH_OBJ_IS: + case ZEND_FETCH_OBJ_UNSET: + case ZEND_FETCH_OBJ_FUNC_ARG: + case ZEND_UNSET_OBJ: + case ZEND_PRE_INC_OBJ: + case ZEND_PRE_DEC_OBJ: + case ZEND_POST_INC_OBJ: + case ZEND_POST_DEC_OBJ: + case ZEND_ISSET_ISEMPTY_PROP_OBJ: + op_array->literals[opline->op2.constant].cache_slot = op_array->last_cache_slot; + op_array->last_cache_slot += 2; + break; + case ZEND_ASSIGN_ADD: + case ZEND_ASSIGN_SUB: + case ZEND_ASSIGN_MUL: + case ZEND_ASSIGN_DIV: + 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: + if (opline->extended_value == ZEND_ASSIGN_OBJ) { + op_array->literals[opline->op2.constant].cache_slot = op_array->last_cache_slot; + op_array->last_cache_slot += 2; + } + break; + default: + break; + } + } +#else + ZEND_OP2_LITERAL(opline) = *val; +#endif + break; + } + opline++; + } +} + #include "Optimizer/nop_removal.c" #include "Optimizer/block_pass.c" #include "Optimizer/optimize_temp_vars_5.c" diff --git a/ext/opcache/tests/bug65845.phpt b/ext/opcache/tests/bug65845.phpt new file mode 100644 index 0000000000..2ae5f3974a --- /dev/null +++ b/ext/opcache/tests/bug65845.phpt @@ -0,0 +1,14 @@ +--TEST-- +Bug #65845 (Error when Zend Opcache Optimizer is fully enabled) +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +--SKIPIF-- + +--FILE-- + +--EXPECT-- +string(4) "tutu" -- 2.40.0