Skip to content

Add ZEND_INIT_ICALL instruction #13445

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Zend/Optimizer/optimize_func_calls.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ static void zend_delete_call_instructions(zend_op_array *op_array, zend_op *opli
case ZEND_INIT_STATIC_METHOD_CALL:
case ZEND_INIT_METHOD_CALL:
case ZEND_INIT_FCALL:
case ZEND_INIT_ICALL:
if (call == 0) {
MAKE_NOP(opline);
return;
Expand Down Expand Up @@ -168,6 +169,7 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
case ZEND_INIT_STATIC_METHOD_CALL:
case ZEND_INIT_METHOD_CALL:
case ZEND_INIT_FCALL:
case ZEND_INIT_ICALL:
case ZEND_NEW:
/* The argument passing optimizations are valid for prototypes as well,
* as inheritance cannot change between ref <-> non-ref arguments. */
Expand All @@ -191,7 +193,7 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
if (call_stack[call].func && call_stack[call].opline) {
zend_op *fcall = call_stack[call].opline;

if (fcall->opcode == ZEND_INIT_FCALL) {
if (fcall->opcode == ZEND_INIT_FCALL || fcall->opcode == ZEND_INIT_ICALL) {
/* nothing to do */
} else if (fcall->opcode == ZEND_INIT_FCALL_BY_NAME) {
fcall->opcode = ZEND_INIT_FCALL;
Expand Down
2 changes: 1 addition & 1 deletion Zend/Optimizer/pass1.c
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
while (init_opline->opcode == ZEND_NOP) {
init_opline--;
}
if (init_opline->opcode != ZEND_INIT_FCALL ||
if ((init_opline->opcode != ZEND_INIT_FCALL && init_opline->opcode != ZEND_INIT_ICALL) ||
init_opline->op2_type != IS_CONST ||
Z_TYPE(ZEND_OP2_LITERAL(init_opline)) != IS_STRING) {
/* don't collect constants after unknown function call */
Expand Down
13 changes: 10 additions & 3 deletions Zend/Optimizer/sccp.c
Original file line number Diff line number Diff line change
Expand Up @@ -1647,7 +1647,8 @@ static void sccp_visit_instr(scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_o
case ZEND_DO_ICALL:
{
zend_call_info *call;
zval *name, *args[3] = {NULL};
zend_string *name;
zval *args[3] = {NULL};
int i;

if (!ctx->call_map) {
Expand All @@ -1656,7 +1657,13 @@ 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];
name = CT_CONSTANT_EX(ctx->scdf.op_array, call->caller_init_opline->op2.constant);
if (call->caller_init_opline->opcode == ZEND_INIT_ICALL) {
zend_function *func = Z_PTR_P(CT_CONSTANT_EX(ctx->scdf.op_array, call->caller_init_opline->op2.constant));
name = func->internal_function.function_name;
} else {
ZEND_ASSERT(call->caller_init_opline->opcode == ZEND_INIT_FCALL);
name = Z_STR_P(CT_CONSTANT_EX(ctx->scdf.op_array, call->caller_init_opline->op2.constant));
}

/* We already know it can't be evaluated, don't bother checking again */
if (ssa_op->result_def < 0 || IS_BOT(&ctx->values[ssa_op->result_def])) {
Expand Down Expand Up @@ -1694,7 +1701,7 @@ static void sccp_visit_instr(scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_o
break;
}

if (ct_eval_func_call(scdf->op_array, &zv, Z_STR_P(name), call->num_args, args) == SUCCESS) {
if (ct_eval_func_call(scdf->op_array, &zv, name, call->num_args, args) == SUCCESS) {
SET_RESULT(result, &zv);
zval_ptr_dtor_nogc(&zv);
break;
Expand Down
1 change: 1 addition & 0 deletions Zend/Optimizer/zend_call_graph.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ ZEND_API void zend_analyze_calls(zend_arena **arena, zend_script *script, uint32
while (opline != end) {
switch (opline->opcode) {
case ZEND_INIT_FCALL:
case ZEND_INIT_ICALL:
case ZEND_INIT_METHOD_CALL:
case ZEND_INIT_STATIC_METHOD_CALL:
call_stack[call] = call_info;
Expand Down
4 changes: 4 additions & 0 deletions Zend/Optimizer/zend_cfg.c
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,10 @@ ZEND_API void zend_build_cfg(zend_arena **arena, const zend_op_array *op_array,
}
}
break;
case ZEND_INIT_ICALL:
fn = Z_PTR_P(CRT_CONSTANT(opline->op2));
flags |= zend_optimizer_classify_function(fn->common.function_name, opline->extended_value);
break;
case ZEND_FAST_CALL:
BB_START(OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes);
BB_START(i + 1);
Expand Down
3 changes: 3 additions & 0 deletions Zend/Optimizer/zend_dump.c
Original file line number Diff line number Diff line change
Expand Up @@ -679,6 +679,9 @@ ZEND_API void zend_dump_op(const zend_op_array *op_array, const zend_basic_block
}
} ZEND_HASH_FOREACH_END();
fprintf(stderr, " default:");
} else if (opline->opcode == ZEND_INIT_ICALL) {
zend_function *func = Z_PTR_P(CRT_CONSTANT(opline->op2));
fprintf(stderr, " (%s)", ZSTR_VAL(func->common.function_name));
} else {
zend_dump_const(op);
}
Expand Down
1 change: 1 addition & 0 deletions Zend/Optimizer/zend_inference.c
Original file line number Diff line number Diff line change
Expand Up @@ -5032,6 +5032,7 @@ ZEND_API bool zend_may_throw_ex(const zend_op *opline, const zend_ssa_op *ssa_op
/* May throw for named params. */
return opline->op2_type == IS_CONST;
case ZEND_INIT_FCALL:
case ZEND_INIT_ICALL:
/* can't throw, because call is resolved at compile time */
return 0;
case ZEND_BIND_GLOBAL:
Expand Down
4 changes: 4 additions & 0 deletions Zend/Optimizer/zend_optimizer.c
Original file line number Diff line number Diff line change
Expand Up @@ -853,6 +853,8 @@ zend_function *zend_optimizer_get_called_func(
}
break;
}
case ZEND_INIT_ICALL:
return Z_PTR_P(CRT_CONSTANT(opline->op2));
case ZEND_INIT_FCALL_BY_NAME:
case ZEND_INIT_NS_FCALL_BY_NAME:
if (opline->op2_type == IS_CONST && Z_TYPE_P(CRT_CONSTANT(opline->op2)) == IS_STRING) {
Expand Down Expand Up @@ -1390,6 +1392,7 @@ static void zend_adjust_fcall_stack_size(zend_op_array *op_array, zend_optimizer
opline = op_array->opcodes;
end = opline + op_array->last;
while (opline < end) {
/* INIT_ICALL stack size will never change. */
if (opline->opcode == ZEND_INIT_FCALL) {
func = zend_hash_find_ptr(
&ctx->script->function_table,
Expand All @@ -1412,6 +1415,7 @@ static void zend_adjust_fcall_stack_size_graph(zend_op_array *op_array)
while (call_info) {
zend_op *opline = call_info->caller_init_opline;

/* INIT_ICALL stack size will never change. */
if (opline && call_info->callee_func && opline->opcode == ZEND_INIT_FCALL) {
ZEND_ASSERT(!call_info->is_prototype);
opline->op1.num = zend_vm_calc_used_stack(opline->extended_value, call_info->callee_func);
Expand Down
29 changes: 21 additions & 8 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -3857,7 +3857,7 @@ ZEND_API uint8_t zend_get_call_op(const zend_op *init_op, zend_function *fbc) /*
if (fbc) {
ZEND_ASSERT(!(fbc->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE));
if (fbc->type == ZEND_INTERNAL_FUNCTION && !(CG(compiler_options) & ZEND_COMPILE_IGNORE_INTERNAL_FUNCTIONS)) {
if (init_op->opcode == ZEND_INIT_FCALL && !zend_execute_internal) {
if ((init_op->opcode == ZEND_INIT_FCALL || init_op->opcode == ZEND_INIT_ICALL) && !zend_execute_internal) {
if (!(fbc->common.fn_flags & ZEND_ACC_DEPRECATED)) {
return ZEND_DO_ICALL;
} else {
Expand Down Expand Up @@ -3892,7 +3892,7 @@ static bool zend_compile_call_common(znode *result, zend_ast *args_ast, zend_fun
zend_error_noreturn(E_COMPILE_ERROR, "Cannot create Closure for new expression");
}

if (opline->opcode == ZEND_INIT_FCALL) {
if (opline->opcode == ZEND_INIT_FCALL || opline->opcode == ZEND_INIT_ICALL) {
opline->op1.num = zend_vm_calc_used_stack(0, fbc);
}

Expand All @@ -3908,7 +3908,7 @@ static bool zend_compile_call_common(znode *result, zend_ast *args_ast, zend_fun
opline = &CG(active_op_array)->opcodes[opnum_init];
opline->extended_value = arg_count;

if (opline->opcode == ZEND_INIT_FCALL) {
if (opline->opcode == ZEND_INIT_FCALL || opline->opcode == ZEND_INIT_ICALL) {
opline->op1.num = zend_vm_calc_used_stack(arg_count, fbc);
}

Expand Down Expand Up @@ -4837,11 +4837,24 @@ static void zend_compile_call(znode *result, zend_ast *ast, uint32_t type) /* {{
return;
}

zval_ptr_dtor(&name_node.u.constant);
ZVAL_NEW_STR(&name_node.u.constant, lcname);

opline = zend_emit_op(NULL, ZEND_INIT_FCALL, NULL, &name_node);
opline->result.num = zend_alloc_cache_slot();
#ifdef PHP_WIN32
/* May not use INIT_ICALL on Windows due to ASLR. */
bool use_init_icall = false;
#else
bool use_init_icall = fbc->type == ZEND_INTERNAL_FUNCTION
&& !(CG(compiler_options) & ZEND_COMPILE_WITH_FILE_CACHE);
#endif
if (use_init_icall) {
zend_string_release_ex(lcname, 0);
zval_ptr_dtor(&name_node.u.constant);
ZVAL_PTR(&name_node.u.constant, fbc);
opline = zend_emit_op(NULL, ZEND_INIT_ICALL, NULL, &name_node);
} else {
zval_ptr_dtor(&name_node.u.constant);
ZVAL_NEW_STR(&name_node.u.constant, lcname);
opline = zend_emit_op(NULL, ZEND_INIT_FCALL, NULL, &name_node);
opline->result.num = zend_alloc_cache_slot();
}

zend_compile_call_common(result, args_ast, fbc, ast->lineno);
}
Expand Down
6 changes: 6 additions & 0 deletions Zend/zend_execute.c
Original file line number Diff line number Diff line change
Expand Up @@ -4210,6 +4210,7 @@ ZEND_API void zend_unfinished_calls_gc(zend_execute_data *execute_data, zend_exe
uint32_t num_args;

if (UNEXPECTED(opline->opcode == ZEND_INIT_FCALL ||
opline->opcode == ZEND_INIT_ICALL ||
opline->opcode == ZEND_INIT_FCALL_BY_NAME ||
opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME ||
opline->opcode == ZEND_INIT_DYNAMIC_CALL ||
Expand All @@ -4235,6 +4236,7 @@ ZEND_API void zend_unfinished_calls_gc(zend_execute_data *execute_data, zend_exe
level++;
break;
case ZEND_INIT_FCALL:
case ZEND_INIT_ICALL:
case ZEND_INIT_FCALL_BY_NAME:
case ZEND_INIT_NS_FCALL_BY_NAME:
case ZEND_INIT_DYNAMIC_CALL:
Expand Down Expand Up @@ -4290,6 +4292,7 @@ ZEND_API void zend_unfinished_calls_gc(zend_execute_data *execute_data, zend_exe
level++;
break;
case ZEND_INIT_FCALL:
case ZEND_INIT_ICALL:
case ZEND_INIT_FCALL_BY_NAME:
case ZEND_INIT_NS_FCALL_BY_NAME:
case ZEND_INIT_DYNAMIC_CALL:
Expand Down Expand Up @@ -4341,6 +4344,7 @@ static void cleanup_unfinished_calls(zend_execute_data *execute_data, uint32_t o
int do_exit;

if (UNEXPECTED(opline->opcode == ZEND_INIT_FCALL ||
opline->opcode == ZEND_INIT_ICALL ||
opline->opcode == ZEND_INIT_FCALL_BY_NAME ||
opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME ||
opline->opcode == ZEND_INIT_DYNAMIC_CALL ||
Expand Down Expand Up @@ -4368,6 +4372,7 @@ static void cleanup_unfinished_calls(zend_execute_data *execute_data, uint32_t o
level++;
break;
case ZEND_INIT_FCALL:
case ZEND_INIT_ICALL:
case ZEND_INIT_FCALL_BY_NAME:
case ZEND_INIT_NS_FCALL_BY_NAME:
case ZEND_INIT_DYNAMIC_CALL:
Expand Down Expand Up @@ -4423,6 +4428,7 @@ static void cleanup_unfinished_calls(zend_execute_data *execute_data, uint32_t o
level++;
break;
case ZEND_INIT_FCALL:
case ZEND_INIT_ICALL:
case ZEND_INIT_FCALL_BY_NAME:
case ZEND_INIT_NS_FCALL_BY_NAME:
case ZEND_INIT_DYNAMIC_CALL:
Expand Down
1 change: 1 addition & 0 deletions Zend/zend_opcode.c
Original file line number Diff line number Diff line change
Expand Up @@ -771,6 +771,7 @@ static void emit_live_range(
start++;
switch (def_opline->opcode) {
case ZEND_INIT_FCALL:
case ZEND_INIT_ICALL:
case ZEND_INIT_FCALL_BY_NAME:
case ZEND_INIT_NS_FCALL_BY_NAME:
case ZEND_INIT_DYNAMIC_CALL:
Expand Down
16 changes: 16 additions & 0 deletions Zend/zend_vm_def.h
Original file line number Diff line number Diff line change
Expand Up @@ -3959,6 +3959,22 @@ ZEND_VM_HOT_HANDLER(61, ZEND_INIT_FCALL, NUM, CONST, NUM|CACHE_SLOT)
ZEND_VM_NEXT_OPCODE();
}

ZEND_VM_HOT_HANDLER(209, ZEND_INIT_ICALL, NUM, CONST, NUM)
{
USE_OPLINE
zend_function *fbc;
zend_execute_data *call;

fbc = Z_PTR_P(RT_CONSTANT(opline, opline->op2));
call = _zend_vm_stack_push_call_frame_ex(
opline->op1.num, ZEND_CALL_NESTED_FUNCTION,
fbc, opline->extended_value, NULL);
call->prev_execute_data = EX(call);
EX(call) = call;

ZEND_VM_NEXT_OPCODE();
}

ZEND_VM_HOT_HANDLER(129, ZEND_DO_ICALL, ANY, ANY, SPEC(RETVAL,OBSERVER))
{
USE_OPLINE
Expand Down
Loading