diff options
-rw-r--r-- | ext/opcache/Optimizer/pass1.c (renamed from ext/opcache/Optimizer/pass1_5.c) | 276 | ||||
-rw-r--r-- | ext/opcache/Optimizer/pass2.c | 225 | ||||
-rw-r--r-- | ext/opcache/Optimizer/pass3.c | 209 | ||||
-rw-r--r-- | ext/opcache/Optimizer/zend_optimizer.c | 28 | ||||
-rw-r--r-- | ext/opcache/Optimizer/zend_optimizer.h | 6 | ||||
-rw-r--r-- | ext/opcache/Optimizer/zend_optimizer_internal.h | 1 | ||||
-rw-r--r-- | ext/opcache/config.m4 | 3 | ||||
-rw-r--r-- | ext/opcache/config.w32 | 2 |
8 files changed, 291 insertions, 459 deletions
diff --git a/ext/opcache/Optimizer/pass1_5.c b/ext/opcache/Optimizer/pass1.c index 8ae1d34fc0..c5677a7a69 100644 --- a/ext/opcache/Optimizer/pass1_5.c +++ b/ext/opcache/Optimizer/pass1.c @@ -19,12 +19,15 @@ +----------------------------------------------------------------------+ */ -/* pass 1 - * - substitute persistent constants (true, false, null, etc) - * - perform compile-time evaluation of constant binary and unary operations +/* pass 1 (Simple local optimizations) + * - persistent constant substitution (true, false, null, etc) + * - constant casting (ADD expects numbers, CONCAT strings, etc) + * - simlple constant subexpression elimination + * - optimize constant conditional JMPs * - convert CAST(IS_BOOL,x) into BOOL(x) * - pre-evaluate constant function calls * - eliminate FETCH $GLOBALS followed by FETCH_DIM/UNSET_DIM/ISSET_ISEMPTY_DIM + * - change $i++ to ++$i where possible */ #include "php.h" @@ -37,7 +40,6 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx) { - int i = 0; zend_op *opline = op_array->opcodes; zend_op *end = opline + op_array->last; zend_bool collect_constants = (ZEND_OPTIMIZER_PASS_15 & ctx->optimization_level)? @@ -49,21 +51,133 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx) case ZEND_SUB: case ZEND_MUL: case ZEND_DIV: - case ZEND_MOD: case ZEND_POW: + if (opline->op1_type == IS_CONST) { + if (Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING) { + /* don't optimise if it should produce a runtime numeric string error */ + if (is_numeric_string(Z_STRVAL(ZEND_OP1_LITERAL(opline)), Z_STRLEN(ZEND_OP1_LITERAL(opline)), NULL, NULL, 0)) { + convert_scalar_to_number(&ZEND_OP1_LITERAL(opline)); + } + } + } + if (opline->op2_type == IS_CONST) { + if (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) { + /* don't optimise if it should produce a runtime numeric string error */ + if (is_numeric_string(Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)), NULL, NULL, 0)) { + convert_scalar_to_number(&ZEND_OP2_LITERAL(opline)); + } + } + if (opline->op1_type == IS_CONST) { + goto constant_binary_op; + } + } + goto try_compound_assignment; + + case ZEND_MOD: case ZEND_SL: case ZEND_SR: + if (opline->op1_type == IS_CONST) { + if (Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_LONG) { + /* don't optimise if it should produce a runtime numeric string error */ + if (!(Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING + && !is_numeric_string(Z_STRVAL(ZEND_OP1_LITERAL(opline)), Z_STRLEN(ZEND_OP1_LITERAL(opline)), NULL, NULL, 0))) { + convert_to_long(&ZEND_OP1_LITERAL(opline)); + } + } + } + if (opline->op2_type == IS_CONST) { + if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_LONG) { + /* don't optimise if it should produce a runtime numeric string error */ + if (!(Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING + && !is_numeric_string(Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)), NULL, NULL, 0))) { + convert_to_long(&ZEND_OP2_LITERAL(opline)); + } + } + if (opline->op1_type == IS_CONST) { + goto constant_binary_op; + } + } + goto try_compound_assignment; + case ZEND_CONCAT: case ZEND_FAST_CONCAT: + if (opline->op1_type == IS_CONST) { + if (Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_STRING) { + convert_to_string(&ZEND_OP1_LITERAL(opline)); + } + } + if (opline->op2_type == IS_CONST) { + if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_STRING) { + convert_to_string(&ZEND_OP2_LITERAL(opline)); + } + if (opline->op1_type == IS_CONST) { + goto constant_binary_op; + } + } + goto try_compound_assignment; + + case ZEND_BW_OR: + case ZEND_BW_AND: + case ZEND_BW_XOR: + if (opline->op1_type == IS_CONST && + opline->op2_type == IS_CONST) { + + goto constant_binary_op; + } else { + zend_op *next_opline; + +try_compound_assignment: + next_opline = opline + 1;; + while (next_opline < end && next_opline->opcode == ZEND_NOP) { + ++next_opline; + } + + if (next_opline >= end || next_opline->opcode != ZEND_ASSIGN) { + break; + } + + /* change $i=expr+$i to $i=$i+expr so that the following optimization + * works on it. Only do this if we are ignoring operator overloading, + * as operand order might be significant otherwise. */ + if ((ctx->optimization_level & ZEND_OPTIMIZER_IGNORE_OVERLOADING) + && (opline->op2_type & (IS_VAR | IS_CV)) + && opline->op2.var == next_opline->op1.var && + (opline->opcode == ZEND_ADD || + opline->opcode == ZEND_MUL || + opline->opcode == ZEND_BW_OR || + opline->opcode == ZEND_BW_AND || + opline->opcode == ZEND_BW_XOR)) { + zend_uchar tmp_type = opline->op1_type; + znode_op tmp = opline->op1; + + if (opline->opcode != ZEND_ADD + || (opline->op1_type == IS_CONST + && Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_ARRAY)) { + /* protection from array add: $a = array + $a is not commutative! */ + COPY_NODE(opline->op1, opline->op2); + COPY_NODE(opline->op2, tmp); + } + } + + if (ZEND_IS_BINARY_ASSIGN_OP_OPCODE(opline->opcode) + && (opline->op1_type & (IS_VAR | IS_CV)) + && opline->op1.var == next_opline->op1.var + && opline->op1_type == next_opline->op1_type) { + opline->extended_value = opline->opcode; + opline->opcode = ZEND_ASSIGN_OP; + COPY_NODE(opline->result, next_opline->result); + MAKE_NOP(next_opline); + opline++; + } + } + break; + case ZEND_IS_EQUAL: case ZEND_IS_NOT_EQUAL: case ZEND_IS_SMALLER: case ZEND_IS_SMALLER_OR_EQUAL: case ZEND_IS_IDENTICAL: case ZEND_IS_NOT_IDENTICAL: - case ZEND_BW_OR: - case ZEND_BW_AND: - case ZEND_BW_XOR: case ZEND_BOOL_XOR: case ZEND_SPACESHIP: case ZEND_CASE: @@ -72,6 +186,7 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx) /* binary operation with constant operands */ zval result; +constant_binary_op: if (zend_optimizer_eval_binary_op(&result, opline->opcode, &ZEND_OP1_LITERAL(opline), &ZEND_OP2_LITERAL(opline)) == SUCCESS) { literal_dtor(&ZEND_OP1_LITERAL(opline)); literal_dtor(&ZEND_OP2_LITERAL(opline)); @@ -86,6 +201,37 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx) } break; + case ZEND_ASSIGN_OP: + if (opline->op2_type == IS_CONST) { + if (opline->extended_value == ZEND_ADD + || opline->extended_value == ZEND_SUB + || opline->extended_value == ZEND_MUL + || opline->extended_value == ZEND_DIV + || opline->extended_value == ZEND_POW) { + if (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) { + /* don't optimise if it should produce a runtime numeric string error */ + if (is_numeric_string(Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)), NULL, NULL, 0)) { + convert_scalar_to_number(&ZEND_OP2_LITERAL(opline)); + } + } + } else if (opline->extended_value == ZEND_MOD + || opline->extended_value == ZEND_SL + || opline->extended_value == ZEND_SR) { + if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_LONG) { + /* don't optimise if it should produce a runtime numeric string error */ + if (!(Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING + && !is_numeric_string(Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)), NULL, NULL, 0))) { + convert_to_long(&ZEND_OP2_LITERAL(opline)); + } + } + } else if (opline->extended_value == ZEND_CONCAT) { + if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_STRING) { + convert_to_string(&ZEND_OP2_LITERAL(opline)); + } + } + } + break; + case ZEND_CAST: if (opline->op1_type == IS_CONST) { /* cast of constant operand */ @@ -559,6 +705,114 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx) } break; + case ZEND_POST_INC_STATIC_PROP: + case ZEND_POST_DEC_STATIC_PROP: + case ZEND_POST_INC_OBJ: + case ZEND_POST_DEC_OBJ: + case ZEND_POST_INC: + case ZEND_POST_DEC: { + /* POST_INC, FREE => PRE_INC */ + zend_op *next_op = opline + 1; + + if (next_op >= end) { + break; + } + if (next_op->opcode == ZEND_FREE && + next_op->op1.var == opline->result.var) { + MAKE_NOP(next_op); + opline->opcode -= 2; + opline->result_type = IS_UNUSED; + } + } + break; + + case ZEND_JMPZ_EX: + case ZEND_JMPNZ_EX: + /* convert Ti = JMPZ_EX(C, L) => Ti = QM_ASSIGN(C) + in case we know it wouldn't jump */ + if (opline->op1_type == IS_CONST) { + if (zend_is_true(&ZEND_OP1_LITERAL(opline))) { + if (opline->opcode == ZEND_JMPZ_EX) { + opline->opcode = ZEND_QM_ASSIGN; + zval_ptr_dtor_nogc(&ZEND_OP1_LITERAL(opline)); + ZVAL_TRUE(&ZEND_OP1_LITERAL(opline)); + opline->op2.num = 0; + break; + } + } else { + if (opline->opcode == ZEND_JMPNZ_EX) { + opline->opcode = ZEND_QM_ASSIGN; + zval_ptr_dtor_nogc(&ZEND_OP1_LITERAL(opline)); + ZVAL_FALSE(&ZEND_OP1_LITERAL(opline)); + opline->op2.num = 0; + break; + } + } + } + collect_constants = 0; + break; + + case ZEND_JMPZ: + case ZEND_JMPNZ: + if (opline->op1_type == IS_CONST) { + int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(opline)); + + if (opline->opcode == ZEND_JMPZ) { + should_jmp = !should_jmp; + } + literal_dtor(&ZEND_OP1_LITERAL(opline)); + opline->op1_type = IS_UNUSED; + if (should_jmp) { + opline->opcode = ZEND_JMP; + COPY_NODE(opline->op1, opline->op2); + opline->op2.num = 0; + } else { + MAKE_NOP(opline); + break; + } + } else if ((opline + 1)->opcode == ZEND_JMP) { + if (ZEND_OP2_JMP_ADDR(opline) == ZEND_OP1_JMP_ADDR(opline + 1)) { + /* JMPZ(X, L1), JMP(L1) => NOP, JMP(L1) */ + if (opline->op1_type == IS_CV) { + opline->opcode = ZEND_CHECK_VAR; + opline->op2.num = 0; + } else if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) { + opline->opcode = ZEND_FREE; + opline->op2.num = 0; + } else { + MAKE_NOP(opline); + } + break; + } else { + if (opline->opcode == ZEND_JMPZ) { + opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, ZEND_OP1_JMP_ADDR(opline + 1)); + } else { + opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, ZEND_OP2_JMP_ADDR(opline)); + ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP1_JMP_ADDR(opline + 1)); + } + opline->opcode = ZEND_JMPZNZ; + } + } + collect_constants = 0; + break; + + case ZEND_JMPZNZ: + if (opline->op1_type == IS_CONST) { + zend_op *target_opline; + + if (zend_is_true(&ZEND_OP1_LITERAL(opline))) { + target_opline = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value); /* JMPNZ */ + } else { + target_opline = ZEND_OP2_JMP_ADDR(opline); /* JMPZ */ + } + literal_dtor(&ZEND_OP1_LITERAL(opline)); + ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target_opline); + opline->op1_type = IS_UNUSED; + opline->opcode = ZEND_JMP; + } + collect_constants = 0; + break; + case ZEND_RETURN: case ZEND_RETURN_BY_REF: case ZEND_GENERATOR_RETURN: @@ -568,11 +822,6 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx) case ZEND_FAST_CALL: case ZEND_FAST_RET: case ZEND_JMP: - case ZEND_JMPZNZ: - case ZEND_JMPZ: - case ZEND_JMPNZ: - case ZEND_JMPZ_EX: - case ZEND_JMPNZ_EX: case ZEND_FE_RESET_R: case ZEND_FE_RESET_RW: case ZEND_FE_FETCH_R: @@ -584,6 +833,5 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx) break; } opline++; - i++; } } diff --git a/ext/opcache/Optimizer/pass2.c b/ext/opcache/Optimizer/pass2.c deleted file mode 100644 index 01e118e7e3..0000000000 --- a/ext/opcache/Optimizer/pass2.c +++ /dev/null @@ -1,225 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Zend OPcache | - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: Andi Gutmans <andi@php.net> | - | Zeev Suraski <zeev@php.net> | - | Stanislav Malyshev <stas@zend.com> | - | Dmitry Stogov <dmitry@php.net> | - +----------------------------------------------------------------------+ -*/ - -/* pass 2: - * - convert non-numeric constants to numeric constants in numeric operators - * - optimize constant conditional JMPs - */ - -#include "php.h" -#include "Optimizer/zend_optimizer.h" -#include "Optimizer/zend_optimizer_internal.h" -#include "zend_API.h" -#include "zend_constants.h" -#include "zend_execute.h" -#include "zend_vm.h" - -void zend_optimizer_pass2(zend_op_array *op_array) -{ - zend_op *opline; - zend_op *end = op_array->opcodes + op_array->last; - - opline = op_array->opcodes; - while (opline < end) { - switch (opline->opcode) { - case ZEND_ADD: - case ZEND_SUB: - case ZEND_MUL: - case ZEND_DIV: - case ZEND_POW: - if (opline->op1_type == IS_CONST) { - if (Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING) { - /* don't optimise if it should produce a runtime numeric string error */ - if (is_numeric_string(Z_STRVAL(ZEND_OP1_LITERAL(opline)), Z_STRLEN(ZEND_OP1_LITERAL(opline)), NULL, NULL, 0)) { - convert_scalar_to_number(&ZEND_OP1_LITERAL(opline)); - } - } - } - if (opline->op2_type == IS_CONST) { - if (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) { - /* don't optimise if it should produce a runtime numeric string error */ - if (is_numeric_string(Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)), NULL, NULL, 0)) { - convert_scalar_to_number(&ZEND_OP2_LITERAL(opline)); - } - } - } - break; - - case ZEND_MOD: - case ZEND_SL: - case ZEND_SR: - if (opline->op1_type == IS_CONST) { - if (Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_LONG) { - /* don't optimise if it should produce a runtime numeric string error */ - if (!(Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING - && !is_numeric_string(Z_STRVAL(ZEND_OP1_LITERAL(opline)), Z_STRLEN(ZEND_OP1_LITERAL(opline)), NULL, NULL, 0))) { - convert_to_long(&ZEND_OP1_LITERAL(opline)); - } - } - } - if (opline->op2_type == IS_CONST) { - if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_LONG) { - /* don't optimise if it should produce a runtime numeric string error */ - if (!(Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING - && !is_numeric_string(Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)), NULL, NULL, 0))) { - convert_to_long(&ZEND_OP2_LITERAL(opline)); - } - } - } - break; - - case ZEND_CONCAT: - case ZEND_FAST_CONCAT: - if (opline->op1_type == IS_CONST) { - if (Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_STRING) { - convert_to_string(&ZEND_OP1_LITERAL(opline)); - } - } - if (opline->op2_type == IS_CONST) { - if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_STRING) { - convert_to_string(&ZEND_OP2_LITERAL(opline)); - } - } - break; - - case ZEND_ASSIGN_OP: - if (opline->op2_type == IS_CONST) { - if (opline->extended_value == ZEND_ADD - || opline->extended_value == ZEND_SUB - || opline->extended_value == ZEND_MUL - || opline->extended_value == ZEND_DIV - || opline->extended_value == ZEND_POW) { - if (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) { - /* don't optimise if it should produce a runtime numeric string error */ - if (is_numeric_string(Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)), NULL, NULL, 0)) { - convert_scalar_to_number(&ZEND_OP2_LITERAL(opline)); - } - } - } else if (opline->extended_value == ZEND_MOD - || opline->extended_value == ZEND_SL - || opline->extended_value == ZEND_SR) { - if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_LONG) { - /* don't optimise if it should produce a runtime numeric string error */ - if (!(Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING - && !is_numeric_string(Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)), NULL, NULL, 0))) { - convert_to_long(&ZEND_OP2_LITERAL(opline)); - } - } - } else if (opline->extended_value == ZEND_CONCAT) { - if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_STRING) { - convert_to_string(&ZEND_OP2_LITERAL(opline)); - } - } - } - break; - - case ZEND_JMPZ_EX: - case ZEND_JMPNZ_EX: - /* convert Ti = JMPZ_EX(Ti, L) to JMPZ(Ti, L) */ -#if 0 - /* Disabled unsafe pattern: in conjunction with - * ZEND_VM_SMART_BRANCH() this may improperly eliminate - * assignment to Ti. - */ - if (opline->op1_type == IS_TMP_VAR && - opline->result_type == IS_TMP_VAR && - opline->op1.var == opline->result.var) { - opline->opcode -= 3; - SET_UNUSED(opline->result); - } else -#endif - /* convert Ti = JMPZ_EX(C, L) => Ti = QM_ASSIGN(C) - in case we know it wouldn't jump */ - if (opline->op1_type == IS_CONST) { - int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(opline)); - if (opline->opcode == ZEND_JMPZ_EX) { - should_jmp = !should_jmp; - } - if (!should_jmp) { - opline->opcode = ZEND_QM_ASSIGN; - SET_UNUSED(opline->op2); - } - } - break; - - case ZEND_JMPZ: - case ZEND_JMPNZ: - if (opline->op1_type == IS_CONST) { - int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(opline)); - - if (opline->opcode == ZEND_JMPZ) { - should_jmp = !should_jmp; - } - literal_dtor(&ZEND_OP1_LITERAL(opline)); - opline->op1_type = IS_UNUSED; - if (should_jmp) { - opline->opcode = ZEND_JMP; - COPY_NODE(opline->op1, opline->op2); - } else { - MAKE_NOP(opline); - } - break; - } - if ((opline + 1)->opcode == ZEND_JMP) { - /* JMPZ(X, L1), JMP(L2) => JMPZNZ(X, L1, L2) */ - /* JMPNZ(X, L1), JMP(L2) => JMPZNZ(X, L2, L1) */ - if (ZEND_OP2_JMP_ADDR(opline) == ZEND_OP1_JMP_ADDR(opline + 1)) { - /* JMPZ(X, L1), JMP(L1) => NOP, JMP(L1) */ - if (opline->op1_type == IS_CV) { - opline->opcode = ZEND_CHECK_VAR; - opline->op2.num = 0; - } else if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) { - opline->opcode = ZEND_FREE; - opline->op2.num = 0; - } else { - MAKE_NOP(opline); - } - } else { - if (opline->opcode == ZEND_JMPZ) { - opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, ZEND_OP1_JMP_ADDR(opline + 1)); - } else { - opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, ZEND_OP2_JMP_ADDR(opline)); - ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP1_JMP_ADDR(opline + 1)); - } - opline->opcode = ZEND_JMPZNZ; - } - } - break; - - case ZEND_JMPZNZ: - if (opline->op1_type == IS_CONST) { - zend_op *target_opline; - - if (zend_is_true(&ZEND_OP1_LITERAL(opline))) { - target_opline = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value); /* JMPNZ */ - } else { - target_opline = ZEND_OP2_JMP_ADDR(opline); /* JMPZ */ - } - literal_dtor(&ZEND_OP1_LITERAL(opline)); - ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target_opline); - opline->op1_type = IS_UNUSED; - opline->opcode = ZEND_JMP; - } - break; - } - opline++; - } -} diff --git a/ext/opcache/Optimizer/pass3.c b/ext/opcache/Optimizer/pass3.c index 5bbb2b0854..0e2d85e36f 100644 --- a/ext/opcache/Optimizer/pass3.c +++ b/ext/opcache/Optimizer/pass3.c @@ -19,10 +19,8 @@ +----------------------------------------------------------------------+ */ -/* pass 3: - * - optimize $i = $i+expr to $i+=expr +/* pass 3: (Jump optimization) * - optimize series of JMPs - * - change $i++ to ++$i where possible */ #include "php.h" @@ -53,89 +51,29 @@ void zend_optimizer_pass3(zend_op_array *op_array, zend_optimizer_ctx *ctx) { zend_op *opline; - zend_op *end = op_array->opcodes + op_array->last; + zend_op *end; zend_op **jmp_hitlist; int jmp_hitlist_count; int i; - uint32_t opline_num = 0; ALLOCA_FLAG(use_heap); + if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) { + return; + } + jmp_hitlist = (zend_op**)do_alloca(sizeof(zend_op*)*op_array->last, use_heap); opline = op_array->opcodes; + end = opline + op_array->last; while (opline < end) { jmp_hitlist_count = 0; switch (opline->opcode) { - case ZEND_ADD: - case ZEND_SUB: - case ZEND_MUL: - case ZEND_DIV: - case ZEND_MOD: - case ZEND_POW: - case ZEND_CONCAT: - case ZEND_SL: - case ZEND_SR: - case ZEND_BW_OR: - case ZEND_BW_AND: - case ZEND_BW_XOR: - { - zend_op *next_opline = opline + 1; - - while (next_opline < end && next_opline->opcode == ZEND_NOP) { - ++next_opline; - } - - if (next_opline >= end || next_opline->opcode != ZEND_ASSIGN) { - break; - } - - /* change $i=expr+$i to $i=$i+expr so that the following optimization - * works on it. Only do this if we are ignoring operator overloading, - * as operand order might be significant otherwise. */ - if ((ctx->optimization_level & ZEND_OPTIMIZER_IGNORE_OVERLOADING) - && (opline->op2_type & (IS_VAR | IS_CV)) - && opline->op2.var == next_opline->op1.var && - (opline->opcode == ZEND_ADD || - opline->opcode == ZEND_MUL || - opline->opcode == ZEND_BW_OR || - opline->opcode == ZEND_BW_AND || - opline->opcode == ZEND_BW_XOR)) { - zend_uchar tmp_type = opline->op1_type; - znode_op tmp = opline->op1; - - if (opline->opcode != ZEND_ADD - || (opline->op1_type == IS_CONST - && Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_ARRAY)) { - /* protection from array add: $a = array + $a is not commutative! */ - COPY_NODE(opline->op1, opline->op2); - COPY_NODE(opline->op2, tmp); - } - } - - if (ZEND_IS_BINARY_ASSIGN_OP_OPCODE(opline->opcode) - && (opline->op1_type & (IS_VAR | IS_CV)) - && opline->op1.var == next_opline->op1.var - && opline->op1_type == next_opline->op1_type) { - opline->extended_value = opline->opcode; - opline->opcode = ZEND_ASSIGN_OP; - COPY_NODE(opline->result, next_opline->result); - MAKE_NOP(next_opline); - opline++; - opline_num++; - } - } - break; - case ZEND_JMP: - if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) { - break; - } - /* convert L: JMP L+1 to NOP */ if (ZEND_OP1_JMP_ADDR(opline) == opline + 1) { MAKE_NOP(opline); - goto done_jmp_optimization; + break; } /* convert JMP L1 ... L1: JMP L2 to JMP L2 .. L1: JMP L2 */ @@ -149,10 +87,6 @@ void zend_optimizer_pass3(zend_op_array *op_array, zend_optimizer_ctx *ctx) case ZEND_JMP_SET: case ZEND_COALESCE: - if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) { - break; - } - while (ZEND_OP2_JMP_ADDR(opline) < end) { zend_op *target = ZEND_OP2_JMP_ADDR(opline); if (target->opcode == ZEND_JMP) { @@ -162,12 +96,9 @@ void zend_optimizer_pass3(zend_op_array *op_array, zend_optimizer_ctx *ctx) } } break; + case ZEND_JMPZ: case ZEND_JMPNZ: - if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) { - break; - } - while (ZEND_OP2_JMP_ADDR(opline) < end) { zend_op *target = ZEND_OP2_JMP_ADDR(opline); @@ -215,20 +146,12 @@ void zend_optimizer_pass3(zend_op_array *op_array, zend_optimizer_ctx *ctx) zend_uchar T_type = opline->result_type; znode_op T = opline->result; - if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) { - break; - } - /* convert L: T = JMPZ_EX X,L+1 to T = BOOL(X) */ /* convert L: T = JMPZ_EX T,L+1 to NOP */ if (ZEND_OP2_JMP_ADDR(opline) == opline + 1) { - if (opline->op1.var == opline->result.var) { - MAKE_NOP(opline); - } else { - opline->opcode = ZEND_BOOL; - SET_UNUSED(opline->op2); - } - goto done_jmp_optimization; + opline->opcode = ZEND_BOOL; + opline->op2.num = 0; + break; } while (ZEND_OP2_JMP_ADDR(opline) < end) { @@ -238,21 +161,21 @@ void zend_optimizer_pass3(zend_op_array *op_array, zend_optimizer_ctx *ctx) SAME_VAR(target->op1, T)) { /* convert T=JMPZ_EX(X,L1), L1: JMPZ(T,L2) to JMPZ_EX(X,L2) */ - CHECK_JMP2(target, continue_jmp_ex_optimization); + CHECK_JMP2(target, done_jmp_optimization); ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP2_JMP_ADDR(target)); } else if (target->opcode == opline->opcode && SAME_VAR(target->op1, T) && SAME_VAR(target->result, T)) { /* convert T=JMPZ_EX(X,L1), L1: T=JMPZ_EX(T,L2) to JMPZ_EX(X,L2) */ - CHECK_JMP2(target, continue_jmp_ex_optimization); + CHECK_JMP2(target, done_jmp_optimization); ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP2_JMP_ADDR(target)); } else if (target->opcode == ZEND_JMPZNZ && SAME_VAR(target->op1, T)) { /* Check for JMPZNZ with same cond variable */ zend_op *new_target; - CHECK_JMP2(target, continue_jmp_ex_optimization); + CHECK_JMP2(target, done_jmp_optimization); if (opline->opcode == ZEND_JMPZ_EX) { new_target = ZEND_OP2_JMP_ADDR(target); } else { @@ -291,92 +214,10 @@ void zend_optimizer_pass3(zend_op_array *op_array, zend_optimizer_ctx *ctx) break; } } /* while */ -continue_jmp_ex_optimization: - break; -#if 0 - /* If Ti = JMPZ_EX(X, L) and Ti is not used, convert to JMPZ(X, L) */ - { - zend_op *op; - for(op = opline+1; op<end; op++) { - if(op->result_type == IS_TMP_VAR && - op->result.var == opline->result.var) { - break; /* can pass to part 2 */ - } - - if(op->opcode == ZEND_JMP || - op->opcode == ZEND_JMPZ || - op->opcode == ZEND_JMPZ_EX || - op->opcode == ZEND_JMPNZ || - op->opcode == ZEND_JMPNZ_EX || - op->opcode == ZEND_JMPZNZ || - op->opcode == ZEND_CASE || - op->opcode == ZEND_RETURN || - op->opcode == ZEND_RETURN_BY_REF || - op->opcode == ZEND_FAST_RET || - op->opcode == ZEND_FE_FETCH_R || - op->opcode == ZEND_FE_FETCH_RW || - op->opcode == ZEND_EXIT) { - break; - } - - if(op->op1_type == IS_TMP_VAR && - op->op1.var == opline->result.var) { - goto done_jmp_optimization; - } - - if(op->op2_type == IS_TMP_VAR && - op->op2.var == opline->result.var) { - goto done_jmp_optimization; - } - } /* for */ - - for(op = &op_array->opcodes[opline->op2.opline_num]; op<end; op++) { - - if(op->result_type == IS_TMP_VAR && - op->result.var == opline->result.var) { - break; /* can pass to optimization */ - } - - if(op->opcode == ZEND_JMP || - op->opcode == ZEND_JMPZ || - op->opcode == ZEND_JMPZ_EX || - op->opcode == ZEND_JMPNZ || - op->opcode == ZEND_JMPNZ_EX || - op->opcode == ZEND_JMPZNZ || - op->opcode == ZEND_CASE || - op->opcode == ZEND_RETURN || - op->opcode == ZEND_RETURN_BY_REF || - op->opcode == ZEND_FAST_RET || - op->opcode == ZEND_FE_FETCH_R || - op->opcode == ZEND_FE_FETCH_RW || - op->opcode == ZEND_EXIT) { - break; - } - - if(op->op1_type == IS_TMP_VAR && - op->op1.var == opline->result.var) { - goto done_jmp_optimization; - } - - if(op->op2_type == IS_TMP_VAR && - op->op2.var == opline->result.var) { - goto done_jmp_optimization; - } - } - - opline->opcode = opline->opcode-3; /* JMP_EX -> JMP */ - SET_UNUSED(opline->result); - break; - } -#endif } break; case ZEND_JMPZNZ: - if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) { - break; - } - /* JMPZNZ(X,L1,L2), L1: JMP(L3) => JMPZNZ(X,L3,L2), L1: JMP(L3) */ while (ZEND_OP2_JMP_ADDR(opline) < end && ZEND_OP2_JMP_ADDR(opline)->opcode == ZEND_JMP) { @@ -393,29 +234,9 @@ continue_jmpznz_optimization: opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, ZEND_OP1_JMP_ADDR(target)); } break; - - case ZEND_POST_INC_OBJ: - case ZEND_POST_DEC_OBJ: - case ZEND_POST_INC: - case ZEND_POST_DEC: { - /* POST_INC, FREE => PRE_INC */ - zend_op *next_op = opline + 1; - - if (next_op >= end) { - break; - } - if (next_op->opcode == ZEND_FREE && - next_op->op1.var == opline->result.var) { - MAKE_NOP(next_op); - opline->opcode -= 2; - opline->result_type = IS_UNUSED; - } - } - break; } done_jmp_optimization: opline++; - opline_num++; } free_alloca(jmp_hitlist, use_heap); } diff --git a/ext/opcache/Optimizer/zend_optimizer.c b/ext/opcache/Optimizer/zend_optimizer.c index 7bac2d7538..23feb01596 100644 --- a/ext/opcache/Optimizer/zend_optimizer.c +++ b/ext/opcache/Optimizer/zend_optimizer.c @@ -934,12 +934,15 @@ static void zend_optimize(zend_op_array *op_array, zend_dump_op_array(op_array, ZEND_DUMP_LIVE_RANGES, "before optimizer", NULL); } - /* pass 1 - * - substitute persistent constants (true, false, null, etc) - * - perform compile-time evaluation of constant binary and unary operations - * - optimize series of ADD_STRING and/or ADD_CHAR + /* pass 1 (Simple local optimizations) + * - persistent constant substitution (true, false, null, etc) + * - constant casting (ADD expects numbers, CONCAT strings, etc) + * - simlple constant subexpression elimination + * - optimize constant conditional JMPs * - convert CAST(IS_BOOL,x) into BOOL(x) - * - pre-evaluate constant function calls + * - pre-evaluate constant function calls + * - eliminate FETCH $GLOBALS followed by FETCH_DIM/UNSET_DIM/ISSET_ISEMPTY_DIM + * - change $i++ to ++$i where possible */ if (ZEND_OPTIMIZER_PASS_1 & ctx->optimization_level) { zend_optimizer_pass1(op_array, ctx); @@ -948,21 +951,8 @@ static void zend_optimize(zend_op_array *op_array, } } - /* pass 2: - * - convert non-numeric constants to numeric constants in numeric operators - * - optimize constant conditional JMPs - */ - if (ZEND_OPTIMIZER_PASS_2 & ctx->optimization_level) { - zend_optimizer_pass2(op_array); - if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_2) { - zend_dump_op_array(op_array, 0, "after pass 2", NULL); - } - } - - /* pass 3: - * - optimize $i = $i+expr to $i+=expr + /* pass 3: (Jump optimization) * - optimize series of JMPs - * - change $i++ to ++$i where possible */ if (ZEND_OPTIMIZER_PASS_3 & ctx->optimization_level) { zend_optimizer_pass3(op_array, ctx); diff --git a/ext/opcache/Optimizer/zend_optimizer.h b/ext/opcache/Optimizer/zend_optimizer.h index 2841d018a5..04528d33e2 100644 --- a/ext/opcache/Optimizer/zend_optimizer.h +++ b/ext/opcache/Optimizer/zend_optimizer.h @@ -25,9 +25,9 @@ #include "zend.h" #include "zend_compile.h" -#define ZEND_OPTIMIZER_PASS_1 (1<<0) /* CSE, STRING construction */ -#define ZEND_OPTIMIZER_PASS_2 (1<<1) /* Constant conversion and jumps */ -#define ZEND_OPTIMIZER_PASS_3 (1<<2) /* ++, +=, series of jumps */ +#define ZEND_OPTIMIZER_PASS_1 (1<<0) /* Simple local optimizations */ +#define ZEND_OPTIMIZER_PASS_2 (1<<1) /* */ +#define ZEND_OPTIMIZER_PASS_3 (1<<2) /* Jump optimization */ #define ZEND_OPTIMIZER_PASS_4 (1<<3) /* INIT_FCALL_BY_NAME -> DO_FCALL */ #define ZEND_OPTIMIZER_PASS_5 (1<<4) /* CFG based optimization */ #define ZEND_OPTIMIZER_PASS_6 (1<<5) /* DFA based optimization */ diff --git a/ext/opcache/Optimizer/zend_optimizer_internal.h b/ext/opcache/Optimizer/zend_optimizer_internal.h index 9ab18f6398..d3ec873e4a 100644 --- a/ext/opcache/Optimizer/zend_optimizer_internal.h +++ b/ext/opcache/Optimizer/zend_optimizer_internal.h @@ -93,7 +93,6 @@ int zend_optimizer_replace_by_const(zend_op_array *op_array, zend_op *zend_optimizer_get_loop_var_def(const zend_op_array *op_array, zend_op *free_opline); void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx); -void zend_optimizer_pass2(zend_op_array *op_array); void zend_optimizer_pass3(zend_op_array *op_array, zend_optimizer_ctx *ctx); void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx); void zend_optimize_cfg(zend_op_array *op_array, zend_optimizer_ctx *ctx); diff --git a/ext/opcache/config.m4 b/ext/opcache/config.m4 index 7b23c7095d..ef1e874b3e 100644 --- a/ext/opcache/config.m4 +++ b/ext/opcache/config.m4 @@ -296,8 +296,7 @@ int main() { shared_alloc_mmap.c \ shared_alloc_posix.c \ Optimizer/zend_optimizer.c \ - Optimizer/pass1_5.c \ - Optimizer/pass2.c \ + Optimizer/pass1.c \ Optimizer/pass3.c \ Optimizer/optimize_func_calls.c \ Optimizer/block_pass.c \ diff --git a/ext/opcache/config.w32 b/ext/opcache/config.w32 index 965467fc58..7c67ccf0f6 100644 --- a/ext/opcache/config.w32 +++ b/ext/opcache/config.w32 @@ -38,7 +38,7 @@ if (PHP_OPCACHE != "no") { } } - ADD_SOURCES(configure_module_dirname + "/Optimizer", "zend_optimizer.c pass1_5.c pass2.c pass3.c optimize_func_calls.c block_pass.c optimize_temp_vars_5.c nop_removal.c compact_literals.c zend_cfg.c zend_dfg.c dfa_pass.c zend_ssa.c zend_inference.c zend_func_info.c zend_call_graph.c zend_dump.c escape_analysis.c compact_vars.c dce.c sccp.c scdf.c", "opcache", "OptimizerObj"); + ADD_SOURCES(configure_module_dirname + "/Optimizer", "zend_optimizer.c pass1.c pass3.c optimize_func_calls.c block_pass.c optimize_temp_vars_5.c nop_removal.c compact_literals.c zend_cfg.c zend_dfg.c dfa_pass.c zend_ssa.c zend_inference.c zend_func_info.c zend_call_graph.c zend_dump.c escape_analysis.c compact_vars.c dce.c sccp.c scdf.c", "opcache", "OptimizerObj"); ADD_FLAG('CFLAGS_OPCACHE', "/I " + configure_module_dirname); |