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:
@@ -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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
67
ext/opcache/tests/gh18050.phpt
Normal file
67
ext/opcache/tests/gh18050.phpt
Normal 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)
|
||||
Reference in New Issue
Block a user