summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS1
-rw-r--r--Zend/tests/bug80046.phpt22
-rw-r--r--ext/opcache/Optimizer/block_pass.c14
-rw-r--r--ext/opcache/Optimizer/zend_cfg.c3
4 files changed, 35 insertions, 5 deletions
diff --git a/NEWS b/NEWS
index e2cb05e949..9eaa1b1c58 100644
--- a/NEWS
+++ b/NEWS
@@ -19,6 +19,7 @@ PHP NEWS
- OPcache:
. Fixed bug #80002 (calc free space for new interned string is wrong).
(t-matsuno)
+ . Fixed bug #80046 (FREE for SWITCH_STRING optimized away). (Nikita)
- PDO:
. Fixed bug #80027 (Terrible performance using $query->fetch on queries with
diff --git a/Zend/tests/bug80046.phpt b/Zend/tests/bug80046.phpt
new file mode 100644
index 0000000000..87a493c203
--- /dev/null
+++ b/Zend/tests/bug80046.phpt
@@ -0,0 +1,22 @@
+--TEST--
+Bug #80046: FREE for SWITCH_STRING optimized away
+--FILE--
+<?php
+
+function test($foo) {
+ switch ($foo . 'Bar') {
+ case 'A':
+ throw new Exception('A');
+ default:
+ throw new Exception('Default');
+ }
+}
+try {
+ test('Foo');
+} catch (Exception $e) {
+ echo $e->getMessage(), "\n";
+}
+
+?>
+--EXPECT--
+Default
diff --git a/ext/opcache/Optimizer/block_pass.c b/ext/opcache/Optimizer/block_pass.c
index 3327ec86df..17b89c4d53 100644
--- a/ext/opcache/Optimizer/block_pass.c
+++ b/ext/opcache/Optimizer/block_pass.c
@@ -921,7 +921,15 @@ static void assemble_code_blocks(zend_cfg *cfg, zend_op_array *op_array, zend_op
if (b->len == 0) {
continue;
}
- if (b->flags & ZEND_BB_REACHABLE) {
+ if (b->flags & (ZEND_BB_REACHABLE|ZEND_BB_UNREACHABLE_FREE)) {
+ if (b->flags & ZEND_BB_UNREACHABLE_FREE) {
+ /* Only keep the FREE for the loop var */
+ ZEND_ASSERT(op_array->opcodes[b->start].opcode == ZEND_FREE
+ || op_array->opcodes[b->start].opcode == ZEND_FE_FREE);
+ len += b->len = 1;
+ continue;
+ }
+
opline = op_array->opcodes + b->start + b->len - 1;
if (opline->opcode == ZEND_JMP) {
zend_basic_block *next = b + 1;
@@ -959,7 +967,7 @@ static void assemble_code_blocks(zend_cfg *cfg, zend_op_array *op_array, zend_op
/* Copy code of reachable blocks into a single buffer */
for (b = blocks; b < end; b++) {
- if (b->flags & ZEND_BB_REACHABLE) {
+ if (b->flags & (ZEND_BB_REACHABLE|ZEND_BB_UNREACHABLE_FREE)) {
memcpy(opline, op_array->opcodes + b->start, b->len * sizeof(zend_op));
b->start = opline - new_opcodes;
opline += b->len;
@@ -1083,7 +1091,7 @@ static void assemble_code_blocks(zend_cfg *cfg, zend_op_array *op_array, zend_op
/* rebuild map (just for printing) */
memset(cfg->map, -1, sizeof(int) * op_array->last);
for (n = 0; n < cfg->blocks_count; n++) {
- if (cfg->blocks[n].flags & ZEND_BB_REACHABLE) {
+ if (cfg->blocks[n].flags & (ZEND_BB_REACHABLE|ZEND_BB_UNREACHABLE_FREE)) {
cfg->map[cfg->blocks[n].start] = n;
}
}
diff --git a/ext/opcache/Optimizer/zend_cfg.c b/ext/opcache/Optimizer/zend_cfg.c
index 66c15be311..76c829bb3e 100644
--- a/ext/opcache/Optimizer/zend_cfg.c
+++ b/ext/opcache/Optimizer/zend_cfg.c
@@ -575,9 +575,8 @@ int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t b
}
/* Build CFG, Step 4, Mark Reachable Basic Blocks */
- zend_mark_reachable_blocks(op_array, cfg, 0);
-
cfg->flags |= flags;
+ zend_mark_reachable_blocks(op_array, cfg, 0);
return SUCCESS;
}