diff --git a/Zend/Optimizer/dfa_pass.c b/Zend/Optimizer/dfa_pass.c index ce2a19f2e8fe9..2c3aaae065997 100644 --- a/Zend/Optimizer/dfa_pass.c +++ b/Zend/Optimizer/dfa_pass.c @@ -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 + 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(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 *send_array; - zend_op *send_needly; + && zend_string_equals_literal_ci(call_info->callee_func->common.function_name, "in_array")) { + 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++; } } } diff --git a/ext/opcache/tests/gh18050.phpt b/ext/opcache/tests/gh18050.phpt new file mode 100644 index 0000000000000..3fb6c8d7b65ed --- /dev/null +++ b/ext/opcache/tests/gh18050.phpt @@ -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-- + +--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)