Skip to content

Commit b277228

Browse files
committed
Fix IN_ARRAY optimization
in_array() calls are compiled to frameless calls. Adjust the optimization appropriately. Luckily, frameless opcodes simplify the optimization quite a bit. Fixes GH-18050
1 parent 4b9c72f commit b277228

File tree

2 files changed

+64
-57
lines changed

2 files changed

+64
-57
lines changed

Zend/Optimizer/dfa_pass.c

Lines changed: 20 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -407,40 +407,27 @@ int zend_dfa_optimize_calls(zend_op_array *op_array, zend_ssa *ssa)
407407
zend_call_info *call_info = func_info->callee_info;
408408

409409
do {
410-
if (call_info->caller_call_opline
411-
&& call_info->caller_call_opline->opcode == ZEND_DO_ICALL
410+
zend_op *op = call_info->caller_init_opline;
411+
412+
if ((op->opcode == ZEND_FRAMELESS_ICALL_2
413+
|| (op->opcode == ZEND_FRAMELESS_ICALL_3 && (op + 1)->op1_type == IS_CONST))
412414
&& call_info->callee_func
413-
&& zend_string_equals_literal(call_info->callee_func->common.function_name, "in_array")
414-
&& (call_info->caller_init_opline->extended_value == 2
415-
|| (call_info->caller_init_opline->extended_value == 3
416-
&& (call_info->caller_call_opline - 1)->opcode == ZEND_SEND_VAL
417-
&& (call_info->caller_call_opline - 1)->op1_type == IS_CONST))) {
418-
419-
zend_op *send_array;
420-
zend_op *send_needly;
415+
&& zend_string_equals_literal(call_info->callee_func->common.function_name, "in_array")) {
416+
421417
bool strict = 0;
422418
ZEND_ASSERT(!call_info->is_prototype);
423419

424-
if (call_info->caller_init_opline->extended_value == 2) {
425-
send_array = call_info->caller_call_opline - 1;
426-
send_needly = call_info->caller_call_opline - 2;
427-
} else {
428-
if (zend_is_true(CT_CONSTANT_EX(op_array, (call_info->caller_call_opline - 1)->op1.constant))) {
420+
if (op->opcode == ZEND_FRAMELESS_ICALL_3) {
421+
if (zend_is_true(CT_CONSTANT_EX(op_array, (op + 1)->op1.constant))) {
429422
strict = 1;
430423
}
431-
send_array = call_info->caller_call_opline - 2;
432-
send_needly = call_info->caller_call_opline - 3;
433424
}
434425

435-
if (send_array->opcode == ZEND_SEND_VAL
436-
&& send_array->op1_type == IS_CONST
437-
&& Z_TYPE_P(CT_CONSTANT_EX(op_array, send_array->op1.constant)) == IS_ARRAY
438-
&& (send_needly->opcode == ZEND_SEND_VAL
439-
|| send_needly->opcode == ZEND_SEND_VAR)
440-
) {
426+
if (op->op2_type == IS_CONST
427+
&& Z_TYPE_P(CT_CONSTANT_EX(op_array, op->op2.constant)) == IS_ARRAY) {
441428
bool ok = 1;
442429

443-
HashTable *src = Z_ARRVAL_P(CT_CONSTANT_EX(op_array, send_array->op1.constant));
430+
HashTable *src = Z_ARRVAL_P(CT_CONSTANT_EX(op_array, op->op2.constant));
444431
HashTable *dst;
445432
zval *val, tmp;
446433
zend_ulong idx;
@@ -471,44 +458,20 @@ int zend_dfa_optimize_calls(zend_op_array *op_array, zend_ssa *ssa)
471458
}
472459

473460
if (ok) {
474-
uint32_t op_num = send_needly - op_array->opcodes;
475-
zend_ssa_op *ssa_op = ssa->ops + op_num;
476-
477-
if (ssa_op->op1_use >= 0) {
478-
/* Reconstruct SSA */
479-
int var_num = ssa_op->op1_use;
480-
zend_ssa_var *var = ssa->vars + var_num;
481-
482-
ZEND_ASSERT(ssa_op->op1_def < 0);
483-
zend_ssa_unlink_use_chain(ssa, op_num, ssa_op->op1_use);
484-
ssa_op->op1_use = -1;
485-
ssa_op->op1_use_chain = -1;
486-
op_num = call_info->caller_call_opline - op_array->opcodes;
487-
ssa_op = ssa->ops + op_num;
488-
ssa_op->op1_use = var_num;
489-
ssa_op->op1_use_chain = var->use_chain;
490-
var->use_chain = op_num;
491-
}
492-
493461
ZVAL_ARR(&tmp, dst);
494462

495463
/* Update opcode */
496-
call_info->caller_call_opline->opcode = ZEND_IN_ARRAY;
497-
call_info->caller_call_opline->extended_value = strict;
498-
call_info->caller_call_opline->op1_type = send_needly->op1_type;
499-
call_info->caller_call_opline->op1.num = send_needly->op1.num;
500-
call_info->caller_call_opline->op2_type = IS_CONST;
501-
call_info->caller_call_opline->op2.constant = zend_optimizer_add_literal(op_array, &tmp);
502-
if (call_info->caller_init_opline->extended_value == 3) {
503-
MAKE_NOP(call_info->caller_call_opline - 1);
464+
op->opcode = ZEND_IN_ARRAY;
465+
op->extended_value = strict;
466+
op->op2_type = IS_CONST;
467+
op->op2.constant = zend_optimizer_add_literal(op_array, &tmp);
468+
if (op->extended_value == ZEND_FRAMELESS_ICALL_3) {
469+
MAKE_NOP(op + 1);
470+
removed_ops++;
504471
}
505-
MAKE_NOP(call_info->caller_init_opline);
506-
MAKE_NOP(send_needly);
507-
MAKE_NOP(send_array);
508-
removed_ops++;
509472

510-
op_num = call_info->caller_call_opline - op_array->opcodes;
511-
ssa_op = ssa->ops + op_num;
473+
uint32_t op_num = op - op_array->opcodes;
474+
zend_ssa_op *ssa_op = ssa->ops + op_num;
512475
if (ssa_op->result_def >= 0) {
513476
int var = ssa_op->result_def;
514477
int use = ssa->vars[var].use_chain;

ext/opcache/tests/gh18050.phpt

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
--TEST--
2+
GH-18050: Frameless calls break IN_ARRAY optimization
3+
--EXTENSIONS--
4+
opcache
5+
--INI--
6+
opcache.enable_cli=1
7+
opcache.optimization_level=-1
8+
opcache.opt_debug_level=0x20000
9+
--FILE--
10+
<?php
11+
12+
function test($v) {
13+
$ary = ['x', 'y'];
14+
var_dump(in_array($v, $ary));
15+
}
16+
test('x');
17+
test('z');
18+
19+
?>
20+
--EXPECTF--
21+
$_main:
22+
; (lines=%d, args=%d, vars=%d, tmps=%d)
23+
; (after optimizer)
24+
; %sgh18050.php:%s
25+
0000 INIT_FCALL 1 %d string("test")
26+
0001 SEND_VAL string("x") 1
27+
0002 DO_UCALL
28+
0003 INIT_FCALL 1 %d string("test")
29+
0004 SEND_VAL string("z") 1
30+
0005 DO_UCALL
31+
0006 RETURN int(1)
32+
33+
test:
34+
; (lines=%d, args=%d, vars=%d, tmps=%d)
35+
; (after optimizer)
36+
; %sgh18050.php:%s
37+
0000 CV0($v) = RECV 1
38+
0001 INIT_FCALL 1 %d string("var_dump")
39+
0002 T1 = IN_ARRAY 0 CV0($v) array(...)
40+
0003 SEND_VAL T1 1
41+
0004 DO_ICALL
42+
0005 RETURN null
43+
bool(true)
44+
bool(false)

0 commit comments

Comments
 (0)