Skip to content

Commit 7f67513

Browse files
committed
Lazy function copying from op_cache SHM into process memory
1 parent 30f7997 commit 7f67513

13 files changed

+107
-36
lines changed

Zend/zend_API.c

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3007,6 +3007,7 @@ static zend_always_inline int zend_is_callable_check_func(int check_flags, zval
30073007
fcc->calling_scope = NULL;
30083008

30093009
if (!ce_org) {
3010+
zend_function *func;
30103011
zend_string *lmname;
30113012

30123013
/* Check if function with given name exists.
@@ -3015,20 +3016,20 @@ static zend_always_inline int zend_is_callable_check_func(int check_flags, zval
30153016
/* Skip leading \ */
30163017
ZSTR_ALLOCA_ALLOC(lmname, Z_STRLEN_P(callable) - 1, use_heap);
30173018
zend_str_tolower_copy(ZSTR_VAL(lmname), Z_STRVAL_P(callable) + 1, Z_STRLEN_P(callable));
3018-
zv = zend_hash_find(EG(function_table), lmname);
3019+
func = zend_fetch_function(lmname);
30193020
ZSTR_ALLOCA_FREE(lmname, use_heap);
30203021
} else {
30213022
lmname = Z_STR_P(callable);
3022-
zv = zend_hash_find(EG(function_table), lmname);
3023-
if (!zv) {
3023+
func = zend_fetch_function(lmname);
3024+
if (!func) {
30243025
ZSTR_ALLOCA_ALLOC(lmname, Z_STRLEN_P(callable), use_heap);
30253026
zend_str_tolower_copy(ZSTR_VAL(lmname), Z_STRVAL_P(callable), Z_STRLEN_P(callable));
3026-
zv = zend_hash_find(EG(function_table), lmname);
3027+
func = zend_fetch_function(lmname);
30273028
ZSTR_ALLOCA_FREE(lmname, use_heap);
30283029
}
30293030
}
3030-
if (EXPECTED(zv != NULL)) {
3031-
fcc->function_handler = Z_PTR_P(zv);
3031+
if (EXPECTED(func != NULL)) {
3032+
fcc->function_handler = func;
30323033
return 1;
30333034
}
30343035
}

Zend/zend_compile.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1077,7 +1077,9 @@ ZEND_API int do_bind_function(const zend_op_array *op_array, const zend_op *opli
10771077
if (function->op_array.refcount) {
10781078
(*function->op_array.refcount)++;
10791079
}
1080-
function->op_array.static_variables = NULL; /* NULL out the unbound function */
1080+
if (!(function->op_array.fn_flags & ZEND_ACC_IMMUTABLE)) {
1081+
function->op_array.static_variables = NULL; /* NULL out the unbound function */
1082+
}
10811083
return SUCCESS;
10821084
}
10831085
}

Zend/zend_compile.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,6 @@ typedef struct _zend_oparray_context {
202202
* Function and method flags
203203
*
204204
* Free flags:
205-
* 0x10
206205
* 0x20
207206
* 0x2000000
208207
*/
@@ -213,6 +212,9 @@ typedef struct _zend_oparray_context {
213212
#define ZEND_ACC_FINAL 0x04
214213
#define ZEND_ACC_IMPLEMENTED_ABSTRACT 0x08
215214

215+
/* Imuttable op_array (lazy loading) */
216+
#define ZEND_ACC_IMMUTABLE 0x10
217+
216218
/* method flags (visibility) */
217219
/* The order of those must be kept - public < protected < private */
218220
#define ZEND_ACC_PUBLIC 0x100

Zend/zend_execute.c

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2531,6 +2531,47 @@ static zend_never_inline void ZEND_FASTCALL init_func_run_time_cache(zend_op_arr
25312531
}
25322532
/* }}} */
25332533

2534+
static zend_always_inline zend_function* ZEND_FASTCALL init_func_run_time_cache_i(zend_op_array *op_array, zval *zv) /* {{{ */
2535+
{
2536+
ZEND_ASSERT(op_array->run_time_cache == NULL);
2537+
if (op_array->fn_flags & ZEND_ACC_IMMUTABLE) {
2538+
zend_op_array *new_op_array = zend_arena_alloc(&CG(arena), sizeof(zend_op_array) + op_array->cache_size);
2539+
2540+
Z_PTR_P(zv) = new_op_array;
2541+
memcpy(new_op_array, op_array, sizeof(zend_op_array));
2542+
new_op_array->fn_flags &= ~ZEND_ACC_IMMUTABLE;
2543+
new_op_array->run_time_cache = (void**)(new_op_array + 1);
2544+
memset(new_op_array->run_time_cache, 0, new_op_array->cache_size);
2545+
return (zend_function*)new_op_array;
2546+
} else {
2547+
op_array->run_time_cache = zend_arena_alloc(&CG(arena), op_array->cache_size);
2548+
memset(op_array->run_time_cache, 0, op_array->cache_size);
2549+
return (zend_function*)op_array;
2550+
}
2551+
}
2552+
/* }}} */
2553+
2554+
static zend_never_inline zend_function* ZEND_FASTCALL init_func_run_time_cache_ex(zend_op_array *op_array, zval *zv) /* {{{ */
2555+
{
2556+
return init_func_run_time_cache_i(op_array, zv);
2557+
}
2558+
/* }}} */
2559+
2560+
ZEND_API zend_function * ZEND_FASTCALL zend_fetch_function(zend_string *name) /* {{{ */
2561+
{
2562+
zval *zv = zend_hash_find(EG(function_table), name);
2563+
2564+
if (EXPECTED(zv != NULL)) {
2565+
zend_function *fbc = Z_FUNC_P(zv);
2566+
2567+
if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
2568+
fbc = (zend_function*)init_func_run_time_cache_i(&fbc->op_array, zv);
2569+
}
2570+
return fbc;
2571+
}
2572+
return NULL;
2573+
} /* }}} */
2574+
25342575
static zend_always_inline void i_init_code_execute_data(zend_execute_data *execute_data, zend_op_array *op_array, zval *return_value) /* {{{ */
25352576
{
25362577
ZEND_ASSERT(EX(func) == (zend_function*)op_array);
@@ -2563,8 +2604,7 @@ ZEND_API void zend_init_func_execute_data(zend_execute_data *ex, zend_op_array *
25632604

25642605
EX(prev_execute_data) = EG(current_execute_data);
25652606
if (!op_array->run_time_cache) {
2566-
op_array->run_time_cache = zend_arena_alloc(&CG(arena), op_array->cache_size);
2567-
memset(op_array->run_time_cache, 0, op_array->cache_size);
2607+
init_func_run_time_cache(op_array);
25682608
}
25692609
i_init_func_execute_data(op_array, return_value, 1 EXECUTE_DATA_CC);
25702610

@@ -2924,6 +2964,9 @@ static zend_never_inline zend_execute_data *zend_init_dynamic_call_string(zend_s
29242964
return NULL;
29252965
}
29262966
}
2967+
if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
2968+
init_func_run_time_cache(&fbc->op_array);
2969+
}
29272970
} else {
29282971
if (ZSTR_VAL(function)[0] == '\\') {
29292972
lcname = zend_string_alloc(ZSTR_LEN(function) - 1, 0);
@@ -2939,13 +2982,12 @@ static zend_never_inline zend_execute_data *zend_init_dynamic_call_string(zend_s
29392982
zend_string_release_ex(lcname, 0);
29402983

29412984
fbc = Z_FUNC_P(func);
2985+
if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
2986+
fbc = init_func_run_time_cache_ex(&fbc->op_array, func);
2987+
}
29422988
called_scope = NULL;
29432989
}
29442990

2945-
if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
2946-
init_func_run_time_cache(&fbc->op_array);
2947-
}
2948-
29492991
return zend_vm_stack_push_call_frame(ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_DYNAMIC,
29502992
fbc, num_args, called_scope, NULL);
29512993
}

Zend/zend_execute.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,8 @@ ZEND_API zend_class_entry *zend_fetch_class(zend_string *class_name, int fetch_t
308308
ZEND_API zend_class_entry *zend_fetch_class_by_name(zend_string *class_name, const zval *key, int fetch_type);
309309
void zend_verify_abstract_class(zend_class_entry *ce);
310310

311+
ZEND_API zend_function * ZEND_FASTCALL zend_fetch_function(zend_string *name);
312+
311313
ZEND_API void zend_fetch_dimension_const(zval *result, zval *container, zval *dim, int type);
312314

313315
ZEND_API zval* zend_get_compiled_variable_value(const zend_execute_data *execute_data_ptr, uint32_t var);

Zend/zend_execute_API.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -902,9 +902,10 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, const zval *k
902902
}
903903

904904
if (!EG(autoload_func)) {
905-
zval *zv = zend_hash_find_ex(EG(function_table), ZSTR_KNOWN(ZEND_STR_MAGIC_AUTOLOAD), 1);
906-
if (zv) {
907-
EG(autoload_func) = (zend_function*)Z_PTR_P(zv);
905+
zend_function *func = zend_fetch_function(ZSTR_KNOWN(ZEND_STR_MAGIC_AUTOLOAD));
906+
907+
if (func) {
908+
EG(autoload_func) = func;
908909
} else {
909910
if (!key) {
910911
zend_string_release_ex(lc_name, 0);

Zend/zend_object_handlers.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1199,7 +1199,7 @@ ZEND_API zend_function *zend_get_call_trampoline_func(zend_class_entry *ce, zend
11991199
func->fn_flags |= ZEND_ACC_STATIC;
12001200
}
12011201
func->opcodes = &EG(call_trampoline_op);
1202-
1202+
func->run_time_cache = (void*)(intptr_t)-1;
12031203
func->reserved[0] = fbc;
12041204
func->scope = fbc->common.scope;
12051205
/* reserve space for arguments, local and temorary variables */

Zend/zend_vm_def.h

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3258,7 +3258,7 @@ ZEND_VM_HOT_HANDLER(59, ZEND_INIT_FCALL_BY_NAME, ANY, CONST, NUM|CACHE_SLOT)
32583258
}
32593259
fbc = Z_FUNC_P(func);
32603260
if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
3261-
init_func_run_time_cache(&fbc->op_array);
3261+
fbc = init_func_run_time_cache_ex(&fbc->op_array, func);
32623262
}
32633263
CACHE_PTR(opline->result.num, fbc);
32643264
}
@@ -3421,10 +3421,10 @@ ZEND_VM_HOT_HANDLER(69, ZEND_INIT_NS_FCALL_BY_NAME, ANY, CONST, NUM|CACHE_SLOT)
34213421
}
34223422
}
34233423
fbc = Z_FUNC_P(func);
3424-
CACHE_PTR(opline->result.num, fbc);
34253424
if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
3426-
init_func_run_time_cache(&fbc->op_array);
3425+
fbc = init_func_run_time_cache_ex(&fbc->op_array, func);
34273426
}
3427+
CACHE_PTR(opline->result.num, fbc);
34283428
}
34293429

34303430
call = zend_vm_stack_push_call_frame(ZEND_CALL_NESTED_FUNCTION,
@@ -3452,10 +3452,10 @@ ZEND_VM_HOT_HANDLER(61, ZEND_INIT_FCALL, NUM, CONST, NUM|CACHE_SLOT)
34523452
ZEND_VM_DISPATCH_TO_HELPER(zend_undefined_function_helper, function_name, fname);
34533453
}
34543454
fbc = Z_FUNC_P(func);
3455-
CACHE_PTR(opline->result.num, fbc);
34563455
if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
3457-
init_func_run_time_cache(&fbc->op_array);
3456+
fbc = init_func_run_time_cache_ex(&fbc->op_array, func);
34583457
}
3458+
CACHE_PTR(opline->result.num, fbc);
34593459
}
34603460

34613461
call = zend_vm_stack_push_call_frame_ex(
@@ -7032,10 +7032,20 @@ ZEND_VM_HANDLER(153, ZEND_DECLARE_LAMBDA_FUNCTION, CONST, UNUSED)
70327032
zval *zfunc;
70337033
zval *object;
70347034
zend_class_entry *called_scope;
7035+
zend_function *fbc;
70357036

70367037
zfunc = zend_hash_find_ex(EG(function_table), Z_STR_P(RT_CONSTANT(opline, opline->op1)), 1);
70377038
ZEND_ASSERT(zfunc != NULL && Z_FUNC_P(zfunc)->type == ZEND_USER_FUNCTION);
70387039

7040+
fbc = Z_PTR_P(zfunc);
7041+
if (fbc->common.fn_flags & ZEND_ACC_IMMUTABLE) {
7042+
zend_function *new_func = zend_arena_alloc(&CG(arena), sizeof(zend_op_array));
7043+
7044+
memcpy(new_func, fbc, sizeof(zend_op_array));
7045+
new_func->common.fn_flags &= ~ZEND_ACC_IMMUTABLE;
7046+
Z_PTR_P(zfunc) = fbc = new_func;
7047+
}
7048+
70397049
if (Z_TYPE(EX(This)) == IS_OBJECT) {
70407050
called_scope = Z_OBJCE(EX(This));
70417051
if (UNEXPECTED((Z_FUNC_P(zfunc)->common.fn_flags & ZEND_ACC_STATIC) ||

Zend/zend_vm_execute.h

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2088,7 +2088,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_FCALL_BY_NAME
20882088
}
20892089
fbc = Z_FUNC_P(func);
20902090
if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
2091-
init_func_run_time_cache(&fbc->op_array);
2091+
fbc = init_func_run_time_cache_ex(&fbc->op_array, func);
20922092
}
20932093
CACHE_PTR(opline->result.num, fbc);
20942094
}
@@ -2176,10 +2176,10 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_NS_FCALL_BY_N
21762176
}
21772177
}
21782178
fbc = Z_FUNC_P(func);
2179-
CACHE_PTR(opline->result.num, fbc);
21802179
if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
2181-
init_func_run_time_cache(&fbc->op_array);
2180+
fbc = init_func_run_time_cache_ex(&fbc->op_array, func);
21822181
}
2182+
CACHE_PTR(opline->result.num, fbc);
21832183
}
21842184

21852185
call = zend_vm_stack_push_call_frame(ZEND_CALL_NESTED_FUNCTION,
@@ -2207,10 +2207,10 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_FCALL_SPEC_CO
22072207
ZEND_VM_TAIL_CALL(zend_undefined_function_helper_SPEC(fname ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
22082208
}
22092209
fbc = Z_FUNC_P(func);
2210-
CACHE_PTR(opline->result.num, fbc);
22112210
if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
2212-
init_func_run_time_cache(&fbc->op_array);
2211+
fbc = init_func_run_time_cache_ex(&fbc->op_array, func);
22132212
}
2213+
CACHE_PTR(opline->result.num, fbc);
22142214
}
22152215

22162216
call = zend_vm_stack_push_call_frame_ex(
@@ -9103,10 +9103,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_C
91039103
zval *zfunc;
91049104
zval *object;
91059105
zend_class_entry *called_scope;
9106+
zend_function *fbc;
91069107

91079108
zfunc = zend_hash_find_ex(EG(function_table), Z_STR_P(RT_CONSTANT(opline, opline->op1)), 1);
91089109
ZEND_ASSERT(zfunc != NULL && Z_FUNC_P(zfunc)->type == ZEND_USER_FUNCTION);
91099110

9111+
fbc = Z_PTR_P(zfunc);
9112+
if (fbc->common.fn_flags & ZEND_ACC_IMMUTABLE) {
9113+
zend_function *new_func = zend_arena_alloc(&CG(arena), sizeof(zend_op_array));
9114+
9115+
memcpy(new_func, fbc, sizeof(zend_op_array));
9116+
new_func->common.fn_flags &= ~ZEND_ACC_IMMUTABLE;
9117+
Z_PTR_P(zfunc) = fbc = new_func;
9118+
}
9119+
91109120
if (Z_TYPE(EX(This)) == IS_OBJECT) {
91119121
called_scope = Z_OBJCE(EX(This));
91129122
if (UNEXPECTED((Z_FUNC_P(zfunc)->common.fn_flags & ZEND_ACC_STATIC) ||

ext/opcache/zend_accelerator_util_funcs.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -544,12 +544,12 @@ static void zend_accel_function_hash_copy_from_shm(HashTable *target, HashTable
544544
if (UNEXPECTED(t != NULL)) {
545545
if (EXPECTED(ZSTR_LEN(p->key) > 0) && EXPECTED(ZSTR_VAL(p->key)[0] == 0)) {
546546
/* Mangled key */
547-
zend_hash_update_ptr(target, p->key, ARENA_REALLOC(Z_PTR(p->val)));
547+
zend_hash_update_ptr(target, p->key, Z_PTR(p->val));
548548
} else {
549549
goto failure;
550550
}
551551
} else {
552-
_zend_hash_append_ptr_ex(target, p->key, ARENA_REALLOC(Z_PTR(p->val)), 1);
552+
_zend_hash_append_ptr_ex(target, p->key, Z_PTR(p->val), 1);
553553
}
554554
}
555555
target->nInternalPointer = 0;

0 commit comments

Comments
 (0)