Merge branch 'PHP-8.1'

* PHP-8.1:
  Handle operand replacement in JMP_NULL
This commit is contained in:
Nikita Popov 2021-10-19 15:19:39 +02:00
commit e7a5ec5fd9
2 changed files with 48 additions and 61 deletions

View file

@ -281,6 +281,14 @@ bool zend_optimizer_update_op1_const(zend_op_array *op_array,
opline->opcode = ZEND_SEND_VAL;
opline->op1.constant = zend_optimizer_add_literal(op_array, val);
break;
case ZEND_CASE:
opline->opcode = ZEND_IS_EQUAL;
opline->op1.constant = zend_optimizer_add_literal(op_array, val);
break;
case ZEND_CASE_STRICT:
opline->opcode = ZEND_IS_IDENTICAL;
opline->op1.constant = zend_optimizer_add_literal(op_array, val);
break;
case ZEND_SEPARATE:
case ZEND_SEND_VAR_NO_REF:
case ZEND_SEND_VAR_NO_REF_EX:
@ -289,9 +297,6 @@ bool zend_optimizer_update_op1_const(zend_op_array *op_array,
/* This would require a non-local change.
* zend_optimizer_replace_by_const() supports this. */
return 0;
case ZEND_CASE:
case ZEND_CASE_STRICT:
case ZEND_FETCH_LIST_R:
case ZEND_COPY_TMP:
case ZEND_FETCH_CLASS_NAME:
return 0;
@ -563,72 +568,38 @@ bool zend_optimizer_replace_by_const(zend_op_array *op_array,
break;
/* In most cases IS_TMP_VAR operand may be used only once.
* The operands are usually destroyed by the opcode handler.
* ZEND_CASE[_STRICT] and ZEND_FETCH_LIST_R are exceptions, they keeps operand
* unchanged, and allows its reuse. these instructions
* usually terminated by ZEND_FREE that finally kills the value.
* However, there are some exception which keep the operand alive. In that case
* we want to try to replace all uses of the temporary.
*/
case ZEND_FETCH_LIST_R: {
zend_op *m = opline;
do {
if (m->opcode == ZEND_FETCH_LIST_R &&
m->op1_type == type &&
m->op1.var == var) {
zval v;
ZVAL_COPY(&v, val);
if (Z_TYPE(v) == IS_STRING) {
zend_string_hash_val(Z_STR(v));
}
m->op1.constant = zend_optimizer_add_literal(op_array, &v);
m->op1_type = IS_CONST;
}
m++;
} while (m->opcode != ZEND_FREE || m->op1_type != type || m->op1.var != var);
ZEND_ASSERT(m->opcode == ZEND_FREE && m->op1_type == type && m->op1.var == var);
MAKE_NOP(m);
zval_ptr_dtor_nogc(val);
return 1;
}
case ZEND_FETCH_LIST_R:
case ZEND_CASE:
case ZEND_CASE_STRICT:
case ZEND_SWITCH_LONG:
case ZEND_SWITCH_STRING:
case ZEND_MATCH:
case ZEND_CASE:
case ZEND_CASE_STRICT: {
case ZEND_JMP_NULL: {
zend_op *end = op_array->opcodes + op_array->last;
while (opline < end) {
if (opline->op1_type == type && opline->op1.var == var) {
if (
opline->opcode == ZEND_CASE
|| opline->opcode == ZEND_CASE_STRICT
|| opline->opcode == ZEND_SWITCH_LONG
|| opline->opcode == ZEND_SWITCH_STRING
|| opline->opcode == ZEND_MATCH
) {
zval v;
/* If this opcode doesn't keep the operand alive, we're done. Check
* this early, because op replacement may modify the opline. */
bool is_last = opline->opcode != ZEND_FETCH_LIST_R
&& opline->opcode != ZEND_CASE
&& opline->opcode != ZEND_CASE_STRICT
&& opline->opcode != ZEND_SWITCH_LONG
&& opline->opcode != ZEND_SWITCH_STRING
&& opline->opcode != ZEND_MATCH
&& opline->opcode != ZEND_JMP_NULL
&& (opline->opcode != ZEND_FREE
|| opline->extended_value != ZEND_FREE_ON_RETURN);
if (opline->opcode == ZEND_CASE) {
opline->opcode = ZEND_IS_EQUAL;
} else if (opline->opcode == ZEND_CASE_STRICT) {
opline->opcode = ZEND_IS_IDENTICAL;
}
ZVAL_COPY(&v, val);
if (Z_TYPE(v) == IS_STRING) {
zend_string_hash_val(Z_STR(v));
}
opline->op1.constant = zend_optimizer_add_literal(op_array, &v);
opline->op1_type = IS_CONST;
} else if (opline->opcode == ZEND_FREE) {
if (opline->extended_value == ZEND_FREE_SWITCH) {
/* We found the end of the switch. */
MAKE_NOP(opline);
break;
}
ZEND_ASSERT(opline->extended_value == ZEND_FREE_ON_RETURN);
MAKE_NOP(opline);
} else {
ZEND_UNREACHABLE();
Z_TRY_ADDREF_P(val);
if (!zend_optimizer_update_op1_const(op_array, opline, val)) {
zval_ptr_dtor(val);
return 0;
}
if (is_last) {
break;
}
}
opline++;

View file

@ -0,0 +1,16 @@
--TEST--
Constant propagation with nullsafe operator
--FILE--
<?php
class Bar { const FOO = "foo"; }
try {
Bar::FOO?->length();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
?>
--EXPECT--
Call to a member function length() on string