((v) & ~GC_COLOR)
#define GC_INFO_GET_COLOR(v) \
(((zend_uintptr_t)(v)) & GC_COLOR)
-#define GC_INFO_SET_ADDRESS(v, a) \
- do {(v) = ((v) & GC_COLOR) | (a);} while (0)
-#define GC_INFO_SET_COLOR(v, c) \
- do {(v) = ((v) & ~GC_COLOR) | (c);} while (0)
-#define GC_INFO_SET_BLACK(v) \
- do {(v) = (v) & ~GC_COLOR;} while (0)
-#define GC_INFO_SET_PURPLE(v) \
- do {(v) = (v) | GC_COLOR;} while (0)
/* one (0) is reserved */
#define GC_ROOT_BUFFER_MAX_ENTRIES 10001
# define GC_TRACE(str)
#endif
-#define GC_REF_SET_ADDRESS(ref, a) \
- GC_INFO_SET_ADDRESS(GC_INFO(ref), a)
+#define GC_REF_SET_INFO(ref, info) do { \
+ GC_TYPE_INFO(ref) = ((info) << GC_INFO_SHIFT) | \
+ (GC_TYPE_INFO(ref) & (GC_TYPE_MASK | GC_FLAGS_MASK)); \
+ } while (0)
+#define GC_REF_SET_ADDRESS(ref, a) do {\
+ GC_TYPE_INFO(ref) = ((info) << GC_INFO_SHIFT) | \
+ (GC_TYPE_INFO(ref) & (GC_TYPE_MASK | GC_FLAGS_MASK | (GC_COLOR << GC_INFO_SHIFT))); \
+ } while (0)
#define GC_REF_GET_COLOR(ref) \
GC_INFO_GET_COLOR(GC_INFO(ref))
-#define GC_REF_SET_COLOR(ref, c) \
- do { GC_TRACE_SET_COLOR(ref, c); GC_INFO_SET_COLOR(GC_INFO(ref), c); } while (0)
-#define GC_REF_SET_BLACK(ref) \
- do { GC_TRACE_SET_COLOR(ref, GC_BLACK); GC_INFO_SET_BLACK(GC_INFO(ref)); } while (0)
-#define GC_REF_SET_PURPLE(ref) \
- do { GC_TRACE_SET_COLOR(ref, GC_PURPLE); GC_INFO_SET_PURPLE(GC_INFO(ref)); } while (0)
+#define GC_REF_SET_COLOR(ref, c) do { \
+ GC_TYPE_INFO(ref) = ((c) << GC_INFO_SHIFT) | \
+ (GC_TYPE_INFO(ref) & (GC_TYPE_MASK | GC_FLAGS_MASK | ~(GC_COLOR << GC_INFO_SHIFT))); \
+ } while (0)
+#define GC_REF_SET_BLACK(ref) do { \
+ GC_TYPE_INFO(ref) &= ~(GC_COLOR << GC_INFO_SHIFT); \
+ } while (0)
+#define GC_REF_SET_PURPLE(ref) do { \
+ GC_TYPE_INFO(ref) |= (GC_COLOR << GC_INFO_SHIFT); \
+ } while (0)
#if ZEND_GC_DEBUG > 1
static const char *gc_color_name(uint32_t color) {
}
GC_TRACE_SET_COLOR(ref, GC_PURPLE);
- GC_INFO(ref) = (newRoot - GC_G(buf)) | GC_PURPLE;
+ GC_REF_SET_INFO(ref, (newRoot - GC_G(buf)) | GC_PURPLE);
newRoot->ref = ref;
newRoot->next = GC_G(roots).next;
if (GC_REF_GET_COLOR(ref) != GC_BLACK) {
GC_TRACE_SET_COLOR(ref, GC_PURPLE);
}
- GC_INFO(ref) = 0;
+ GC_REF_SET_INFO(ref, 0);
/* updete next root that is going to be freed */
if (GC_G(next_to_free) == root) {
GC_G(unused) = buf->prev;
#if 1
/* optimization: color is already GC_BLACK (0) */
- GC_INFO(ref) = buf - GC_G(buf);
+ GC_REF_SET_INFO(ref, buf - GC_G(buf));
#else
GC_REF_SET_ADDRESS(ref, buf - GC_G(buf));
#endif
GC_G(first_unused)++;
#if 1
/* optimization: color is already GC_BLACK (0) */
- GC_INFO(ref) = buf - GC_G(buf);
+ GC_REF_SET_INFO(ref, buf - GC_G(buf));
#else
GC_REF_SET_ADDRESS(ref, buf - GC_G(buf));
#endif
buf = GC_G(additional_buffer)->buf + GC_G(additional_buffer)->used;
#if 1
/* optimization: color is already GC_BLACK (0) */
- GC_INFO(ref) = GC_ROOT_BUFFER_MAX_ENTRIES + GC_G(additional_buffer)->used;
+ GC_REF_SET_INFO(ref, GC_ROOT_BUFFER_MAX_ENTRIES + GC_G(additional_buffer)->used);
#else
GC_REF_SET_ADDRESS(ref, GC_ROOT_BUFFER_MAX_ENTRIES) + GC_G(additional_buffer)->used;
#endif
} else {
gc_remove_from_additional_roots(current);
}
- GC_INFO(current->ref) = 0; /* reset GC_ADDRESS() and keep GC_BLACK */
+ GC_REF_SET_INFO(current->ref, 0); /* reset GC_ADDRESS() and keep GC_BLACK */
}
current = next;
}
} else {
gc_remove_from_additional_roots(root);
}
- GC_INFO(ref) = 0;
+ GC_REF_SET_INFO(ref, 0);
root = NULL;
} else {
GC_REMOVE_FROM_BUFFER(ref);
if (!(OBJ_FLAGS(obj) & IS_OBJ_DESTRUCTOR_CALLED)) {
GC_TRACE_REF(obj, "calling destructor");
- OBJ_FLAGS(obj) |= IS_OBJ_DESTRUCTOR_CALLED;
+ GC_ADD_FLAGS(obj, IS_OBJ_DESTRUCTOR_CALLED);
if (obj->handlers->dtor_obj
&& (obj->handlers->dtor_obj != zend_objects_destroy_object
|| obj->ce->destructor)) {
zend_object *obj = (zend_object*)p;
EG(objects_store).object_buckets[obj->handle] = SET_OBJ_INVALID(obj);
- GC_TYPE(obj) = IS_NULL;
+ GC_TYPE_INFO(obj) = IS_NULL |
+ (GC_TYPE_INFO(obj) & ~GC_TYPE_MASK);
if (!(OBJ_FLAGS(obj) & IS_OBJ_FREE_CALLED)) {
- OBJ_FLAGS(obj) |= IS_OBJ_FREE_CALLED;
+ GC_ADD_FLAGS(obj, IS_OBJ_FREE_CALLED);
if (obj->handlers->free_obj) {
GC_ADDREF(obj);
obj->handlers->free_obj(obj);
} else if (GC_TYPE(p) == IS_ARRAY) {
zend_array *arr = (zend_array*)p;
- GC_TYPE(arr) = IS_NULL;
+ GC_TYPE_INFO(arr) = IS_NULL |
+ (GC_TYPE_INFO(arr) & ~GC_TYPE_MASK);
/* GC may destroy arrays with rc>1. This is valid and safe. */
HT_ALLOW_COW_VIOLATION(arr);
static zend_always_inline void gc_check_possible_root(zend_refcounted *ref)
{
- if (GC_TYPE(ref) == IS_REFERENCE) {
+ if (GC_TYPE_INFO(ref) == IS_REFERENCE) {
zval *zv = &((zend_reference*)ref)->val;
if (!Z_REFCOUNTED_P(zv)) {
zend_object *obj = objects->object_buckets[i];
if (IS_OBJ_VALID(obj)) {
if (!(OBJ_FLAGS(obj) & IS_OBJ_DESTRUCTOR_CALLED)) {
- OBJ_FLAGS(obj) |= IS_OBJ_DESTRUCTOR_CALLED;
+ GC_ADD_FLAGS(obj, IS_OBJ_DESTRUCTOR_CALLED);
if (obj->handlers->dtor_obj
&& (obj->handlers->dtor_obj != zend_objects_destroy_object
zend_object *obj = *obj_ptr;
if (IS_OBJ_VALID(obj)) {
- OBJ_FLAGS(obj) |= IS_OBJ_DESTRUCTOR_CALLED;
+ GC_ADD_FLAGS(obj, IS_OBJ_DESTRUCTOR_CALLED);
}
obj_ptr++;
} while (obj_ptr != end);
obj = *obj_ptr;
if (IS_OBJ_VALID(obj)) {
if (!(OBJ_FLAGS(obj) & IS_OBJ_FREE_CALLED)) {
- OBJ_FLAGS(obj) |= IS_OBJ_FREE_CALLED;
+ GC_ADD_FLAGS(obj, IS_OBJ_FREE_CALLED);
if (obj->handlers->free_obj && obj->handlers->free_obj != zend_object_std_dtor) {
GC_ADDREF(obj);
obj->handlers->free_obj(obj);
obj = *obj_ptr;
if (IS_OBJ_VALID(obj)) {
if (!(OBJ_FLAGS(obj) & IS_OBJ_FREE_CALLED)) {
- OBJ_FLAGS(obj) |= IS_OBJ_FREE_CALLED;
+ GC_ADD_FLAGS(obj, IS_OBJ_FREE_CALLED);
if (obj->handlers->free_obj) {
GC_ADDREF(obj);
obj->handlers->free_obj(obj);
ZEND_ASSERT(GC_REFCOUNT(object) == 0);
if (!(OBJ_FLAGS(object) & IS_OBJ_DESTRUCTOR_CALLED)) {
- OBJ_FLAGS(object) |= IS_OBJ_DESTRUCTOR_CALLED;
+ GC_ADD_FLAGS(object, IS_OBJ_DESTRUCTOR_CALLED);
if (object->handlers->dtor_obj
&& (object->handlers->dtor_obj != zend_objects_destroy_object
EG(objects_store).object_buckets[handle] = SET_OBJ_INVALID(object);
if (!(OBJ_FLAGS(object) & IS_OBJ_FREE_CALLED)) {
- OBJ_FLAGS(object) |= IS_OBJ_FREE_CALLED;
+ GC_ADD_FLAGS(object, IS_OBJ_FREE_CALLED);
if (object->handlers->free_obj) {
GC_ADDREF(object);
object->handlers->free_obj(object);
/* Called when the ctor was terminated by an exception */
static zend_always_inline void zend_object_store_ctor_failed(zend_object *obj)
{
- OBJ_FLAGS(obj) |= IS_OBJ_DESTRUCTOR_CALLED;
+ GC_ADD_FLAGS(obj, IS_OBJ_DESTRUCTOR_CALLED);
}
#define ZEND_OBJECTS_STORE_HANDLERS 0, zend_object_std_dtor, zend_objects_destroy_object, zend_objects_clone_obj
zval val;
GC_SET_REFCOUNT(str, 1);
- GC_FLAGS(str) |= IS_STR_INTERNED | flags;
+ GC_ADD_FLAGS(str, IS_STR_INTERNED | flags);
ZVAL_INTERNED_STR(&val, str);
#define GC_ADDREF_EX(p, rc) zend_gc_addref_ex(&(p)->gc, rc)
#define GC_DELREF_EX(p, rc) zend_gc_delref_ex(&(p)->gc, rc)
-#define GC_TYPE(p) (p)->gc.u.v.type
-#define GC_FLAGS(p) (p)->gc.u.v.flags
-#define GC_INFO(p) (p)->gc.u.v.gc_info
+#define GC_TYPE_MASK 0x000000ff
+#define GC_FLAGS_MASK 0x0000ff00
+#define GC_INFO_MASK 0xffff0000
+#define GC_FLAGS_SHIFT 8
+#define GC_INFO_SHIFT 16
+
+static zend_always_inline zend_uchar zval_gc_type(uint32_t gc_type_info) {
+ return (gc_type_info & GC_TYPE_MASK);
+}
+
+static zend_always_inline zend_uchar zval_gc_flags(uint32_t gc_type_info) {
+ return (gc_type_info >> GC_FLAGS_SHIFT) & (GC_FLAGS_MASK >> GC_FLAGS_SHIFT);
+}
+
+static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) {
+ return (gc_type_info >> GC_INFO_SHIFT);
+}
+
#define GC_TYPE_INFO(p) (p)->gc.u.type_info
+#define GC_TYPE(p) zval_gc_type(GC_TYPE_INFO(p))
+#define GC_FLAGS(p) zval_gc_flags(GC_TYPE_INFO(p))
+#define GC_INFO(p) zval_gc_info(GC_TYPE_INFO(p))
+
+#define GC_ADD_FLAGS(p, flags) do { \
+ GC_TYPE_INFO(p) |= (flags) << GC_FLAGS_SHIFT; \
+ } while (0)
+#define GC_DEL_FLAGS(p, flags) do { \
+ GC_TYPE_INFO(p) &= ~((flags) << GC_FLAGS_SHIFT); \
+ } while (0)
#define Z_GC_TYPE(zval) GC_TYPE(Z_COUNTED(zval))
#define Z_GC_TYPE_P(zval_p) Z_GC_TYPE(*(zval_p))
#define Z_GC_TYPE_INFO(zval) GC_TYPE_INFO(Z_COUNTED(zval))
#define Z_GC_TYPE_INFO_P(zval_p) Z_GC_TYPE_INFO(*(zval_p))
-#define GC_FLAGS_SHIFT 8
-#define GC_INFO_SHIFT 16
-#define GC_INFO_MASK 0xffff0000
-
/* zval.value->gc.u.v.flags (common flags) */
#define GC_COLLECTABLE (1<<0)
#define GC_PROTECTED (1<<1) /* used for recursion detection */
(GC_FLAGS(p) & GC_PROTECTED)
#define GC_PROTECT_RECURSION(p) do { \
- GC_FLAGS(p) |= GC_PROTECTED; \
+ GC_ADD_FLAGS(p, GC_PROTECTED); \
} while (0)
#define GC_UNPROTECT_RECURSION(p) do { \
- GC_FLAGS(p) &= ~GC_PROTECTED; \
+ GC_DEL_FLAGS(p, GC_PROTECTED); \
} while (0)
#define Z_IS_RECURSIVE(zval) GC_IS_RECURSIVE(Z_COUNTED(zval))
} \
} while (0)
# define GC_MAKE_PERSISTENT_LOCAL(p) do { \
- GC_FLAGS(p) |= GC_PERSISTENT_LOCAL; \
+ GC_ADD_FLAGS(p, GC_PERSISTENT_LOCAL); \
} while (0)
#else
# define ZEND_RC_MOD_CHECK(p) \
STRTAB_COLLISION(s) = *hash_slot;
*hash_slot = STRTAB_STR_TO_POS(&ZCSG(interned_strings), s);
GC_SET_REFCOUNT(s, 1);
-#if 1
- /* optimized single assignment */
GC_TYPE_INFO(s) = IS_STRING | ((IS_STR_INTERNED | IS_STR_PERMANENT) << GC_FLAGS_SHIFT);
-#else
- GC_TYPE(s) = IS_STRING;
- GC_FLAGS(s) = IS_STR_INTERNED | IS_STR_PERMANENT;
-#endif
ZSTR_H(s) = h;
ZSTR_LEN(s) = ZSTR_LEN(str);
memcpy(ZSTR_VAL(s), ZSTR_VAL(str), ZSTR_LEN(s) + 1);
ZEND_ASSERT(IS_UNSERIALIZED(ptr)); \
/* script->corrupted shows if the script in SHM or not */ \
if (EXPECTED(script->corrupted)) { \
- GC_FLAGS(ptr) |= IS_STR_INTERNED; \
- GC_FLAGS(ptr) &= ~IS_STR_PERMANENT; \
+ GC_ADD_FLAGS(ptr, IS_STR_INTERNED); \
+ GC_DEL_FLAGS(ptr, IS_STR_PERMANENT); \
} \
(ptr) = (void*)((char*)(ptr) - (char*)script->mem); \
} \
(ptr) = (void*)((char*)buf + (size_t)(ptr)); \
/* script->corrupted shows if the script in SHM or not */ \
if (EXPECTED(!script->corrupted)) { \
- GC_FLAGS(ptr) |= IS_STR_INTERNED | IS_STR_PERMANENT; \
+ GC_ADD_FLAGS(ptr, IS_STR_INTERNED | IS_STR_PERMANENT); \
} else { \
- GC_FLAGS(ptr) |= IS_STR_INTERNED; \
- GC_FLAGS(ptr) &= ~IS_STR_PERMANENT; \
+ GC_ADD_FLAGS(ptr, IS_STR_INTERNED); \
+ GC_DEL_FLAGS(ptr, IS_STR_PERMANENT); \
} \
} \
} \
}
} else {
ret = str;
- GC_FLAGS(ret) |= IS_STR_INTERNED;
- GC_FLAGS(ret) &= ~IS_STR_PERMANENT;
+ GC_ADD_FLAGS(ret, IS_STR_INTERNED);
+ GC_DEL_FLAGS(ret, IS_STR_PERMANENT);
}
return ret;
}
#ifdef HAVE_OPCACHE_FILE_CACHE
#define zend_set_str_gc_flags(str) do { \
if (ZCG(accel_directives).file_cache_only) { \
- GC_FLAGS(str) = IS_STR_INTERNED; \
+ GC_TYPE_INFO(str) = IS_STRING | (IS_STR_INTERNED << GC_FLAGS_SHIFT); \
} else { \
- GC_FLAGS(str) = IS_STR_INTERNED | IS_STR_PERMANENT; \
+ GC_TYPE_INFO(str) = IS_STRING | ((IS_STR_INTERNED | IS_STR_PERMANENT) << GC_FLAGS_SHIFT); \
} \
} while (0)
#else
-#define zend_set_str_gc_flags(str) GC_FLAGS(str) = IS_STR_INTERNED | IS_STR_PERMANENT
+#define zend_set_str_gc_flags(str) do {\
+ GC_TYPE_INFO(str) = IS_STRING | ((IS_STR_INTERNED | IS_STR_PERMANENT) << GC_FLAGS_SHIFT); \
+} while (0)
#endif
#define zend_accel_store_string(str) do { \
/* make immutable array */
Z_TYPE_FLAGS_P(z) = 0;
GC_SET_REFCOUNT(Z_COUNTED_P(z), 2);
- GC_FLAGS(Z_COUNTED_P(z)) |= IS_ARRAY_IMMUTABLE;
+ GC_ADD_FLAGS(Z_COUNTED_P(z), IS_ARRAY_IMMUTABLE);
HT_FLAGS(Z_ARRVAL_P(z)) |= HASH_FLAG_STATIC_KEYS;
}
}
BG(serialize_lock)++;
if (call_user_function_ex(CG(function_table), zv, &wakeup_name, &retval, 0, 0, 1, NULL) == FAILURE || Z_ISUNDEF(retval)) {
wakeup_failed = 1;
- OBJ_FLAGS(Z_OBJ_P(zv)) |= IS_OBJ_DESTRUCTOR_CALLED;
+ GC_ADD_FLAGS(Z_OBJ_P(zv), IS_OBJ_DESTRUCTOR_CALLED);
}
BG(serialize_lock)--;
zval_ptr_dtor(&retval);
} else {
- OBJ_FLAGS(Z_OBJ_P(zv)) |= IS_OBJ_DESTRUCTOR_CALLED;
+ GC_ADD_FLAGS(Z_OBJ_P(zv), IS_OBJ_DESTRUCTOR_CALLED);
}
}
if (!process_nested_data(UNSERIALIZE_PASSTHRU, ht, elements, 1)) {
if (has_wakeup) {
ZVAL_DEREF(rval);
- OBJ_FLAGS(Z_OBJ_P(rval)) |= IS_OBJ_DESTRUCTOR_CALLED;
+ GC_ADD_FLAGS(Z_OBJ_P(rval), IS_OBJ_DESTRUCTOR_CALLED);
}
return 0;
}
BG(serialize_lock)++;
if (call_user_function_ex(CG(function_table), zv, &wakeup_name, &retval, 0, 0, 1, NULL) == FAILURE || Z_ISUNDEF(retval)) {
wakeup_failed = 1;
- OBJ_FLAGS(Z_OBJ_P(zv)) |= IS_OBJ_DESTRUCTOR_CALLED;
+ GC_ADD_FLAGS(Z_OBJ_P(zv), IS_OBJ_DESTRUCTOR_CALLED);
}
BG(serialize_lock)--;
zval_ptr_dtor(&retval);
} else {
- OBJ_FLAGS(Z_OBJ_P(zv)) |= IS_OBJ_DESTRUCTOR_CALLED;
+ GC_ADD_FLAGS(Z_OBJ_P(zv), IS_OBJ_DESTRUCTOR_CALLED);
}
}
if (!process_nested_data(UNSERIALIZE_PASSTHRU, ht, elements, 1)) {
if (has_wakeup) {
ZVAL_DEREF(rval);
- OBJ_FLAGS(Z_OBJ_P(rval)) |= IS_OBJ_DESTRUCTOR_CALLED;
+ GC_ADD_FLAGS(Z_OBJ_P(rval), IS_OBJ_DESTRUCTOR_CALLED);
}
return 0;
}