Skip to content

Commit bbae5f4

Browse files
committed
Implement INIT_METHOD_CALL_PTR
1 parent c8e8970 commit bbae5f4

17 files changed

+1033
-564
lines changed

Zend/Optimizer/compact_literals.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,14 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
189189
LITERAL_INFO(opline->op2.constant, 2);
190190
}
191191
break;
192+
case ZEND_INIT_METHOD_CALL_PTR:
193+
if (opline->op1_type == IS_CONST) {
194+
LITERAL_INFO(opline->op1.constant, 1);
195+
}
196+
if (opline->op2_type == IS_CONST) {
197+
LITERAL_INFO(opline->op2.constant, 2);
198+
}
199+
break;
192200
case ZEND_INIT_STATIC_METHOD_CALL:
193201
if (opline->op1_type == IS_CONST) {
194202
LITERAL_INFO(opline->op1.constant, 2);
@@ -460,6 +468,22 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
460468
break;
461469
}
462470
ZEND_FALLTHROUGH;
471+
case IS_PTR:
472+
// FIXME: Support IS_PTR+IS_STRING
473+
map[i] = j;
474+
if (i != j) {
475+
op_array->literals[j] = op_array->literals[i];
476+
info[j] = info[i];
477+
}
478+
j++;
479+
n = info[i].num_related;
480+
while (n > 1) {
481+
i++;
482+
if (i != j) op_array->literals[j] = op_array->literals[i];
483+
j++;
484+
n--;
485+
}
486+
break;
463487
default:
464488
/* don't merge other types */
465489
ZEND_ASSERT(info[i].num_related == 1);
@@ -611,6 +635,7 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
611635
}
612636
break;
613637
case ZEND_INIT_METHOD_CALL:
638+
case ZEND_INIT_METHOD_CALL_PTR:
614639
if (opline->op2_type == IS_CONST) {
615640
// op2 method
616641
if (opline->op1_type == IS_UNUSED &&

Zend/Optimizer/optimize_func_calls.c

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ static void zend_delete_call_instructions(zend_op_array *op_array, zend_op *opli
4747
case ZEND_INIT_NS_FCALL_BY_NAME:
4848
case ZEND_INIT_STATIC_METHOD_CALL:
4949
case ZEND_INIT_METHOD_CALL:
50+
case ZEND_INIT_METHOD_CALL_PTR:
5051
case ZEND_INIT_FCALL:
5152
if (call == 0) {
5253
MAKE_NOP(opline);
@@ -167,6 +168,7 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
167168
case ZEND_INIT_NS_FCALL_BY_NAME:
168169
case ZEND_INIT_STATIC_METHOD_CALL:
169170
case ZEND_INIT_METHOD_CALL:
171+
case ZEND_INIT_METHOD_CALL_PTR:
170172
case ZEND_INIT_FCALL:
171173
case ZEND_NEW:
172174
/* The argument passing optimizations are valid for prototypes as well,
@@ -212,8 +214,33 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
212214
if (opline->opcode != ZEND_CALLABLE_CONVERT) {
213215
opline->opcode = zend_get_call_op(fcall, call_stack[call].func);
214216
}
217+
} else if (fcall->opcode == ZEND_INIT_METHOD_CALL) {
218+
zend_function *func = call_stack[call].func;
219+
// FIXME: Check create_object and get_method object handlers
220+
if (!call_stack[call].is_prototype
221+
/* Allows us to drop an user check in the handler. */
222+
&& func->type == ZEND_INTERNAL_FUNCTION
223+
&& !(func->common.fn_flags & ZEND_ACC_STATIC)
224+
&& (func->common.scope->ce_flags & ZEND_ACC_LINKED)) {
225+
fcall->opcode = ZEND_INIT_METHOD_CALL_PTR;
226+
zend_string *orig_function_name;
227+
if (fcall->op2_type == IS_CONST) {
228+
orig_function_name = Z_STR(op_array->literals[fcall->op2.constant]);
229+
ZVAL_NULL(&op_array->literals[fcall->op2.constant]);
230+
literal_dtor(&op_array->literals[fcall->op2.constant + 1]);
231+
} else {
232+
orig_function_name = zend_string_copy(func->common.function_name);
233+
}
234+
zval func_zv;
235+
ZVAL_PTR(&func_zv, func);
236+
fcall->op2_type = IS_CONST;
237+
fcall->op2.constant = zend_optimizer_add_literal(op_array, &func_zv);
238+
zval func_name_zv;
239+
ZVAL_STR(&func_name_zv, orig_function_name);
240+
zend_optimizer_add_literal(op_array, &func_name_zv);
241+
}
215242
} else if (fcall->opcode == ZEND_INIT_STATIC_METHOD_CALL
216-
|| fcall->opcode == ZEND_INIT_METHOD_CALL
243+
|| fcall->opcode == ZEND_INIT_METHOD_CALL_PTR
217244
|| fcall->opcode == ZEND_NEW) {
218245
/* We don't have specialized opcodes for this, do nothing */
219246
} else {

Zend/Optimizer/zend_call_graph.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ ZEND_API void zend_analyze_calls(zend_arena **arena, zend_script *script, uint32
6060
switch (opline->opcode) {
6161
case ZEND_INIT_FCALL:
6262
case ZEND_INIT_METHOD_CALL:
63+
case ZEND_INIT_METHOD_CALL_PTR:
6364
case ZEND_INIT_STATIC_METHOD_CALL:
6465
call_stack[call] = call_info;
6566
func = zend_optimizer_get_called_func(

Zend/Optimizer/zend_dump.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -679,14 +679,18 @@ 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) {
682+
} else if (opline->opcode == ZEND_INIT_FCALL || opline->opcode == ZEND_INIT_METHOD_CALL_PTR) {
683683
zval *func_zv = CRT_CONSTANT(opline->op2);
684684
if (Z_TYPE_P(func_zv) == IS_STRING) {
685685
zend_dump_const(op);
686686
} else {
687687
ZEND_ASSERT(Z_TYPE_P(func_zv) == IS_PTR);
688688
zend_function *func = Z_PTR_P(func_zv);
689-
fprintf(stderr, " (%s)", ZSTR_VAL(func->common.function_name));
689+
fprintf(stderr, " (");
690+
if (func->common.scope) {
691+
fprintf(stderr, "%s::", ZSTR_VAL(func->common.scope->name));
692+
}
693+
fprintf(stderr, "%s)", ZSTR_VAL(func->common.function_name));
690694
}
691695
} else {
692696
zend_dump_const(op);

Zend/Optimizer/zend_optimizer.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -922,6 +922,8 @@ zend_function *zend_optimizer_get_called_func(
922922
}
923923
}
924924
break;
925+
case ZEND_INIT_METHOD_CALL_PTR:
926+
return Z_PTR_P(CRT_CONSTANT(opline->op2));
925927
case ZEND_NEW:
926928
{
927929
zend_class_entry *ce = zend_optimizer_get_class_entry_from_op1(

Zend/zend_execute.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4216,6 +4216,7 @@ ZEND_API void zend_unfinished_calls_gc(zend_execute_data *execute_data, zend_exe
42164216
opline->opcode == ZEND_INIT_DYNAMIC_CALL ||
42174217
opline->opcode == ZEND_INIT_USER_CALL ||
42184218
opline->opcode == ZEND_INIT_METHOD_CALL ||
4219+
opline->opcode == ZEND_INIT_METHOD_CALL_PTR ||
42194220
opline->opcode == ZEND_INIT_STATIC_METHOD_CALL ||
42204221
opline->opcode == ZEND_NEW)) {
42214222
ZEND_ASSERT(op_num);
@@ -4241,6 +4242,7 @@ ZEND_API void zend_unfinished_calls_gc(zend_execute_data *execute_data, zend_exe
42414242
case ZEND_INIT_DYNAMIC_CALL:
42424243
case ZEND_INIT_USER_CALL:
42434244
case ZEND_INIT_METHOD_CALL:
4245+
case ZEND_INIT_METHOD_CALL_PTR:
42444246
case ZEND_INIT_STATIC_METHOD_CALL:
42454247
case ZEND_NEW:
42464248
if (level == 0) {
@@ -4296,6 +4298,7 @@ ZEND_API void zend_unfinished_calls_gc(zend_execute_data *execute_data, zend_exe
42964298
case ZEND_INIT_DYNAMIC_CALL:
42974299
case ZEND_INIT_USER_CALL:
42984300
case ZEND_INIT_METHOD_CALL:
4301+
case ZEND_INIT_METHOD_CALL_PTR:
42994302
case ZEND_INIT_STATIC_METHOD_CALL:
43004303
case ZEND_NEW:
43014304
if (level == 0) {
@@ -4347,6 +4350,7 @@ static void cleanup_unfinished_calls(zend_execute_data *execute_data, uint32_t o
43474350
opline->opcode == ZEND_INIT_DYNAMIC_CALL ||
43484351
opline->opcode == ZEND_INIT_USER_CALL ||
43494352
opline->opcode == ZEND_INIT_METHOD_CALL ||
4353+
opline->opcode == ZEND_INIT_METHOD_CALL_PTR ||
43504354
opline->opcode == ZEND_INIT_STATIC_METHOD_CALL ||
43514355
opline->opcode == ZEND_NEW)) {
43524356
ZEND_ASSERT(op_num);
@@ -4374,6 +4378,7 @@ static void cleanup_unfinished_calls(zend_execute_data *execute_data, uint32_t o
43744378
case ZEND_INIT_DYNAMIC_CALL:
43754379
case ZEND_INIT_USER_CALL:
43764380
case ZEND_INIT_METHOD_CALL:
4381+
case ZEND_INIT_METHOD_CALL_PTR:
43774382
case ZEND_INIT_STATIC_METHOD_CALL:
43784383
case ZEND_NEW:
43794384
if (level == 0) {
@@ -4429,6 +4434,7 @@ static void cleanup_unfinished_calls(zend_execute_data *execute_data, uint32_t o
44294434
case ZEND_INIT_DYNAMIC_CALL:
44304435
case ZEND_INIT_USER_CALL:
44314436
case ZEND_INIT_METHOD_CALL:
4437+
case ZEND_INIT_METHOD_CALL_PTR:
44324438
case ZEND_INIT_STATIC_METHOD_CALL:
44334439
case ZEND_NEW:
44344440
if (level == 0) {

Zend/zend_opcode.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -785,6 +785,7 @@ static void emit_live_range(
785785
case ZEND_INIT_DYNAMIC_CALL:
786786
case ZEND_INIT_USER_CALL:
787787
case ZEND_INIT_METHOD_CALL:
788+
case ZEND_INIT_METHOD_CALL_PTR:
788789
case ZEND_INIT_STATIC_METHOD_CALL:
789790
case ZEND_NEW:
790791
level++;

Zend/zend_vm_def.h

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3621,6 +3621,74 @@ ZEND_VM_HOT_OBJ_HANDLER(112, ZEND_INIT_METHOD_CALL, CONST|TMPVAR|UNUSED|THIS|CV,
36213621
ZEND_VM_NEXT_OPCODE();
36223622
}
36233623

3624+
ZEND_VM_HOT_OBJ_HANDLER(209, ZEND_INIT_METHOD_CALL_PTR, CONST|TMPVAR|UNUSED|THIS|CV, CONST, NUM)
3625+
{
3626+
USE_OPLINE
3627+
zval *object;
3628+
zend_function *fbc;
3629+
zend_object *obj;
3630+
zend_execute_data *call;
3631+
uint32_t call_info;
3632+
3633+
SAVE_OPLINE();
3634+
3635+
object = GET_OP1_OBJ_ZVAL_PTR_UNDEF(BP_VAR_R);
3636+
if (OP1_TYPE == IS_UNUSED) {
3637+
obj = Z_OBJ_P(object);
3638+
} else if (OP1_TYPE != IS_CONST && EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) {
3639+
obj = Z_OBJ_P(object);
3640+
} else {
3641+
if ((OP1_TYPE & (IS_VAR|IS_CV)) && EXPECTED(Z_ISREF_P(object))) {
3642+
zend_reference *ref = Z_REF_P(object);
3643+
3644+
object = &ref->val;
3645+
if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) {
3646+
obj = Z_OBJ_P(object);
3647+
if (OP1_TYPE & IS_VAR) {
3648+
if (UNEXPECTED(GC_DELREF(ref) == 0)) {
3649+
efree_size(ref, sizeof(zend_reference));
3650+
} else {
3651+
Z_ADDREF_P(object);
3652+
}
3653+
}
3654+
ZEND_VM_C_GOTO(fetched_obj);
3655+
}
3656+
}
3657+
if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
3658+
object = ZVAL_UNDEFINED_OP1();
3659+
if (UNEXPECTED(EG(exception) != NULL)) {
3660+
HANDLE_EXCEPTION();
3661+
}
3662+
}
3663+
zval *func_name = RT_CONSTANT(opline, opline->op2) + 1;
3664+
zend_invalid_method_call(object, func_name);
3665+
FREE_OP1();
3666+
HANDLE_EXCEPTION();
3667+
}
3668+
ZEND_VM_C_LABEL(fetched_obj):
3669+
3670+
fbc = Z_PTR_P(RT_CONSTANT(opline, opline->op2));
3671+
if (UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) {
3672+
init_func_run_time_cache(&fbc->op_array);
3673+
}
3674+
3675+
call_info = ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_HAS_THIS;
3676+
if (OP1_TYPE & (IS_VAR|IS_TMP_VAR|IS_CV)) {
3677+
if (OP1_TYPE == IS_CV) {
3678+
GC_ADDREF(obj); /* For $this pointer */
3679+
}
3680+
/* CV may be changed indirectly (e.g. when it's a reference) */
3681+
call_info = ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_HAS_THIS | ZEND_CALL_RELEASE_THIS;
3682+
}
3683+
3684+
call = zend_vm_stack_push_call_frame(call_info,
3685+
fbc, opline->extended_value, obj);
3686+
call->prev_execute_data = EX(call);
3687+
EX(call) = call;
3688+
3689+
ZEND_VM_NEXT_OPCODE();
3690+
}
3691+
36243692
ZEND_VM_HANDLER(113, ZEND_INIT_STATIC_METHOD_CALL, UNUSED|CLASS_FETCH|CONST|VAR, CONST|TMPVAR|UNUSED|CONSTRUCTOR|CV, NUM|CACHE_SLOT)
36253693
{
36263694
USE_OPLINE

0 commit comments

Comments
 (0)