1
0
mirror of https://github.com/php/php-src.git synced 2026-03-24 00:02:20 +01:00

Merge branch 'PHP-8.4'

* PHP-8.4:
  Fix IN_ARRAY optimization
This commit is contained in:
Ilija Tovilo
2025-03-18 13:43:12 +01:00
2 changed files with 85 additions and 74 deletions

View File

@@ -407,40 +407,28 @@ int zend_dfa_optimize_calls(zend_op_array *op_array, zend_ssa *ssa)
zend_call_info *call_info = func_info->callee_info;
do {
if (call_info->caller_call_opline
&& call_info->caller_call_opline->opcode == ZEND_DO_ICALL
&& call_info->callee_func
&& zend_string_equals_literal(call_info->callee_func->common.function_name, "in_array")
&& (call_info->caller_init_opline->extended_value == 2
|| (call_info->caller_init_opline->extended_value == 3
&& (call_info->caller_call_opline - 1)->opcode == ZEND_SEND_VAL
&& (call_info->caller_call_opline - 1)->op1_type == IS_CONST))) {
zend_op *op = call_info->caller_init_opline;
if ((op->opcode == ZEND_FRAMELESS_ICALL_2
|| (op->opcode == ZEND_FRAMELESS_ICALL_3 && (op + 1)->op1_type == IS_CONST))
&& call_info->callee_func
&& zend_string_equals_literal_ci(call_info->callee_func->common.function_name, "in_array")) {
zend_op *send_array;
zend_op *send_needly;
bool strict = 0;
bool has_opdata = op->opcode == ZEND_FRAMELESS_ICALL_3;
ZEND_ASSERT(!call_info->is_prototype);
if (call_info->caller_init_opline->extended_value == 2) {
send_array = call_info->caller_call_opline - 1;
send_needly = call_info->caller_call_opline - 2;
} else {
if (zend_is_true(CT_CONSTANT_EX(op_array, (call_info->caller_call_opline - 1)->op1.constant))) {
if (has_opdata) {
if (zend_is_true(CT_CONSTANT_EX(op_array, (op + 1)->op1.constant))) {
strict = 1;
}
send_array = call_info->caller_call_opline - 2;
send_needly = call_info->caller_call_opline - 3;
}
if (send_array->opcode == ZEND_SEND_VAL
&& send_array->op1_type == IS_CONST
&& Z_TYPE_P(CT_CONSTANT_EX(op_array, send_array->op1.constant)) == IS_ARRAY
&& (send_needly->opcode == ZEND_SEND_VAL
|| send_needly->opcode == ZEND_SEND_VAR)
) {
if (op->op2_type == IS_CONST
&& Z_TYPE_P(CT_CONSTANT_EX(op_array, op->op2.constant)) == IS_ARRAY) {
bool ok = 1;
HashTable *src = Z_ARRVAL_P(CT_CONSTANT_EX(op_array, send_array->op1.constant));
HashTable *src = Z_ARRVAL_P(CT_CONSTANT_EX(op_array, op->op2.constant));
HashTable *dst;
zval *val, tmp;
zend_ulong idx;
@@ -471,59 +459,15 @@ int zend_dfa_optimize_calls(zend_op_array *op_array, zend_ssa *ssa)
}
if (ok) {
uint32_t op_num = send_needly - op_array->opcodes;
zend_ssa_op *ssa_op = ssa->ops + op_num;
if (ssa_op->op1_use >= 0) {
/* Reconstruct SSA */
int var_num = ssa_op->op1_use;
zend_ssa_var *var = ssa->vars + var_num;
ZEND_ASSERT(ssa_op->op1_def < 0);
zend_ssa_unlink_use_chain(ssa, op_num, ssa_op->op1_use);
ssa_op->op1_use = -1;
ssa_op->op1_use_chain = -1;
op_num = call_info->caller_call_opline - op_array->opcodes;
ssa_op = ssa->ops + op_num;
ssa_op->op1_use = var_num;
ssa_op->op1_use_chain = var->use_chain;
var->use_chain = op_num;
}
ZVAL_ARR(&tmp, dst);
/* Update opcode */
call_info->caller_call_opline->opcode = ZEND_IN_ARRAY;
call_info->caller_call_opline->extended_value = strict;
call_info->caller_call_opline->op1_type = send_needly->op1_type;
call_info->caller_call_opline->op1.num = send_needly->op1.num;
call_info->caller_call_opline->op2_type = IS_CONST;
call_info->caller_call_opline->op2.constant = zend_optimizer_add_literal(op_array, &tmp);
if (call_info->caller_init_opline->extended_value == 3) {
MAKE_NOP(call_info->caller_call_opline - 1);
}
MAKE_NOP(call_info->caller_init_opline);
MAKE_NOP(send_needly);
MAKE_NOP(send_array);
removed_ops++;
op_num = call_info->caller_call_opline - op_array->opcodes;
ssa_op = ssa->ops + op_num;
if (ssa_op->result_def >= 0) {
int var = ssa_op->result_def;
int use = ssa->vars[var].use_chain;
/* If the result is used only in a JMPZ/JMPNZ, replace result type with
* IS_TMP_VAR, which will enable use of smart branches. Don't do this
* in other cases, as not all opcodes support both VAR and TMP. */
if (ssa->vars[var].phi_use_chain == NULL
&& ssa->ops[use].op1_use == var
&& ssa->ops[use].op1_use_chain == -1
&& (op_array->opcodes[use].opcode == ZEND_JMPZ
|| op_array->opcodes[use].opcode == ZEND_JMPNZ)) {
call_info->caller_call_opline->result_type = IS_TMP_VAR;
op_array->opcodes[use].op1_type = IS_TMP_VAR;
}
op->opcode = ZEND_IN_ARRAY;
op->extended_value = strict;
op->op2.constant = zend_optimizer_add_literal(op_array, &tmp);
if (has_opdata) {
MAKE_NOP(op + 1);
removed_ops++;
}
}
}

View File

@@ -0,0 +1,67 @@
--TEST--
GH-18050: Frameless calls break IN_ARRAY optimization
--EXTENSIONS--
opcache
--INI--
opcache.enable_cli=1
opcache.optimization_level=-1
opcache.opt_debug_level=0x20000
--FILE--
<?php
function test($v) {
$ary = ['x', 'y'];
var_dump(in_array($v, $ary));
var_dump(in_array($v, $ary, false));
var_dump(IN_ARRAY($v, $ary, true));
if (in_array($v, $ary, true)) {
echo "True\n";
}
}
test('x');
test('z');
?>
--EXPECTF--
$_main:
; (lines=%d, args=%d, vars=%d, tmps=%d)
; (after optimizer)
; %sgh18050.php:%s
0000 INIT_FCALL 1 %d string("test")
0001 SEND_VAL string("x") 1
0002 DO_UCALL
0003 INIT_FCALL 1 %d string("test")
0004 SEND_VAL string("z") 1
0005 DO_UCALL
0006 RETURN int(1)
test:
; (lines=%d, args=%d, vars=%d, tmps=%d)
; (after optimizer)
; %sgh18050.php:%s
0000 CV0($v) = RECV 1
0001 INIT_FCALL 1 %d string("var_dump")
0002 T1 = IN_ARRAY 0 CV0($v) array(...)
0003 SEND_VAL T1 1
0004 DO_ICALL
0005 INIT_FCALL 1 %d string("var_dump")
0006 T1 = IN_ARRAY 0 CV0($v) array(...)
0007 SEND_VAL T1 1
0008 DO_ICALL
0009 INIT_FCALL 1 %d string("var_dump")
0010 T1 = IN_ARRAY 1 CV0($v) array(...)
0011 SEND_VAL T1 1
0012 DO_ICALL
0013 T1 = IN_ARRAY 1 CV0($v) array(...)
0014 JMPZ T1 0016
0015 ECHO string("True
")
0016 RETURN null
bool(true)
bool(true)
bool(true)
True
bool(false)
bool(false)
bool(false)