Improved reference counting

This commit is contained in:
Dmitry Stogov 2015-04-03 01:32:20 +03:00
parent f26592846f
commit adcf0c6052
6 changed files with 648 additions and 322 deletions

View file

@ -971,7 +971,7 @@ static inline int zend_verify_missing_return_type(zend_function *zf)
static zend_always_inline void zend_assign_to_object(zval *retval, zval *object, uint32_t object_op_type, zval *property_name, uint32_t property_op_type, int value_type, znode_op value_op, const zend_execute_data *execute_data, void **cache_slot) static zend_always_inline void zend_assign_to_object(zval *retval, zval *object, uint32_t object_op_type, zval *property_name, uint32_t property_op_type, int value_type, znode_op value_op, const zend_execute_data *execute_data, void **cache_slot)
{ {
zend_free_op free_value; zend_free_op free_value;
zval *value = get_zval_ptr_deref(value_type, value_op, execute_data, &free_value, BP_VAR_R); zval *value = get_zval_ptr(value_type, value_op, execute_data, &free_value, BP_VAR_R);
zval tmp; zval tmp;
if (object_op_type != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { if (object_op_type != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
@ -1033,9 +1033,6 @@ fast_assign:
if (retval && !EG(exception)) { if (retval && !EG(exception)) {
ZVAL_COPY(retval, value); ZVAL_COPY(retval, value);
} }
if (value_type == IS_VAR) {
FREE_OP(free_value);
}
return; return;
} }
} else { } else {
@ -1057,9 +1054,11 @@ fast_assign:
zval_copy_ctor_func(&tmp); zval_copy_ctor_func(&tmp);
value = &tmp; value = &tmp;
} }
} else if (value_type != IS_TMP_VAR && } else if (value_type != IS_TMP_VAR) {
Z_REFCOUNTED_P(value)) { ZVAL_DEREF(value);
Z_ADDREF_P(value); if (Z_REFCOUNTED_P(value)) {
Z_ADDREF_P(value);
}
} }
zend_hash_add_new(zobj->properties, Z_STR_P(property_name), value); zend_hash_add_new(zobj->properties, Z_STR_P(property_name), value);
if (retval && !EG(exception)) { if (retval && !EG(exception)) {
@ -1089,9 +1088,11 @@ fast_assign:
zval_copy_ctor_func(&tmp); zval_copy_ctor_func(&tmp);
value = &tmp; value = &tmp;
} }
} else if (value_type != IS_TMP_VAR && } else if (value_type != IS_TMP_VAR) {
Z_REFCOUNTED_P(value)) { ZVAL_DEREF(value);
Z_ADDREF_P(value); if (Z_REFCOUNTED_P(value)) {
Z_ADDREF_P(value);
}
} }
Z_OBJ_HT_P(object)->write_property(object, property_name, value, cache_slot); Z_OBJ_HT_P(object)->write_property(object, property_name, value, cache_slot);

View file

@ -55,6 +55,13 @@ ZEND_API void zend_verify_internal_return_error(const zend_function *zf, const c
static zend_always_inline zval* zend_assign_to_variable(zval *variable_ptr, zval *value, zend_uchar value_type) static zend_always_inline zval* zend_assign_to_variable(zval *variable_ptr, zval *value, zend_uchar value_type)
{ {
zend_refcounted *ref = NULL;
if ((value_type & (IS_VAR|IS_CV)) && Z_ISREF_P(value)) {
ref = Z_COUNTED_P(value);
value = Z_REFVAL_P(value);
}
do { do {
if (UNEXPECTED(Z_REFCOUNTED_P(variable_ptr))) { if (UNEXPECTED(Z_REFCOUNTED_P(variable_ptr))) {
zend_refcounted *garbage; zend_refcounted *garbage;
@ -81,12 +88,18 @@ static zend_always_inline zval* zend_assign_to_variable(zval *variable_ptr, zval
if (UNEXPECTED(Z_OPT_COPYABLE_P(variable_ptr))) { if (UNEXPECTED(Z_OPT_COPYABLE_P(variable_ptr))) {
zval_copy_ctor_func(variable_ptr); zval_copy_ctor_func(variable_ptr);
} }
} else if (value_type != IS_TMP_VAR) { } else if (value_type == IS_CV) {
if (UNEXPECTED(Z_OPT_REFCOUNTED_P(variable_ptr))) { if (UNEXPECTED(Z_OPT_REFCOUNTED_P(variable_ptr))) {
Z_ADDREF_P(variable_ptr); Z_ADDREF_P(variable_ptr);
} }
} else if (/* value_type == IS_VAR && */ UNEXPECTED(ref)) {
if (UNEXPECTED(--GC_REFCOUNT(ref) == 0)) {
efree_size(ref, sizeof(zend_reference));
} else if (Z_OPT_REFCOUNTED_P(variable_ptr)) {
Z_ADDREF_P(variable_ptr);
}
} }
_zval_dtor_func_for_ptr(garbage ZEND_FILE_LINE_CC); zval_dtor_func_for_ptr(garbage);
return variable_ptr; return variable_ptr;
} else { /* we need to split */ } else { /* we need to split */
/* optimized version of GC_ZVAL_CHECK_POSSIBLE_ROOT(variable_ptr) */ /* optimized version of GC_ZVAL_CHECK_POSSIBLE_ROOT(variable_ptr) */
@ -104,10 +117,16 @@ static zend_always_inline zval* zend_assign_to_variable(zval *variable_ptr, zval
if (UNEXPECTED(Z_OPT_COPYABLE_P(variable_ptr))) { if (UNEXPECTED(Z_OPT_COPYABLE_P(variable_ptr))) {
zval_copy_ctor_func(variable_ptr); zval_copy_ctor_func(variable_ptr);
} }
} else if (value_type != IS_TMP_VAR) { } else if (value_type == IS_CV) {
if (UNEXPECTED(Z_OPT_REFCOUNTED_P(variable_ptr))) { if (UNEXPECTED(Z_OPT_REFCOUNTED_P(variable_ptr))) {
Z_ADDREF_P(variable_ptr); Z_ADDREF_P(variable_ptr);
} }
} else if (/* value_type == IS_VAR && */ UNEXPECTED(ref)) {
if (UNEXPECTED(--GC_REFCOUNT(ref) == 0)) {
efree_size(ref, sizeof(zend_reference));
} else if (Z_OPT_REFCOUNTED_P(variable_ptr)) {
Z_ADDREF_P(variable_ptr);
}
} }
return variable_ptr; return variable_ptr;
} }

View file

@ -625,7 +625,7 @@ ZEND_API void zend_std_write_property(zval *object, zval *member, zval *value, v
} else if (EXPECTED(zobj->properties != NULL)) { } else if (EXPECTED(zobj->properties != NULL)) {
if ((variable_ptr = zend_hash_find(zobj->properties, Z_STR_P(member))) != NULL) { if ((variable_ptr = zend_hash_find(zobj->properties, Z_STR_P(member))) != NULL) {
found: found:
zend_assign_to_variable(variable_ptr, value, (IS_VAR|IS_TMP_VAR)); zend_assign_to_variable(variable_ptr, value, IS_CV);
goto exit; goto exit;
} }
} }

View file

@ -31,9 +31,9 @@ ZEND_API void ZEND_FASTCALL _zval_dtor_func(zend_refcounted *p ZEND_FILE_LINE_DC
ZEND_API void ZEND_FASTCALL _zval_dtor_func_for_ptr(zend_refcounted *p ZEND_FILE_LINE_DC); ZEND_API void ZEND_FASTCALL _zval_dtor_func_for_ptr(zend_refcounted *p ZEND_FILE_LINE_DC);
ZEND_API void ZEND_FASTCALL _zval_copy_ctor_func(zval *zvalue ZEND_FILE_LINE_DC); ZEND_API void ZEND_FASTCALL _zval_copy_ctor_func(zval *zvalue ZEND_FILE_LINE_DC);
#define zval_dtor_func(p) _zval_dtor_func(zv ZEND_FILE_LINE_CC) #define zval_dtor_func(zv) _zval_dtor_func(zv ZEND_FILE_LINE_CC)
#define zval_dtor_func_for_ptr(p) _zval_dtor_func_for_ptr(zv ZEND_FILE_LINE_CC) #define zval_dtor_func_for_ptr(zv) _zval_dtor_func_for_ptr(zv ZEND_FILE_LINE_CC)
#define zval_copy_ctor_func(zv) _zval_copy_ctor_func(zv ZEND_FILE_LINE_CC) #define zval_copy_ctor_func(zv) _zval_copy_ctor_func(zv ZEND_FILE_LINE_CC)
static zend_always_inline void _zval_dtor(zval *zvalue ZEND_FILE_LINE_DC) static zend_always_inline void _zval_dtor(zval *zvalue ZEND_FILE_LINE_DC)
{ {

View file

@ -2184,17 +2184,14 @@ ZEND_VM_C_LABEL(try_assign_dim_array):
variable_ptr = zend_fetch_dimension_address_inner(Z_ARRVAL_P(object_ptr), dim, OP2_TYPE, BP_VAR_W); variable_ptr = zend_fetch_dimension_address_inner(Z_ARRVAL_P(object_ptr), dim, OP2_TYPE, BP_VAR_W);
FREE_OP2(); FREE_OP2();
} }
value = get_zval_ptr_deref((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1, BP_VAR_R); value = get_zval_ptr((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1, BP_VAR_R);
if (UNEXPECTED(variable_ptr == &EG(error_zval))) { if (UNEXPECTED(variable_ptr == &EG(error_zval))) {
FREE_OP(free_op_data1); FREE_OP(free_op_data1);
if (UNEXPECTED(RETURN_VALUE_USED(opline))) { if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_NULL(EX_VAR(opline->result.var)); ZVAL_NULL(EX_VAR(opline->result.var));
} }
} else { } else {
value = zend_assign_to_variable(variable_ptr, value, (opline+1)->op1_type); value = zend_assign_to_variable(variable_ptr, value, (opline+1)->op1_type);
if ((opline+1)->op1_type == IS_VAR) {
FREE_OP(free_op_data1);
}
if (UNEXPECTED(RETURN_VALUE_USED(opline))) { if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_COPY(EX_VAR(opline->result.var), value); ZVAL_COPY(EX_VAR(opline->result.var), value);
} }
@ -2263,27 +2260,23 @@ ZEND_VM_HANDLER(38, ZEND_ASSIGN, VAR|CV, CONST|TMP|VAR|CV)
zval *variable_ptr; zval *variable_ptr;
SAVE_OPLINE(); SAVE_OPLINE();
value = GET_OP2_ZVAL_PTR_DEREF(BP_VAR_R); value = GET_OP2_ZVAL_PTR(BP_VAR_R);
variable_ptr = GET_OP1_ZVAL_PTR_PTR_UNDEF(BP_VAR_W); variable_ptr = GET_OP1_ZVAL_PTR_PTR_UNDEF(BP_VAR_W);
if (OP1_TYPE == IS_VAR && UNEXPECTED(variable_ptr == &EG(error_zval))) { if (OP1_TYPE == IS_VAR && UNEXPECTED(variable_ptr == &EG(error_zval))) {
if (OP2_TYPE == IS_TMP_VAR) { FREE_OP2();
FREE_OP2();
}
if (UNEXPECTED(RETURN_VALUE_USED(opline))) { if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_NULL(EX_VAR(opline->result.var)); ZVAL_NULL(EX_VAR(opline->result.var));
} }
} else { } else {
value = zend_assign_to_variable(variable_ptr, value, OP2_TYPE); value = zend_assign_to_variable(variable_ptr, value, OP2_TYPE);
if (UNEXPECTED(RETURN_VALUE_USED(opline))) { if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_COPY(EX_VAR(opline->result.var), value); ZVAL_COPY(EX_VAR(opline->result.var), value);
} }
FREE_OP1_VAR_PTR(); FREE_OP1_VAR_PTR();
/* zend_assign_to_variable() always takes care of op2, never free it! */
} }
/* zend_assign_to_variable() always takes care of op2, never free it! */
FREE_OP2_IF_VAR();
CHECK_EXCEPTION(); CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE(); ZEND_VM_NEXT_OPCODE();
} }
@ -3788,13 +3781,22 @@ ZEND_VM_HANDLER(62, ZEND_RETURN, CONST|TMP|VAR|CV, ANY)
zval_copy_ctor_func(EX(return_value)); zval_copy_ctor_func(EX(return_value));
} }
} }
} else if ((OP1_TYPE == IS_CV || OP1_TYPE == IS_VAR) && Z_ISREF_P(retval_ptr)) { } else if (OP1_TYPE == IS_CV) {
ZVAL_COPY(EX(return_value), Z_REFVAL_P(retval_ptr)); ZVAL_DEREF(retval_ptr);
FREE_OP1_IF_VAR(); ZVAL_COPY(EX(return_value), retval_ptr);
} else { } else /* if (OP1_TYPE == IS_VAR) */ {
ZVAL_COPY_VALUE(EX(return_value), retval_ptr); if (UNEXPECTED(Z_ISREF_P(retval_ptr))) {
if (OP1_TYPE == IS_CV) { zend_refcounted *ref = Z_COUNTED_P(retval_ptr);
if (Z_OPT_REFCOUNTED_P(retval_ptr)) Z_ADDREF_P(retval_ptr);
retval_ptr = Z_REFVAL_P(retval_ptr);
ZVAL_COPY_VALUE(EX(return_value), retval_ptr);
if (UNEXPECTED(--GC_REFCOUNT(ref) == 0)) {
efree_size(ref, sizeof(zend_reference));
} else if (Z_OPT_REFCOUNTED_P(retval_ptr)) {
Z_ADDREF_P(retval_ptr);
}
} else {
ZVAL_COPY_VALUE(EX(return_value), retval_ptr);
} }
} }
} }
@ -3884,13 +3886,22 @@ ZEND_VM_HANDLER(161, ZEND_GENERATOR_RETURN, CONST|TMP|VAR|CV, ANY)
zval_copy_ctor_func(&generator->retval); zval_copy_ctor_func(&generator->retval);
} }
} }
} else if ((OP1_TYPE == IS_CV || OP1_TYPE == IS_VAR) && Z_ISREF_P(retval)) { } else if (OP1_TYPE == IS_CV) {
ZVAL_COPY(&generator->retval, Z_REFVAL_P(retval)); ZVAL_DEREF(retval);
FREE_OP1_IF_VAR();
} else {
ZVAL_COPY_VALUE(&generator->retval, retval); ZVAL_COPY_VALUE(&generator->retval, retval);
if (OP1_TYPE == IS_CV) { } else /* if (OP1_TYPE == IS_VAR) */ {
if (Z_OPT_REFCOUNTED_P(retval)) Z_ADDREF_P(retval); if (UNEXPECTED(Z_ISREF_P(retval))) {
zend_refcounted *ref = Z_COUNTED_P(retval);
retval = Z_REFVAL_P(retval);
ZVAL_COPY_VALUE(&generator->retval, retval);
if (UNEXPECTED(--GC_REFCOUNT(ref) == 0)) {
efree_size(ref, sizeof(zend_reference));
} else if (Z_OPT_REFCOUNTED_P(retval)) {
Z_ADDREF_P(retval);
}
} else {
ZVAL_COPY_VALUE(&generator->retval, retval);
} }
} }
@ -4037,19 +4048,31 @@ ZEND_VM_HANDLER(117, ZEND_SEND_VAR, VAR|CV, ANY)
USE_OPLINE USE_OPLINE
zval *varptr, *arg; zval *varptr, *arg;
zend_free_op free_op1; zend_free_op free_op1;
zend_refcounted *ref;
SAVE_OPLINE(); SAVE_OPLINE();
varptr = GET_OP1_ZVAL_PTR(BP_VAR_R); varptr = GET_OP1_ZVAL_PTR(BP_VAR_R);
arg = ZEND_CALL_VAR(EX(call), opline->result.var); arg = ZEND_CALL_VAR(EX(call), opline->result.var);
if (Z_ISREF_P(varptr)) {
ZVAL_COPY(arg, Z_REFVAL_P(varptr)); if (OP1_TYPE == IS_CV) {
FREE_OP1(); ZVAL_DEREF(varptr);
} else { ZVAL_COPY(arg, varptr);
ZVAL_COPY_VALUE(arg, varptr); } else /* if (OP1_TYPE == IS_VAR) */ {
if (OP1_TYPE == IS_CV) { if (UNEXPECTED(Z_ISREF_P(varptr))) {
if (Z_OPT_REFCOUNTED_P(arg)) Z_ADDREF_P(arg); zend_refcounted *ref = Z_COUNTED_P(varptr);
varptr = Z_REFVAL_P(varptr);
ZVAL_COPY_VALUE(arg, varptr);
if (UNEXPECTED(--GC_REFCOUNT(ref) == 0)) {
efree_size(ref, sizeof(zend_reference));
} else if (Z_OPT_REFCOUNTED_P(arg)) {
Z_ADDREF_P(arg);
}
} else {
ZVAL_COPY_VALUE(arg, varptr);
} }
} }
ZEND_VM_NEXT_OPCODE(); ZEND_VM_NEXT_OPCODE();
} }
@ -4140,15 +4163,26 @@ ZEND_VM_HANDLER(66, ZEND_SEND_VAR_EX, VAR|CV, ANY)
SAVE_OPLINE(); SAVE_OPLINE();
varptr = GET_OP1_ZVAL_PTR(BP_VAR_R); varptr = GET_OP1_ZVAL_PTR(BP_VAR_R);
arg = ZEND_CALL_VAR(EX(call), opline->result.var); arg = ZEND_CALL_VAR(EX(call), opline->result.var);
if (Z_ISREF_P(varptr)) {
ZVAL_COPY(arg, Z_REFVAL_P(varptr)); if (OP1_TYPE == IS_CV) {
FREE_OP1(); ZVAL_DEREF(varptr);
} else { ZVAL_COPY(arg, varptr);
ZVAL_COPY_VALUE(arg, varptr); } else /* if (OP1_TYPE == IS_VAR) */ {
if (OP1_TYPE == IS_CV) { if (UNEXPECTED(Z_ISREF_P(varptr))) {
if (Z_OPT_REFCOUNTED_P(arg)) Z_ADDREF_P(arg); zend_refcounted *ref = Z_COUNTED_P(varptr);
varptr = Z_REFVAL_P(varptr);
ZVAL_COPY_VALUE(arg, varptr);
if (UNEXPECTED(--GC_REFCOUNT(ref) == 0)) {
efree_size(ref, sizeof(zend_reference));
} else if (Z_OPT_REFCOUNTED_P(arg)) {
Z_ADDREF_P(arg);
}
} else {
ZVAL_COPY_VALUE(arg, varptr);
} }
} }
ZEND_VM_NEXT_OPCODE(); ZEND_VM_NEXT_OPCODE();
} }
@ -5008,12 +5042,24 @@ ZEND_VM_HANDLER(72, ZEND_ADD_ARRAY_ELEMENT, CONST|TMP|VAR|CV, CONST|TMPVAR|UNUSE
ZVAL_DUP(&new_expr, expr_ptr); ZVAL_DUP(&new_expr, expr_ptr);
expr_ptr = &new_expr; expr_ptr = &new_expr;
} }
} else if ((OP1_TYPE == IS_CV || OP1_TYPE == IS_VAR) && Z_ISREF_P(expr_ptr)) { } else if (OP1_TYPE == IS_CV) {
expr_ptr = Z_REFVAL_P(expr_ptr); ZVAL_DEREF(expr_ptr);
if (Z_REFCOUNTED_P(expr_ptr)) Z_ADDREF_P(expr_ptr); if (Z_REFCOUNTED_P(expr_ptr)) {
FREE_OP1_IF_VAR(); Z_ADDREF_P(expr_ptr);
} else if (OP1_TYPE == IS_CV && Z_REFCOUNTED_P(expr_ptr)) { }
Z_ADDREF_P(expr_ptr); } else /* if (OP1_TYPE == IS_VAR) */ {
if (UNEXPECTED(Z_ISREF_P(expr_ptr))) {
zend_refcounted *ref = Z_COUNTED_P(expr_ptr);
expr_ptr = Z_REFVAL_P(expr_ptr);
if (UNEXPECTED(--GC_REFCOUNT(ref) == 0)) {
ZVAL_COPY_VALUE(&new_expr, expr_ptr);
expr_ptr = &new_expr;
efree_size(ref, sizeof(zend_reference));
} else if (Z_OPT_REFCOUNTED_P(expr_ptr)) {
Z_ADDREF_P(expr_ptr);
}
}
} }
} }
@ -5349,7 +5395,7 @@ ZEND_VM_HANDLER(74, ZEND_UNSET_VAR, CONST|TMPVAR|CV, UNUSED|CONST|VAR)
if (!--GC_REFCOUNT(garbage)) { if (!--GC_REFCOUNT(garbage)) {
ZVAL_UNDEF(var); ZVAL_UNDEF(var);
_zval_dtor_func_for_ptr(garbage ZEND_FILE_LINE_CC); zval_dtor_func_for_ptr(garbage);
} else { } else {
GC_ZVAL_CHECK_POSSIBLE_ROOT(var); GC_ZVAL_CHECK_POSSIBLE_ROOT(var);
ZVAL_UNDEF(var); ZVAL_UNDEF(var);

File diff suppressed because it is too large Load diff