From 1cec2e7271b789b84601f8acf385950af1bb0c7c Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Thu, 25 Jun 2015 23:33:21 +0200 Subject: [PATCH 01/16] 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/16] 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/16] 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/16] 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/16] 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/16] 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/16] 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/16] 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/16] 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/16] 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/16] 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++) { From 501ae8aaac0a92368b50e9f342b04d7334d263f6 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 2 Jul 2015 22:31:48 +0300 Subject: [PATCH 12/16] Reverted changes to Zend/zend_arena.h. Reuse CG(arena) instead of creating a new one. --- Zend/zend_arena.h | 2 +- Zend/zend_opcode.c | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Zend/zend_arena.h b/Zend/zend_arena.h index fc663192c7f..b39493a7999 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_MM_ALIGNED_SIZE(sizeof(zend_arena))); + zend_arena *arena = (zend_arena*)emalloc(size); arena->ptr = (char*) arena + ZEND_MM_ALIGNED_SIZE(sizeof(zend_arena)); arena->end = (char*) arena + size; diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index 7195f876bf0..0ca73a37b83 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -860,16 +860,16 @@ typedef struct _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) + 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); + void *checkpoint = zend_arena_checkpoint(CG(arena)); + var_live_info **TsTop = zend_arena_alloc(&CG(arena), sizeof(var_live_info *) * op_array->T); + var_live_info **Ts = zend_arena_alloc(&CG(arena), sizeof(var_live_info *) * op_array->T); int i, op_live_total = 0; 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)); + op_var_info *opTsTop = zend_arena_alloc(&CG(arena), sizeof(op_var_info) * (op_array->last + 1)); + op_var_info **opTs = zend_arena_alloc(&CG(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)); + TsTop[i] = Ts[i] = zend_arena_alloc(&CG(arena), sizeof(var_live_info)); Ts[i]->next = NULL; Ts[i]->start = Ts[i]->end = -1; } @@ -888,7 +888,7 @@ ZEND_API uint32_t *generate_var_liveliness_info(zend_op_array *op_array) && (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 = Ts[cur_op->result.var] = T->next = zend_arena_alloc(&CG(arena), sizeof(var_live_info)); T->next = NULL; T->start = T->end = -1; } @@ -944,7 +944,7 @@ ZEND_API uint32_t *generate_var_liveliness_info(zend_op_array *op_array) 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 = opTs[j] = opT->next = zend_arena_alloc(&CG(arena), sizeof(op_var_info)); opT->next = NULL; } opT->T = i; @@ -969,7 +969,7 @@ ZEND_API uint32_t *generate_var_liveliness_info(zend_op_array *op_array) } info[i] = info_off; - zend_arena_destroy(arena); + zend_arena_release(&CG(arena), checkpoint); return info; } From 80c1d0d779e6e9609a211907838f3727aa7b301a Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Fri, 3 Jul 2015 11:05:39 +0300 Subject: [PATCH 13/16] Don't keep empty T_liveliness --- Zend/zend_execute.c | 2 +- Zend/zend_opcode.c | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 32168a73ebf..bcb285298fd 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -2386,7 +2386,7 @@ static zend_always_inline void i_cleanup_unfinished_execution(zend_execute_data { int i; - if (op_num < EX(func)->op_array.last) { + if (EX(func)->op_array.T_liveliness && 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) { diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index 0ca73a37b83..fc2a48cd8c7 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -954,6 +954,11 @@ ZEND_API uint32_t *generate_var_liveliness_info(zend_op_array *op_array) } while ((T = T->next)); } + if (!op_live_total) { + zend_arena_release(&CG(arena), checkpoint); + return NULL; + } + info = emalloc(op_live_total * sizeof(uint32_t) + (op_array->last + 1) * sizeof(uint32_t)); for (i = 0; i < op_array->last; i++) { From 8d1f88fe91e62b4333703c58b871d85b66fb7b70 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Fri, 3 Jul 2015 13:31:56 +0300 Subject: [PATCH 14/16] Use op_array->T_liveliness to free incomplete ropes and restore error_reporting level on exception --- Zend/zend_compile.c | 14 --------- Zend/zend_compile.h | 3 ++ Zend/zend_execute.c | 48 +++++++++++++++++------------- Zend/zend_opcode.c | 21 ++++++++----- ext/opcache/Optimizer/block_pass.c | 12 ++------ 5 files changed, 46 insertions(+), 52 deletions(-) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index df19f93be26..06581db9e53 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -6265,10 +6265,8 @@ void zend_compile_silence(znode *result, zend_ast *ast) /* {{{ */ { zend_ast *expr_ast = ast->child[0]; znode silence_node; - uint32_t begin_opline_num, end_opline_num; zend_brk_cont_element *brk_cont_element; - begin_opline_num = get_next_op_number(CG(active_op_array)); zend_emit_op_tmp(&silence_node, ZEND_BEGIN_SILENCE, NULL, NULL); if (expr_ast->kind == ZEND_AST_VAR) { @@ -6279,15 +6277,7 @@ void zend_compile_silence(znode *result, zend_ast *ast) /* {{{ */ zend_compile_expr(result, expr_ast); } - end_opline_num = get_next_op_number(CG(active_op_array)); zend_emit_op(NULL, ZEND_END_SILENCE, &silence_node, NULL); - - /* Store BEGIN_SILENCE/END_SILENCE pair to restore previous - * EG(error_reporting) value on exception */ - brk_cont_element = get_next_brk_cont_element(CG(active_op_array)); - brk_cont_element->start = begin_opline_num; - brk_cont_element->cont = brk_cont_element->brk = end_opline_num; - brk_cont_element->parent = -1; } /* }}} */ @@ -6615,10 +6605,6 @@ static void zend_compile_encaps_list(znode *result, zend_ast *ast) /* {{{ */ GET_NODE(result, opline->result); } else { uint32_t var; - zend_brk_cont_element *info = get_next_brk_cont_element(CG(active_op_array)); - info->start = rope_init_lineno; - info->parent = CG(context).current_brk_cont; - info->cont = info->brk = opline - CG(active_op_array)->opcodes; init_opline->extended_value = j; opline->opcode = ZEND_ROPE_END; diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 43297a4a052..d478348fe3f 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -328,6 +328,9 @@ typedef struct _zend_internal_function_info { zend_bool _is_variadic; } zend_internal_function_info; +#define ZEND_LIVE_ROPE (1 << 0) +#define ZEND_LIVE_SILENCE (1 << 1) + struct _zend_op_array { /* Common elements */ zend_uchar type; diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index bcb285298fd..c09d77f61f6 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -2390,7 +2390,33 @@ static zend_always_inline void i_cleanup_unfinished_execution(zend_execute_data 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++))); + uint32_t var = *(off++); + + if (var & ZEND_LIVE_ROPE) { + /* free incomplete rope */ + zend_string **rope = (zend_string **) EX_VAR(var & ~ZEND_LIVE_ROPE); + zend_op *last = EX(func)->op_array.opcodes + op_num; + while (last->opcode != ZEND_ROPE_ADD && last->opcode != ZEND_ROPE_INIT) { + ZEND_ASSERT(last >= EX(func)->op_array.opcodes); + last--; + } + if (last->opcode == ZEND_ROPE_INIT) { + zend_string_release(*rope); + } else { + int j = last->extended_value; + do { + zend_string_release(rope[j]); + } while (j--); + } + } else if (var & ZEND_LIVE_SILENCE) { + /* restore previous error_reporting value */ + var = var & ~ZEND_LIVE_SILENCE; + if (!EG(error_reporting) && Z_LVAL_P(EX_VAR(var)) != 0) { + EG(error_reporting) = Z_LVAL_P(EX_VAR(var)); + } + } else { + zval_ptr_dtor_nogc(EX_VAR(var)); + } } } @@ -2527,26 +2553,6 @@ static zend_always_inline void i_cleanup_unfinished_execution(zend_execute_data zend_hash_iterator_del(Z_FE_ITER_P(var)); } zval_ptr_dtor_nogc(var); - } else if (brk_opline->opcode == ZEND_ROPE_END) { - zend_string **rope = (zend_string **) EX_VAR(brk_opline->op1.var); - zend_op *last = EX(func)->op_array.opcodes + op_num; - while (last->opcode != ZEND_ROPE_ADD && last->opcode != ZEND_ROPE_INIT) { - ZEND_ASSERT(last >= EX(func)->op_array.opcodes); - last--; - } - if (last->opcode == ZEND_ROPE_INIT) { - zend_string_release(*rope); - } else { - int j = last->extended_value; - do { - zend_string_release(rope[j]); - } while (j--); - } - } else if (brk_opline->opcode == ZEND_END_SILENCE) { - /* restore previous error_reporting value */ - if (!EG(error_reporting) && Z_LVAL_P(EX_VAR(brk_opline->op1.var)) != 0) { - EG(error_reporting) = Z_LVAL_P(EX_VAR(brk_opline->op1.var)); - } } } } diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index fc2a48cd8c7..4d707b6ab70 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -855,7 +855,7 @@ typedef struct _var_live_info { typedef struct _op_var_info { struct _op_var_info *next; - uint32_t T; + uint32_t var; } op_var_info; ZEND_API uint32_t *generate_var_liveliness_info(zend_op_array *op_array) @@ -876,7 +876,7 @@ ZEND_API uint32_t *generate_var_liveliness_info(zend_op_array *op_array) for (i = 0; i <= op_array->last; i++) { opTs[i] = &opTsTop[i]; - opTs[i]->T = -1; + opTs[i]->var = -1; opTs[i]->next = NULL; } @@ -894,7 +894,7 @@ ZEND_API uint32_t *generate_var_liveliness_info(zend_op_array *op_array) } 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_ROPE_ADD /* reuses TMP created in ROPE_INIT */ && 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 @@ -909,7 +909,7 @@ 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) { + && cur_op->opcode != ZEND_ROPE_ADD) { var_live_info *T = Ts[cur_op->op1.var]; if (~T->start) { T->end = cur_op - op_array->opcodes; @@ -943,11 +943,16 @@ ZEND_API uint32_t *generate_var_liveliness_info(zend_op_array *op_array) 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) { + if (~opT->var) { opT = opTs[j] = opT->next = zend_arena_alloc(&CG(arena), sizeof(op_var_info)); opT->next = NULL; } - opT->T = i; + opT->var = (uint32_t)(zend_intptr_t)ZEND_CALL_VAR_NUM(NULL, op_array->last_var + i);; + if (op_array->opcodes[T->end].opcode == ZEND_ROPE_END) { + opT->var |= ZEND_LIVE_ROPE; + } else if (op_array->opcodes[T->end].opcode == ZEND_END_SILENCE) { + opT->var |= ZEND_LIVE_SILENCE; + } op_live_total++; } } @@ -964,11 +969,11 @@ ZEND_API uint32_t *generate_var_liveliness_info(zend_op_array *op_array) for (i = 0; i < op_array->last; i++) { op_var_info *opT = &opTsTop[i]; info[i] = info_off; - if (!~opT->T) { + if (!~opT->var) { opT = NULL; } while (opT) { - info[info_off++] = op_array->last_var + opT->T; + info[info_off++] = opT->var; opT = opT->next; } } diff --git a/ext/opcache/Optimizer/block_pass.c b/ext/opcache/Optimizer/block_pass.c index ee9bda6094c..799fec42dc1 100644 --- a/ext/opcache/Optimizer/block_pass.c +++ b/ext/opcache/Optimizer/block_pass.c @@ -207,17 +207,13 @@ static int find_code_blocks(zend_op_array *op_array, zend_cfg *cfg, zend_optimiz for (i = 0; i< op_array->last_brk_cont; i++) { if (op_array->brk_cont_array[i].start >= 0 && (op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FREE || - op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FE_FREE || - op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_ROPE_END || - op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_END_SILENCE)) { + op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FE_FREE)) { int parent = op_array->brk_cont_array[i].parent; while (parent >= 0 && op_array->brk_cont_array[parent].start < 0 && (op_array->opcodes[op_array->brk_cont_array[parent].brk].opcode != ZEND_FREE || - op_array->opcodes[op_array->brk_cont_array[parent].brk].opcode != ZEND_FE_FREE || - op_array->opcodes[op_array->brk_cont_array[i].brk].opcode != ZEND_ROPE_END || - op_array->opcodes[op_array->brk_cont_array[parent].brk].opcode != ZEND_END_SILENCE)) { + op_array->opcodes[op_array->brk_cont_array[parent].brk].opcode != ZEND_FE_FREE)) { parent = op_array->brk_cont_array[parent].parent; } op_array->brk_cont_array[i].parent = parent; @@ -232,9 +228,7 @@ static int find_code_blocks(zend_op_array *op_array, zend_cfg *cfg, zend_optimiz for (i = 0; i< op_array->last_brk_cont; i++) { if (op_array->brk_cont_array[i].start >= 0 && (op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FREE || - op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FE_FREE || - op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_ROPE_END || - op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_END_SILENCE)) { + op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FE_FREE)) { if (i != j) { op_array->brk_cont_array[j] = op_array->brk_cont_array[i]; } From ba721efa2cbd2136668fec956ef3b034ac1a29d6 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Fri, 3 Jul 2015 14:16:09 +0300 Subject: [PATCH 15/16] Print list of live temp variables (at least for internal debugging) --- sapi/phpdbg/phpdbg_opcode.c | 39 +++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/sapi/phpdbg/phpdbg_opcode.c b/sapi/phpdbg/phpdbg_opcode.c index 8adbbba36ff..ab5a1d9d500 100644 --- a/sapi/phpdbg/phpdbg_opcode.c +++ b/sapi/phpdbg/phpdbg_opcode.c @@ -24,6 +24,7 @@ #include "phpdbg_opcode.h" #include "phpdbg_utils.h" #include "ext/standard/php_string.h" +#include "zend_smart_str.h" ZEND_EXTERN_MODULE_GLOBALS(phpdbg); @@ -124,6 +125,44 @@ char *phpdbg_decode_opline(zend_op_array *ops, zend_op *op, HashTable *vars) /*{ break; } +#if 1 + if (ops->T_liveliness) { + uint32_t *var = ops->T_liveliness + ops->T_liveliness[op - ops->opcodes]; + uint32_t *end = ops->T_liveliness + ops->T_liveliness[op - ops->opcodes + 1]; + + if (var != end) { + smart_str str = {0}; + + smart_str_appends(&str, "; [@"); + smart_str_append_long(&str, EX_VAR_TO_NUM(((*var) & ~0x3)) - ops->last_var); + while (++var != end) { + smart_str_appends(&str, ", @"); + smart_str_append_long(&str, EX_VAR_TO_NUM(((*var) & ~0x3)) - ops->last_var); + } + smart_str_appendc(&str, ']'); + smart_str_0(&str); + + asprintf(&decode[0], + "%-20s %-20s %-20s%-20s", + decode[1] ? decode[1] : "", + decode[2] ? decode[2] : "", + decode[3] ? decode[3] : "", + ZSTR_VAL(str.s)); + + smart_str_free(&str); + + if (decode[1]) + free(decode[1]); + if (decode[2]) + free(decode[2]); + if (decode[3]) + free(decode[3]); + + return decode[0]; + } + } +#endif + asprintf(&decode[0], "%-20s %-20s %-20s", decode[1] ? decode[1] : "", From 3a8af245290ceb507108340831254672f24022fa Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Fri, 3 Jul 2015 16:15:36 +0300 Subject: [PATCH 16/16] More exceptions from regular liveliness analyses (with explanation in comments). Mark old "unexplained" exceptions with ???. --- Zend/zend_opcode.c | 57 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 13 deletions(-) diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index 4d707b6ab70..3b2b4b7a0f4 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -883,22 +883,32 @@ 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_BOOL && cur_op->opcode != ZEND_JMPZ_EX && cur_op->opcode != ZEND_JMPNZ_EX - && (cur_op->opcode != ZEND_QM_ASSIGN || (cur_op + 1)->opcode != ZEND_JMP)) { + if ((cur_op->result_type & (IS_VAR | IS_TMP_VAR)) + && !(cur_op->result_type & EXT_TYPE_UNUSED) + && cur_op->opcode != ZEND_BOOL /* why ??? */ + && cur_op->opcode != ZEND_JMPZ_EX /* why ??? */ + && cur_op->opcode != ZEND_JMPNZ_EX /* why ??? */ + && (cur_op->opcode != ZEND_QM_ASSIGN + || (cur_op + 1)->opcode != ZEND_JMP) /* why ??? */ + && cur_op->opcode != ZEND_CASE /* ??? exception for opcache, is anyway bool */ + && cur_op->opcode != ZEND_FE_RESET_R /* FOREACH TMP is handled using brk_cont_array */ + && cur_op->opcode != ZEND_FE_RESET_RW /* FOREACH TMP is handled using brk_cont_array */ + && cur_op->opcode != ZEND_ROPE_ADD /* the following opocodes reuse TMP created before */ + && cur_op->opcode != ZEND_ADD_ARRAY_ELEMENT + && cur_op->opcode != ZEND_FAST_CALL /* passes fast_call */ + && cur_op->opcode != ZEND_FETCH_CLASS /* the following opcodes pass class_entry */ + && 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[cur_op->result.var] = T->next = zend_arena_alloc(&CG(arena), sizeof(var_live_info)); T->next = NULL; T->start = T->end = -1; } - if (!~T->start - && cur_op->opcode != ZEND_CASE /* exception for opcache, is anyway bool */ - && cur_op->opcode != ZEND_ROPE_ADD /* reuses TMP created in ROPE_INIT */ - && 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) { + 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; @@ -908,8 +918,20 @@ 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_FE_FETCH_R /* FOREACH TMP is handled using brk_cont_array */ + && cur_op->opcode != ZEND_FE_FETCH_RW /* FOREACH TMP is handled using brk_cont_array */ + && cur_op->opcode != ZEND_FE_FREE /* FOREACH TMP is handled using brk_cont_array */ + && cur_op->opcode != ZEND_ROPE_ADD /* the following opcodes don't free TMP */ + && cur_op->opcode != ZEND_CASE + && cur_op->opcode != ZEND_FETCH_LIST + && cur_op->opcode != ZEND_FAST_RET /* uses fast_call */ + && cur_op->opcode != ZEND_NEW /* the following opcodes use class_entry */ + && cur_op->opcode != ZEND_INIT_STATIC_METHOD_CALL + && cur_op->opcode != ZEND_FETCH_CONSTANT + && cur_op->opcode != ZEND_ADD_INTERFACE + && cur_op->opcode != ZEND_ADD_TRAIT + && cur_op->opcode != ZEND_BIND_TRAITS + && cur_op->opcode != ZEND_VERIFY_ABSTRACT_CLASS) { var_live_info *T = Ts[cur_op->op1.var]; if (~T->start) { T->end = cur_op - op_array->opcodes; @@ -918,7 +940,16 @@ ZEND_API uint32_t *generate_var_liveliness_info(zend_op_array *op_array) } } } - if (cur_op->op2_type & (IS_VAR | IS_TMP_VAR)) { + if ((cur_op->op2_type & (IS_VAR | IS_TMP_VAR)) + && cur_op->opcode != ZEND_FETCH_R /* the following opcodes use class_entry */ + && cur_op->opcode != ZEND_FETCH_W + && cur_op->opcode != ZEND_FETCH_RW + && cur_op->opcode != ZEND_FETCH_IS + && cur_op->opcode != ZEND_FETCH_FUNC_ARG + && cur_op->opcode != ZEND_FETCH_UNSET + && cur_op->opcode != ZEND_UNSET_VAR + && cur_op->opcode != ZEND_ISSET_ISEMPTY_VAR + && cur_op->opcode != ZEND_INSTANCEOF) { var_live_info *T = Ts[cur_op->op2.var]; if (~T->start) { T->end = cur_op - op_array->opcodes;