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; 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..7195f876bf0 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,6 +390,9 @@ 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); } @@ -576,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; @@ -751,6 +755,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 +847,133 @@ 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; + +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) + 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 + 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)); + + for (i = 0; i < op_array->T; i++) { + TsTop[i] = Ts[i] = zend_arena_alloc(&arena, sizeof(var_live_info)); + Ts[i]->next = NULL; + 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_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)) { + 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 + && 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; + } else { + T->start = cur_op - op_array->opcodes; + } + } + } + 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) { + 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)) { + 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->T; i++) { + int j; + var_live_info *T = TsTop[i]; + + do { + if (!~T->start) { + continue; + } + + 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 + 1) * sizeof(uint32_t)); + + for (i = 0; i < op_array->last; i++) { + op_var_info *opT = &opTsTop[i]; + info[i] = info_off; + if (!~opT->T) { + opT = NULL; + } + while (opT) { + info[info_off++] = op_array->last_var + opT->T; + opT = opT->next; + } + } + info[i] = info_off; + + zend_arena_destroy(arena); + + return info; +} + int print_class(zend_class_entry *class_entry) { printf("Class %s:\n", ZSTR_VAL(class_entry->name)); diff --git a/ext/opcache/Optimizer/zend_optimizer.c b/ext/opcache/Optimizer/zend_optimizer.c index dc69d2511ec..44c400ca3fd 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,15 +529,39 @@ 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() */ opline = op_array->opcodes; end = opline + op_array->last; 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..309d44b80d6 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]); + } + 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..00f613273b4 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]); + } + if (op_array->vars) { int i;