summaryrefslogtreecommitdiff
path: root/ext/opcache/Optimizer/block_pass.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/opcache/Optimizer/block_pass.c')
-rw-r--r--ext/opcache/Optimizer/block_pass.c1081
1 files changed, 503 insertions, 578 deletions
diff --git a/ext/opcache/Optimizer/block_pass.c b/ext/opcache/Optimizer/block_pass.c
index 3327ec86df..b4cd2407ba 100644
--- a/ext/opcache/Optimizer/block_pass.c
+++ b/ext/opcache/Optimizer/block_pass.c
@@ -33,27 +33,9 @@
/* Checks if a constant (like "true") may be replaced by its value */
int zend_optimizer_get_persistent_constant(zend_string *name, zval *result, int copy)
{
- zend_constant *c;
- char *lookup_name;
- int retval = 1;
- ALLOCA_FLAG(use_heap);
-
- if ((c = zend_hash_find_ptr(EG(zend_constants), name)) == NULL) {
- lookup_name = do_alloca(ZSTR_LEN(name) + 1, use_heap);
- memcpy(lookup_name, ZSTR_VAL(name), ZSTR_LEN(name) + 1);
- zend_str_tolower(lookup_name, ZSTR_LEN(name));
-
- if ((c = zend_hash_str_find_ptr(EG(zend_constants), lookup_name, ZSTR_LEN(name))) != NULL) {
- if (!(ZEND_CONSTANT_FLAGS(c) & CONST_CT_SUBST) || (ZEND_CONSTANT_FLAGS(c) & CONST_CS)) {
- retval = 0;
- }
- } else {
- retval = 0;
- }
- free_alloca(lookup_name, use_heap);
- }
-
- if (retval) {
+ zval *zv;
+ zend_constant *c = zend_hash_find_ptr(EG(zend_constants), name);
+ if (c) {
if ((ZEND_CONSTANT_FLAGS(c) & CONST_PERSISTENT)
&& (!(ZEND_CONSTANT_FLAGS(c) & CONST_NO_FILE_CACHE)
|| !(CG(compiler_options) & ZEND_COMPILE_WITH_FILE_CACHE))) {
@@ -61,23 +43,23 @@ int zend_optimizer_get_persistent_constant(zend_string *name, zval *result, int
if (copy) {
Z_TRY_ADDREF_P(result);
}
+ return 1;
} else {
- retval = 0;
+ return 0;
}
}
- return retval;
+ /* Special constants null/true/false can always be substituted. */
+ zv = zend_get_special_const(ZSTR_VAL(name), ZSTR_LEN(name));
+ if (zv) {
+ ZVAL_COPY_VALUE(result, zv);
+ return 1;
+ }
+ return 0;
}
-/* CFG back references management */
-
-#define DEL_SOURCE(from, to)
-#define ADD_SOURCE(from, to)
-
/* Data dependencies macros */
-#define VAR_NUM_EX(op) VAR_NUM((op).var)
-
#define VAR_SOURCE(op) Tsource[VAR_NUM(op.var)]
#define SET_VAR_SOURCE(opline) Tsource[VAR_NUM(opline->result.var)] = opline
@@ -86,15 +68,6 @@ static void strip_leading_nops(zend_op_array *op_array, zend_basic_block *b)
zend_op *opcodes = op_array->opcodes;
do {
- /* check if NOP breaks incorrect smart branch */
- if (b->len == 2
- && (opcodes[b->start + 1].opcode == ZEND_JMPZ
- || opcodes[b->start + 1].opcode == ZEND_JMPNZ)
- && (opcodes[b->start + 1].op1_type & (IS_CV|IS_CONST))
- && b->start > 0
- && zend_is_smart_branch(opcodes + b->start - 1)) {
- break;
- }
b->start++;
b->len--;
} while (b->len > 0 && opcodes[b->start].opcode == ZEND_NOP);
@@ -125,14 +98,6 @@ static void strip_nops(zend_op_array *op_array, zend_basic_block *b)
}
j++;
}
- if (i + 1 < b->start + b->len
- && (op_array->opcodes[i+1].opcode == ZEND_JMPZ
- || op_array->opcodes[i+1].opcode == ZEND_JMPNZ)
- && op_array->opcodes[i+1].op1_type & (IS_CV|IS_CONST)
- && zend_is_smart_branch(op_array->opcodes + j - 1)) {
- /* don't remove NOP, that splits incorrect smart branch */
- j++;
- }
i++;
}
b->len = j - b->start;
@@ -203,52 +168,6 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
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))) {
- MAKE_NOP(opline);
- DEL_SOURCE(block, block->successors[0]);
- block->successors_count = 1;
- block->successors[0] = block->successors[1];
- } else {
- opline->opcode = ZEND_JMP;
- COPY_NODE(opline->op1, opline->op2);
- DEL_SOURCE(block, block->successors[1]);
- block->successors_count = 1;
- }
- break;
- case ZEND_JMPNZ:
- if (zend_is_true(&ZEND_OP1_LITERAL(opline))) {
- opline->opcode = ZEND_JMP;
- COPY_NODE(opline->op1, opline->op2);
- DEL_SOURCE(block, block->successors[1]);
- block->successors_count = 1;
- } else {
- MAKE_NOP(opline);
- DEL_SOURCE(block, block->successors[0]);
- block->successors_count = 1;
- block->successors[0] = block->successors[1];
- }
- break;
- case ZEND_JMPZNZ:
- if (zend_is_true(&ZEND_OP1_LITERAL(opline))) {
- zend_op *target_opline = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value);
- ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target_opline);
- DEL_SOURCE(block, block->successors[0]);
- block->successors[0] = block->successors[1];
- } else {
- zend_op *target_opline = ZEND_OP2_JMP_ADDR(opline);
- ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target_opline);
- DEL_SOURCE(block, block->successors[0]);
- }
- block->successors_count = 1;
- opline->op1_type = IS_UNUSED;
- opline->extended_value = 0;
- opline->opcode = ZEND_JMP;
- break;
- default:
- break;
- }
} else {
zval_ptr_dtor_nogc(&c);
}
@@ -278,64 +197,55 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
}
}
- if (opline->opcode == ZEND_ECHO) {
- if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
- src = VAR_SOURCE(opline->op1);
- if (src &&
- src->opcode == ZEND_CAST &&
- src->extended_value == IS_STRING) {
- /* T = CAST(X, String), ECHO(T) => NOP, ECHO(X) */
- VAR_SOURCE(opline->op1) = NULL;
- COPY_NODE(opline->op1, src->op1);
- MAKE_NOP(src);
- ++(*opt_count);
- }
- }
-
- if (opline->op1_type == IS_CONST) {
- if (last_op && last_op->opcode == ZEND_ECHO &&
- last_op->op1_type == IS_CONST &&
- Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_DOUBLE &&
- Z_TYPE(ZEND_OP1_LITERAL(last_op)) != IS_DOUBLE) {
- /* compress consecutive ECHO's.
- * Float to string conversion may be affected by current
- * locale setting.
- */
- int l, old_len;
-
- if (Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_STRING) {
- convert_to_string(&ZEND_OP1_LITERAL(opline));
- }
- if (Z_TYPE(ZEND_OP1_LITERAL(last_op)) != IS_STRING) {
- convert_to_string(&ZEND_OP1_LITERAL(last_op));
+ switch (opline->opcode) {
+ case ZEND_ECHO:
+ if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
+ src = VAR_SOURCE(opline->op1);
+ if (src &&
+ src->opcode == ZEND_CAST &&
+ src->extended_value == IS_STRING) {
+ /* T = CAST(X, String), ECHO(T) => NOP, ECHO(X) */
+ VAR_SOURCE(opline->op1) = NULL;
+ COPY_NODE(opline->op1, src->op1);
+ MAKE_NOP(src);
+ ++(*opt_count);
}
- old_len = Z_STRLEN(ZEND_OP1_LITERAL(last_op));
- l = old_len + Z_STRLEN(ZEND_OP1_LITERAL(opline));
- if (!Z_REFCOUNTED(ZEND_OP1_LITERAL(last_op))) {
- zend_string *tmp = zend_string_alloc(l, 0);
- memcpy(ZSTR_VAL(tmp), Z_STRVAL(ZEND_OP1_LITERAL(last_op)), old_len);
- Z_STR(ZEND_OP1_LITERAL(last_op)) = tmp;
- } else {
- Z_STR(ZEND_OP1_LITERAL(last_op)) = zend_string_extend(Z_STR(ZEND_OP1_LITERAL(last_op)), l, 0);
+ } else if (opline->op1_type == IS_CONST &&
+ Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_DOUBLE) {
+ if (last_op == opline - 1) {
+ /* compress consecutive ECHO's.
+ * Float to string conversion may be affected by current
+ * locale setting.
+ */
+ int l, old_len;
+
+ if (Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_STRING) {
+ convert_to_string(&ZEND_OP1_LITERAL(opline));
+ }
+ if (Z_TYPE(ZEND_OP1_LITERAL(last_op)) != IS_STRING) {
+ convert_to_string(&ZEND_OP1_LITERAL(last_op));
+ }
+ old_len = Z_STRLEN(ZEND_OP1_LITERAL(last_op));
+ l = old_len + Z_STRLEN(ZEND_OP1_LITERAL(opline));
+ if (!Z_REFCOUNTED(ZEND_OP1_LITERAL(last_op))) {
+ zend_string *tmp = zend_string_alloc(l, 0);
+ memcpy(ZSTR_VAL(tmp), Z_STRVAL(ZEND_OP1_LITERAL(last_op)), old_len);
+ Z_STR(ZEND_OP1_LITERAL(last_op)) = tmp;
+ } else {
+ Z_STR(ZEND_OP1_LITERAL(last_op)) = zend_string_extend(Z_STR(ZEND_OP1_LITERAL(last_op)), l, 0);
+ }
+ Z_TYPE_INFO(ZEND_OP1_LITERAL(last_op)) = IS_STRING_EX;
+ memcpy(Z_STRVAL(ZEND_OP1_LITERAL(last_op)) + old_len, Z_STRVAL(ZEND_OP1_LITERAL(opline)), Z_STRLEN(ZEND_OP1_LITERAL(opline)));
+ Z_STRVAL(ZEND_OP1_LITERAL(last_op))[l] = '\0';
+ zval_ptr_dtor_nogc(&ZEND_OP1_LITERAL(opline));
+ 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);
}
- Z_TYPE_INFO(ZEND_OP1_LITERAL(last_op)) = IS_STRING_EX;
- memcpy(Z_STRVAL(ZEND_OP1_LITERAL(last_op)) + old_len, Z_STRVAL(ZEND_OP1_LITERAL(opline)), Z_STRLEN(ZEND_OP1_LITERAL(opline)));
- Z_STRVAL(ZEND_OP1_LITERAL(last_op))[l] = '\0';
- zval_ptr_dtor_nogc(&ZEND_OP1_LITERAL(opline));
- 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;
}
- last_op = opline;
- } else {
- last_op = NULL;
- }
- } else {
- last_op = NULL;
- }
-
- switch (opline->opcode) {
+ break;
case ZEND_FREE:
if (opline->op1_type == IS_TMP_VAR) {
@@ -592,6 +502,7 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
MAKE_NOP(opline);
++(*opt_count);
break;
+ case ZEND_ISSET_ISEMPTY_CV:
case ZEND_ISSET_ISEMPTY_VAR:
case ZEND_ISSET_ISEMPTY_DIM_OBJ:
case ZEND_ISSET_ISEMPTY_PROP_OBJ:
@@ -600,6 +511,7 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
case ZEND_TYPE_CHECK:
case ZEND_DEFINED:
case ZEND_IN_ARRAY:
+ case ZEND_ARRAY_KEY_EXISTS:
if (opline->opcode == ZEND_BOOL_NOT) {
break;
}
@@ -615,56 +527,135 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
case ZEND_JMPZ:
case ZEND_JMPNZ:
- case ZEND_JMPZ_EX:
- case ZEND_JMPNZ_EX:
- case ZEND_JMPZNZ:
- optimize_jmpznz:
- if (opline->op1_type == IS_TMP_VAR &&
- (!zend_bitset_in(used_ext, VAR_NUM(opline->op1.var)) ||
- (opline->result_type == opline->op1_type &&
- opline->result.var == opline->op1.var))) {
- src = VAR_SOURCE(opline->op1);
- if (src) {
- if (src->opcode == ZEND_BOOL_NOT &&
- opline->opcode != ZEND_JMPZ_EX &&
- opline->opcode != ZEND_JMPNZ_EX) {
- VAR_SOURCE(opline->op1) = NULL;
- COPY_NODE(opline->op1, src->op1);
- if (opline->opcode == ZEND_JMPZ) {
+ while (1) {
+ if (opline->op1_type == IS_CONST) {
+ ++(*opt_count);
+ block->successors_count = 1;
+ if (zend_is_true(&ZEND_OP1_LITERAL(opline)) ==
+ (opline->opcode == ZEND_JMPZ)) {
+
+ MAKE_NOP(opline);
+ block->successors[0] = block->successors[1];
+ block->len--;
+ cfg->blocks[block->successors[0]].flags |= ZEND_BB_FOLLOW;
+ break;
+ } else {
+ zend_basic_block *next = cfg->blocks + block->successors[1];
+
+ next->flags &= ~ZEND_BB_FOLLOW;
+ if (!(next->flags & (ZEND_BB_TARGET|ZEND_BB_PROTECTED))) {
+ next->flags &= ~ZEND_BB_REACHABLE;
+ }
+ opline->opcode = ZEND_JMP;
+ COPY_NODE(opline->op1, opline->op2);
+ break;
+ }
+ } else if (opline->op1_type == IS_TMP_VAR &&
+ !zend_bitset_in(used_ext, VAR_NUM(opline->op1.var))) {
+ src = VAR_SOURCE(opline->op1);
+ if (src) {
+ if (src->opcode == ZEND_BOOL_NOT) {
+ VAR_SOURCE(opline->op1) = NULL;
+ COPY_NODE(opline->op1, src->op1);
/* T = BOOL_NOT(X) + JMPZ(T) -> NOP, JMPNZ(X) */
- opline->opcode = ZEND_JMPNZ;
- } else if (opline->opcode == ZEND_JMPNZ) {
- /* T = BOOL_NOT(X) + JMPNZ(T) -> NOP, JMPZ(X) */
- opline->opcode = ZEND_JMPZ;
-#if 0
- } else if (opline->opcode == ZEND_JMPZ_EX) {
- /* T = BOOL_NOT(X) + JMPZ_EX(T) -> NOP, JMPNZ_EX(X) */
- opline->opcode = ZEND_JMPNZ_EX;
- } else if (opline->opcode == ZEND_JMPNZ_EX) {
- /* T = BOOL_NOT(X) + JMPNZ_EX(T) -> NOP, JMPZ_EX(X) */
- opline->opcode = ZEND_JMPZ;
-#endif
- } else {
+ opline->opcode = INV_COND(opline->opcode);
+ MAKE_NOP(src);
+ ++(*opt_count);
+ continue;
+ } 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);
+ continue;
+ }
+ }
+ }
+ break;
+ }
+ break;
+
+ case ZEND_JMPZNZ:
+ while (1) {
+ if (opline->op1_type == IS_CONST) {
+ ++(*opt_count);
+ if (zend_is_true(&ZEND_OP1_LITERAL(opline))) {
+ zend_op *target_opline = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value);
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target_opline);
+ block->successors[0] = block->successors[1];
+ } else {
+ zend_op *target_opline = ZEND_OP2_JMP_ADDR(opline);
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target_opline);
+ }
+ block->successors_count = 1;
+ opline->op1_type = IS_UNUSED;
+ opline->extended_value = 0;
+ opline->opcode = ZEND_JMP;
+ break;
+ } else if (opline->op1_type == IS_TMP_VAR &&
+ !zend_bitset_in(used_ext, VAR_NUM(opline->op1.var))) {
+ src = VAR_SOURCE(opline->op1);
+ if (src) {
+ if (src->opcode == ZEND_BOOL_NOT) {
/* T = BOOL_NOT(X) + JMPZNZ(T,L1,L2) -> NOP, JMPZNZ(X,L2,L1) */
uint32_t tmp;
- ZEND_ASSERT(opline->opcode == ZEND_JMPZNZ);
+ VAR_SOURCE(opline->op1) = NULL;
+ COPY_NODE(opline->op1, src->op1);
tmp = block->successors[0];
block->successors[0] = block->successors[1];
block->successors[1] = tmp;
+ MAKE_NOP(src);
+ ++(*opt_count);
+ continue;
+ } 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);
+ continue;
}
- 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);
+ }
+ }
+ break;
+ }
+ break;
+
+ case ZEND_JMPZ_EX:
+ case ZEND_JMPNZ_EX:
+ while (1) {
+ if (opline->op1_type == IS_CONST) {
+ if (zend_is_true(&ZEND_OP1_LITERAL(opline)) ==
+ (opline->opcode == ZEND_JMPZ_EX)) {
+
++(*opt_count);
- goto optimize_jmpznz;
+ opline->opcode = ZEND_QM_ASSIGN;
+ zval_ptr_dtor_nogc(&ZEND_OP1_LITERAL(opline));
+ ZVAL_BOOL(&ZEND_OP1_LITERAL(opline), opline->opcode == ZEND_JMPZ_EX);
+ opline->op2.num = 0;
+ block->successors_count = 1;
+ block->successors[0] = block->successors[1];
+ cfg->blocks[block->successors[0]].flags |= ZEND_BB_FOLLOW;
+ break;
+ }
+ } else if (opline->op1_type == IS_TMP_VAR &&
+ (!zend_bitset_in(used_ext, VAR_NUM(opline->op1.var)) ||
+ opline->result.var == opline->op1.var)) {
+ src = VAR_SOURCE(opline->op1);
+ if (src) {
+ 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);
+ continue;
+ }
}
}
+ break;
}
break;
@@ -894,6 +885,23 @@ optimize_const_unary_op:
/* strip T = QM_ASSIGN(T) */
MAKE_NOP(opline);
++(*opt_count);
+ } else if (opline->op1_type == IS_TMP_VAR &&
+ opline->result_type == IS_TMP_VAR &&
+ !zend_bitset_in(used_ext, VAR_NUM(opline->op1.var))) {
+ /* T1 = ..., T2 = QM_ASSIGN(T1) to T2 = ..., NOP */
+ src = VAR_SOURCE(opline->op1);
+ if (src &&
+ src->opcode != ZEND_COPY_TMP &&
+ src->opcode != ZEND_ADD_ARRAY_ELEMENT &&
+ src->opcode != ZEND_ADD_ARRAY_UNPACK &&
+ (src->opcode != ZEND_DECLARE_LAMBDA_FUNCTION ||
+ src == opline -1)) {
+ src->result.var = opline->result.var;
+ VAR_SOURCE(opline->op1) = NULL;
+ VAR_SOURCE(opline->result) = src;
+ MAKE_NOP(opline);
+ ++(*opt_count);
+ }
}
break;
}
@@ -1089,11 +1097,84 @@ static void assemble_code_blocks(zend_cfg *cfg, zend_op_array *op_array, zend_op
}
}
-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)
+static zend_always_inline zend_basic_block *get_target_block(const zend_cfg *cfg, zend_basic_block *block, int n, uint32_t *opt_count)
+{
+ int b;
+ zend_basic_block *target_block = cfg->blocks + block->successors[n];
+
+ if (target_block->len == 0 && !(target_block->flags & ZEND_BB_PROTECTED)) {
+ do {
+ b = target_block->successors[0];
+ target_block = cfg->blocks + b;
+ } while (target_block->len == 0 && !(target_block->flags & ZEND_BB_PROTECTED));
+ block->successors[n] = b;
+ ++(*opt_count);
+ }
+ return target_block;
+}
+
+static zend_always_inline zend_basic_block *get_follow_block(const zend_cfg *cfg, zend_basic_block *block, int n, uint32_t *opt_count)
+{
+ int b;
+ zend_basic_block *target_block = cfg->blocks + block->successors[n];
+
+ if (target_block->len == 0 && !(target_block->flags & ZEND_BB_PROTECTED)) {
+ do {
+ b = target_block->successors[0];
+ target_block = cfg->blocks + b;
+ } while (target_block->len == 0 && !(target_block->flags & ZEND_BB_PROTECTED));
+ block->successors[n] = b;
+ ++(*opt_count);
+ }
+ return target_block;
+}
+
+static zend_always_inline zend_basic_block *get_next_block(const zend_cfg *cfg, zend_basic_block *block)
+{
+ zend_basic_block *next_block = block + 1;
+ zend_basic_block *end = cfg->blocks + cfg->blocks_count;
+
+ while (1) {
+ if (next_block == end) {
+ return NULL;
+ } else if (next_block->flags & ZEND_BB_REACHABLE) {
+ break;
+ }
+ next_block++;
+ }
+ while (next_block->len == 0 && !(next_block->flags & ZEND_BB_PROTECTED)) {
+ next_block = cfg->blocks + next_block->successors[0];
+ }
+ return next_block;
+}
+
+
+/* we use "jmp_hitlist" to avoid infinity loops during jmp optimization */
+static zend_always_inline int in_hitlist(int target, int *jmp_hitlist, int jmp_hitlist_count)
+{
+ int i;
+
+ for (i = 0; i < jmp_hitlist_count; i++) {
+ if (jmp_hitlist[i] == target) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+#define CHECK_LOOP(target) \
+ if (EXPECTED(!in_hitlist(target, jmp_hitlist, jmp_hitlist_count))) { \
+ jmp_hitlist[jmp_hitlist_count++] = target; \
+ } else { \
+ break; \
+ }
+
+static void zend_jmp_optimization(zend_basic_block *block, zend_op_array *op_array, const zend_cfg *cfg, int *jmp_hitlist, uint32_t *opt_count)
{
/* last_op is the last opcode of the current block */
- zend_basic_block *blocks = cfg->blocks;
- zend_op *last_op;
+ zend_basic_block *target_block, *follow_block, *next_block;
+ zend_op *last_op, *target;
+ int next, jmp_hitlist_count;
if (block->len == 0) {
return;
@@ -1102,35 +1183,32 @@ static void zend_jmp_optimization(zend_basic_block *block, zend_op_array *op_arr
last_op = op_array->opcodes + block->start + block->len - 1;
switch (last_op->opcode) {
case ZEND_JMP:
- {
- zend_basic_block *target_block = blocks + block->successors[0];
- zend_op *target = op_array->opcodes + target_block->start;
- int next = (block - blocks) + 1;
-
- while (next < cfg->blocks_count && !(blocks[next].flags & ZEND_BB_REACHABLE)) {
- /* find used one */
- next++;
- }
+ jmp_hitlist_count = 0;
- /* JMP(next) -> NOP */
- if (block->successors[0] == next) {
- MAKE_NOP(last_op);
- ++(*opt_count);
- block->len--;
+ target_block = get_target_block(cfg, block, 0, opt_count);
+ while (target_block->len == 1) {
+ target = op_array->opcodes + target_block->start;
+ if (target->opcode == ZEND_JMP) {
+ /* JMP L, L: JMP L1 -> JMP L1 */
+ next = target_block->successors[0];
+ } else {
break;
}
+ CHECK_LOOP(next);
+ block->successors[0] = next;
+ ++(*opt_count);
+ target_block = get_target_block(cfg, block, 0, opt_count);
+ }
- if (target->opcode == ZEND_JMP &&
- block->successors[0] != target_block->successors[0] &&
- !(target_block->flags & ZEND_BB_PROTECTED)) {
- /* JMP L, L: JMP L1 -> JMP L1 */
- *last_op = *target;
- 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)) {
+ next_block = get_next_block(cfg, block);
+ if (target_block == next_block) {
+ /* JMP(next) -> NOP */
+ MAKE_NOP(last_op);
+ ++(*opt_count);
+ block->len--;
+ } else if (target_block->len == 1) {
+ target = op_array->opcodes + target_block->start;
+ if (target->opcode == ZEND_JMPZNZ) {
/* JMP L, L: JMPZNZ L1,L2 -> JMPZNZ L1,L2 */
*last_op = *target;
if (last_op->op1_type == IS_CONST) {
@@ -1138,15 +1216,14 @@ static void zend_jmp_optimization(zend_basic_block *block, zend_op_array *op_arr
ZVAL_COPY(&zv, &ZEND_OP1_LITERAL(last_op));
last_op->op1.constant = zend_optimizer_add_literal(op_array, &zv);
}
- DEL_SOURCE(block, block->successors[0]);
block->successors_count = 2;
block->successors[0] = target_block->successors[0];
block->successors[1] = target_block->successors[1];
- ADD_SOURCE(block, block->successors[0]);
- ADD_SOURCE(block, block->successors[1]);
++(*opt_count);
+ goto optimize_jmpznz;
} else if ((target->opcode == ZEND_RETURN ||
target->opcode == ZEND_RETURN_BY_REF ||
+ target->opcode == ZEND_GENERATOR_RETURN ||
target->opcode == ZEND_EXIT) &&
!(op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK)) {
/* JMP L, L: RETURN to immediate RETURN */
@@ -1156,96 +1233,68 @@ static void zend_jmp_optimization(zend_basic_block *block, zend_op_array *op_arr
ZVAL_COPY(&zv, &ZEND_OP1_LITERAL(last_op));
last_op->op1.constant = zend_optimizer_add_literal(op_array, &zv);
}
- 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->op1_to != blocks &&
- op_array->last_try_catch == 0 &&
- target->opcode != ZEND_FREE) {
- /* Block Reordering (saves one JMP on each "for" loop iteration)
- * It is disabled for some cases (ZEND_FREE)
- * which may break register allocation.
- */
- zend_bool can_reorder = 0;
- zend_block_source *cs = block->op1_to->sources;
-
- /* the "target" block doesn't had any followed block */
- while(cs) {
- if (cs->from->follow_to == block->op1_to) {
- can_reorder = 0;
- break;
- }
- cs = cs->next;
- }
- if (can_reorder) {
- next = block->op1_to;
- /* the "target" block is not followed by current "block" */
- while (next->follow_to != NULL) {
- if (next->follow_to == block) {
- can_reorder = 0;
- break;
- }
- next = next->follow_to;
- }
- if (can_reorder) {
- zend_basic_block *prev = blocks;
+ }
+ }
+ break;
- while (prev->next != block->op1_to) {
- prev = prev->next;
- }
- prev->next = next->next;
- next->next = block->next;
- block->next = block->op1_to;
+ case ZEND_JMP_SET:
+ case ZEND_COALESCE:
+ jmp_hitlist_count = 0;
- block->follow_to = block->op1_to;
- block->op1_to = NULL;
- MAKE_NOP(last_op);
- block->len--;
- if(block->len == 0) {
- /* this block is nothing but NOP now */
- delete_code_block(block, ctx);
- }
- break;
- }
- }
-#endif
+ target_block = get_target_block(cfg, block, 0, opt_count);
+ while (target_block->len == 1) {
+ target = op_array->opcodes + target_block->start;
+
+ if (target->opcode == ZEND_JMP) {
+ /* JMP_SET(X, L), L: JMP(L2) -> JMP_SET(X, L2) */
+ next = target_block->successors[0];
+ CHECK_LOOP(next);
+ block->successors[0] = next;
+ ++(*opt_count);
+ } else {
+ break;
}
+ target_block = get_target_block(cfg, block, 0, opt_count);
}
break;
case ZEND_JMPZ:
case ZEND_JMPNZ:
- /* constant conditional JMPs */
- if (last_op->op1_type == IS_CONST) {
- int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(last_op));
+ jmp_hitlist_count = 0;
- if (last_op->opcode == ZEND_JMPZ) {
- should_jmp = !should_jmp;
- }
- literal_dtor(&ZEND_OP1_LITERAL(last_op));
- last_op->op1_type = IS_UNUSED;
- if (should_jmp) {
- /* JMPNZ(true) -> JMP */
- last_op->opcode = ZEND_JMP;
- DEL_SOURCE(block, block->successors[1]);
- block->successors_count = 1;
+ target_block = get_target_block(cfg, block, 0, opt_count);
+ while (target_block->len == 1) {
+ target = op_array->opcodes + target_block->start;
+
+ if (target->opcode == ZEND_JMP) {
+ /* JMPZ(X, L), L: JMP(L2) -> JMPZ(X, L2) */
+ next = target_block->successors[0];
+ } else if (target->opcode == last_op->opcode &&
+ SAME_VAR(target->op1, last_op->op1)) {
+ /* JMPZ(X, L), L: JMPZ(X, L2) -> JMPZ(X, L2) */
+ next = target_block->successors[0];
+ } else if (target->opcode == INV_COND(last_op->opcode) &&
+ SAME_VAR(target->op1, last_op->op1)) {
+ /* JMPZ(X, L), L: JMPNZ(X, L2) -> JMPZ(X, L+1) */
+ next = target_block->successors[1];
+ } else if (target->opcode == ZEND_JMPZNZ &&
+ SAME_VAR(target->op1, last_op->op1)) {
+ /* JMPZ(X, L), L: JMPZNZ(X, L2, L3) -> JMPZ(X, L2) */
+ next = target_block->successors[last_op->opcode == ZEND_JMPNZ];
} else {
- /* JMPNZ(false) -> NOP */
- MAKE_NOP(last_op);
- DEL_SOURCE(block, block->successors[0]);
- block->successors_count = 1;
- block->successors[0] = block->successors[1];
+ break;
}
+ CHECK_LOOP(next);
+ block->successors[0] = next;
++(*opt_count);
- break;
+ target_block = get_target_block(cfg, block, 0, opt_count);
}
- if (block->successors[0] == block->successors[1]) {
+ follow_block = get_follow_block(cfg, block, 1, opt_count);
+ if (target_block == follow_block) {
/* L: JMP[N]Z(X, L+1) -> NOP or FREE(X) */
-
if (last_op->op1_type == IS_CV) {
last_op->opcode = ZEND_CHECK_VAR;
last_op->op2.num = 0;
@@ -1254,122 +1303,55 @@ static void zend_jmp_optimization(zend_basic_block *block, zend_op_array *op_arr
last_op->op2.num = 0;
} else {
MAKE_NOP(last_op);
+ block->len--;
}
block->successors_count = 1;
++(*opt_count);
- break;
- }
+ } else if (follow_block->len == 1) {
+ target = op_array->opcodes + follow_block->start;
+ if (target->opcode == ZEND_JMP) {
+ if (block->successors[0] == follow_block->successors[0]) {
+ /* JMPZ(X,L1), JMP(L1) -> NOP, JMP(L1) */
+ if (last_op->op1_type == IS_CV) {
+ last_op->opcode = ZEND_CHECK_VAR;
+ last_op->op2.num = 0;
+ } else if (last_op->op1_type & (IS_VAR|IS_TMP_VAR)) {
+ last_op->opcode = ZEND_FREE;
+ last_op->op2.num = 0;
+ } else {
+ MAKE_NOP(last_op);
+ block->len--;
+ }
+ block->successors_count = 1;
+ ++(*opt_count);
+ break;
+ } else if (!(follow_block->flags & (ZEND_BB_TARGET | ZEND_BB_PROTECTED))) {
+ next_block = get_next_block(cfg, follow_block);
- if (1) {
- zend_uchar same_type = last_op->op1_type;
- uint32_t same_var = VAR_NUM_EX(last_op->op1);
- zend_op *target;
- zend_op *target_end;
- zend_basic_block *target_block = blocks + block->successors[0];
+ if (target_block == next_block) {
+ /* JMPZ(X,L1) JMP(L2) L1: -> JMPNZ(X,L2) NOP*/
-next_target:
- target = op_array->opcodes + target_block->start;
- target_end = target + target_block->len;
- while (target < target_end && target->opcode == ZEND_NOP) {
- target++;
- }
+ last_op->opcode = INV_COND(last_op->opcode);
- /* 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) */
- (target->op1_type & (IS_TMP_VAR|IS_CV)) &&
- same_type == target->op1_type &&
- same_var == VAR_NUM_EX(target->op1) &&
- !(target_block->flags & ZEND_BB_PROTECTED)
- ) {
- 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 &&
- same_var == VAR_NUM_EX(target->op1) &&
- !(target_block->flags & ZEND_BB_PROTECTED)) {
- /* JMPZ(X, L), L: T = JMPNZ_EX(X, L2) -> T = JMPZ_EX(X, L+1) */
- last_op->opcode += 3;
- COPY_NODE(last_op->result, target->result);
- 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 &&
- same_var == VAR_NUM_EX(target->op1) &&
- !(target_block->flags & ZEND_BB_PROTECTED)) {
- /* JMPZ(X, L), L: JMPZ(X, 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_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 &&
- same_var == VAR_NUM_EX(target->op1) &&
- !(target_block->flags & ZEND_BB_PROTECTED)) {
- /* JMPZ(X, L), L: JMPZNZ(X, L2, L3) -> JMPZ(X, L2) */
- DEL_SOURCE(block, block->successors[0]);
- if (last_op->opcode == ZEND_JMPZ) {
- block->successors[0] = target_block->successors[0];
- } else {
- block->successors[0] = target_block->successors[1];
- }
- ADD_SOURCE(block, block->successors[0]);
- ++(*opt_count);
- }
- }
+ block->successors[0] = follow_block->successors[0];
+ block->successors[1] = next_block - cfg->blocks;
- if (last_op->opcode == ZEND_JMPZ || last_op->opcode == ZEND_JMPNZ) {
- zend_op *target;
- zend_op *target_end;
- zend_basic_block *target_block;
+ follow_block->flags &= ~ZEND_BB_REACHABLE;
+ MAKE_NOP(target);
+ follow_block->len = 0;
- while (1) {
- target_block = blocks + block->successors[1];
- target = op_array->opcodes + target_block->start;
- target_end = op_array->opcodes + target_block->start + 1;
- while (target < target_end && target->opcode == ZEND_NOP) {
- target++;
- }
+ next_block->flags |= ZEND_BB_FOLLOW;
- /* next block is only NOP's */
- if (target == target_end && !(target_block->flags & ZEND_BB_PROTECTED)) {
- DEL_SOURCE(block, block->successors[1]);
- block->successors[1] = target_block->successors[0];
- ADD_SOURCE(block, block->successors[1]);
- ++(*opt_count);
- } else {
- break;
+ break;
+ }
}
- }
- /* JMPZ(X,L1), JMP(L2) -> JMPZNZ(X,L1,L2) */
- if (target->opcode == ZEND_JMP &&
- !(target_block->flags & ZEND_BB_PROTECTED)) {
- DEL_SOURCE(block, block->successors[1]);
+
+ /* JMPZ(X,L1), JMP(L2) -> JMPZNZ(X,L1,L2) */
if (last_op->opcode == ZEND_JMPZ) {
- block->successors[1] = target_block->successors[0];
- ADD_SOURCE(block, block->successors[1]);
+ block->successors[1] = follow_block->successors[0];
} else {
block->successors[1] = block->successors[0];
- block->successors[0] = target_block->successors[0];
- ADD_SOURCE(block, block->successors[0]);
+ block->successors[0] = follow_block->successors[0];
}
last_op->opcode = ZEND_JMPZNZ;
++(*opt_count);
@@ -1379,212 +1361,153 @@ next_target:
case ZEND_JMPNZ_EX:
case ZEND_JMPZ_EX:
- /* constant conditional JMPs */
- if (last_op->op1_type == IS_CONST) {
- int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(last_op));
-
- if (last_op->opcode == ZEND_JMPZ_EX) {
- should_jmp = !should_jmp;
- }
- if (!should_jmp) {
- /* T = JMPZ_EX(true,L) -> T = QM_ASSIGN(true)
- * T = JMPNZ_EX(false,L) -> T = QM_ASSIGN(false)
- */
- last_op->opcode = ZEND_QM_ASSIGN;
- SET_UNUSED(last_op->op2);
- DEL_SOURCE(block, block->successors[0]);
- block->successors_count = 1;
- block->successors[0] = block->successors[1];
- ++(*opt_count);
- }
- break;
- }
-
- if (1) {
- zend_op *target, *target_end;
- zend_basic_block *target_block;
- int var_num = op_array->last_var + op_array->T;
+ jmp_hitlist_count = 0;
- if (var_num <= 0) {
- return;
- }
- memset(same_t, 0, var_num);
- same_t[VAR_NUM_EX(last_op->op1)] |= last_op->op1_type;
- same_t[VAR_NUM_EX(last_op->result)] |= last_op->result_type;
- target_block = blocks + block->successors[0];
-next_target_ex:
+ target_block = get_target_block(cfg, block, 0, opt_count);
+ while (target_block->len == 1) {
target = op_array->opcodes + target_block->start;
- target_end = target + target_block->len;
- while (target < target_end && target->opcode == ZEND_NOP) {
- target++;
- }
- /* next block is only NOP's */
- if (target == target_end) {
- target_block = blocks + target_block->successors[0];
- ++(*opt_count);
- goto next_target_ex;
+
+ if (target->opcode == ZEND_JMP) {
+ /* T = JMPZ_EX(X, L), L: JMP(L2) -> T = JMPZ(X, L2) */
+ next = target_block->successors[0];
} else if (target->opcode == last_op->opcode-3 &&
- (target->op1_type & (IS_TMP_VAR|IS_CV)) &&
- (same_t[VAR_NUM_EX(target->op1)] & target->op1_type) != 0 &&
- !(target_block->flags & ZEND_BB_PROTECTED)) {
+ (SAME_VAR(target->op1, last_op->result) ||
+ SAME_VAR(target->op1, last_op->op1))) {
/* T = JMPZ_EX(X, L1), L1: JMPZ({X|T}, L2) -> T = JMPZ_EX(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 == 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 &&
- !(target_block->flags & ZEND_BB_PROTECTED)) {
- /* T = JMPZ_EX(X, L1), L1: JMPNZ({X|T1}, L2) -> T = JMPZ_EX(X, L1+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_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 &&
- (same_t[VAR_NUM_EX(target->result)] & target->result_type) != 0 &&
- !(target_block->flags & ZEND_BB_PROTECTED)) {
- /* T = JMPZ_EX(X, L1), L1: T = JMPNZ_EX(T, L2) -> T = JMPZ_EX(X, L1+1) */
- DEL_SOURCE(block, block->successors[0]);
- block->successors[0] = target_block->successors[1];
- ADD_SOURCE(block, block->successors[0]);
- ++(*opt_count);
+ next = target_block->successors[0];
} 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 &&
- (same_t[VAR_NUM_EX(target->result)] & target->result_type) != 0 &&
- !(target_block->flags & ZEND_BB_PROTECTED)) {
- /* T = JMPZ_EX(X, L1), L1: T = JMPZ({X|T}, L2) -> T = JMPZ_EX(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_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);
+ target->result.var == last_op->result.var &&
+ (SAME_VAR(target->op1, last_op->result) ||
+ SAME_VAR(target->op1, last_op->op1))) {
+ /* T = JMPZ_EX(X, L1), L1: T = JMPZ_EX({X|T}, L2) -> T = JMPZ_EX(X, L2) */
+ next = target_block->successors[0];
} 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 &&
- !(target_block->flags & ZEND_BB_PROTECTED)) {
+ (SAME_VAR(target->op1, last_op->result) ||
+ SAME_VAR(target->op1, last_op->op1))) {
/* T = JMPZ_EX(X, L), L: JMPZNZ({X|T}, L2, L3) -> T = JMPZ_EX(X, L2) */
- DEL_SOURCE(block, block->successors[0]);
- if (last_op->opcode == ZEND_JMPZ_EX) {
- block->successors[0] = target_block->successors[0];
- } else {
- block->successors[0] = target_block->successors[1];
- }
- ADD_SOURCE(block, block->successors[0]);
- ++(*opt_count);
+ next = target_block->successors[last_op->opcode == ZEND_JMPNZ_EX];
+ } else if (target->opcode == INV_EX_COND(last_op->opcode) &&
+ (SAME_VAR(target->op1, last_op->result) ||
+ SAME_VAR(target->op1, last_op->op1))) {
+ /* T = JMPZ_EX(X, L1), L1: JMPNZ({X|T1}, L2) -> T = JMPZ_EX(X, L1+1) */
+ next = target_block->successors[1];
+ } else if (target->opcode == INV_EX_COND_EX(last_op->opcode) &&
+ target->result.var == last_op->result.var &&
+ (SAME_VAR(target->op1, last_op->result) ||
+ SAME_VAR(target->op1, last_op->op1))) {
+ /* T = JMPZ_EX(X, L1), L1: T = JMPNZ_EX({X|T}, L2) -> T = JMPZ_EX(X, L1+1) */
+ next = target_block->successors[1];
+ } else if (target->opcode == ZEND_BOOL &&
+ (SAME_VAR(target->op1, last_op->result) ||
+ SAME_VAR(target->op1, last_op->op1))) {
+ /* convert Y = JMPZ_EX(X,L1), L1: Z = BOOL(Y) to
+ Z = JMPZ_EX(X,L1+1) */
+
+ /* NOTE: This optimization pattern is not safe, but works, */
+ /* because result of JMPZ_EX instruction */
+ /* is not used on the following path and */
+ /* should be used once on the branch path. */
+ /* */
+ /* The pattern works well only if jums processed in */
+ /* direct order, otherwise it breakes JMPZ_EX */
+ /* sequences too early. */
+ last_op->result.var = target->result.var;
+ next = target_block->successors[0];
+ } else {
+ break;
}
+ CHECK_LOOP(next);
+ block->successors[0] = next;
+ ++(*opt_count);
+ target_block = get_target_block(cfg, block, 0, opt_count);
+ }
+
+ follow_block = get_follow_block(cfg, block, 1, opt_count);
+ if (target_block == follow_block) {
+ /* L: T = JMP[N]Z_EX(X, L+1) -> T = BOOL(X) */
+ last_op->opcode = ZEND_BOOL;
+ last_op->op2.num = 0;
+ block->successors_count = 1;
+ ++(*opt_count);
+ break;
}
break;
case ZEND_JMPZNZ: {
- int next = (block - blocks) + 1;
+optimize_jmpznz:
+ jmp_hitlist_count = 0;
+ target_block = get_target_block(cfg, block, 0, opt_count);
+ while (target_block->len == 1) {
+ target = op_array->opcodes + target_block->start;
- while (next < cfg->blocks_count && !(blocks[next].flags & ZEND_BB_REACHABLE)) {
- /* find first accessed one */
- next++;
+ if (target->opcode == ZEND_JMP) {
+ /* JMPZNZ(X, L1, L2), L1: JMP(L3) -> JMPZNZ(X, L3, L2) */
+ next = target_block->successors[0];
+ } else if ((target->opcode == ZEND_JMPZ || target->opcode == ZEND_JMPZNZ) &&
+ SAME_VAR(target->op1, last_op->op1)) {
+ /* JMPZNZ(X, L1, L2), L1: JMPZ(X, L3) -> JMPZNZ(X, L3, L2) */
+ next = target_block->successors[0];
+ } else if (target->opcode == ZEND_JMPNZ &&
+ SAME_VAR(target->op1, last_op->op1)) {
+ /* JMPZNZ(X, L1, L2), L1: X = JMPNZ(X, L3) -> JMPZNZ(X, L1+1, L2) */
+ next = target_block->successors[1];
+ } else {
+ break;
+ }
+ CHECK_LOOP(next);
+ block->successors[0] = next;
+ ++(*opt_count);
+ target_block = get_target_block(cfg, block, 0, opt_count);
}
- if (last_op->op1_type == IS_CONST) {
- if (!zend_is_true(&ZEND_OP1_LITERAL(last_op))) {
- /* JMPZNZ(false,L1,L2) -> JMP(L1) */
- literal_dtor(&ZEND_OP1_LITERAL(last_op));
- last_op->opcode = ZEND_JMP;
- SET_UNUSED(last_op->op1);
- SET_UNUSED(last_op->op2);
- DEL_SOURCE(block, block->successors[1]);
- block->successors_count = 1;
+ jmp_hitlist_count = 0;
+ follow_block = get_target_block(cfg, block, 1, opt_count);
+ while (follow_block->len == 1) {
+ target = op_array->opcodes + follow_block->start;
+
+ if (target->opcode == ZEND_JMP) {
+ /* JMPZNZ(X, L1, L2), L1: JMP(L3) -> JMPZNZ(X, L3, L2) */
+ next = follow_block->successors[0];
+ } else if (target->opcode == ZEND_JMPNZ &&
+ SAME_VAR(target->op1, last_op->op1)) {
+ /* JMPZNZ(X, L1, L2), L1: X = JMPNZ(X, L3) -> JMPZNZ(X, L1+1, L2) */
+ next = follow_block->successors[0];
+ } else if ((target->opcode == ZEND_JMPZ || target->opcode == ZEND_JMPZNZ) &&
+ SAME_VAR(target->op1, last_op->op1)) {
+ /* JMPZNZ(X, L1, L2), L1: JMPZ(X, L3) -> JMPZNZ(X, L3, L2) */
+ next = target_block->successors[1];
} else {
- /* JMPZNZ(true,L1,L2) -> JMP(L2) */
- literal_dtor(&ZEND_OP1_LITERAL(last_op));
- last_op->opcode = ZEND_JMP;
- SET_UNUSED(last_op->op1);
- SET_UNUSED(last_op->op2);
- DEL_SOURCE(block, block->successors[0]);
- block->successors_count = 1;
- block->successors[0] = block->successors[1];
+ break;
}
+ CHECK_LOOP(next);
+ block->successors[1] = next;
++(*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))) {
- /* JMPZNZ(?,L,L) -> JMP(L) */
- last_op->opcode = ZEND_JMP;
- SET_UNUSED(last_op->op1);
- SET_UNUSED(last_op->op2);
- block->successors_count = 1;
- ++(*opt_count);
- }
- } else if (block->successors[0] == next) {
+ follow_block = get_target_block(cfg, block, 1, opt_count);
+ }
+
+ next_block = get_next_block(cfg, block);
+ if (target_block == follow_block &&
+ !(last_op->op1_type & (IS_VAR|IS_TMP_VAR))) {
+ /* JMPZNZ(?,L,L) -> JMP(L) */
+ last_op->opcode = ZEND_JMP;
+ SET_UNUSED(last_op->op1);
+ SET_UNUSED(last_op->op2);
+ last_op->extended_value = 0;
+ block->successors_count = 1;
+ ++(*opt_count);
+ } else if (target_block == next_block) {
/* jumping to next on Z - can follow to it and jump only on NZ */
/* JMPZNZ(X,L1,L2) L1: -> JMPNZ(X,L2) */
+ int tmp = block->successors[0];
last_op->opcode = ZEND_JMPNZ;
block->successors[0] = block->successors[1];
- block->successors[1] = next;
+ block->successors[1] = tmp;
++(*opt_count);
- /* no need to add source */
- } else if (block->successors[1] == next) {
+ } else if (follow_block == next_block) {
/* 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 */
- }
-
- if (last_op->opcode == ZEND_JMPZNZ) {
- zend_uchar same_type = last_op->op1_type;
- zend_uchar same_var = VAR_NUM_EX(last_op->op1);
- zend_op *target;
- zend_op *target_end;
- zend_basic_block *target_block = blocks + block->successors[0];
-
-next_target_znz:
- target = op_array->opcodes + target_block->start;
- target_end = target + target_block->len;
- while (target < target_end && target->opcode == ZEND_NOP) {
- target++;
- }
- /* 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)) &&
- same_type == target->op1_type &&
- same_var == VAR_NUM_EX(target->op1) &&
- !(target_block->flags & ZEND_BB_PROTECTED)) {
- /* JMPZNZ(X, L1, L2), L1: JMPZ(X, 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);
- } else if (target->opcode == ZEND_JMPNZ &&
- (target->op1_type & (IS_TMP_VAR|IS_CV)) &&
- same_type == target->op1_type &&
- same_var == VAR_NUM_EX(target->op1) &&
- !(target_block->flags & ZEND_BB_PROTECTED)) {
- /* JMPZNZ(X, L1, L2), L1: X = JMPNZ(X, L3) -> JMPZNZ(X, L1+1, L2) */
- 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;
}
@@ -1747,6 +1670,8 @@ static void zend_t_usage(zend_cfg *cfg, zend_op_array *op_array, zend_bitset use
case ZEND_POST_DEC:
case ZEND_POST_INC_OBJ:
case ZEND_POST_DEC_OBJ:
+ case ZEND_POST_INC_STATIC_PROP:
+ case ZEND_POST_DEC_STATIC_PROP:
opline->opcode -= 2;
opline->result_type = IS_UNUSED;
break;
@@ -1884,8 +1809,8 @@ void zend_optimize_cfg(zend_op_array *op_array, zend_optimizer_ctx *ctx)
zend_bitset usage;
void *checkpoint;
zend_op **Tsource;
- zend_uchar *same_t;
uint32_t opt_count;
+ int *jmp_hitlist;
/* Build CFG */
checkpoint = zend_arena_checkpoint(ctx->arena);
@@ -1905,8 +1830,8 @@ void zend_optimize_cfg(zend_op_array *op_array, zend_optimizer_ctx *ctx)
bitset_len = zend_bitset_len(op_array->last_var + op_array->T);
Tsource = zend_arena_calloc(&ctx->arena, op_array->last_var + op_array->T, sizeof(zend_op *));
- same_t = zend_arena_alloc(&ctx->arena, op_array->last_var + op_array->T);
usage = zend_arena_alloc(&ctx->arena, bitset_len * ZEND_BITSET_ELM_SIZE);
+ jmp_hitlist = zend_arena_alloc(&ctx->arena, cfg.blocks_count * sizeof(int));
blocks = cfg.blocks;
end = blocks + cfg.blocks_count;
@@ -1938,10 +1863,12 @@ void zend_optimize_cfg(zend_op_array *op_array, zend_optimizer_ctx *ctx)
}
}
+ opt_count = 0;
+
/* 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, &opt_count);
+ zend_jmp_optimization(b, op_array, &cfg, jmp_hitlist, &opt_count);
}
}
@@ -1956,8 +1883,6 @@ void zend_optimize_cfg(zend_op_array *op_array, zend_optimizer_ctx *ctx)
}
}
- zend_bitset_clear(usage, bitset_len);
- zend_t_usage(&cfg, op_array, usage, ctx);
assemble_code_blocks(&cfg, op_array, ctx);
if (ctx->debug_level & ZEND_DUMP_AFTER_BLOCK_PASS) {