Skip to content

Commit 33011be

Browse files
committed
Merge branch 'PHP-7.3' of git.php.net:/php-src into PHP-7.3
* 'PHP-7.3' of git.php.net:/php-src: Fixed bug #77257
2 parents 54a58a7 + 91888cc commit 33011be

File tree

2 files changed

+65
-12
lines changed

2 files changed

+65
-12
lines changed

ext/opcache/Optimizer/dfa_pass.c

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -510,13 +510,54 @@ static void compress_block(zend_op_array *op_array, zend_basic_block *block)
510510
}
511511
}
512512

513+
static void replace_predecessor(zend_ssa *ssa, int block_id, int old_pred, int new_pred) {
514+
zend_basic_block *block = &ssa->cfg.blocks[block_id];
515+
int *predecessors = &ssa->cfg.predecessors[block->predecessor_offset];
516+
zend_ssa_phi *phi;
517+
518+
int i;
519+
int old_pred_idx = -1;
520+
int new_pred_idx = -1;
521+
for (i = 0; i < block->predecessors_count; i++) {
522+
if (predecessors[i] == old_pred) {
523+
old_pred_idx = i;
524+
}
525+
if (predecessors[i] == new_pred) {
526+
new_pred_idx = i;
527+
}
528+
}
529+
530+
ZEND_ASSERT(old_pred_idx != -1);
531+
if (new_pred_idx == -1) {
532+
/* If the new predecessor doesn't exist yet, simply rewire the old one */
533+
predecessors[old_pred_idx] = new_pred;
534+
} else {
535+
/* Otherwise, rewiring the old predecessor would make the new predecessor appear
536+
* twice, which violates our CFG invariants. Remove the old predecessor instead. */
537+
memmove(
538+
predecessors + old_pred_idx,
539+
predecessors + old_pred_idx + 1,
540+
sizeof(int) * (block->predecessors_count - old_pred_idx - 1)
541+
);
542+
543+
/* Also remove the corresponding phi node entries */
544+
for (phi = ssa->blocks[block_id].phis; phi; phi = phi->next) {
545+
memmove(
546+
phi->sources + old_pred_idx,
547+
phi->sources + old_pred_idx + 1,
548+
sizeof(int) * (block->predecessors_count - old_pred_idx - 1)
549+
);
550+
}
551+
552+
block->predecessors_count--;
553+
}
554+
}
555+
513556
static void zend_ssa_replace_control_link(zend_op_array *op_array, zend_ssa *ssa, int from, int to, int new_to)
514557
{
515558
zend_basic_block *src = &ssa->cfg.blocks[from];
516559
zend_basic_block *old = &ssa->cfg.blocks[to];
517560
zend_basic_block *dst = &ssa->cfg.blocks[new_to];
518-
int *predecessors = &ssa->cfg.predecessors[dst->predecessor_offset];
519-
int dup = 0;
520561
int i;
521562
zend_op *opline;
522563

@@ -585,16 +626,7 @@ static void zend_ssa_replace_control_link(zend_op_array *op_array, zend_ssa *ssa
585626
}
586627
}
587628

588-
for (i = 0; i < dst->predecessors_count; i++) {
589-
if (dup) {
590-
predecessors[i] = predecessors[i+1];
591-
} else if (predecessors[i] == to) {
592-
predecessors[i] = from;
593-
} else if (predecessors[i] == from) {
594-
dup = 1;
595-
dst->predecessors_count--;
596-
}
597-
}
629+
replace_predecessor(ssa, new_to, to, from);
598630
}
599631

600632
static void zend_ssa_unlink_block(zend_op_array *op_array, zend_ssa *ssa, zend_basic_block *block, int block_num)

ext/opcache/tests/bug77257.phpt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
--TEST--
2+
Bug #77257: value of variable assigned in a switch() construct gets lost
3+
--FILE--
4+
<?php
5+
function test($x) {
6+
$a = false;
7+
switch($x["y"]) {
8+
case "a":
9+
$a = true;
10+
break;
11+
case "b":
12+
break;
13+
case "c":
14+
break;
15+
}
16+
return $a;
17+
}
18+
var_dump(test(["y" => "a"]));
19+
?>
20+
--EXPECT--
21+
bool(true)

0 commit comments

Comments
 (0)