From 1cec2e7271b789b84601f8acf385950af1bb0c7c Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Thu, 25 Jun 2015 23:33:21 +0200 Subject: [PATCH 01/11] Fix arena on small sizes (size < sizeof(zend_arena)) --- Zend/zend_arena.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zend/zend_arena.h b/Zend/zend_arena.h index b39493a7999..fc663192c7f 100644 --- a/Zend/zend_arena.h +++ b/Zend/zend_arena.h @@ -33,7 +33,7 @@ struct _zend_arena { static zend_always_inline zend_arena* zend_arena_create(size_t size) { - zend_arena *arena = (zend_arena*)emalloc(size); + zend_arena *arena = (zend_arena*)emalloc(size + ZEND_MM_ALIGNED_SIZE(sizeof(zend_arena))); arena->ptr = (char*) arena + ZEND_MM_ALIGNED_SIZE(sizeof(zend_arena)); arena->end = (char*) arena + size; From fef649f4067823a1f96f85340cf715e5877310bc Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Fri, 26 Jun 2015 01:02:27 +0200 Subject: [PATCH 02/11] Add proper temporary cleaning upon frame abortion --- Zend/zend_compile.h | 2 + Zend/zend_execute.c | 14 +++++-- Zend/zend_opcode.c | 95 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 108 insertions(+), 3 deletions(-) diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index a3bc17f4256..43297a4a052 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -351,6 +351,7 @@ struct _zend_op_array { int last_var; uint32_t T; zend_string **vars; + uint32_t *T_liveliness; int last_brk_cont; int last_try_catch; @@ -727,6 +728,7 @@ ZEND_API int zend_execute_scripts(int type, zval *retval, int file_count, ...); ZEND_API int open_file_for_scanning(zend_file_handle *file_handle); ZEND_API void init_op_array(zend_op_array *op_array, zend_uchar type, int initial_ops_size); ZEND_API void destroy_op_array(zend_op_array *op_array); +ZEND_API uint32_t *generate_var_liveliness_info(zend_op_array *op_array); ZEND_API void zend_destroy_file_handle(zend_file_handle *file_handle); ZEND_API void zend_cleanup_user_class_data(zend_class_entry *ce); ZEND_API void zend_cleanup_internal_class_data(zend_class_entry *ce); diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 4c60eb5ae18..32168a73ebf 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -2385,6 +2385,15 @@ static zend_always_inline zend_generator *zend_get_running_generator(zend_execut static zend_always_inline void i_cleanup_unfinished_execution(zend_execute_data *execute_data, uint32_t op_num, uint32_t catch_op_num) /* {{{ */ { int i; + + if (op_num < EX(func)->op_array.last) { + uint32_t *off = EX(func)->op_array.T_liveliness + EX(func)->op_array.T_liveliness[op_num]; + uint32_t *until = EX(func)->op_array.T_liveliness + EX(func)->op_array.T_liveliness[op_num + 1]; + while (off < until) { + zval_ptr_dtor_nogc(ZEND_CALL_VAR_NUM(execute_data, *(off++))); + } + } + if (UNEXPECTED(EX(call))) { zend_execute_data *call = EX(call); zend_op *opline = EX(func)->op_array.opcodes + op_num; @@ -2511,9 +2520,8 @@ static zend_always_inline void i_cleanup_unfinished_execution(zend_execute_data if (!catch_op_num || catch_op_num >= brk_cont->brk) { zend_op *brk_opline = &EX(func)->op_array.opcodes[brk_cont->brk]; - if (brk_opline->opcode == ZEND_FREE) { - zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var)); - } else if (brk_opline->opcode == ZEND_FE_FREE) { + /* ZEND_FREE opcodes are handled by temporary variable freeing */ + if (brk_opline->opcode == ZEND_FE_FREE) { zval *var = EX_VAR(brk_opline->op1.var); if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) { zend_hash_iterator_del(Z_FE_ITER_P(var)); diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index a971a5e9004..9955cf83ba1 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -391,6 +391,7 @@ ZEND_API void destroy_op_array(zend_op_array *op_array) } if (op_array->fn_flags & ZEND_ACC_DONE_PASS_TWO) { zend_llist_apply_with_argument(&zend_extensions, (llist_apply_with_arg_func_t) zend_extension_op_array_dtor_handler, op_array); + efree(op_array->T_liveliness); } if (op_array->arg_info) { int32_t num_args = op_array->num_args; @@ -751,6 +752,9 @@ ZEND_API int pass_two(zend_op_array *op_array) op_array->literals = (zval*)erealloc(op_array->literals, sizeof(zval) * op_array->last_literal); CG(context).literals_size = op_array->last_literal; } + + op_array->T_liveliness = generate_var_liveliness_info(op_array); + opline = op_array->opcodes; end = opline + op_array->last; while (opline < end) { @@ -840,6 +844,97 @@ int pass_two_wrapper(zval *el) return pass_two((zend_op_array *) Z_PTR_P(el)); } +typedef struct _var_live_info { + struct _var_live_info *next; + uint32_t start; + uint32_t end; +} var_live_info; + +ZEND_API uint32_t *generate_var_liveliness_info(zend_op_array *op_array) +{ + zend_arena *arena = zend_arena_create((sizeof(var_live_info) + 2 * sizeof(var_live_info *)) * op_array->T + sizeof(zend_op *) * op_array->last); + var_live_info **TsTop = zend_arena_alloc(&arena, sizeof(var_live_info *) * op_array->T); + var_live_info **Ts = zend_arena_alloc(&arena, sizeof(var_live_info *) * op_array->T); + uint32_t *op_list = zend_arena_alloc(&arena, sizeof(uint32_t) * op_array->last), *op_cur = op_list; + int i, op_live_total = 0; + uint32_t *info, info_off = op_array->last; + + for (i = 0; i < op_array->T; i++) { + TsTop[i] = Ts[i] = zend_arena_alloc(&arena, sizeof(var_live_info)); + memset(Ts[i], 0, sizeof(var_live_info)); + } + + zend_op *end_op = op_array->opcodes + op_array->last; + zend_op *cur_op = op_array->opcodes; + for (; cur_op < end_op; cur_op++) { + if ((cur_op->result_type & (IS_VAR | IS_TMP_VAR)) && !(cur_op->result_type & EXT_TYPE_UNUSED) + && (cur_op->opcode != ZEND_QM_ASSIGN || (cur_op + 1)->opcode != ZEND_JMP)) { + var_live_info *T = Ts[cur_op->result.var]; + if (T->end) { + T->next = zend_arena_alloc(&arena, sizeof(var_live_info)); + memset(T = T->next, 0, sizeof(var_live_info)); + } + if (T->start == 0) { + /* Objects created via ZEND_NEW are only fully initialized after the DO_FCALL (constructor call) */ + if (cur_op->opcode == ZEND_NEW) { + T->start = cur_op->op2.opline_num - 1; + } else { + T->start = cur_op - op_array->opcodes; + } + } + } + if ((cur_op->op1_type & (IS_VAR | IS_TMP_VAR)) && cur_op->opcode != ZEND_FE_FREE) { + Ts[cur_op->op1.var]->end = cur_op - op_array->opcodes; + } + if (cur_op->op2_type & (IS_VAR | IS_TMP_VAR)) { + Ts[cur_op->op2.var]->end = cur_op - op_array->opcodes; + } + /* All the expression ops where an exception may be thrown in */ + if (cur_op->opcode == ZEND_DO_FCALL + || cur_op->opcode == ZEND_DO_FCALL_BY_NAME + || cur_op->opcode == ZEND_YIELD + || cur_op->opcode == ZEND_YIELD_FROM + || cur_op->opcode == ZEND_DO_ICALL + || cur_op->opcode == ZEND_DO_UCALL) { + *op_cur++ = cur_op - op_array->opcodes; + } + } + + *op_cur = -1; + for (op_cur = op_list; 1 + *op_cur; op_cur++) { + uint32_t off = *op_cur; + for (i = 0; i < op_array->T; i++) { + var_live_info *T = TsTop[i]; + do { + op_live_total += off > T->start && off < T->end; + } while ((T = T->next)); + } + } + + info = emalloc(op_live_total * sizeof(uint32_t) + op_array->last * sizeof(uint32_t)); + op_cur = op_list; + for (i = 0; i < op_array->last; i++) { + uint32_t off = *op_cur; + uint32_t j; + info[i] = info_off; + if (i == *op_cur) { + op_cur++; + for (j = 0; j < op_array->T; j++) { + var_live_info *T = TsTop[j]; + do { + if (off > T->start && off < T->end) { + info[info_off++] = op_array->last_var + j; + } + } while ((T = T->next)); + } + } + } + + zend_arena_destroy(arena); + + return info; +} + int print_class(zend_class_entry *class_entry) { printf("Class %s:\n", ZSTR_VAL(class_entry->name)); From cbcaedbd78199897e5cacffd700b706f21590abf Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Fri, 26 Jun 2015 01:04:09 +0200 Subject: [PATCH 03/11] Add opcache support for cleaning in optimization step (Opcache seems to have a few unrelated issues which blow up together with that patch) --- ext/opcache/Optimizer/zend_optimizer.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ext/opcache/Optimizer/zend_optimizer.c b/ext/opcache/Optimizer/zend_optimizer.c index dc69d2511ec..3ceb688fcab 100644 --- a/ext/opcache/Optimizer/zend_optimizer.c +++ b/ext/opcache/Optimizer/zend_optimizer.c @@ -482,6 +482,8 @@ static void zend_accel_optimize(zend_op_array *op_array, { zend_op *opline, *end; + efree(op_array->T_liveliness); + /* Revert pass_two() */ opline = op_array->opcodes; end = opline + op_array->last; @@ -527,6 +529,8 @@ static void zend_accel_optimize(zend_op_array *op_array, /* Do actual optimizations */ zend_optimize(op_array, ctx); + op_array->T_liveliness = generate_var_liveliness_info(op_array); + /* Redo pass_two() */ opline = op_array->opcodes; end = opline + op_array->last; From 02585f77085427baea48448c134a96c542af3337 Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Fri, 26 Jun 2015 16:20:55 +0200 Subject: [PATCH 04/11] Hmm, we need temporary info for all the opcodes --- Zend/zend_opcode.c | 34 +++++++++++++--------------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index 9955cf83ba1..2113ee2d93c 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -855,7 +855,6 @@ ZEND_API uint32_t *generate_var_liveliness_info(zend_op_array *op_array) zend_arena *arena = zend_arena_create((sizeof(var_live_info) + 2 * sizeof(var_live_info *)) * op_array->T + sizeof(zend_op *) * op_array->last); var_live_info **TsTop = zend_arena_alloc(&arena, sizeof(var_live_info *) * op_array->T); var_live_info **Ts = zend_arena_alloc(&arena, sizeof(var_live_info *) * op_array->T); - uint32_t *op_list = zend_arena_alloc(&arena, sizeof(uint32_t) * op_array->last), *op_cur = op_list; int i, op_live_total = 0; uint32_t *info, info_off = op_array->last; @@ -885,44 +884,37 @@ ZEND_API uint32_t *generate_var_liveliness_info(zend_op_array *op_array) } if ((cur_op->op1_type & (IS_VAR | IS_TMP_VAR)) && cur_op->opcode != ZEND_FE_FREE) { Ts[cur_op->op1.var]->end = cur_op - op_array->opcodes; + if (cur_op->opcode == ZEND_OP_DATA) { + Ts[cur_op->op1.var]->end--; + } } if (cur_op->op2_type & (IS_VAR | IS_TMP_VAR)) { Ts[cur_op->op2.var]->end = cur_op - op_array->opcodes; - } - /* All the expression ops where an exception may be thrown in */ - if (cur_op->opcode == ZEND_DO_FCALL - || cur_op->opcode == ZEND_DO_FCALL_BY_NAME - || cur_op->opcode == ZEND_YIELD - || cur_op->opcode == ZEND_YIELD_FROM - || cur_op->opcode == ZEND_DO_ICALL - || cur_op->opcode == ZEND_DO_UCALL) { - *op_cur++ = cur_op - op_array->opcodes; + if (cur_op->opcode == ZEND_OP_DATA) { + Ts[cur_op->op2.var]->end--; + } } } - *op_cur = -1; - for (op_cur = op_list; 1 + *op_cur; op_cur++) { - uint32_t off = *op_cur; - for (i = 0; i < op_array->T; i++) { - var_live_info *T = TsTop[i]; + for (i = 0; i < op_array->last; i++) { + int j; + for (j = 0; j < op_array->T; j++) { + var_live_info *T = TsTop[j]; do { - op_live_total += off > T->start && off < T->end; + op_live_total += i > T->start && i < T->end; } while ((T = T->next)); } } info = emalloc(op_live_total * sizeof(uint32_t) + op_array->last * sizeof(uint32_t)); - op_cur = op_list; for (i = 0; i < op_array->last; i++) { - uint32_t off = *op_cur; uint32_t j; info[i] = info_off; - if (i == *op_cur) { - op_cur++; + if (op_array->opcodes[i].opcode != ZEND_THROW) { for (j = 0; j < op_array->T; j++) { var_live_info *T = TsTop[j]; do { - if (off > T->start && off < T->end) { + if (i > T->start && i < T->end) { info[info_off++] = op_array->last_var + j; } } while ((T = T->next)); From 333a7c4a8813a45dc79ce55b8e9c0a0b98671e13 Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Sat, 27 Jun 2015 22:40:21 +0200 Subject: [PATCH 05/11] Exempt ROPE temporaries from freeing --- Zend/zend_opcode.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index 2113ee2d93c..0b11722f973 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -866,8 +866,10 @@ ZEND_API uint32_t *generate_var_liveliness_info(zend_op_array *op_array) zend_op *end_op = op_array->opcodes + op_array->last; zend_op *cur_op = op_array->opcodes; for (; cur_op < end_op; cur_op++) { + if ((cur_op->result_type & (IS_VAR | IS_TMP_VAR)) && !(cur_op->result_type & EXT_TYPE_UNUSED) - && (cur_op->opcode != ZEND_QM_ASSIGN || (cur_op + 1)->opcode != ZEND_JMP)) { + && (cur_op->opcode != ZEND_QM_ASSIGN || (cur_op + 1)->opcode != ZEND_JMP) + && cur_op->opcode != ZEND_ROPE_INIT && cur_op->opcode != ZEND_ROPE_ADD) { var_live_info *T = Ts[cur_op->result.var]; if (T->end) { T->next = zend_arena_alloc(&arena, sizeof(var_live_info)); @@ -882,7 +884,9 @@ ZEND_API uint32_t *generate_var_liveliness_info(zend_op_array *op_array) } } } - if ((cur_op->op1_type & (IS_VAR | IS_TMP_VAR)) && cur_op->opcode != ZEND_FE_FREE) { + if ((cur_op->op1_type & (IS_VAR | IS_TMP_VAR)) + && cur_op->opcode != ZEND_FE_FREE + && cur_op->opcode != ZEND_ROPE_ADD && cur_op->opcode != ZEND_ROPE_END) { Ts[cur_op->op1.var]->end = cur_op - op_array->opcodes; if (cur_op->opcode == ZEND_OP_DATA) { Ts[cur_op->op1.var]->end--; From 6538269bfa5bcbad34fc2f051b0fd5e4ebf2ff00 Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Wed, 1 Jul 2015 13:05:52 +0200 Subject: [PATCH 06/11] Fix opcache support --- Zend/zend_opcode.c | 5 ++++- ext/opcache/Optimizer/zend_optimizer.c | 22 ++++++++++++++++++++++ ext/opcache/zend_file_cache.c | 2 ++ ext/opcache/zend_persist.c | 4 ++++ ext/opcache/zend_persist_calc.c | 4 ++++ 5 files changed, 36 insertions(+), 1 deletion(-) diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index 0b11722f973..617adae25a0 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -65,6 +65,7 @@ void init_op_array(zend_op_array *op_array, zend_uchar type, int initial_ops_siz op_array->vars = NULL; op_array->T = 0; + op_array->T_liveliness = NULL; op_array->function_name = NULL; op_array->filename = zend_get_compiled_filename(); @@ -389,9 +390,11 @@ ZEND_API void destroy_op_array(zend_op_array *op_array) if (op_array->try_catch_array) { efree(op_array->try_catch_array); } + if (op_array->T_liveliness) { + efree(op_array->T_liveliness); + } if (op_array->fn_flags & ZEND_ACC_DONE_PASS_TWO) { zend_llist_apply_with_argument(&zend_extensions, (llist_apply_with_arg_func_t) zend_extension_op_array_dtor_handler, op_array); - efree(op_array->T_liveliness); } if (op_array->arg_info) { int32_t num_args = op_array->num_args; diff --git a/ext/opcache/Optimizer/zend_optimizer.c b/ext/opcache/Optimizer/zend_optimizer.c index 3ceb688fcab..44c400ca3fd 100644 --- a/ext/opcache/Optimizer/zend_optimizer.c +++ b/ext/opcache/Optimizer/zend_optimizer.c @@ -529,6 +529,21 @@ static void zend_accel_optimize(zend_op_array *op_array, /* Do actual optimizations */ zend_optimize(op_array, ctx); + opline = op_array->opcodes; + end = opline + op_array->last; + while (opline < end) { + if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) { + opline->op1.var = EX_VAR_TO_NUM(opline->op1.var) - op_array->last_var; + } + if (opline->op2_type & (IS_VAR|IS_TMP_VAR)) { + opline->op2.var = EX_VAR_TO_NUM(opline->op2.var) - op_array->last_var; + } + if (opline->result_type & (IS_VAR|IS_TMP_VAR)) { + opline->result.var = EX_VAR_TO_NUM(opline->result.var) - op_array->last_var; + } + opline++; + } + op_array->T_liveliness = generate_var_liveliness_info(op_array); /* Redo pass_two() */ @@ -537,9 +552,16 @@ static void zend_accel_optimize(zend_op_array *op_array, while (opline < end) { if (opline->op1_type == IS_CONST) { ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline->op1); + } else if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) { + opline->op1.var = (uint32_t)(zend_intptr_t)ZEND_CALL_VAR_NUM(NULL, opline->op1.var + op_array->last_var); } if (opline->op2_type == IS_CONST) { ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline->op2); + } else if (opline->op2_type & (IS_VAR|IS_TMP_VAR)) { + opline->op2.var = (uint32_t)(zend_intptr_t)ZEND_CALL_VAR_NUM(NULL, opline->op2.var + op_array->last_var); + } + if (opline->result_type & (IS_VAR|IS_TMP_VAR)) { + opline->result.var = (uint32_t)(zend_intptr_t)ZEND_CALL_VAR_NUM(NULL, opline->result.var + op_array->last_var); } switch (opline->opcode) { case ZEND_JMP: diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c index 0781b91c9d3..084d9b9a9db 100644 --- a/ext/opcache/zend_file_cache.c +++ b/ext/opcache/zend_file_cache.c @@ -464,6 +464,7 @@ static void zend_file_cache_serialize_op_array(zend_op_array *op_arra SERIALIZE_STR(op_array->doc_comment); SERIALIZE_PTR(op_array->try_catch_array); SERIALIZE_PTR(op_array->prototype); + SERIALIZE_PTR(op_array->T_liveliness); } } @@ -987,6 +988,7 @@ static void zend_file_cache_unserialize_op_array(zend_op_array *op_arr UNSERIALIZE_STR(op_array->doc_comment); UNSERIALIZE_PTR(op_array->try_catch_array); UNSERIALIZE_PTR(op_array->prototype); + UNSERIALIZE_PTR(op_array->T_liveliness); } } diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index bbcb20713b0..dd39048d5c3 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -618,6 +618,10 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc zend_accel_store(op_array->try_catch_array, sizeof(zend_try_catch_element) * op_array->last_try_catch); } + if (op_array->T_liveliness) { + zend_accel_store(op_array->T_liveliness, sizeof(uint32_t) * op_array->T_liveliness[op_array->last - 1]); + } + if (op_array->vars) { if (already_stored) { persist_ptr = zend_shared_alloc_get_xlat_entry(op_array->vars); diff --git a/ext/opcache/zend_persist_calc.c b/ext/opcache/zend_persist_calc.c index d78cc592591..6ece7d16215 100644 --- a/ext/opcache/zend_persist_calc.c +++ b/ext/opcache/zend_persist_calc.c @@ -241,6 +241,10 @@ static void zend_persist_op_array_calc_ex(zend_op_array *op_array) ADD_DUP_SIZE(op_array->try_catch_array, sizeof(zend_try_catch_element) * op_array->last_try_catch); } + if (op_array->T_liveliness) { + ADD_DUP_SIZE(op_array->T_liveliness, sizeof(uint32_t) * op_array->T_liveliness[op_array->last - 1]); + } + if (op_array->vars) { int i; From 22d9d05350e35d180018d0bccbad6f173cb4797d Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Wed, 1 Jul 2015 16:48:46 +0200 Subject: [PATCH 07/11] Dumb bug in opcode.c (forgot to update Ts[i]) --- Zend/zend_opcode.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index 617adae25a0..b00b5c6a730 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -863,22 +863,23 @@ ZEND_API uint32_t *generate_var_liveliness_info(zend_op_array *op_array) for (i = 0; i < op_array->T; i++) { TsTop[i] = Ts[i] = zend_arena_alloc(&arena, sizeof(var_live_info)); - memset(Ts[i], 0, sizeof(var_live_info)); + Ts[i]->next = NULL; + Ts[i]->start = Ts[i]->end = -1; } zend_op *end_op = op_array->opcodes + op_array->last; zend_op *cur_op = op_array->opcodes; for (; cur_op < end_op; cur_op++) { - if ((cur_op->result_type & (IS_VAR | IS_TMP_VAR)) && !(cur_op->result_type & EXT_TYPE_UNUSED) && (cur_op->opcode != ZEND_QM_ASSIGN || (cur_op + 1)->opcode != ZEND_JMP) && cur_op->opcode != ZEND_ROPE_INIT && cur_op->opcode != ZEND_ROPE_ADD) { var_live_info *T = Ts[cur_op->result.var]; - if (T->end) { - T->next = zend_arena_alloc(&arena, sizeof(var_live_info)); - memset(T = T->next, 0, sizeof(var_live_info)); + if (~T->end) { + T = Ts[i] = T->next = zend_arena_alloc(&arena, sizeof(var_live_info)); + T->next = NULL; + T->start = T->end = -1; } - if (T->start == 0) { + if (!~T->start) { /* Objects created via ZEND_NEW are only fully initialized after the DO_FCALL (constructor call) */ if (cur_op->opcode == ZEND_NEW) { T->start = cur_op->op2.opline_num - 1; From 25b231b7841fa4078c65976cabdd843845a6cbe6 Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Wed, 1 Jul 2015 20:59:24 +0200 Subject: [PATCH 08/11] Speed algorithm up, more fail safety when reusing temporaries --- Zend/zend_opcode.c | 88 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 62 insertions(+), 26 deletions(-) diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index b00b5c6a730..b6b96e1fc16 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -580,7 +580,7 @@ static void zend_resolve_finally_call(zend_op_array *op_array, uint32_t op_num, fast_call_var = op_array->opcodes[op_array->try_catch_array[i].finally_end].op1.var; /* generate a FAST_CALL to finally block */ - start_op = get_next_op_number(op_array); + start_op = get_next_op_number(op_array); opline = get_next_op(op_array); opline->opcode = ZEND_FAST_CALL; @@ -853,13 +853,20 @@ typedef struct _var_live_info { uint32_t end; } var_live_info; +typedef struct _op_var_info { + struct _op_var_info *next; + uint32_t T; +} op_var_info; + ZEND_API uint32_t *generate_var_liveliness_info(zend_op_array *op_array) { - zend_arena *arena = zend_arena_create((sizeof(var_live_info) + 2 * sizeof(var_live_info *)) * op_array->T + sizeof(zend_op *) * op_array->last); + zend_arena *arena = zend_arena_create((sizeof(var_live_info) + sizeof(op_var_info) + 2 * sizeof(var_live_info *)) * op_array->T + (sizeof(op_var_info) + sizeof(op_var_info *) + sizeof(zend_op *)) * (op_array->last + 1)); var_live_info **TsTop = zend_arena_alloc(&arena, sizeof(var_live_info *) * op_array->T); var_live_info **Ts = zend_arena_alloc(&arena, sizeof(var_live_info *) * op_array->T); int i, op_live_total = 0; uint32_t *info, info_off = op_array->last; + op_var_info *opTsTop = zend_arena_alloc(&arena, sizeof(op_var_info) * (op_array->last + 1)); + op_var_info **opTs = zend_arena_alloc(&arena, sizeof(op_var_info *) * (op_array->last + 1)); for (i = 0; i < op_array->T; i++) { TsTop[i] = Ts[i] = zend_arena_alloc(&arena, sizeof(var_live_info)); @@ -867,15 +874,26 @@ ZEND_API uint32_t *generate_var_liveliness_info(zend_op_array *op_array) Ts[i]->start = Ts[i]->end = -1; } + for (i = 0; i <= op_array->last; i++) { + opTs[i] = &opTsTop[i]; + opTs[i]->T = -1; + opTs[i]->next = NULL; + } + zend_op *end_op = op_array->opcodes + op_array->last; zend_op *cur_op = op_array->opcodes; for (; cur_op < end_op; cur_op++) { if ((cur_op->result_type & (IS_VAR | IS_TMP_VAR)) && !(cur_op->result_type & EXT_TYPE_UNUSED) && (cur_op->opcode != ZEND_QM_ASSIGN || (cur_op + 1)->opcode != ZEND_JMP) - && cur_op->opcode != ZEND_ROPE_INIT && cur_op->opcode != ZEND_ROPE_ADD) { + && cur_op->opcode != ZEND_ROPE_INIT && cur_op->opcode != ZEND_ROPE_ADD + && cur_op->opcode != ZEND_BOOL && cur_op->opcode != ZEND_JMPZ_EX && cur_op->opcode != ZEND_JMPNZ_EX + && cur_op->opcode != ZEND_FAST_CALL && cur_op->opcode != ZEND_FAST_RET + && cur_op->opcode != ZEND_FETCH_CLASS && cur_op->opcode != ZEND_DECLARE_CLASS + && cur_op->opcode != ZEND_DECLARE_INHERITED_CLASS && cur_op->opcode != ZEND_DECLARE_INHERITED_CLASS_DELAYED + && cur_op->opcode != ZEND_DECLARE_ANON_CLASS && cur_op->opcode != ZEND_DECLARE_ANON_INHERITED_CLASS) { var_live_info *T = Ts[cur_op->result.var]; if (~T->end) { - T = Ts[i] = T->next = zend_arena_alloc(&arena, sizeof(var_live_info)); + T = Ts[cur_op->result.var] = T->next = zend_arena_alloc(&arena, sizeof(var_live_info)); T->next = NULL; T->start = T->end = -1; } @@ -891,42 +909,60 @@ ZEND_API uint32_t *generate_var_liveliness_info(zend_op_array *op_array) if ((cur_op->op1_type & (IS_VAR | IS_TMP_VAR)) && cur_op->opcode != ZEND_FE_FREE && cur_op->opcode != ZEND_ROPE_ADD && cur_op->opcode != ZEND_ROPE_END) { - Ts[cur_op->op1.var]->end = cur_op - op_array->opcodes; - if (cur_op->opcode == ZEND_OP_DATA) { - Ts[cur_op->op1.var]->end--; + var_live_info *T = Ts[cur_op->op1.var]; + if (~T->start) { + T->end = cur_op - op_array->opcodes; + if (cur_op->opcode == ZEND_OP_DATA) { + T->end--; + } } } if (cur_op->op2_type & (IS_VAR | IS_TMP_VAR)) { - Ts[cur_op->op2.var]->end = cur_op - op_array->opcodes; - if (cur_op->opcode == ZEND_OP_DATA) { - Ts[cur_op->op2.var]->end--; + var_live_info *T = Ts[cur_op->op2.var]; + if (~T->start) { + T->end = cur_op - op_array->opcodes; + if (cur_op->opcode == ZEND_OP_DATA) { + T->end--; + } } } } - for (i = 0; i < op_array->last; i++) { + for (i = 0; i < op_array->T; i++) { int j; - for (j = 0; j < op_array->T; j++) { - var_live_info *T = TsTop[j]; - do { - op_live_total += i > T->start && i < T->end; - } while ((T = T->next)); + var_live_info *T = TsTop[i]; + if (!~T->start) { + continue; } + + do { + ZEND_ASSERT(~T->end); + + for (j = T->start + 1; j < T->end; j++) { + if (op_array->opcodes[j].opcode != ZEND_THROW) { + op_var_info *opT = opTs[j]; + if (~opT->T) { + opT = opTs[j] = opT->next = zend_arena_alloc(&arena, sizeof(op_var_info)); + opT->next = NULL; + } + opT->T = i; + op_live_total++; + } + } + } while ((T = T->next)); } info = emalloc(op_live_total * sizeof(uint32_t) + op_array->last * sizeof(uint32_t)); + for (i = 0; i < op_array->last; i++) { - uint32_t j; + op_var_info *opT = &opTsTop[i]; info[i] = info_off; - if (op_array->opcodes[i].opcode != ZEND_THROW) { - for (j = 0; j < op_array->T; j++) { - var_live_info *T = TsTop[j]; - do { - if (i > T->start && i < T->end) { - info[info_off++] = op_array->last_var + j; - } - } while ((T = T->next)); - } + if (!~opT->T) { + opT = NULL; + } + while (opT) { + info[info_off++] = op_array->last_var + opT->T; + opT = opT->next; } } From 1adcf56a6e9f09e7ad06331d4d6280035b17a7d1 Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Wed, 1 Jul 2015 22:17:07 +0200 Subject: [PATCH 09/11] Fix off-by-one (opcache may remove last ZEND_RETURN) --- Zend/zend_opcode.c | 7 ++++--- ext/opcache/zend_persist.c | 2 +- ext/opcache/zend_persist_calc.c | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index b6b96e1fc16..cc2e65c7c29 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -864,7 +864,7 @@ ZEND_API uint32_t *generate_var_liveliness_info(zend_op_array *op_array) var_live_info **TsTop = zend_arena_alloc(&arena, sizeof(var_live_info *) * op_array->T); var_live_info **Ts = zend_arena_alloc(&arena, sizeof(var_live_info *) * op_array->T); int i, op_live_total = 0; - uint32_t *info, info_off = op_array->last; + uint32_t *info, info_off = op_array->last + 1; op_var_info *opTsTop = zend_arena_alloc(&arena, sizeof(op_var_info) * (op_array->last + 1)); op_var_info **opTs = zend_arena_alloc(&arena, sizeof(op_var_info *) * (op_array->last + 1)); @@ -952,7 +952,7 @@ ZEND_API uint32_t *generate_var_liveliness_info(zend_op_array *op_array) } while ((T = T->next)); } - info = emalloc(op_live_total * sizeof(uint32_t) + op_array->last * sizeof(uint32_t)); + info = emalloc(op_live_total * sizeof(uint32_t) + (op_array->last + 1) * sizeof(uint32_t)); for (i = 0; i < op_array->last; i++) { op_var_info *opT = &opTsTop[i]; @@ -961,10 +961,11 @@ ZEND_API uint32_t *generate_var_liveliness_info(zend_op_array *op_array) opT = NULL; } while (opT) { - info[info_off++] = op_array->last_var + opT->T; + info[info_off++] = op_array->last_var + 1 + opT->T; opT = opT->next; } } + info[i] = info_off; zend_arena_destroy(arena); diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index dd39048d5c3..309d44b80d6 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -619,7 +619,7 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc } if (op_array->T_liveliness) { - zend_accel_store(op_array->T_liveliness, sizeof(uint32_t) * op_array->T_liveliness[op_array->last - 1]); + zend_accel_store(op_array->T_liveliness, sizeof(uint32_t) * op_array->T_liveliness[op_array->last]); } if (op_array->vars) { diff --git a/ext/opcache/zend_persist_calc.c b/ext/opcache/zend_persist_calc.c index 6ece7d16215..00f613273b4 100644 --- a/ext/opcache/zend_persist_calc.c +++ b/ext/opcache/zend_persist_calc.c @@ -242,7 +242,7 @@ static void zend_persist_op_array_calc_ex(zend_op_array *op_array) } if (op_array->T_liveliness) { - ADD_DUP_SIZE(op_array->T_liveliness, sizeof(uint32_t) * op_array->T_liveliness[op_array->last - 1]); + ADD_DUP_SIZE(op_array->T_liveliness, sizeof(uint32_t) * op_array->T_liveliness[op_array->last]); } if (op_array->vars) { From 427dc58bbb93022d1c2077f874afcdb9dd82d5c5 Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Wed, 1 Jul 2015 22:49:12 +0200 Subject: [PATCH 10/11] Fix regression from last commit (+1 ?!) --- Zend/zend_opcode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index cc2e65c7c29..953e009edb0 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -961,7 +961,7 @@ ZEND_API uint32_t *generate_var_liveliness_info(zend_op_array *op_array) opT = NULL; } while (opT) { - info[info_off++] = op_array->last_var + 1 + opT->T; + info[info_off++] = op_array->last_var + opT->T; opT = opT->next; } } From fd0fcce81177717f3a05ac87192b5ed05eead0a1 Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Thu, 2 Jul 2015 20:00:33 +0200 Subject: [PATCH 11/11] Fix remaining issues with compacted temporaries --- Zend/zend_opcode.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index 953e009edb0..7195f876bf0 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -884,20 +884,21 @@ ZEND_API uint32_t *generate_var_liveliness_info(zend_op_array *op_array) zend_op *cur_op = op_array->opcodes; for (; cur_op < end_op; cur_op++) { if ((cur_op->result_type & (IS_VAR | IS_TMP_VAR)) && !(cur_op->result_type & EXT_TYPE_UNUSED) - && (cur_op->opcode != ZEND_QM_ASSIGN || (cur_op + 1)->opcode != ZEND_JMP) - && cur_op->opcode != ZEND_ROPE_INIT && cur_op->opcode != ZEND_ROPE_ADD && cur_op->opcode != ZEND_BOOL && cur_op->opcode != ZEND_JMPZ_EX && cur_op->opcode != ZEND_JMPNZ_EX - && cur_op->opcode != ZEND_FAST_CALL && cur_op->opcode != ZEND_FAST_RET - && cur_op->opcode != ZEND_FETCH_CLASS && cur_op->opcode != ZEND_DECLARE_CLASS - && cur_op->opcode != ZEND_DECLARE_INHERITED_CLASS && cur_op->opcode != ZEND_DECLARE_INHERITED_CLASS_DELAYED - && cur_op->opcode != ZEND_DECLARE_ANON_CLASS && cur_op->opcode != ZEND_DECLARE_ANON_INHERITED_CLASS) { + && (cur_op->opcode != ZEND_QM_ASSIGN || (cur_op + 1)->opcode != ZEND_JMP)) { var_live_info *T = Ts[cur_op->result.var]; if (~T->end) { T = Ts[cur_op->result.var] = T->next = zend_arena_alloc(&arena, sizeof(var_live_info)); T->next = NULL; T->start = T->end = -1; } - if (!~T->start) { + if (!~T->start + && cur_op->opcode != ZEND_CASE /* exception for opcache, is anyway bool */ + && cur_op->opcode != ZEND_ROPE_INIT && cur_op->opcode != ZEND_ROPE_ADD + && cur_op->opcode != ZEND_FAST_CALL && cur_op->opcode != ZEND_FAST_RET + && cur_op->opcode != ZEND_FETCH_CLASS && cur_op->opcode != ZEND_DECLARE_CLASS + && cur_op->opcode != ZEND_DECLARE_INHERITED_CLASS && cur_op->opcode != ZEND_DECLARE_INHERITED_CLASS_DELAYED + && cur_op->opcode != ZEND_DECLARE_ANON_CLASS && cur_op->opcode != ZEND_DECLARE_ANON_INHERITED_CLASS) { /* Objects created via ZEND_NEW are only fully initialized after the DO_FCALL (constructor call) */ if (cur_op->opcode == ZEND_NEW) { T->start = cur_op->op2.opline_num - 1; @@ -931,11 +932,12 @@ ZEND_API uint32_t *generate_var_liveliness_info(zend_op_array *op_array) for (i = 0; i < op_array->T; i++) { int j; var_live_info *T = TsTop[i]; - if (!~T->start) { - continue; - } do { + if (!~T->start) { + continue; + } + ZEND_ASSERT(~T->end); for (j = T->start + 1; j < T->end; j++) {