Skip to content

Commit e7a5ec5

Browse files
committed
Merge branch 'PHP-8.1'
* PHP-8.1: Handle operand replacement in JMP_NULL
2 parents 08cbb99 + 1f19401 commit e7a5ec5

File tree

2 files changed

+49
-62
lines changed

2 files changed

+49
-62
lines changed

Zend/Optimizer/zend_optimizer.c

Lines changed: 33 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,14 @@ bool zend_optimizer_update_op1_const(zend_op_array *op_array,
281281
opline->opcode = ZEND_SEND_VAL;
282282
opline->op1.constant = zend_optimizer_add_literal(op_array, val);
283283
break;
284+
case ZEND_CASE:
285+
opline->opcode = ZEND_IS_EQUAL;
286+
opline->op1.constant = zend_optimizer_add_literal(op_array, val);
287+
break;
288+
case ZEND_CASE_STRICT:
289+
opline->opcode = ZEND_IS_IDENTICAL;
290+
opline->op1.constant = zend_optimizer_add_literal(op_array, val);
291+
break;
284292
case ZEND_SEPARATE:
285293
case ZEND_SEND_VAR_NO_REF:
286294
case ZEND_SEND_VAR_NO_REF_EX:
@@ -289,9 +297,6 @@ bool zend_optimizer_update_op1_const(zend_op_array *op_array,
289297
/* This would require a non-local change.
290298
* zend_optimizer_replace_by_const() supports this. */
291299
return 0;
292-
case ZEND_CASE:
293-
case ZEND_CASE_STRICT:
294-
case ZEND_FETCH_LIST_R:
295300
case ZEND_COPY_TMP:
296301
case ZEND_FETCH_CLASS_NAME:
297302
return 0;
@@ -563,72 +568,38 @@ bool zend_optimizer_replace_by_const(zend_op_array *op_array,
563568
break;
564569
/* In most cases IS_TMP_VAR operand may be used only once.
565570
* The operands are usually destroyed by the opcode handler.
566-
* ZEND_CASE[_STRICT] and ZEND_FETCH_LIST_R are exceptions, they keeps operand
567-
* unchanged, and allows its reuse. these instructions
568-
* usually terminated by ZEND_FREE that finally kills the value.
571+
* However, there are some exception which keep the operand alive. In that case
572+
* we want to try to replace all uses of the temporary.
569573
*/
570-
case ZEND_FETCH_LIST_R: {
571-
zend_op *m = opline;
572-
573-
do {
574-
if (m->opcode == ZEND_FETCH_LIST_R &&
575-
m->op1_type == type &&
576-
m->op1.var == var) {
577-
zval v;
578-
ZVAL_COPY(&v, val);
579-
if (Z_TYPE(v) == IS_STRING) {
580-
zend_string_hash_val(Z_STR(v));
581-
}
582-
m->op1.constant = zend_optimizer_add_literal(op_array, &v);
583-
m->op1_type = IS_CONST;
584-
}
585-
m++;
586-
} while (m->opcode != ZEND_FREE || m->op1_type != type || m->op1.var != var);
587-
588-
ZEND_ASSERT(m->opcode == ZEND_FREE && m->op1_type == type && m->op1.var == var);
589-
MAKE_NOP(m);
590-
zval_ptr_dtor_nogc(val);
591-
return 1;
592-
}
574+
case ZEND_FETCH_LIST_R:
575+
case ZEND_CASE:
576+
case ZEND_CASE_STRICT:
593577
case ZEND_SWITCH_LONG:
594578
case ZEND_SWITCH_STRING:
595579
case ZEND_MATCH:
596-
case ZEND_CASE:
597-
case ZEND_CASE_STRICT: {
580+
case ZEND_JMP_NULL: {
598581
zend_op *end = op_array->opcodes + op_array->last;
599582
while (opline < end) {
600583
if (opline->op1_type == type && opline->op1.var == var) {
601-
if (
602-
opline->opcode == ZEND_CASE
603-
|| opline->opcode == ZEND_CASE_STRICT
604-
|| opline->opcode == ZEND_SWITCH_LONG
605-
|| opline->opcode == ZEND_SWITCH_STRING
606-
|| opline->opcode == ZEND_MATCH
607-
) {
608-
zval v;
609-
610-
if (opline->opcode == ZEND_CASE) {
611-
opline->opcode = ZEND_IS_EQUAL;
612-
} else if (opline->opcode == ZEND_CASE_STRICT) {
613-
opline->opcode = ZEND_IS_IDENTICAL;
614-
}
615-
ZVAL_COPY(&v, val);
616-
if (Z_TYPE(v) == IS_STRING) {
617-
zend_string_hash_val(Z_STR(v));
618-
}
619-
opline->op1.constant = zend_optimizer_add_literal(op_array, &v);
620-
opline->op1_type = IS_CONST;
621-
} else if (opline->opcode == ZEND_FREE) {
622-
if (opline->extended_value == ZEND_FREE_SWITCH) {
623-
/* We found the end of the switch. */
624-
MAKE_NOP(opline);
625-
break;
626-
}
627-
628-
ZEND_ASSERT(opline->extended_value == ZEND_FREE_ON_RETURN);
629-
MAKE_NOP(opline);
630-
} else {
631-
ZEND_UNREACHABLE();
584+
/* If this opcode doesn't keep the operand alive, we're done. Check
585+
* this early, because op replacement may modify the opline. */
586+
bool is_last = opline->opcode != ZEND_FETCH_LIST_R
587+
&& opline->opcode != ZEND_CASE
588+
&& opline->opcode != ZEND_CASE_STRICT
589+
&& opline->opcode != ZEND_SWITCH_LONG
590+
&& opline->opcode != ZEND_SWITCH_STRING
591+
&& opline->opcode != ZEND_MATCH
592+
&& opline->opcode != ZEND_JMP_NULL
593+
&& (opline->opcode != ZEND_FREE
594+
|| opline->extended_value != ZEND_FREE_ON_RETURN);
595+
596+
Z_TRY_ADDREF_P(val);
597+
if (!zend_optimizer_update_op1_const(op_array, opline, val)) {
598+
zval_ptr_dtor(val);
599+
return 0;
600+
}
601+
if (is_last) {
602+
break;
632603
}
633604
}
634605
opline++;
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
--TEST--
2+
Constant propagation with nullsafe operator
3+
--FILE--
4+
<?php
5+
6+
class Bar { const FOO = "foo"; }
7+
8+
try {
9+
Bar::FOO?->length();
10+
} catch (Error $e) {
11+
echo $e->getMessage(), "\n";
12+
}
13+
14+
?>
15+
--EXPECT--
16+
Call to a member function length() on string

0 commit comments

Comments
 (0)