Skip to content

Commit ea547c2

Browse files
committed
Switch unwrapping approach
Unrwap in ZEND_RETURN_BY_REF instead, as this is a really uncommon opcode.
1 parent 8e73c30 commit ea547c2

18 files changed

+111
-371
lines changed

Zend/zend_closures.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,7 @@ static ZEND_NAMED_FUNCTION(zend_closure_call_magic) /* {{{ */ {
339339
fcc.called_scope = zend_get_called_scope(EG(current_execute_data));
340340

341341
zend_call_function(&fci, &fcc);
342-
342+
zend_return_unwrap_ref(EG(current_execute_data), return_value);
343343
zval_ptr_dtor(&fci.params[1]);
344344
}
345345
/* }}} */

Zend/zend_compile.c

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4028,16 +4028,13 @@ static bool zend_compile_call_common(znode *result, zend_ast *args_ast, const ze
40284028
opline = zend_emit_op(result, call_op, NULL, NULL);
40294029

40304030
if (type == BP_VAR_R || type == BP_VAR_IS) {
4031-
if (call_op == ZEND_DO_FCALL || call_op == ZEND_DO_FCALL_BY_NAME) {
4032-
opline->extended_value |= ZEND_DO_FCALL_DEREF;
4033-
}
40344031
if (init_opcode != ZEND_NEW && opline->result_type == IS_VAR) {
40354032
opline->result_type = IS_TMP_VAR;
40364033
result->op_type = IS_TMP_VAR;
40374034
}
40384035
}
40394036
if (may_have_extra_named_args) {
4040-
opline->extended_value = ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS;
4037+
opline->extended_value |= ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS;
40414038
}
40424039
opline->lineno = lineno;
40434040
zend_do_extended_fcall_end();
@@ -4376,9 +4373,8 @@ static zend_result zend_compile_func_cufa(znode *result, zend_ast_list *args, ze
43764373
zend_emit_op(NULL, ZEND_SEND_ARRAY, &arg_node, NULL);
43774374
zend_emit_op(NULL, ZEND_CHECK_UNDEF_ARGS, NULL, NULL);
43784375
opline = zend_emit_op(result, ZEND_DO_FCALL, NULL, NULL);
4379-
opline->extended_value = ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS;
4376+
opline->extended_value |= ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS;
43804377
if (type == BP_VAR_R || type == BP_VAR_IS) {
4381-
opline->extended_value |= ZEND_DO_FCALL_DEREF;
43824378
opline->result_type = IS_TMP_VAR;
43834379
result->op_type = IS_TMP_VAR;
43844380
}
@@ -4410,7 +4406,6 @@ static zend_result zend_compile_func_cuf(znode *result, const zend_ast_list *arg
44104406
}
44114407
zend_op *opline = zend_emit_op(result, ZEND_DO_FCALL, NULL, NULL);
44124408
if (type == BP_VAR_R || type == BP_VAR_IS) {
4413-
opline->extended_value |= ZEND_DO_FCALL_DEREF;
44144409
opline->result_type = IS_TMP_VAR;
44154410
result->op_type = IS_TMP_VAR;
44164411
}

Zend/zend_compile.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -667,7 +667,6 @@ struct _zend_execute_data {
667667
#define ZEND_CALL_OBSERVED (1 << 28) /* "fcall_begin" observer handler may set this flag */
668668
/* to prevent optimization in RETURN handler and */
669669
/* keep all local variables for "fcall_end" handler */
670-
#define ZEND_CALL_DEREF_RESULT (1 << 28) // FIXME: Disambiguate. ZEND_CALL_OBSERVED isn't used internally, but may be externally.
671670
#define ZEND_CALL_JIT_RESERVED (1 << 29) /* reserved for tracing JIT */
672671
#define ZEND_CALL_NEEDS_REATTACH (1 << 30)
673672
#define ZEND_CALL_SEND_ARG_BY_REF (1u << 31)
@@ -1127,9 +1126,6 @@ ZEND_API zend_string *zend_type_to_string(zend_type type);
11271126

11281127
#define ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS 1
11291128

1130-
// FIXME: Conflicts with ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS.
1131-
#define ZEND_DO_FCALL_DEREF 1
1132-
11331129
/* The send mode, the is_variadic, the is_promoted, and the is_tentative flags are stored as part of zend_type */
11341130
#define _ZEND_SEND_MODE_SHIFT _ZEND_TYPE_EXTRA_FLAGS_SHIFT
11351131
#define _ZEND_IS_VARIADIC_BIT (1 << (_ZEND_TYPE_EXTRA_FLAGS_SHIFT + 2))

Zend/zend_execute.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5947,3 +5947,32 @@ ZEND_API zval *zend_get_zval_ptr(const zend_op *opline, int op_type, const znode
59475947
}
59485948
return ret;
59495949
}
5950+
5951+
ZEND_API void zend_return_unwrap_ref(zend_execute_data *execute_data, zval *return_value)
5952+
{
5953+
if (!Z_ISREF_P(return_value)) {
5954+
return;
5955+
}
5956+
5957+
zend_execute_data *prev_ex = EX(prev_execute_data);
5958+
if (!prev_ex || !prev_ex->func || !ZEND_USER_CODE(prev_ex->func->type)) {
5959+
return;
5960+
}
5961+
5962+
const zend_op *do_opline = prev_ex->opline;
5963+
if (do_opline->result_type != IS_TMP_VAR) {
5964+
return;
5965+
}
5966+
5967+
if (do_opline->opcode != ZEND_DO_FCALL && do_opline->opcode != ZEND_DO_FCALL_BY_NAME) {
5968+
return;
5969+
}
5970+
5971+
zend_reference *ref = Z_REF_P(return_value);
5972+
ZVAL_COPY_VALUE(return_value, &ref->val);
5973+
if (GC_DELREF(ref) == 0) {
5974+
efree_size(ref, sizeof(zend_reference));
5975+
} else {
5976+
Z_TRY_ADDREF_P(return_value);
5977+
}
5978+
}

Zend/zend_execute.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,8 @@ static zend_always_inline void *zend_get_bad_ptr(void)
632632
return NULL;
633633
}
634634

635+
ZEND_API void zend_return_unwrap_ref(zend_execute_data *call, zval *return_value);
636+
635637
END_EXTERN_C()
636638

637639
#endif /* ZEND_EXECUTE_H */

Zend/zend_vm_def.h

Lines changed: 9 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -2967,7 +2967,7 @@ ZEND_VM_HOT_HELPER(zend_leave_helper, ANY, ANY)
29672967
SAVE_OPLINE();
29682968
#endif
29692969

2970-
if (EXPECTED((call_info & (ZEND_CALL_CODE|ZEND_CALL_TOP|ZEND_CALL_HAS_SYMBOL_TABLE|ZEND_CALL_FREE_EXTRA_ARGS|ZEND_CALL_ALLOCATED|ZEND_CALL_HAS_EXTRA_NAMED_PARAMS|ZEND_CALL_DEREF_RESULT)) == 0)) {
2970+
if (EXPECTED((call_info & (ZEND_CALL_CODE|ZEND_CALL_TOP|ZEND_CALL_HAS_SYMBOL_TABLE|ZEND_CALL_FREE_EXTRA_ARGS|ZEND_CALL_ALLOCATED|ZEND_CALL_HAS_EXTRA_NAMED_PARAMS)) == 0)) {
29712971
EG(current_execute_data) = EX(prev_execute_data);
29722972
i_free_compiled_variables(execute_data);
29732973

@@ -2990,19 +2990,6 @@ ZEND_VM_HOT_HELPER(zend_leave_helper, ANY, ANY)
29902990
LOAD_NEXT_OPLINE();
29912991
ZEND_VM_LEAVE();
29922992
} else if (EXPECTED((call_info & (ZEND_CALL_CODE|ZEND_CALL_TOP)) == 0)) {
2993-
if (UNEXPECTED(call_info & ZEND_CALL_DEREF_RESULT)
2994-
&& EX(return_value)
2995-
&& Z_TYPE_P(EX(return_value)) == IS_REFERENCE) {
2996-
zval *ret = EX(return_value);
2997-
zend_reference *ref = Z_REF_P(ret);
2998-
ZVAL_COPY_VALUE(ret, &ref->val);
2999-
if (GC_DELREF(ref) == 0) {
3000-
efree_size(ref, sizeof(zend_reference));
3001-
} else {
3002-
Z_TRY_ADDREF_P(ret);
3003-
}
3004-
}
3005-
30062993
EG(current_execute_data) = EX(prev_execute_data);
30072994
i_free_compiled_variables(execute_data);
30082995

@@ -4202,7 +4189,7 @@ ZEND_VM_HOT_HANDLER(130, ZEND_DO_UCALL, ANY, ANY, SPEC(RETVAL,OBSERVER))
42024189
ZEND_VM_ENTER_EX();
42034190
}
42044191

4205-
ZEND_VM_HOT_HANDLER(131, ZEND_DO_FCALL_BY_NAME, ANY, ANY, NUM, SPEC(RETVAL,OBSERVER))
4192+
ZEND_VM_HOT_HANDLER(131, ZEND_DO_FCALL_BY_NAME, ANY, ANY, SPEC(RETVAL,OBSERVER))
42064193
{
42074194
USE_OPLINE
42084195
zend_execute_data *call = EX(call);
@@ -4238,14 +4225,6 @@ ZEND_VM_HOT_HANDLER(131, ZEND_DO_FCALL_BY_NAME, ANY, ANY, NUM, SPEC(RETVAL,OBSER
42384225
ret = EX_VAR(opline->result.var);
42394226
}
42404227

4241-
/* Optimization: ZEND_DO_FCALL_DEREF is the only used flag. */
4242-
if (RETURN_VALUE_USED(opline)
4243-
&& EXPECTED(opline->extended_value)
4244-
&& UNEXPECTED(call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE)) {
4245-
ZEND_ASSERT(opline->extended_value == ZEND_DO_FCALL_DEREF);
4246-
ZEND_ADD_CALL_FLAG(call, ZEND_CALL_DEREF_RESULT);
4247-
}
4248-
42494228
call->prev_execute_data = execute_data;
42504229
execute_data = call;
42514230
i_init_func_execute_data(&fbc->op_array, ret, 0 EXECUTE_DATA_CC);
@@ -4284,20 +4263,9 @@ ZEND_VM_HOT_HANDLER(131, ZEND_DO_FCALL_BY_NAME, ANY, ANY, NUM, SPEC(RETVAL,OBSER
42844263
? Z_ISREF_P(ret) : !Z_ISREF_P(ret));
42854264
zend_verify_internal_func_info(call->func, ret);
42864265
}
4266+
ZEND_ASSERT(opline->result_type != IS_TMP_VAR || !Z_ISREF_P(ret));
42874267
#endif
42884268

4289-
/* Optimization: ZEND_DO_FCALL_DEREF is the only used flag. */
4290-
if (RETURN_VALUE_USED(opline) && EXPECTED(opline->extended_value)) {
4291-
ZEND_ASSERT(opline->extended_value == ZEND_DO_FCALL_DEREF);
4292-
if (UNEXPECTED(Z_ISREF_P(ret))) {
4293-
zend_reference *ref = Z_REF_P(ret);
4294-
ZVAL_COPY_VALUE(ret, &ref->val);
4295-
if (GC_DELREF(ref) == 0) {
4296-
efree_size(ref, sizeof(zend_reference));
4297-
}
4298-
}
4299-
}
4300-
43014269
ZEND_OBSERVER_FCALL_END(call, EG(exception) ? NULL : ret);
43024270
ZEND_VM_FCALL_INTERRUPT_CHECK(call);
43034271

@@ -4334,7 +4302,7 @@ ZEND_VM_C_LABEL(fcall_by_name_end):
43344302
ZEND_VM_CONTINUE();
43354303
}
43364304

4337-
ZEND_VM_HOT_HANDLER(60, ZEND_DO_FCALL, ANY, ANY, NUM, SPEC(RETVAL,OBSERVER))
4305+
ZEND_VM_HOT_HANDLER(60, ZEND_DO_FCALL, ANY, ANY, SPEC(RETVAL,OBSERVER))
43384306
{
43394307
USE_OPLINE
43404308
zend_execute_data *call = EX(call);
@@ -4372,14 +4340,6 @@ ZEND_VM_HOT_HANDLER(60, ZEND_DO_FCALL, ANY, ANY, NUM, SPEC(RETVAL,OBSERVER))
43724340
if (RETURN_VALUE_USED(opline)) {
43734341
ret = EX_VAR(opline->result.var);
43744342
}
4375-
4376-
/* Optimization: ZEND_DO_FCALL_DEREF is the only used flag. */
4377-
if (RETURN_VALUE_USED(opline)
4378-
&& EXPECTED(opline->extended_value)
4379-
&& UNEXPECTED(call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE)) {
4380-
ZEND_ASSERT(opline->extended_value == ZEND_DO_FCALL_DEREF);
4381-
ZEND_ADD_CALL_FLAG(call, ZEND_CALL_DEREF_RESULT);
4382-
}
43834343

43844344
call->prev_execute_data = execute_data;
43854345
execute_data = call;
@@ -4433,19 +4393,8 @@ ZEND_VM_HOT_HANDLER(60, ZEND_DO_FCALL, ANY, ANY, NUM, SPEC(RETVAL,OBSERVER))
44334393
? Z_ISREF_P(ret) : !Z_ISREF_P(ret));
44344394
zend_verify_internal_func_info(call->func, ret);
44354395
}
4396+
ZEND_ASSERT(opline->result_type != IS_TMP_VAR || !Z_ISREF_P(ret));
44364397
#endif
4437-
4438-
/* Optimization: ZEND_DO_FCALL_DEREF is the only used flag. */
4439-
if (RETURN_VALUE_USED(opline) && EXPECTED(opline->extended_value)) {
4440-
ZEND_ASSERT(opline->extended_value == ZEND_DO_FCALL_DEREF);
4441-
if (UNEXPECTED(Z_ISREF_P(ret))) {
4442-
zend_reference *ref = Z_REF_P(ret);
4443-
ZVAL_COPY_VALUE(ret, &ref->val);
4444-
if (GC_DELREF(ref) == 0) {
4445-
efree_size(ref, sizeof(zend_reference));
4446-
}
4447-
}
4448-
}
44494398

44504399
ZEND_OBSERVER_FCALL_END(call, EG(exception) ? NULL : ret);
44514400
ZEND_VM_FCALL_INTERRUPT_CHECK(call);
@@ -4696,6 +4645,10 @@ ZEND_VM_COLD_CONST_HANDLER(111, ZEND_RETURN_BY_REF, CONST|TMP|VAR|CV, ANY, SRC,
46964645

46974646
ZEND_OBSERVER_FCALL_END(execute_data, return_value);
46984647
ZEND_OBSERVER_FREE_RETVAL();
4648+
4649+
// FIXME: Don't create the ref in the first place?
4650+
zend_return_unwrap_ref(execute_data, return_value);
4651+
46994652
ZEND_VM_DISPATCH_TO_HELPER(zend_leave_helper);
47004653
}
47014654

0 commit comments

Comments
 (0)