From d85113702cc6030a9149f518c55953ccb6933a7c Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Fri, 20 Feb 2015 13:28:26 +0300 Subject: [PATCH] Lazy duplication of op_array->static_variables --- Zend/zend_compile.c | 10 +++++++++- Zend/zend_execute.c | 6 ++++++ Zend/zend_opcode.c | 12 ++++++++---- Zend/zend_types.h | 3 +++ ext/opcache/ZendAccelerator.c | 18 +++++++++++++++--- ext/opcache/zend_accelerator_util_funcs.c | 17 +++++------------ ext/opcache/zend_persist.c | 18 ++++++++++++++++-- ext/opcache/zend_persist_calc.c | 9 +++++++-- ext/reflection/php_reflection.c | 6 ++++++ 9 files changed, 75 insertions(+), 24 deletions(-) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 36055258f6..67b6183159 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -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); diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 539b1e4dd7..00a68613fc 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -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)) { diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index d75cdd0ff3..999ee65957 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -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) { diff --git a/Zend/zend_types.h b/Zend/zend_types.h index 0f6fabd3c4..33fb78b6b9 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -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) diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index 350bd209eb..62e62e1821 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -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; } } diff --git a/ext/opcache/zend_accelerator_util_funcs.c b/ext/opcache/zend_accelerator_util_funcs.c index dc717ca768..d902604767 100644 --- a/ext/opcache/zend_accelerator_util_funcs.c +++ b/ext/opcache/zend_accelerator_util_funcs.c @@ -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) diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index 5b32baa64b..634dbda4f5 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -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)) { diff --git a/ext/opcache/zend_persist_calc.c b/ext/opcache/zend_persist_calc.c index c5523e7d89..2f211e43cb 100644 --- a/ext/opcache/zend_persist_calc.c +++ b/ext/opcache/zend_persist_calc.c @@ -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)) { diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 1f16c4da78..80e04046ed 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -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); } -- 2.40.0