Fix incorrect match default branch optimization

Fixes GH-11134
Closes GH-11135
This commit is contained in:
Ilija Tovilo 2023-04-26 11:55:23 +02:00
parent 725f136f9a
commit 3a76f795f8
No known key found for this signature in database
GPG key ID: A4F5D403F118200A
3 changed files with 49 additions and 10 deletions

2
NEWS
View file

@ -2,6 +2,8 @@ PHP NEWS
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
?? ??? ????, PHP 8.1.20 ?? ??? ????, PHP 8.1.20
- Opcache:
. Fixed bug GH-11134 (Incorrect match default branch optimization). (ilutov)
11 May 2023, PHP 8.1.19 11 May 2023, PHP 8.1.19

View file

@ -1000,32 +1000,41 @@ optimize_jmpnz:
|| (opline->opcode == ZEND_SWITCH_STRING && type == IS_STRING) || (opline->opcode == ZEND_SWITCH_STRING && type == IS_STRING)
|| (opline->opcode == ZEND_MATCH && (type == IS_LONG || type == IS_STRING)); || (opline->opcode == ZEND_MATCH && (type == IS_LONG || type == IS_STRING));
if (!correct_type) { /* Switch statements have a fallback chain for loose comparison. In those
* cases the SWITCH_* instruction is a NOP. Match does strict comparison and
* thus jumps to the default branch on mismatched types, so we need to
* convert MATCH to a jmp. */
if (!correct_type && opline->opcode != ZEND_MATCH) {
removed_ops++; removed_ops++;
MAKE_NOP(opline); MAKE_NOP(opline);
opline->extended_value = 0; opline->extended_value = 0;
take_successor_ex(ssa, block_num, block, block->successors[block->successors_count - 1]); take_successor_ex(ssa, block_num, block, block->successors[block->successors_count - 1]);
goto optimize_nop; goto optimize_nop;
} else { }
uint32_t target;
if (correct_type) {
HashTable *jmptable = Z_ARRVAL_P(CT_CONSTANT_EX(op_array, opline->op2.constant)); HashTable *jmptable = Z_ARRVAL_P(CT_CONSTANT_EX(op_array, opline->op2.constant));
zval *jmp_zv = type == IS_LONG zval *jmp_zv = type == IS_LONG
? zend_hash_index_find(jmptable, Z_LVAL_P(zv)) ? zend_hash_index_find(jmptable, Z_LVAL_P(zv))
: zend_hash_find(jmptable, Z_STR_P(zv)); : zend_hash_find(jmptable, Z_STR_P(zv));
uint32_t target;
if (jmp_zv) { if (jmp_zv) {
target = ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(jmp_zv)); target = ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(jmp_zv));
} else { } else {
target = ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value); target = ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value);
} }
opline->opcode = ZEND_JMP; } else {
opline->extended_value = 0; ZEND_ASSERT(opline->opcode == ZEND_MATCH);
SET_UNUSED(opline->op1); target = ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value);
ZEND_SET_OP_JMP_ADDR(opline, opline->op1, op_array->opcodes + target);
SET_UNUSED(opline->op2);
take_successor_ex(ssa, block_num, block, ssa->cfg.map[target]);
goto optimize_jmp;
} }
opline->opcode = ZEND_JMP;
opline->extended_value = 0;
SET_UNUSED(opline->op1);
ZEND_SET_OP_JMP_ADDR(opline, opline->op1, op_array->opcodes + target);
SET_UNUSED(opline->op2);
take_successor_ex(ssa, block_num, block, ssa->cfg.map[target]);
goto optimize_jmp;
} }
break; break;
case ZEND_NOP: case ZEND_NOP:

View file

@ -0,0 +1,28 @@
--TEST--
GH-11134: Incorrect match optimization
--FILE--
<?php
function testMatch() {
return match ($unset ?? null) {
'foo' => 'foo',
'bar' => 'bar',
default => 'baz',
};
}
function testSwitch() {
switch ($unset ?? null) {
case 'foo': return 'foo';
case 'bar': return 'bar';
default: return 'baz';
}
}
var_dump(testMatch());
var_dump(testSwitch());
?>
--EXPECT--
string(3) "baz"
string(3) "baz"