Skip to content

Commit 1789c86

Browse files
committed
Add ZEND_INIT_ICALL instruction
1 parent e72f0c8 commit 1789c86

35 files changed

+713
-600
lines changed

Zend/Optimizer/optimize_func_calls.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ static void zend_delete_call_instructions(zend_op_array *op_array, zend_op *opli
4848
case ZEND_INIT_STATIC_METHOD_CALL:
4949
case ZEND_INIT_METHOD_CALL:
5050
case ZEND_INIT_FCALL:
51+
case ZEND_INIT_ICALL:
5152
if (call == 0) {
5253
MAKE_NOP(opline);
5354
return;
@@ -168,6 +169,7 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
168169
case ZEND_INIT_STATIC_METHOD_CALL:
169170
case ZEND_INIT_METHOD_CALL:
170171
case ZEND_INIT_FCALL:
172+
case ZEND_INIT_ICALL:
171173
case ZEND_NEW:
172174
/* The argument passing optimizations are valid for prototypes as well,
173175
* as inheritance cannot change between ref <-> non-ref arguments. */
@@ -191,7 +193,7 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
191193
if (call_stack[call].func && call_stack[call].opline) {
192194
zend_op *fcall = call_stack[call].opline;
193195

194-
if (fcall->opcode == ZEND_INIT_FCALL) {
196+
if (fcall->opcode == ZEND_INIT_FCALL || fcall->opcode == ZEND_INIT_ICALL) {
195197
/* nothing to do */
196198
} else if (fcall->opcode == ZEND_INIT_FCALL_BY_NAME) {
197199
fcall->opcode = ZEND_INIT_FCALL;

Zend/Optimizer/pass1.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
214214
while (init_opline->opcode == ZEND_NOP) {
215215
init_opline--;
216216
}
217-
if (init_opline->opcode != ZEND_INIT_FCALL ||
217+
if ((init_opline->opcode != ZEND_INIT_FCALL && init_opline->opcode != ZEND_INIT_ICALL) ||
218218
init_opline->op2_type != IS_CONST ||
219219
Z_TYPE(ZEND_OP2_LITERAL(init_opline)) != IS_STRING) {
220220
/* don't collect constants after unknown function call */

Zend/Optimizer/sccp.c

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1647,7 +1647,8 @@ static void sccp_visit_instr(scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_o
16471647
case ZEND_DO_ICALL:
16481648
{
16491649
zend_call_info *call;
1650-
zval *name, *args[3] = {NULL};
1650+
zend_string *name;
1651+
zval *args[3] = {NULL};
16511652
int i;
16521653

16531654
if (!ctx->call_map) {
@@ -1656,7 +1657,13 @@ static void sccp_visit_instr(scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_o
16561657
}
16571658

16581659
call = ctx->call_map[opline - ctx->scdf.op_array->opcodes];
1659-
name = CT_CONSTANT_EX(ctx->scdf.op_array, call->caller_init_opline->op2.constant);
1660+
if (call->caller_init_opline->opcode == ZEND_INIT_ICALL) {
1661+
zend_function *func = Z_PTR_P(CT_CONSTANT_EX(ctx->scdf.op_array, call->caller_init_opline->op2.constant));
1662+
name = func->internal_function.function_name;
1663+
} else {
1664+
ZEND_ASSERT(call->caller_init_opline->opcode == ZEND_INIT_FCALL);
1665+
name = Z_STR_P(CT_CONSTANT_EX(ctx->scdf.op_array, call->caller_init_opline->op2.constant));
1666+
}
16601667

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

1697-
if (ct_eval_func_call(scdf->op_array, &zv, Z_STR_P(name), call->num_args, args) == SUCCESS) {
1704+
if (ct_eval_func_call(scdf->op_array, &zv, name, call->num_args, args) == SUCCESS) {
16981705
SET_RESULT(result, &zv);
16991706
zval_ptr_dtor_nogc(&zv);
17001707
break;

Zend/Optimizer/zend_call_graph.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ ZEND_API void zend_analyze_calls(zend_arena **arena, zend_script *script, uint32
5959
while (opline != end) {
6060
switch (opline->opcode) {
6161
case ZEND_INIT_FCALL:
62+
case ZEND_INIT_ICALL:
6263
case ZEND_INIT_METHOD_CALL:
6364
case ZEND_INIT_STATIC_METHOD_CALL:
6465
call_stack[call] = call_info;

Zend/Optimizer/zend_cfg.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,10 @@ ZEND_API void zend_build_cfg(zend_arena **arena, const zend_op_array *op_array,
346346
}
347347
}
348348
break;
349+
case ZEND_INIT_ICALL:
350+
fn = Z_PTR_P(CRT_CONSTANT(opline->op2));
351+
flags |= zend_optimizer_classify_function(fn->common.function_name, opline->extended_value);
352+
break;
349353
case ZEND_FAST_CALL:
350354
BB_START(OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes);
351355
BB_START(i + 1);

Zend/Optimizer/zend_dump.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -679,6 +679,9 @@ ZEND_API void zend_dump_op(const zend_op_array *op_array, const zend_basic_block
679679
}
680680
} ZEND_HASH_FOREACH_END();
681681
fprintf(stderr, " default:");
682+
} else if (opline->opcode == ZEND_INIT_ICALL) {
683+
zend_function *func = Z_PTR_P(CRT_CONSTANT(opline->op2));
684+
fprintf(stderr, " (%s)", ZSTR_VAL(func->common.function_name));
682685
} else {
683686
zend_dump_const(op);
684687
}

Zend/Optimizer/zend_inference.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5032,6 +5032,7 @@ ZEND_API bool zend_may_throw_ex(const zend_op *opline, const zend_ssa_op *ssa_op
50325032
/* May throw for named params. */
50335033
return opline->op2_type == IS_CONST;
50345034
case ZEND_INIT_FCALL:
5035+
case ZEND_INIT_ICALL:
50355036
/* can't throw, because call is resolved at compile time */
50365037
return 0;
50375038
case ZEND_BIND_GLOBAL:

Zend/Optimizer/zend_optimizer.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -853,6 +853,8 @@ zend_function *zend_optimizer_get_called_func(
853853
}
854854
break;
855855
}
856+
case ZEND_INIT_ICALL:
857+
return Z_PTR_P(CRT_CONSTANT(opline->op2));
856858
case ZEND_INIT_FCALL_BY_NAME:
857859
case ZEND_INIT_NS_FCALL_BY_NAME:
858860
if (opline->op2_type == IS_CONST && Z_TYPE_P(CRT_CONSTANT(opline->op2)) == IS_STRING) {
@@ -1390,6 +1392,7 @@ static void zend_adjust_fcall_stack_size(zend_op_array *op_array, zend_optimizer
13901392
opline = op_array->opcodes;
13911393
end = opline + op_array->last;
13921394
while (opline < end) {
1395+
/* INIT_ICALL stack size will never change. */
13931396
if (opline->opcode == ZEND_INIT_FCALL) {
13941397
func = zend_hash_find_ptr(
13951398
&ctx->script->function_table,
@@ -1412,6 +1415,7 @@ static void zend_adjust_fcall_stack_size_graph(zend_op_array *op_array)
14121415
while (call_info) {
14131416
zend_op *opline = call_info->caller_init_opline;
14141417

1418+
/* INIT_ICALL stack size will never change. */
14151419
if (opline && call_info->callee_func && opline->opcode == ZEND_INIT_FCALL) {
14161420
ZEND_ASSERT(!call_info->is_prototype);
14171421
opline->op1.num = zend_vm_calc_used_stack(opline->extended_value, call_info->callee_func);

Zend/zend_compile.c

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3857,7 +3857,7 @@ ZEND_API uint8_t zend_get_call_op(const zend_op *init_op, zend_function *fbc) /*
38573857
if (fbc) {
38583858
ZEND_ASSERT(!(fbc->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE));
38593859
if (fbc->type == ZEND_INTERNAL_FUNCTION && !(CG(compiler_options) & ZEND_COMPILE_IGNORE_INTERNAL_FUNCTIONS)) {
3860-
if (init_op->opcode == ZEND_INIT_FCALL && !zend_execute_internal) {
3860+
if ((init_op->opcode == ZEND_INIT_FCALL || init_op->opcode == ZEND_INIT_ICALL) && !zend_execute_internal) {
38613861
if (!(fbc->common.fn_flags & ZEND_ACC_DEPRECATED)) {
38623862
return ZEND_DO_ICALL;
38633863
} else {
@@ -3892,7 +3892,7 @@ static bool zend_compile_call_common(znode *result, zend_ast *args_ast, zend_fun
38923892
zend_error_noreturn(E_COMPILE_ERROR, "Cannot create Closure for new expression");
38933893
}
38943894

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

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

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

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

4840-
zval_ptr_dtor(&name_node.u.constant);
4841-
ZVAL_NEW_STR(&name_node.u.constant, lcname);
4842-
4843-
opline = zend_emit_op(NULL, ZEND_INIT_FCALL, NULL, &name_node);
4844-
opline->result.num = zend_alloc_cache_slot();
4840+
#ifdef PHP_WIN32
4841+
/* May not use INIT_ICALL on Windows due to ASLR. */
4842+
bool use_init_icall = false;
4843+
#else
4844+
bool use_init_icall = fbc->type == ZEND_INTERNAL_FUNCTION
4845+
&& !(CG(compiler_options) & ZEND_COMPILE_WITH_FILE_CACHE);
4846+
#endif
4847+
if (use_init_icall) {
4848+
zend_string_release_ex(lcname, 0);
4849+
zval_ptr_dtor(&name_node.u.constant);
4850+
ZVAL_PTR(&name_node.u.constant, fbc);
4851+
opline = zend_emit_op(NULL, ZEND_INIT_ICALL, NULL, &name_node);
4852+
} else {
4853+
zval_ptr_dtor(&name_node.u.constant);
4854+
ZVAL_NEW_STR(&name_node.u.constant, lcname);
4855+
opline = zend_emit_op(NULL, ZEND_INIT_FCALL, NULL, &name_node);
4856+
opline->result.num = zend_alloc_cache_slot();
4857+
}
48454858

48464859
zend_compile_call_common(result, args_ast, fbc, ast->lineno);
48474860
}

Zend/zend_execute.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4210,6 +4210,7 @@ ZEND_API void zend_unfinished_calls_gc(zend_execute_data *execute_data, zend_exe
42104210
uint32_t num_args;
42114211

42124212
if (UNEXPECTED(opline->opcode == ZEND_INIT_FCALL ||
4213+
opline->opcode == ZEND_INIT_ICALL ||
42134214
opline->opcode == ZEND_INIT_FCALL_BY_NAME ||
42144215
opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME ||
42154216
opline->opcode == ZEND_INIT_DYNAMIC_CALL ||
@@ -4235,6 +4236,7 @@ ZEND_API void zend_unfinished_calls_gc(zend_execute_data *execute_data, zend_exe
42354236
level++;
42364237
break;
42374238
case ZEND_INIT_FCALL:
4239+
case ZEND_INIT_ICALL:
42384240
case ZEND_INIT_FCALL_BY_NAME:
42394241
case ZEND_INIT_NS_FCALL_BY_NAME:
42404242
case ZEND_INIT_DYNAMIC_CALL:
@@ -4290,6 +4292,7 @@ ZEND_API void zend_unfinished_calls_gc(zend_execute_data *execute_data, zend_exe
42904292
level++;
42914293
break;
42924294
case ZEND_INIT_FCALL:
4295+
case ZEND_INIT_ICALL:
42934296
case ZEND_INIT_FCALL_BY_NAME:
42944297
case ZEND_INIT_NS_FCALL_BY_NAME:
42954298
case ZEND_INIT_DYNAMIC_CALL:
@@ -4341,6 +4344,7 @@ static void cleanup_unfinished_calls(zend_execute_data *execute_data, uint32_t o
43414344
int do_exit;
43424345

43434346
if (UNEXPECTED(opline->opcode == ZEND_INIT_FCALL ||
4347+
opline->opcode == ZEND_INIT_ICALL ||
43444348
opline->opcode == ZEND_INIT_FCALL_BY_NAME ||
43454349
opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME ||
43464350
opline->opcode == ZEND_INIT_DYNAMIC_CALL ||
@@ -4368,6 +4372,7 @@ static void cleanup_unfinished_calls(zend_execute_data *execute_data, uint32_t o
43684372
level++;
43694373
break;
43704374
case ZEND_INIT_FCALL:
4375+
case ZEND_INIT_ICALL:
43714376
case ZEND_INIT_FCALL_BY_NAME:
43724377
case ZEND_INIT_NS_FCALL_BY_NAME:
43734378
case ZEND_INIT_DYNAMIC_CALL:
@@ -4423,6 +4428,7 @@ static void cleanup_unfinished_calls(zend_execute_data *execute_data, uint32_t o
44234428
level++;
44244429
break;
44254430
case ZEND_INIT_FCALL:
4431+
case ZEND_INIT_ICALL:
44264432
case ZEND_INIT_FCALL_BY_NAME:
44274433
case ZEND_INIT_NS_FCALL_BY_NAME:
44284434
case ZEND_INIT_DYNAMIC_CALL:

Zend/zend_opcode.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -771,6 +771,7 @@ static void emit_live_range(
771771
start++;
772772
switch (def_opline->opcode) {
773773
case ZEND_INIT_FCALL:
774+
case ZEND_INIT_ICALL:
774775
case ZEND_INIT_FCALL_BY_NAME:
775776
case ZEND_INIT_NS_FCALL_BY_NAME:
776777
case ZEND_INIT_DYNAMIC_CALL:

Zend/zend_vm_def.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3959,6 +3959,22 @@ ZEND_VM_HOT_HANDLER(61, ZEND_INIT_FCALL, NUM, CONST, NUM|CACHE_SLOT)
39593959
ZEND_VM_NEXT_OPCODE();
39603960
}
39613961

3962+
ZEND_VM_HOT_HANDLER(209, ZEND_INIT_ICALL, NUM, CONST, NUM)
3963+
{
3964+
USE_OPLINE
3965+
zend_function *fbc;
3966+
zend_execute_data *call;
3967+
3968+
fbc = Z_PTR_P(RT_CONSTANT(opline, opline->op2));
3969+
call = _zend_vm_stack_push_call_frame_ex(
3970+
opline->op1.num, ZEND_CALL_NESTED_FUNCTION,
3971+
fbc, opline->extended_value, NULL);
3972+
call->prev_execute_data = EX(call);
3973+
EX(call) = call;
3974+
3975+
ZEND_VM_NEXT_OPCODE();
3976+
}
3977+
39623978
ZEND_VM_HOT_HANDLER(129, ZEND_DO_ICALL, ANY, ANY, SPEC(RETVAL,OBSERVER))
39633979
{
39643980
USE_OPLINE

0 commit comments

Comments
 (0)