diff options
author | Nikita Popov <nikita.ppv@gmail.com> | 2019-07-10 17:28:52 +0200 |
---|---|---|
committer | Nikita Popov <nikita.ppv@gmail.com> | 2019-07-10 17:28:52 +0200 |
commit | df84d680bfca7eb3195f2044496b3432fa57a5c5 (patch) | |
tree | a4fc99da185c25675805b0333baeaa328204c656 | |
parent | d63b91db601bfdecd593d022081a4e82ad8b3b1a (diff) | |
parent | a54ee8a94748329c77849bed2caf86ac52ea3f41 (diff) | |
download | php-git-df84d680bfca7eb3195f2044496b3432fa57a5c5.tar.gz |
Merge branch 'PHP-7.4'
-rw-r--r-- | Zend/tests/bug78271.phpt | 27 | ||||
-rw-r--r-- | ext/opcache/Optimizer/dfa_pass.c | 41 |
2 files changed, 58 insertions, 10 deletions
diff --git a/Zend/tests/bug78271.phpt b/Zend/tests/bug78271.phpt new file mode 100644 index 0000000000..fbbe016d75 --- /dev/null +++ b/Zend/tests/bug78271.phpt @@ -0,0 +1,27 @@ +--TEST-- +Bug #78271: Invalid result of if-else +--FILE-- +<? +function test($a, $b){ + if ($a==10) { + $w="x"; + } else { + $w="y"; + } + + if ($b) { + $d1="none"; + $d2="block"; + } else { + $d1="block"; + $d2="none"; + } + + echo $d2.$b."\n"; + +} + +test(1, 1); +?> +--EXPECT-- +block1 diff --git a/ext/opcache/Optimizer/dfa_pass.c b/ext/opcache/Optimizer/dfa_pass.c index b0d58f9566..2da656e0c1 100644 --- a/ext/opcache/Optimizer/dfa_pass.c +++ b/ext/opcache/Optimizer/dfa_pass.c @@ -125,10 +125,37 @@ int zend_dfa_analyze_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx, return SUCCESS; } +static zend_bool is_smart_branch_inhibiting_nop( + zend_op_array *op_array, uint32_t target, uint32_t current, + zend_basic_block *b, zend_basic_block *blocks_end) +{ + uint32_t next; + /* Target points one past the last non-nop instruction. Make sure there is one. */ + if (target == 0) { + return 0; + } + + /* Find the next instruction, skipping unreachable or empty blocks. */ + next = current + 1; + if (next >= b->start + b->len) { + do { + b++; + if (b == blocks_end) { + return 0; + } + } while (!(b->flags & ZEND_BB_REACHABLE) || b->len == 0); + next = b->start; + } + + return (op_array->opcodes[next].opcode == ZEND_JMPZ || + op_array->opcodes[next].opcode == ZEND_JMPNZ) && + zend_is_smart_branch(op_array->opcodes + target - 1); +} + static void zend_ssa_remove_nops(zend_op_array *op_array, zend_ssa *ssa, zend_optimizer_ctx *ctx) { zend_basic_block *blocks = ssa->cfg.blocks; - zend_basic_block *end = blocks + ssa->cfg.blocks_count; + zend_basic_block *blocks_end = blocks + ssa->cfg.blocks_count; zend_basic_block *b; zend_func_info *func_info; int j; @@ -152,7 +179,7 @@ static void zend_ssa_remove_nops(zend_op_array *op_array, zend_ssa *ssa, zend_op } } - for (b = blocks; b < end; b++) { + for (b = blocks; b < blocks_end; b++) { if (b->flags & (ZEND_BB_REACHABLE|ZEND_BB_UNREACHABLE_FREE)) { uint32_t end; @@ -174,13 +201,7 @@ static void zend_ssa_remove_nops(zend_op_array *op_array, zend_ssa *ssa, zend_op while (i < end) { shiftlist[i] = i - target; if (EXPECTED(op_array->opcodes[i].opcode != ZEND_NOP) || - /* Keep NOP to support ZEND_VM_SMART_BRANCH. Using "target-1" instead of - * "i-1" here to check the last non-NOP instruction. */ - (target > 0 && - i + 1 < op_array->last && - (op_array->opcodes[i+1].opcode == ZEND_JMPZ || - op_array->opcodes[i+1].opcode == ZEND_JMPNZ) && - zend_is_smart_branch(op_array->opcodes + target - 1))) { + is_smart_branch_inhibiting_nop(op_array, target, i, b, blocks_end)) { if (i != target) { op_array->opcodes[target] = op_array->opcodes[i]; ssa->ops[target] = ssa->ops[i]; @@ -240,7 +261,7 @@ static void zend_ssa_remove_nops(zend_op_array *op_array, zend_ssa *ssa, zend_op } /* update branch targets */ - for (b = blocks; b < end; b++) { + for (b = blocks; b < blocks_end; b++) { if ((b->flags & ZEND_BB_REACHABLE) && b->len != 0) { zend_op *opline = op_array->opcodes + b->start + b->len - 1; zend_optimizer_shift_jump(op_array, opline, shiftlist); |