return cfg->map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv))];
}
-static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array, zend_bitset used_ext, zend_cfg *cfg, zend_op **Tsource)
+static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array, zend_bitset used_ext, zend_cfg *cfg, zend_op **Tsource, uint32_t *opt_count)
{
zend_op *opline, *src;
zend_op *end, *last_op = NULL;
COPY_NODE(opline->op1, src->op1);
VAR_SOURCE(op1) = NULL;
MAKE_NOP(src);
+ ++(*opt_count);
} else {
zval c;
ZVAL_COPY(&c, &ZEND_OP1_LITERAL(src));
VAR_SOURCE(op1) = NULL;
literal_dtor(&ZEND_OP1_LITERAL(src));
MAKE_NOP(src);
+ ++(*opt_count);
switch (opline->opcode) {
case ZEND_JMPZ:
if (zend_is_true(&ZEND_OP1_LITERAL(opline))) {
VAR_SOURCE(op2) = NULL;
literal_dtor(&ZEND_OP1_LITERAL(src));
MAKE_NOP(src);
+ ++(*opt_count);
} else {
zval_ptr_dtor_nogc(&c);
}
VAR_SOURCE(opline->op1) = NULL;
COPY_NODE(opline->op1, src->op1);
MAKE_NOP(src);
+ ++(*opt_count);
}
}
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);
+ ++(*opt_count);
}
last_op = opline;
} else {
/* The remaining BOOL is removed by a separate optimization */
VAR_SOURCE(opline->op1) = NULL;
MAKE_NOP(opline);
+ ++(*opt_count);
}
} else if (opline->op1_type == IS_VAR) {
src = VAR_SOURCE(opline->op1);
src->opcode != ZEND_NEW) {
src->result_type = IS_UNUSED;
MAKE_NOP(opline);
+ ++(*opt_count);
}
}
break;
ZEND_BOOL : ZEND_BOOL_NOT;
COPY_NODE(opline->op1, opline->op2);
SET_UNUSED(opline->op2);
+ ++(*opt_count);
goto optimize_bool;
} else if (opline->op2_type == IS_CONST &&
(Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_FALSE ||
((opline->opcode != ZEND_IS_NOT_EQUAL) == ((Z_TYPE(ZEND_OP2_LITERAL(opline))) == IS_TRUE)) ?
ZEND_BOOL : ZEND_BOOL_NOT;
SET_UNUSED(opline->op2);
+ ++(*opt_count);
goto optimize_bool;
}
break;
COPY_NODE(opline->op1, src->op1);
opline->opcode = (opline->opcode == ZEND_BOOL) ? ZEND_BOOL_NOT : ZEND_BOOL;
MAKE_NOP(src);
+ ++(*opt_count);
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);
+ ++(*opt_count);
goto optimize_bool;
case ZEND_IS_EQUAL:
if (opline->opcode == ZEND_BOOL_NOT) {
COPY_NODE(src->result, opline->result);
SET_VAR_SOURCE(src);
MAKE_NOP(opline);
+ ++(*opt_count);
break;
case ZEND_IS_NOT_EQUAL:
if (opline->opcode == ZEND_BOOL_NOT) {
COPY_NODE(src->result, opline->result);
SET_VAR_SOURCE(src);
MAKE_NOP(opline);
+ ++(*opt_count);
break;
case ZEND_IS_IDENTICAL:
if (opline->opcode == ZEND_BOOL_NOT) {
COPY_NODE(src->result, opline->result);
SET_VAR_SOURCE(src);
MAKE_NOP(opline);
+ ++(*opt_count);
break;
case ZEND_IS_NOT_IDENTICAL:
if (opline->opcode == ZEND_BOOL_NOT) {
COPY_NODE(src->result, opline->result);
SET_VAR_SOURCE(src);
MAKE_NOP(opline);
+ ++(*opt_count);
break;
case ZEND_IS_SMALLER:
if (opline->opcode == ZEND_BOOL_NOT) {
COPY_NODE(src->result, opline->result);
SET_VAR_SOURCE(src);
MAKE_NOP(opline);
+ ++(*opt_count);
break;
case ZEND_IS_SMALLER_OR_EQUAL:
if (opline->opcode == ZEND_BOOL_NOT) {
COPY_NODE(src->result, opline->result);
SET_VAR_SOURCE(src);
MAKE_NOP(opline);
+ ++(*opt_count);
break;
case ZEND_ISSET_ISEMPTY_VAR:
case ZEND_ISSET_ISEMPTY_DIM_OBJ:
COPY_NODE(src->result, opline->result);
SET_VAR_SOURCE(src);
MAKE_NOP(opline);
+ ++(*opt_count);
break;
}
}
block->successors[1] = tmp;
}
MAKE_NOP(src);
+ ++(*opt_count);
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);
+ ++(*opt_count);
goto optimize_jmpznz;
}
}
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);
+ ++(*opt_count);
}
}
VAR_SOURCE(opline->op1) = NULL;
COPY_NODE(opline->op1, src->op1);
MAKE_NOP(src);
+ ++(*opt_count);
}
}
if (opline->op2_type & (IS_TMP_VAR|IS_VAR)) {
VAR_SOURCE(opline->op2) = NULL;
COPY_NODE(opline->op2, src->op1);
MAKE_NOP(src);
+ ++(*opt_count);
}
}
if (opline->op1_type == IS_CONST &&
COPY_NODE(opline->op1, opline->op2);
opline->op2_type = IS_UNUSED;
opline->op2.var = 0;
+ ++(*opt_count);
} else if (opline->op2_type == IS_CONST &&
Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING &&
Z_STRLEN(ZEND_OP2_LITERAL(opline)) == 0) {
opline->extended_value = IS_STRING;
opline->op2_type = IS_UNUSED;
opline->op2.var = 0;
+ ++(*opt_count);
} else if (opline->opcode == ZEND_CONCAT &&
(opline->op1_type == IS_CONST ||
(opline->op1_type == IS_TMP_VAR &&
VAR_SOURCE(opline->op2)->opcode == ZEND_FETCH_CONSTANT ||
VAR_SOURCE(opline->op2)->opcode == ZEND_FETCH_CLASS_CONSTANT)))) {
opline->opcode = ZEND_FAST_CONCAT;
+ ++(*opt_count);
}
break;
opline->opcode = ZEND_QM_ASSIGN;
SET_UNUSED(opline->op2);
zend_optimizer_update_op1_const(op_array, opline, &result);
+ ++(*opt_count);
}
}
break;
literal_dtor(&ZEND_OP1_LITERAL(opline));
opline->opcode = ZEND_QM_ASSIGN;
zend_optimizer_update_op1_const(op_array, opline, &result);
+ ++(*opt_count);
}
}
break;
opline->opcode = ZEND_QM_ASSIGN;
opline->extended_value = 0;
zend_optimizer_update_op1_const(op_array, opline, &result);
+ ++(*opt_count);
}
}
break;
literal_dtor(&ZEND_OP1_LITERAL(opline));
opline->opcode = ZEND_QM_ASSIGN;
zend_optimizer_update_op1_const(op_array, opline, &result);
+ ++(*opt_count);
}
}
break;
VAR_SOURCE(opline->op1) = NULL;
COPY_NODE(opline->op1, src->op1);
MAKE_NOP(src);
+ ++(*opt_count);
}
}
}
opline->op1.var == opline->result.var) {
/* strip T = QM_ASSIGN(T) */
MAKE_NOP(opline);
+ ++(*opt_count);
}
break;
}
}
}
-static void zend_jmp_optimization(zend_basic_block *block, zend_op_array *op_array, zend_cfg *cfg, zend_uchar *same_t)
+static void zend_jmp_optimization(zend_basic_block *block, zend_op_array *op_array, zend_cfg *cfg, zend_uchar *same_t, uint32_t *opt_count)
{
/* last_op is the last opcode of the current block */
zend_basic_block *blocks = cfg->blocks;
/* JMP(next) -> NOP */
if (block->successors[0] == next) {
MAKE_NOP(last_op);
+ ++(*opt_count);
block->len--;
break;
}
DEL_SOURCE(block, block->successors[0]);
block->successors[0] = target_block->successors[0];
ADD_SOURCE(block, block->successors[0]);
+ ++(*opt_count);
} else if (target->opcode == ZEND_JMPZNZ &&
!(target_block->flags & ZEND_BB_PROTECTED)) {
/* JMP L, L: JMPZNZ L1,L2 -> JMPZNZ L1,L2 */
block->successors[1] = target_block->successors[1];
ADD_SOURCE(block, block->successors[0]);
ADD_SOURCE(block, block->successors[1]);
+ ++(*opt_count);
} else if ((target->opcode == ZEND_RETURN ||
target->opcode == ZEND_RETURN_BY_REF ||
target->opcode == ZEND_EXIT) &&
}
DEL_SOURCE(block, block->successors[0]);
block->successors_count = 0;
+ ++(*opt_count);
#if 0
/* Temporarily disabled - see bug #0025274 */
} else if (0&& block->op1_to != block &&
block->successors_count = 1;
block->successors[0] = block->successors[1];
}
+ ++(*opt_count);
break;
}
MAKE_NOP(last_op);
}
block->successors_count = 1;
+ ++(*opt_count);
break;
}
/* next block is only NOP's */
if (target == target_end) {
target_block = blocks + target_block->successors[0];
+ ++(*opt_count);
goto next_target;
} else if (target->opcode == INV_COND(last_op->opcode) &&
/* JMPZ(X, L), L: JMPNZ(X, L2) -> JMPZ(X, L+1) */
DEL_SOURCE(block, block->successors[0]);
block->successors[0] = target_block->successors[1];
ADD_SOURCE(block, block->successors[0]);
+ ++(*opt_count);
} else if (target->opcode == INV_COND_EX(last_op->opcode) &&
(target->op1_type & (IS_TMP_VAR|IS_CV)) &&
same_type == target->op1_type &&
DEL_SOURCE(block, block->successors[0]);
block->successors[0] = target_block->successors[1];
ADD_SOURCE(block, block->successors[0]);
+ ++(*opt_count);
} else if (target->opcode == last_op->opcode &&
(target->op1_type & (IS_TMP_VAR|IS_CV)) &&
same_type == target->op1_type &&
DEL_SOURCE(block, block->successors[0]);
block->successors[0] = target_block->successors[0];
ADD_SOURCE(block, block->successors[0]);
+ ++(*opt_count);
} else if (target->opcode == ZEND_JMP &&
!(target_block->flags & ZEND_BB_PROTECTED)) {
/* JMPZ(X, L), L: JMP(L2) -> JMPZ(X, L2) */
DEL_SOURCE(block, block->successors[0]);
block->successors[0] = target_block->successors[0];
ADD_SOURCE(block, block->successors[0]);
+ ++(*opt_count);
} else if (target->opcode == ZEND_JMPZNZ &&
(target->op1_type & (IS_TMP_VAR|IS_CV)) &&
same_type == target->op1_type &&
block->successors[0] = target_block->successors[1];
}
ADD_SOURCE(block, block->successors[0]);
+ ++(*opt_count);
}
}
DEL_SOURCE(block, block->successors[1]);
block->successors[1] = target_block->successors[0];
ADD_SOURCE(block, block->successors[1]);
+ ++(*opt_count);
} else {
break;
}
ADD_SOURCE(block, block->successors[0]);
}
last_op->opcode = ZEND_JMPZNZ;
+ ++(*opt_count);
}
}
break;
DEL_SOURCE(block, block->successors[0]);
block->successors_count = 1;
block->successors[0] = block->successors[1];
+ ++(*opt_count);
}
break;
}
/* next block is only NOP's */
if (target == target_end) {
target_block = blocks + target_block->successors[0];
+ ++(*opt_count);
goto next_target_ex;
} else if (target->opcode == last_op->opcode-3 &&
(target->op1_type & (IS_TMP_VAR|IS_CV)) &&
DEL_SOURCE(block, block->successors[0]);
block->successors[0] = target_block->successors[0];
ADD_SOURCE(block, block->successors[0]);
+ ++(*opt_count);
} else if (target->opcode == INV_EX_COND(last_op->opcode) &&
(target->op1_type & (IS_TMP_VAR|IS_CV)) &&
(same_t[VAR_NUM_EX(target->op1)] & target->op1_type) != 0 &&
DEL_SOURCE(block, block->successors[0]);
block->successors[0] = target_block->successors[1];
ADD_SOURCE(block, block->successors[0]);
+ ++(*opt_count);
} else if (target->opcode == INV_EX_COND_EX(last_op->opcode) &&
(target->op1_type & (IS_TMP_VAR|IS_CV)) &&
(same_t[VAR_NUM_EX(target->op1)] & target->op1_type) != 0 &&
DEL_SOURCE(block, block->successors[0]);
block->successors[0] = target_block->successors[1];
ADD_SOURCE(block, block->successors[0]);
+ ++(*opt_count);
} else if (target->opcode == last_op->opcode &&
(target->op1_type & (IS_TMP_VAR|IS_CV)) &&
(same_t[VAR_NUM_EX(target->op1)] & target->op1_type) != 0 &&
DEL_SOURCE(block, block->successors[0]);
block->successors[0] = target_block->successors[0];
ADD_SOURCE(block, block->successors[0]);
+ ++(*opt_count);
} else if (target->opcode == ZEND_JMP &&
!(target_block->flags & ZEND_BB_PROTECTED)) {
/* T = JMPZ_EX(X, L), L: JMP(L2) -> T = JMPZ(X, L2) */
DEL_SOURCE(block, block->successors[0]);
block->successors[0] = target_block->successors[0];
ADD_SOURCE(block, block->successors[0]);
+ ++(*opt_count);
} else if (target->opcode == ZEND_JMPZNZ &&
(target->op1_type & (IS_TMP_VAR|IS_CV)) &&
(same_t[VAR_NUM_EX(target->op1)] & target->op1_type) != 0 &&
block->successors[0] = target_block->successors[1];
}
ADD_SOURCE(block, block->successors[0]);
+ ++(*opt_count);
}
}
break;
block->successors_count = 1;
block->successors[0] = block->successors[1];
}
+ ++(*opt_count);
} else if (block->successors[0] == block->successors[1]) {
/* both goto the same one - it's JMP */
if (!(last_op->op1_type & (IS_VAR|IS_TMP_VAR))) {
SET_UNUSED(last_op->op1);
SET_UNUSED(last_op->op2);
block->successors_count = 1;
+ ++(*opt_count);
}
} else if (block->successors[0] == next) {
/* jumping to next on Z - can follow to it and jump only on NZ */
last_op->opcode = ZEND_JMPNZ;
block->successors[0] = block->successors[1];
block->successors[1] = next;
+ ++(*opt_count);
/* no need to add source */
} else if (block->successors[1] == next) {
/* jumping to next on NZ - can follow to it and jump only on Z */
/* JMPZNZ(X,L1,L2) L2: -> JMPZ(X,L1) */
last_op->opcode = ZEND_JMPZ;
+ ++(*opt_count);
/* no need to add source */
}
/* next block is only NOP's */
if (target == target_end) {
target_block = blocks + target_block->successors[0];
+ ++(*opt_count);
goto next_target_znz;
} else if ((target->opcode == ZEND_JMPZ || target->opcode == ZEND_JMPZNZ) &&
(target->op1_type & (IS_TMP_VAR|IS_CV)) &&
DEL_SOURCE(block, block->successors[0]);
block->successors[0] = target_block->successors[0];
ADD_SOURCE(block, block->successors[0]);
+ ++(*opt_count);
} else if (target->opcode == ZEND_JMPNZ &&
(target->op1_type & (IS_TMP_VAR|IS_CV)) &&
same_type == target->op1_type &&
DEL_SOURCE(block, block->successors[0]);
block->successors[0] = target_block->successors[1];
ADD_SOURCE(block, block->successors[0]);
+ ++(*opt_count);
} else if (target->opcode == ZEND_JMP &&
!(target_block->flags & ZEND_BB_PROTECTED)) {
/* JMPZNZ(X, L1, L2), L1: JMP(L3) -> JMPZNZ(X, L3, L2) */
DEL_SOURCE(block, block->successors[0]);
block->successors[0] = target_block->successors[0];
ADD_SOURCE(block, block->successors[0]);
+ ++(*opt_count);
}
}
break;
zend_arena_release(&ctx->arena, checkpoint);
}
-static void zend_merge_blocks(zend_op_array *op_array, zend_cfg *cfg)
+static void zend_merge_blocks(zend_op_array *op_array, zend_cfg *cfg, uint32_t *opt_count)
{
int i;
zend_basic_block *b, *bb;
b->flags = 0;
b->len = 0;
b->successors_count = 0;
+ ++(*opt_count);
} else {
prev = b;
}
void *checkpoint;
zend_op **Tsource;
zend_uchar *same_t;
+ uint32_t opt_count;
/* Build CFG */
checkpoint = zend_arena_checkpoint(ctx->arena);
blocks = cfg.blocks;
end = blocks + cfg.blocks_count;
for (pass = 0; pass < PASSES; pass++) {
+ opt_count = 0;
+
/* Compute data dependencies */
zend_bitset_clear(usage, bitset_len);
zend_t_usage(&cfg, op_array, usage, ctx);
/* Skip continuation of "extended" BB */
memset(Tsource, 0, (op_array->last_var + op_array->T) * sizeof(zend_op *));
}
- zend_optimize_block(b, op_array, usage, &cfg, Tsource);
+ zend_optimize_block(b, op_array, usage, &cfg, Tsource, &opt_count);
}
/* Eliminate NOPs */
/* Jump optimization for each block */
for (b = blocks; b < end; b++) {
if (b->flags & ZEND_BB_REACHABLE) {
- zend_jmp_optimization(b, op_array, &cfg, same_t);
+ zend_jmp_optimization(b, op_array, &cfg, same_t, &opt_count);
}
}
zend_cfg_remark_reachable_blocks(op_array, &cfg);
/* Merge Blocks */
- zend_merge_blocks(op_array, &cfg);
+ zend_merge_blocks(op_array, &cfg, &opt_count);
+
+ if (opt_count == 0) {
+ break;
+ }
}
zend_bitset_clear(usage, bitset_len);