diff --git a/Zend/Optimizer/dfa_pass.c b/Zend/Optimizer/dfa_pass.c index ac99455c1f808..adfb6d701f7f5 100644 --- a/Zend/Optimizer/dfa_pass.c +++ b/Zend/Optimizer/dfa_pass.c @@ -466,7 +466,10 @@ int zend_dfa_optimize_calls(zend_op_array *op_array, zend_ssa *ssa) int var_num = ssa_op->op1_use; zend_ssa_var *var = ssa->vars + var_num; - ZEND_ASSERT(ssa_op->op1_def < 0); + if (ssa_op->op1_def >= 0) { + zend_ssa_replace_op1_def_by_op1_use(ssa, ssa_op); + } + zend_ssa_unlink_use_chain(ssa, op_num, ssa_op->op1_use); ssa_op->op1_use = -1; ssa_op->op1_use_chain = -1; @@ -1066,6 +1069,50 @@ static bool zend_dfa_try_to_replace_result(zend_op_array *op_array, zend_ssa *ss return 0; } +/* Sets a flag on SEND ops when a copy can be a avoided. */ +static void zend_dfa_optimize_send_copies(zend_op_array *op_array, zend_ssa *ssa) +{ + /* func_get_args(), indirect accesses and exceptions could make the optimization observable. + * The latter two cases are already tested before applying the DFA pass. */ + if (ssa->cfg.flags & ZEND_FUNC_VARARG) { + return; + } + + for (uint32_t i = 0; i < op_array->last; i++) { + zend_op *opline = op_array->opcodes + i; + if ((opline->opcode != ZEND_SEND_VAR && opline->opcode != ZEND_SEND_VAR_EX) || opline->op2_type != IS_UNUSED || opline->op1_type != IS_CV) { + continue; + } + + zend_ssa_op *ssa_op = ssa->ops + i; + int op1_def = ssa_op->op1_def; + if (op1_def == -1) { + continue; + } + + int ssa_cv = ssa_op->op1_use; + + /* Argument move must not be observable in backtraces */ + if (ssa->vars[ssa_cv].var < op_array->num_args) { + continue; + } + + /* Unsetting a CV is always fine if it gets overwritten afterwards. + * Since type inference often infers very wide types, we are very loose in matching types. */ + uint32_t type = ssa->var_info[ssa_cv].type; + if ((type & (MAY_BE_REF|MAY_BE_UNDEF)) || !(type & MAY_BE_RC1) || !(type & (MAY_BE_STRING|MAY_BE_ARRAY))) { + continue; + } + + zend_ssa_var *ssa_var = ssa->vars + op1_def; + + if (ssa_var->no_val && !ssa_var->alias) { + /* Flag will be used by VM type spec handler */ + opline->extended_value = 1; + } + } +} + void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx, zend_ssa *ssa, zend_call_info **call_map) { if (ctx->debug_level & ZEND_DUMP_BEFORE_DFA_PASS) { @@ -1124,6 +1171,14 @@ void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx #endif } + /* Optimization should not be done on main because of globals. */ + if (op_array->function_name) { + zend_dfa_optimize_send_copies(op_array, ssa); +#if ZEND_DEBUG_DFA + ssa_verify_integrity(op_array, ssa, "after optimize send copies"); +#endif + } + for (v = op_array->last_var; v < ssa->vars_count; v++) { op_1 = ssa->vars[v].definition; diff --git a/Zend/Optimizer/sccp.c b/Zend/Optimizer/sccp.c index f6144f87b4fac..e5a390292ec52 100644 --- a/Zend/Optimizer/sccp.c +++ b/Zend/Optimizer/sccp.c @@ -98,7 +98,7 @@ typedef struct _sccp_ctx { #define MAKE_TOP(zv) (Z_TYPE_INFO_P(zv) = TOP) #define MAKE_BOT(zv) (Z_TYPE_INFO_P(zv) = BOT) -static void scp_dump_value(zval *zv) { +static void scp_dump_value(const zval *zv) { if (IS_TOP(zv)) { fprintf(stderr, " top"); } else if (IS_BOT(zv)) { @@ -1050,6 +1050,12 @@ static void sccp_visit_instr(scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_o case ZEND_SEND_VAL: case ZEND_SEND_VAR: { + SKIP_IF_TOP(op1); + + if (opline->opcode == ZEND_SEND_VAR) { + SET_RESULT(op1, op1); + } + /* If the value of a SEND for an ICALL changes, we need to reconsider the * ICALL result value. Otherwise we can ignore the opcode. */ zend_call_info *call; @@ -1058,7 +1064,7 @@ static void sccp_visit_instr(scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_o } call = ctx->call_map[opline - ctx->scdf.op_array->opcodes]; - if (IS_TOP(op1) || !call || !call->caller_call_opline + if (!call || !call->caller_call_opline || call->caller_call_opline->opcode != ZEND_DO_ICALL) { return; } @@ -2034,8 +2040,15 @@ static int remove_call(sccp_ctx *ctx, zend_op *opline, zend_ssa_op *ssa_op) &ssa->ops[call->caller_init_opline - op_array->opcodes]); for (i = 0; i < call->num_args; i++) { - zend_ssa_remove_instr(ssa, call->arg_info[i].opline, - &ssa->ops[call->arg_info[i].opline - op_array->opcodes]); + zend_op *op = call->arg_info[i].opline; + zend_ssa_op *this_ssa_op = &ssa->ops[op - op_array->opcodes]; + + /* Not necessary to check ZEND_SEND_VAR_EX, because compile time calls don't support ZEND_SEND_VAR_EX. */ + if (op->opcode == ZEND_SEND_VAR && this_ssa_op->op1_def >= 0) { + zend_ssa_replace_op1_def_by_op1_use(ssa, this_ssa_op); + } + + zend_ssa_remove_instr(ssa, op, this_ssa_op); } // TODO: remove call_info completely??? @@ -2188,6 +2201,10 @@ static int try_remove_definition(sccp_ctx *ctx, int var_num, zend_ssa_var *var, return 0; } + if (opline->opcode == ZEND_SEND_VAR) { + return 0; + } + /* Compound assign or incdec -> convert to direct ASSIGN */ if (!value) { @@ -2330,6 +2347,14 @@ static int replace_constant_operands(sccp_ctx *ctx) { FOREACH_USE(var, use) { zend_op *opline = &op_array->opcodes[use]; zend_ssa_op *ssa_op = &ssa->ops[use]; + /* Removing the def in try_remove_definition() may reduce optimisation opportunities. + * We want to keep the no_val definition until we actually replace it with a constant. + * For SEND_VAR_EX we can't replace the def because the instruction may pass an argument by ref, + * which can make the variable a ref if the def is replaced by the use. */ + if (opline->opcode == ZEND_SEND_VAR && ssa_op->op1_use == i && ssa_op->op1_def >= 0) { + zend_ssa_replace_op1_def_by_op1_use(ssa, ssa_op); + opline->extended_value = 0; + } if (try_replace_op1(ctx, opline, ssa_op, i, value)) { if (opline->opcode == ZEND_NOP) { removed_ops++; diff --git a/Zend/Optimizer/zend_dfg.c b/Zend/Optimizer/zend_dfg.c index 2207b594b85a5..73cc205cd5374 100644 --- a/Zend/Optimizer/zend_dfg.c +++ b/Zend/Optimizer/zend_dfg.c @@ -174,6 +174,10 @@ static zend_always_inline void _zend_dfg_add_use_def_op(const zend_op_array *op_ } break; case ZEND_SEND_VAR: + if (opline->op1_type == IS_CV && ((build_flags & ZEND_SSA_RC_INFERENCE) || opline->op2_type == IS_UNUSED)) { + goto add_op1_def; + } + break; case ZEND_CAST: case ZEND_QM_ASSIGN: case ZEND_JMP_SET: diff --git a/Zend/Optimizer/zend_inference.c b/Zend/Optimizer/zend_inference.c index 944283af71abc..2cb491efa5f3d 100644 --- a/Zend/Optimizer/zend_inference.c +++ b/Zend/Optimizer/zend_inference.c @@ -2950,6 +2950,10 @@ static zend_always_inline zend_result _zend_update_type_info( if (t1 & (MAY_BE_RC1|MAY_BE_REF)) { tmp |= MAY_BE_RCN; } + if ((t1 & (MAY_BE_ARRAY|MAY_BE_STRING)) && (t1 & MAY_BE_RC1) && !(t1 & (MAY_BE_UNDEF|MAY_BE_REF)) && ssa_vars[ssa_op->op1_def].no_val && !ssa_vars[ssa_op->op1_def].alias) { + /* implicit move may make value undef */ + tmp |= MAY_BE_UNDEF; + } UPDATE_SSA_TYPE(tmp, ssa_op->op1_def); COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->op1_def); } @@ -2991,6 +2995,10 @@ static zend_always_inline zend_result _zend_update_type_info( case ZEND_SEND_FUNC_ARG: if (ssa_op->op1_def >= 0) { tmp = (t1 & MAY_BE_UNDEF)|MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN|MAY_BE_ANY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF; + if (opline->opcode == ZEND_SEND_VAR_EX && (t1 & (MAY_BE_ARRAY|MAY_BE_STRING)) && (t1 & MAY_BE_RC1) && !(t1 & (MAY_BE_UNDEF|MAY_BE_REF)) && ssa_vars[ssa_op->op1_def].no_val && !ssa_vars[ssa_op->op1_def].alias) { + /* implicit move may make value undef */ + tmp |= MAY_BE_UNDEF; + } UPDATE_SSA_TYPE(tmp, ssa_op->op1_def); } break; diff --git a/Zend/Optimizer/zend_ssa.c b/Zend/Optimizer/zend_ssa.c index 67165a9b26d7a..a236ef7dfb8b6 100644 --- a/Zend/Optimizer/zend_ssa.c +++ b/Zend/Optimizer/zend_ssa.c @@ -703,6 +703,10 @@ static zend_always_inline int _zend_ssa_rename_op(const zend_op_array *op_array, } break; case ZEND_SEND_VAR: + if (opline->op1_type == IS_CV && ((build_flags & ZEND_SSA_RC_INFERENCE) || opline->op2_type == IS_UNUSED)) { + goto add_op1_def; + } + break; case ZEND_CAST: case ZEND_QM_ASSIGN: case ZEND_JMP_SET: @@ -1680,3 +1684,13 @@ void zend_ssa_rename_var_uses(zend_ssa *ssa, int old, int new, bool update_types old_var->phi_use_chain = NULL; } /* }}} */ + +void zend_ssa_replace_op1_def_by_op1_use(zend_ssa *ssa, zend_ssa_op *ssa_op) +{ + int op1_new = ssa_op->op1_use; + ZEND_ASSERT(op1_new >= 0); + ZEND_ASSERT(ssa_op->op1_def >= 0); + /* zend_ssa_rename_var_uses() clear use_chain & phi_use_chain for us */ + zend_ssa_rename_var_uses(ssa, ssa_op->op1_def, op1_new, true); + zend_ssa_remove_op1_def(ssa, ssa_op); +} diff --git a/Zend/Optimizer/zend_ssa.h b/Zend/Optimizer/zend_ssa.h index 5a6fce38d2f81..568f0cf9914aa 100644 --- a/Zend/Optimizer/zend_ssa.h +++ b/Zend/Optimizer/zend_ssa.h @@ -159,6 +159,7 @@ void zend_ssa_remove_uses_of_var(zend_ssa *ssa, int var_num); void zend_ssa_remove_block(zend_op_array *op_array, zend_ssa *ssa, int b); void zend_ssa_rename_var_uses(zend_ssa *ssa, int old_var, int new_var, bool update_types); void zend_ssa_remove_block_from_cfg(zend_ssa *ssa, int b); +void zend_ssa_replace_op1_def_by_op1_use(zend_ssa *ssa, zend_ssa_op *ssa_op); static zend_always_inline void _zend_ssa_remove_def(zend_ssa_var *var) { diff --git a/Zend/array_micro_bench.php b/Zend/array_micro_bench.php new file mode 100644 index 0000000000000..c99854d616ec7 --- /dev/null +++ b/Zend/array_micro_bench.php @@ -0,0 +1,66 @@ +op2_type == IS_UNUSED && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0, ZEND_SEND_VAR_SIMPLE, CV|VAR, NUM) +ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_SEND_VAR, op->op2_type == IS_UNUSED && !op->extended_value && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0, ZEND_SEND_VAR_SIMPLE, CV|VAR, NUM) { USE_OPLINE zval *varptr, *arg; @@ -9917,7 +9917,21 @@ ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_SEND_VAR, op->op2_type == IS_UNUSED && (op1_i ZEND_VM_NEXT_OPCODE(); } -ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_SEND_VAR_EX, op->op2_type == IS_UNUSED && op->op2.num <= MAX_ARG_FLAG_NUM && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0, ZEND_SEND_VAR_EX_SIMPLE, CV|VAR, UNUSED|NUM) +ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_SEND_VAR, op->extended_value /* extended_value implies here OP2 UNUSED and OP1 not UNDEF or REF */, ZEND_SEND_VAR_SIMPLE_EXT, CV, NUM) +{ + USE_OPLINE + zval *varptr, *arg; + + varptr = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R); + arg = ZEND_CALL_VAR(EX(call), opline->result.var); + + ZVAL_COPY_VALUE(arg, varptr); + ZVAL_UNDEF(varptr); + + ZEND_VM_NEXT_OPCODE(); +} + +ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_SEND_VAR_EX, !op->extended_value && op->op2_type == IS_UNUSED && op->op2.num <= MAX_ARG_FLAG_NUM && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0, ZEND_SEND_VAR_EX_SIMPLE, CV|VAR, UNUSED|NUM) { USE_OPLINE zval *varptr, *arg; @@ -9939,6 +9953,25 @@ ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_SEND_VAR_EX, op->op2_type == IS_UNUSED && op- ZEND_VM_NEXT_OPCODE(); } +ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_SEND_VAR_EX, op->extended_value && op->op2.num <= MAX_ARG_FLAG_NUM /* extended_value implies here OP2 UNUSED and OP1 not UNDEF or REF */, ZEND_SEND_VAR_EX_SIMPLE_EXT, CV, UNUSED|NUM) +{ + USE_OPLINE + zval *varptr, *arg; + uint32_t arg_num = opline->op2.num; + + if (QUICK_ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) { + ZEND_VM_DISPATCH_TO_HANDLER(ZEND_SEND_REF); + } + + varptr = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R); + arg = ZEND_CALL_VAR(EX(call), opline->result.var); + + ZVAL_COPY_VALUE(arg, varptr); + ZVAL_UNDEF(varptr); + + ZEND_VM_NEXT_OPCODE(); +} + ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_SEND_VAL, op->op1_type == IS_CONST && op->op2_type == IS_UNUSED && !Z_REFCOUNTED_P(RT_CONSTANT(op, op->op1)), ZEND_SEND_VAL_SIMPLE, CONST, NUM) { USE_OPLINE diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 21b927c02b895..4286880cd2f91 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -40531,6 +40531,20 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_VAR_SIMPLE_SP ZEND_VM_NEXT_OPCODE(); } +static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_VAR_SIMPLE_EXT_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + zval *varptr, *arg; + + varptr = EX_VAR(opline->op1.var); + arg = ZEND_CALL_VAR(EX(call), opline->result.var); + + ZVAL_COPY_VALUE(arg, varptr); + ZVAL_UNDEF(varptr); + + ZEND_VM_NEXT_OPCODE(); +} + static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DIV_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -49788,6 +49802,25 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_VAR_EX_SIMPLE ZEND_VM_NEXT_OPCODE(); } +static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_VAR_EX_SIMPLE_EXT_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + zval *varptr, *arg; + uint32_t arg_num = opline->op2.num; + + if (QUICK_ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) { + ZEND_VM_TAIL_CALL(ZEND_SEND_REF_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); + } + + varptr = EX_VAR(opline->op1.var); + arg = ZEND_CALL_VAR(EX(call), opline->result.var); + + ZVAL_COPY_VALUE(arg, varptr); + ZVAL_UNDEF(varptr); + + ZEND_VM_NEXT_OPCODE(); +} + static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DIV_SPEC_CV_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -56760,11 +56793,13 @@ ZEND_API void execute_ex(zend_execute_data *ex) (void*)&&ZEND_SEND_VAR_SIMPLE_SPEC_VAR_LABEL, (void*)&&ZEND_NULL_LABEL, (void*)&&ZEND_SEND_VAR_SIMPLE_SPEC_CV_LABEL, + (void*)&&ZEND_SEND_VAR_SIMPLE_EXT_SPEC_CV_LABEL, (void*)&&ZEND_NULL_LABEL, (void*)&&ZEND_NULL_LABEL, (void*)&&ZEND_SEND_VAR_EX_SIMPLE_SPEC_VAR_UNUSED_LABEL, (void*)&&ZEND_NULL_LABEL, (void*)&&ZEND_SEND_VAR_EX_SIMPLE_SPEC_CV_UNUSED_LABEL, + (void*)&&ZEND_SEND_VAR_EX_SIMPLE_EXT_SPEC_CV_UNUSED_LABEL, (void*)&&ZEND_SEND_VAL_SIMPLE_SPEC_CONST_LABEL, (void*)&&ZEND_SEND_VAL_EX_SIMPLE_SPEC_CONST_LABEL, (void*)&&ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_UNUSED_LABEL, @@ -60487,6 +60522,10 @@ ZEND_API void execute_ex(zend_execute_data *ex) VM_TRACE(ZEND_SEND_VAR_SIMPLE_SPEC_CV) ZEND_SEND_VAR_SIMPLE_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); HYBRID_BREAK(); + HYBRID_CASE(ZEND_SEND_VAR_SIMPLE_EXT_SPEC_CV): + VM_TRACE(ZEND_SEND_VAR_SIMPLE_EXT_SPEC_CV) + ZEND_SEND_VAR_SIMPLE_EXT_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + HYBRID_BREAK(); HYBRID_CASE(ZEND_DIV_SPEC_CV_CONST): VM_TRACE(ZEND_DIV_SPEC_CV_CONST) ZEND_DIV_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); @@ -61107,6 +61146,10 @@ ZEND_API void execute_ex(zend_execute_data *ex) VM_TRACE(ZEND_SEND_VAR_EX_SIMPLE_SPEC_CV_UNUSED) ZEND_SEND_VAR_EX_SIMPLE_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); HYBRID_BREAK(); + HYBRID_CASE(ZEND_SEND_VAR_EX_SIMPLE_EXT_SPEC_CV_UNUSED): + VM_TRACE(ZEND_SEND_VAR_EX_SIMPLE_EXT_SPEC_CV_UNUSED) + ZEND_SEND_VAR_EX_SIMPLE_EXT_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + HYBRID_BREAK(); HYBRID_CASE(ZEND_DIV_SPEC_CV_CV): VM_TRACE(ZEND_DIV_SPEC_CV_CV) ZEND_DIV_SPEC_CV_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); @@ -64864,11 +64907,13 @@ void zend_vm_init(void) ZEND_SEND_VAR_SIMPLE_SPEC_VAR_HANDLER, ZEND_NULL_HANDLER, ZEND_SEND_VAR_SIMPLE_SPEC_CV_HANDLER, + ZEND_SEND_VAR_SIMPLE_EXT_SPEC_CV_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_SEND_VAR_EX_SIMPLE_SPEC_VAR_UNUSED_HANDLER, ZEND_NULL_HANDLER, ZEND_SEND_VAR_EX_SIMPLE_SPEC_CV_UNUSED_HANDLER, + ZEND_SEND_VAR_EX_SIMPLE_EXT_SPEC_CV_UNUSED_HANDLER, ZEND_SEND_VAL_SIMPLE_SPEC_CONST_HANDLER, ZEND_SEND_VAL_EX_SIMPLE_SPEC_CONST_HANDLER, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_UNUSED_HANDLER, @@ -64921,7 +64966,7 @@ void zend_vm_init(void) 1255, 1256 | SPEC_RULE_OP1, 1261 | SPEC_RULE_OP1, - 3470, + 3472, 1266 | SPEC_RULE_OP1, 1271 | SPEC_RULE_OP1, 1276 | SPEC_RULE_OP2, @@ -65079,59 +65124,59 @@ void zend_vm_init(void) 2564, 2565, 2566, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, }; #if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID) zend_opcode_handler_funcs = labels; @@ -65508,17 +65553,19 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t break; case ZEND_SEND_VAL: if (op->op1_type == IS_CONST && op->op2_type == IS_UNUSED && !Z_REFCOUNTED_P(RT_CONSTANT(op, op->op1))) { - spec = 3466; + spec = 3468; } break; case ZEND_SEND_VAR_EX: - if (op->op2_type == IS_UNUSED && op->op2.num <= MAX_ARG_FLAG_NUM && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0) { - spec = 3461 | SPEC_RULE_OP1; + if (!op->extended_value && op->op2_type == IS_UNUSED && op->op2.num <= MAX_ARG_FLAG_NUM && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0) { + spec = 3462 | SPEC_RULE_OP1; + } else if (op->extended_value && op->op2.num <= MAX_ARG_FLAG_NUM /* extended_value implies here OP2 UNUSED and OP1 not UNDEF or REF */) { + spec = 3467; } break; case ZEND_FE_FETCH_R: if (op->op2_type == IS_CV && (op1_info & (MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_ARRAY) { - spec = 3468 | SPEC_RULE_RETVAL; + spec = 3470 | SPEC_RULE_RETVAL; } break; case ZEND_FETCH_DIM_R: @@ -65531,12 +65578,14 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t break; case ZEND_SEND_VAL_EX: if (op->op2_type == IS_UNUSED && op->op2.num <= MAX_ARG_FLAG_NUM && op->op1_type == IS_CONST && !Z_REFCOUNTED_P(RT_CONSTANT(op, op->op1))) { - spec = 3467; + spec = 3469; } break; case ZEND_SEND_VAR: - if (op->op2_type == IS_UNUSED && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0) { + if (op->op2_type == IS_UNUSED && !op->extended_value && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0) { spec = 3456 | SPEC_RULE_OP1; + } else if (op->extended_value /* extended_value implies here OP2 UNUSED and OP1 not UNDEF or REF */) { + spec = 3461; } break; case ZEND_BW_OR: diff --git a/Zend/zend_vm_handlers.h b/Zend/zend_vm_handlers.h index fae2138ef912e..38c1dc3eae3b3 100644 --- a/Zend/zend_vm_handlers.h +++ b/Zend/zend_vm_handlers.h @@ -1850,10 +1850,12 @@ _(3455, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \ _(3458, ZEND_SEND_VAR_SIMPLE_SPEC_VAR) \ _(3460, ZEND_SEND_VAR_SIMPLE_SPEC_CV) \ - _(3463, ZEND_SEND_VAR_EX_SIMPLE_SPEC_VAR_UNUSED) \ - _(3465, ZEND_SEND_VAR_EX_SIMPLE_SPEC_CV_UNUSED) \ - _(3466, ZEND_SEND_VAL_SIMPLE_SPEC_CONST) \ - _(3467, ZEND_SEND_VAL_EX_SIMPLE_SPEC_CONST) \ - _(3468, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_UNUSED) \ - _(3469, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_USED) \ - _(3469+1, ZEND_NULL) + _(3461, ZEND_SEND_VAR_SIMPLE_EXT_SPEC_CV) \ + _(3464, ZEND_SEND_VAR_EX_SIMPLE_SPEC_VAR_UNUSED) \ + _(3466, ZEND_SEND_VAR_EX_SIMPLE_SPEC_CV_UNUSED) \ + _(3467, ZEND_SEND_VAR_EX_SIMPLE_EXT_SPEC_CV_UNUSED) \ + _(3468, ZEND_SEND_VAL_SIMPLE_SPEC_CONST) \ + _(3469, ZEND_SEND_VAL_EX_SIMPLE_SPEC_CONST) \ + _(3470, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_UNUSED) \ + _(3471, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_USED) \ + _(3471+1, ZEND_NULL)