summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ext/opcache/Optimizer/pass1.c (renamed from ext/opcache/Optimizer/pass1_5.c)276
-rw-r--r--ext/opcache/Optimizer/pass2.c225
-rw-r--r--ext/opcache/Optimizer/pass3.c209
-rw-r--r--ext/opcache/Optimizer/zend_optimizer.c28
-rw-r--r--ext/opcache/Optimizer/zend_optimizer.h6
-rw-r--r--ext/opcache/Optimizer/zend_optimizer_internal.h1
-rw-r--r--ext/opcache/config.m43
-rw-r--r--ext/opcache/config.w322
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);