Skip to content

Commit e79074b

Browse files
committed
Implement INIT_FCALL IS_PTR specialization
1 parent 7b7f97b commit e79074b

25 files changed

+707
-593
lines changed

Zend/Optimizer/optimize_func_calls.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
197197
fcall->opcode = ZEND_INIT_FCALL;
198198
fcall->op1.num = zend_vm_calc_used_stack(fcall->extended_value, call_stack[call].func);
199199
literal_dtor(&ZEND_OP2_LITERAL(fcall));
200+
// FIXME: Use IS_PTR if possible
200201
fcall->op2.constant = fcall->op2.constant + 1;
201202
if (opline->opcode != ZEND_CALLABLE_CONVERT) {
202203
opline->opcode = zend_get_call_op(fcall, call_stack[call].func);
@@ -206,6 +207,7 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
206207
fcall->op1.num = zend_vm_calc_used_stack(fcall->extended_value, call_stack[call].func);
207208
literal_dtor(&op_array->literals[fcall->op2.constant]);
208209
literal_dtor(&op_array->literals[fcall->op2.constant + 2]);
210+
// FIXME: Use IS_PTR if possible
209211
fcall->op2.constant = fcall->op2.constant + 1;
210212
if (opline->opcode != ZEND_CALLABLE_CONVERT) {
211213
opline->opcode = zend_get_call_op(fcall, call_stack[call].func);

Zend/Optimizer/sccp.c

Lines changed: 14 additions & 6 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+
zval *func_zv, *args[3] = {NULL};
1651+
zend_string *name;
16511652
int i;
16521653

16531654
if (!ctx->call_map) {
@@ -1656,7 +1657,14 @@ 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+
func_zv = CT_CONSTANT_EX(ctx->scdf.op_array, call->caller_init_opline->op2.constant);
1661+
if (Z_TYPE_P(func_zv) == IS_STRING) {
1662+
name = Z_STR_P(func_zv);
1663+
} else {
1664+
ZEND_ASSERT(Z_TYPE_P(func_zv) == IS_PTR);
1665+
zend_function *func = Z_PTR_P(func_zv);
1666+
name = func->common.function_name;
1667+
}
16601668

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

1697-
if (ct_eval_func_call(scdf->op_array, &zv, Z_STR_P(name), call->num_args, args) == SUCCESS) {
1705+
if (ct_eval_func_call(scdf->op_array, &zv, name, call->num_args, args) == SUCCESS) {
16981706
SET_RESULT(result, &zv);
16991707
zval_ptr_dtor_nogc(&zv);
17001708
break;
17011709
}
17021710

17031711
#if 0
17041712
/* sort out | uniq -c | sort -n */
1705-
fprintf(stderr, "%s\n", Z_STRVAL_P(name));
1713+
fprintf(stderr, "%s\n", name);
17061714
/*if (args[1]) {
1707-
php_printf("%s %Z %Z\n", Z_STRVAL_P(name), args[0], args[1]);
1715+
php_printf("%s %Z %Z\n", name, args[0], args[1]);
17081716
} else {
1709-
php_printf("%s %Z\n", Z_STRVAL_P(name), args[0]);
1717+
php_printf("%s %Z\n", name, args[0]);
17101718
}*/
17111719
#endif
17121720

Zend/Optimizer/zend_cfg.c

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -333,19 +333,26 @@ ZEND_API void zend_build_cfg(zend_arena **arena, const zend_op_array *op_array,
333333
flags |= ZEND_FUNC_HAS_CALLS;
334334
break;
335335
case ZEND_INIT_FCALL:
336-
case ZEND_INIT_NS_FCALL_BY_NAME:
336+
case ZEND_INIT_NS_FCALL_BY_NAME: {
337337
zv = CRT_CONSTANT(opline->op2);
338338
if (opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME) {
339339
/* The third literal is the lowercased unqualified name */
340340
zv += 2;
341341
}
342-
if ((fn = zend_hash_find_ptr(EG(function_table), Z_STR_P(zv))) != NULL) {
342+
if (Z_TYPE_P(zv) == IS_STRING) {
343+
fn = zend_hash_find_ptr(EG(function_table), Z_STR_P(zv));
344+
} else {
345+
ZEND_ASSERT(Z_TYPE_P(zv) == IS_PTR);
346+
fn = Z_PTR_P(zv);
347+
}
348+
if (fn != NULL) {
343349
if (fn->type == ZEND_INTERNAL_FUNCTION) {
344350
flags |= zend_optimizer_classify_function(
345-
Z_STR_P(zv), opline->extended_value);
351+
fn->common.function_name, opline->extended_value);
346352
}
347353
}
348354
break;
355+
}
349356
case ZEND_FAST_CALL:
350357
BB_START(OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes);
351358
BB_START(i + 1);

Zend/Optimizer/zend_dump.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -679,6 +679,15 @@ 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_FCALL) {
683+
zval *func_zv = CRT_CONSTANT(opline->op2);
684+
if (Z_TYPE_P(func_zv) == IS_STRING) {
685+
zend_dump_const(op);
686+
} else {
687+
ZEND_ASSERT(Z_TYPE_P(func_zv) == IS_PTR);
688+
zend_function *func = Z_PTR_P(func_zv);
689+
fprintf(stderr, " (%s)", ZSTR_VAL(func->common.function_name));
690+
}
682691
} else {
683692
zend_dump_const(op);
684693
}

Zend/Optimizer/zend_optimizer.c

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,7 @@ bool zend_optimizer_update_op2_const(zend_op_array *op_array,
485485
}
486486
break;
487487
case ZEND_INIT_FCALL:
488+
ZEND_ASSERT(opline->op2_type == IS_CONST && Z_TYPE_P(CRT_CONSTANT(opline->op2)) == IS_STRING);
488489
REQUIRES_STRING(val);
489490
if (Z_REFCOUNT_P(val) == 1) {
490491
zend_str_tolower(Z_STRVAL_P(val), Z_STRLEN_P(val));
@@ -838,7 +839,12 @@ zend_function *zend_optimizer_get_called_func(
838839
switch (opline->opcode) {
839840
case ZEND_INIT_FCALL:
840841
{
841-
zend_string *function_name = Z_STR_P(CRT_CONSTANT(opline->op2));
842+
zval *func_zv = CRT_CONSTANT(opline->op2);
843+
if (Z_TYPE_P(func_zv) == IS_PTR) {
844+
return Z_PTR_P(func_zv);
845+
}
846+
ZEND_ASSERT(Z_TYPE_P(func_zv) == IS_STRING);
847+
zend_string *function_name = Z_STR_P(func_zv);
842848
zend_function *func;
843849
if (script && (func = zend_hash_find_ptr(&script->function_table, function_name)) != NULL) {
844850
return func;
@@ -1217,6 +1223,11 @@ static void zend_redo_pass_two(zend_op_array *op_array)
12171223
break;
12181224
}
12191225
ZEND_VM_SET_OPCODE_HANDLER(opline);
1226+
/* INIT_FCALL with IS_PTR *must* use the PTR specialization, because the default spec doesn't
1227+
* handle IS_PTR. */
1228+
if (opline->opcode == ZEND_INIT_FCALL && Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) == IS_PTR) {
1229+
zend_vm_set_opcode_handler_ex(opline, IS_UNUSED, IS_CONST, IS_UNUSED);
1230+
}
12201231
opline++;
12211232
}
12221233

@@ -1391,9 +1402,13 @@ static void zend_adjust_fcall_stack_size(zend_op_array *op_array, zend_optimizer
13911402
end = opline + op_array->last;
13921403
while (opline < end) {
13931404
if (opline->opcode == ZEND_INIT_FCALL) {
1394-
func = zend_hash_find_ptr(
1395-
&ctx->script->function_table,
1396-
Z_STR_P(RT_CONSTANT(opline, opline->op2)));
1405+
zval *func_zv = RT_CONSTANT(opline, opline->op2);
1406+
if (Z_TYPE_P(func_zv) == IS_STRING) {
1407+
func = zend_hash_find_ptr(&ctx->script->function_table, Z_STR_P(func_zv));
1408+
} else {
1409+
ZEND_ASSERT(Z_TYPE_P(func_zv) == IS_PTR);
1410+
func = Z_PTR_P(func_zv);
1411+
}
13971412
if (func) {
13981413
opline->op1.num = zend_vm_calc_used_stack(opline->extended_value, func);
13991414
}

Zend/zend_compile.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4837,7 +4837,13 @@ static void zend_compile_call(znode *result, zend_ast *ast, uint32_t type) /* {{
48374837
}
48384838

48394839
zval_ptr_dtor(&name_node.u.constant);
4840-
ZVAL_NEW_STR(&name_node.u.constant, lcname);
4840+
if (CG(compiler_options) & ZEND_COMPILE_WITH_INIT_FCALL_PTR
4841+
&& fbc->type == ZEND_INTERNAL_FUNCTION) {
4842+
zend_string_release_ex(lcname, 0);
4843+
ZVAL_PTR(&name_node.u.constant, fbc);
4844+
} else {
4845+
ZVAL_NEW_STR(&name_node.u.constant, lcname);
4846+
}
48414847

48424848
opline = zend_emit_op(NULL, ZEND_INIT_FCALL, NULL, &name_node);
48434849
opline->result.num = zend_alloc_cache_slot();

Zend/zend_compile.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1230,6 +1230,9 @@ END_EXTERN_C()
12301230
/* ignore observer notifications, e.g. to manually notify afterwards in a post-processing step after compilation */
12311231
#define ZEND_COMPILE_IGNORE_OBSERVER (1<<18)
12321232

1233+
/* Allow storing IS_PTR to function in INIT_ICALL op2. */
1234+
#define ZEND_COMPILE_WITH_INIT_FCALL_PTR (1<<19)
1235+
12331236
/* The default value for CG(compiler_options) */
12341237
#define ZEND_COMPILE_DEFAULT ZEND_COMPILE_HANDLE_OP_ARRAY
12351238

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_TYPE_SPEC_HANDLER(ZEND_INIT_FCALL, Z_TYPE_P(RT_CONSTANT(op, op->op2)) == IS_PTR, ZEND_INIT_FCALL_PTR, NUM, CONST, NUM|CACHE_SLOT)
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)