Lazy duplication of op_array->static_variables
authorDmitry Stogov <dmitry@zend.com>
Fri, 20 Feb 2015 10:28:26 +0000 (13:28 +0300)
committerDmitry Stogov <dmitry@zend.com>
Fri, 20 Feb 2015 10:28:26 +0000 (13:28 +0300)
Zend/zend_compile.c
Zend/zend_execute.c
Zend/zend_opcode.c
Zend/zend_types.h
ext/opcache/ZendAccelerator.c
ext/opcache/zend_accelerator_util_funcs.c
ext/opcache/zend_persist.c
ext/opcache/zend_persist_calc.c
ext/reflection/php_reflection.c

index 36055258f6f1695468a5f448b1c16130cc118437..67b61831598e2f6d4e2c689315ffdb9997f3d2be 100644 (file)
@@ -865,7 +865,9 @@ ZEND_API void function_add_ref(zend_function *function) /* {{{ */
 
                (*op_array->refcount)++;
                if (op_array->static_variables) {
-                       op_array->static_variables = zend_array_dup(op_array->static_variables);
+                       if (!(GC_FLAGS(op_array->static_variables) & IS_ARRAY_IMMUTABLE)) {
+                               GC_REFCOUNT(op_array->static_variables)++;
+                       }
                }
                op_array->run_time_cache = NULL;
        } else if (function->type == ZEND_INTERNAL_FUNCTION) {
@@ -3092,6 +3094,12 @@ static void zend_compile_static_var_common(zend_ast *var_ast, zval *value, zend_
                zend_hash_init(CG(active_op_array)->static_variables, 8, NULL, ZVAL_PTR_DTOR, 0);
        }
 
+       if (GC_REFCOUNT(CG(active_op_array)->static_variables) > 1) {
+               if (!(GC_FLAGS(CG(active_op_array)->static_variables) & IS_ARRAY_IMMUTABLE)) {
+                       GC_REFCOUNT(CG(active_op_array)->static_variables)--;
+               }
+               CG(active_op_array)->static_variables = zend_array_dup(CG(active_op_array)->static_variables);
+       }
        zend_hash_update(CG(active_op_array)->static_variables, Z_STR(var_node.u.constant), value);
 
        opline = zend_emit_op(&result, by_ref ? ZEND_FETCH_W : ZEND_FETCH_R, &var_node, NULL);
index 539b1e4dd7cefb4eb567928bbf41650131b07dd2..00a68613fcbb58517422d4e68dde11eb356e52a3 100644 (file)
@@ -1166,6 +1166,12 @@ static zend_always_inline HashTable *zend_get_target_symbol_table(zend_execute_d
        } else if (EXPECTED(fetch_type == ZEND_FETCH_STATIC)) {
                ZEND_ASSERT(EX(func)->op_array.static_variables != NULL);
                ht = EX(func)->op_array.static_variables;
+               if (GC_REFCOUNT(ht) > 1) {
+                       if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {
+                               GC_REFCOUNT(ht)--;
+                       }
+                       EX(func)->op_array.static_variables = ht = zend_array_dup(ht);
+               }
        } else {
                ZEND_ASSERT(fetch_type == ZEND_FETCH_LOCAL);
                if (!EX(symbol_table)) {
index d75cdd0ff3f73a20a510238feb00bcb8688b9f1e..999ee659571a59e7e595f38cfd9144b38b838b0b 100644 (file)
@@ -130,7 +130,8 @@ ZEND_API void zend_function_dtor(zval *zv)
 
 ZEND_API void zend_cleanup_op_array_data(zend_op_array *op_array)
 {
-       if (op_array->static_variables) {
+       if (op_array->static_variables &&
+           !(GC_FLAGS(op_array->static_variables) & IS_ARRAY_IMMUTABLE)) {
                zend_hash_clean(op_array->static_variables);
        }
 }
@@ -317,9 +318,12 @@ ZEND_API void destroy_op_array(zend_op_array *op_array)
        zval *end;
        uint32_t i;
 
-       if (op_array->static_variables) {
-               zend_hash_destroy(op_array->static_variables);
-               FREE_HASHTABLE(op_array->static_variables);
+       if (op_array->static_variables &&
+           !(GC_FLAGS(op_array->static_variables) & IS_ARRAY_IMMUTABLE)) {
+           if (--GC_REFCOUNT(op_array->static_variables) == 0) {
+                       zend_array_destroy(op_array->static_variables);
+                       FREE_HASHTABLE(op_array->static_variables);
+               }
        }
 
        if (op_array->run_time_cache && !op_array->function_name) {
index 0f6fabd3c4a7620129da76b235aa1ac474d76056..33fb78b6b965addad0eeb8546f7281667ea34ebf 100644 (file)
@@ -345,6 +345,9 @@ static zend_always_inline zend_uchar zval_get_type(const zval* pz) {
 #define IS_STR_CONSTANT             (1<<3) /* constant index */
 #define IS_STR_CONSTANT_UNQUALIFIED (1<<4) /* the same as IS_CONSTANT_UNQUALIFIED */
 
+/* array flags */
+#define IS_ARRAY_IMMUTABLE                     (1<<1) /* the same as IS_TYPE_IMMUTABLE */
+
 /* object flags (zval.value->gc.u.flags) */
 #define IS_OBJ_APPLY_COUNT                     0x07
 #define IS_OBJ_DESTRUCTOR_CALLED       (1<<3)
index 350bd209eb43d67b3a082473fb9c34931fe1940c..62e62e18214df228fa03fc67a4b6170802650aee 100644 (file)
@@ -1973,7 +1973,11 @@ static int accel_clean_non_persistent_function(zval *zv)
                return ZEND_HASH_APPLY_STOP;
        } else {
                if (function->op_array.static_variables) {
-                       accel_fast_hash_destroy(function->op_array.static_variables);
+                       if (!(GC_FLAGS(function->op_array.static_variables) & IS_ARRAY_IMMUTABLE)) {
+                               if (--GC_REFCOUNT(function->op_array.static_variables) == 0) {
+                                       accel_fast_hash_destroy(function->op_array.static_variables);
+                               }
+                       }
                        function->op_array.static_variables = NULL;
                }
                return ZEND_HASH_APPLY_REMOVE;
@@ -2025,7 +2029,11 @@ static void zend_accel_fast_shutdown(void)
                                break;
                        } else {
                                if (func->op_array.static_variables) {
-                                       accel_fast_hash_destroy(func->op_array.static_variables);
+                                       if (!(GC_FLAGS(func->op_array.static_variables) & IS_ARRAY_IMMUTABLE)) {
+                                               if (--GC_REFCOUNT(func->op_array.static_variables) == 0) {
+                                                       accel_fast_hash_destroy(func->op_array.static_variables);
+                                               }
+                                       }
                                }
                                zend_accel_fast_del_bucket(EG(function_table), _idx-1, _p);
                        }
@@ -2043,7 +2051,11 @@ static void zend_accel_fast_shutdown(void)
                                        ZEND_HASH_FOREACH_PTR(&ce->function_table, func) {
                                                if (func->type == ZEND_USER_FUNCTION) {
                                                        if (func->op_array.static_variables) {
-                                                               accel_fast_hash_destroy(func->op_array.static_variables);
+                                                               if (!(GC_FLAGS(func->op_array.static_variables) & IS_ARRAY_IMMUTABLE)) {
+                                                                       if (--GC_REFCOUNT(func->op_array.static_variables) == 0) {
+                                                                               accel_fast_hash_destroy(func->op_array.static_variables);
+                                                                       }
+                                                               }
                                                                func->op_array.static_variables = NULL;
                                                        }
                                                }
index dc717ca768b1ca580bca2ae3c8b49bdb5cca3ac0..d90260476777a13c2f8c6a540f7887e4ceef30de 100644 (file)
@@ -55,8 +55,11 @@ static void zend_accel_destroy_zend_function(zval *zv)
 
        if (function->type == ZEND_USER_FUNCTION) {
                if (function->op_array.static_variables) {
-
-                       FREE_HASHTABLE(function->op_array.static_variables);
+                       if (!(GC_FLAGS(function->op_array.static_variables) & IS_ARRAY_IMMUTABLE)) {
+                               if (--GC_REFCOUNT(function->op_array.static_variables) == 0) {
+                                       FREE_HASHTABLE(function->op_array.static_variables);
+                               }
+                       }
                        function->op_array.static_variables = NULL;
                }
        }
@@ -372,16 +375,6 @@ static zend_always_inline void zend_prepare_function_for_execution(zend_op_array
        /* protect reference count */
        op_array->refcount = &zend_accel_refcount;
        (*op_array->refcount) = ZEND_PROTECTED_REFCOUNT;
-
-       /* copy statics */
-       if (UNEXPECTED(op_array->static_variables)) {
-               HashTable *shared_statics = op_array->static_variables;
-
-               ALLOC_HASHTABLE(op_array->static_variables);
-               GC_REFCOUNT(op_array->static_variables) = 1;
-               GC_TYPE(op_array->static_variables) = IS_ARRAY;
-               zend_hash_clone_zval(op_array->static_variables, shared_statics, 0);
-       }
 }
 
 static void zend_hash_clone_methods(HashTable *ht, HashTable *source, zend_class_entry *old_ce, zend_class_entry *ce)
index 5b32baa64b5e4e50c27c451f40df65b6300bdb1b..634dbda4f54e5b0d34f9798c528d3cdab9ba37cf 100644 (file)
@@ -204,6 +204,7 @@ static void zend_persist_zval(zval *z)
                                        /* make immutable array */
                                        Z_TYPE_FLAGS_P(z) = IS_TYPE_IMMUTABLE;
                                        GC_REFCOUNT(Z_COUNTED_P(z)) = 2;
+                                       GC_FLAGS(Z_COUNTED_P(z)) |= IS_ARRAY_IMMUTABLE;
                                        Z_ARRVAL_P(z)->u.flags &= ~HASH_FLAG_APPLY_PROTECTION;
                                }
                        }
@@ -258,6 +259,7 @@ static void zend_persist_zval_const(zval *z)
                                        /* make immutable array */
                                        Z_TYPE_FLAGS_P(z) = IS_TYPE_IMMUTABLE;
                                        GC_REFCOUNT(Z_COUNTED_P(z)) = 2;
+                                       GC_FLAGS(Z_COUNTED_P(z)) |= IS_ARRAY_IMMUTABLE;
                                        Z_ARRVAL_P(z)->u.flags &= ~HASH_FLAG_APPLY_PROTECTION;
                                }
                        }
@@ -278,6 +280,8 @@ static void zend_persist_zval_const(zval *z)
                        } else {
                                zend_accel_store(Z_AST_P(z), sizeof(zend_ast_ref));
                                Z_ASTVAL_P(z) = zend_persist_ast(Z_ASTVAL_P(z));
+                               Z_TYPE_FLAGS_P(z) = IS_TYPE_CONSTANT | IS_TYPE_IMMUTABLE;
+                               GC_REFCOUNT(Z_COUNTED_P(z)) = 2;
                        }
                        break;
        }
@@ -313,8 +317,18 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc
        }
 
        if (op_array->static_variables) {
-               zend_hash_persist(op_array->static_variables, zend_persist_zval);
-               zend_accel_store(op_array->static_variables, sizeof(HashTable));
+               HashTable *stored = zend_shared_alloc_get_xlat_entry(op_array->static_variables);
+
+               if (stored) {
+                       op_array->static_variables = stored;
+               } else {
+                       zend_hash_persist(op_array->static_variables, zend_persist_zval_const);
+                       zend_accel_store(op_array->static_variables, sizeof(HashTable));
+                       /* make immutable array */
+                       GC_REFCOUNT(op_array->static_variables) = 2;
+                       GC_TYPE_INFO(op_array->static_variables) = IS_ARRAY | (IS_ARRAY_IMMUTABLE << 8);
+                       op_array->static_variables->u.flags &= ~HASH_FLAG_APPLY_PROTECTION;
+               }
        }
 
        if (zend_shared_alloc_get_xlat_entry(op_array->opcodes)) {
index c5523e7d89af6a2edc1343b1da0aff732eecd606..2f211e43cb8d1a7bf92f0ff90d3a0c05e316e9f5 100644 (file)
@@ -150,8 +150,13 @@ static void zend_persist_op_array_calc_ex(zend_op_array *op_array)
        }
 
        if (op_array->static_variables) {
-               ADD_DUP_SIZE(op_array->static_variables, sizeof(HashTable));
-               zend_hash_persist_calc(op_array->static_variables, zend_persist_zval_calc);
+               if (!zend_shared_alloc_get_xlat_entry(op_array->static_variables)) {
+                       HashTable *old = op_array->static_variables;
+
+                       ADD_DUP_SIZE(op_array->static_variables, sizeof(HashTable));
+                       zend_hash_persist_calc(op_array->static_variables, zend_persist_zval_calc);
+                       zend_shared_alloc_register_xlat_entry(old, op_array->static_variables);
+               }
        }
 
        if (zend_shared_alloc_get_xlat_entry(op_array->opcodes)) {
index 1f16c4da78b0c3cad8740b53977b166c4641967f..80e04046ed17e75a9b2c1916d836cb28cbba25af 100644 (file)
@@ -1852,6 +1852,12 @@ ZEND_METHOD(reflection_function, getStaticVariables)
        /* Return an empty array in case no static variables exist */
        array_init(return_value);
        if (fptr->type == ZEND_USER_FUNCTION && fptr->op_array.static_variables != NULL) {
+               if (GC_REFCOUNT(fptr->op_array.static_variables) > 1) {
+                       if (!(GC_FLAGS(fptr->op_array.static_variables) & IS_ARRAY_IMMUTABLE)) {
+                               GC_REFCOUNT(fptr->op_array.static_variables)--;
+                       }
+                       fptr->op_array.static_variables = zend_array_dup(fptr->op_array.static_variables);
+               }
                zend_hash_apply_with_argument(fptr->op_array.static_variables, (apply_func_arg_t) zval_update_constant_inline_change, fptr->common.scope);
                zend_hash_copy(Z_ARRVAL_P(return_value), fptr->op_array.static_variables, zval_add_ref);
        }