Merge branch 'temporary_cleaning' of github.com:bwoebi/php-src into temporary_cleaning

* 'temporary_cleaning' of github.com:bwoebi/php-src:
  Fix remaining issues with compacted temporaries
  Fix regression from last commit (+1 ?!)
  Fix off-by-one (opcache may remove last ZEND_RETURN)
  Speed algorithm up, more fail safety when reusing temporaries
  Dumb bug in opcode.c (forgot to update Ts[i])
  Fix opcache support
  Exempt ROPE temporaries from freeing
  Hmm, we need temporary info for all the opcodes
  Add opcache support for cleaning in optimization step (Opcache seems to have a few unrelated issues which blow up together with that patch)
  Add proper temporary cleaning upon frame abortion
  Fix arena on small sizes (size < sizeof(zend_arena))
This commit is contained in:
Dmitry Stogov 2015-07-02 22:01:42 +03:00
commit a4fce36907
8 changed files with 185 additions and 5 deletions

View file

@ -33,7 +33,7 @@ struct _zend_arena {
static zend_always_inline zend_arena* zend_arena_create(size_t size) 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->ptr = (char*) arena + ZEND_MM_ALIGNED_SIZE(sizeof(zend_arena));
arena->end = (char*) arena + size; arena->end = (char*) arena + size;

View file

@ -351,6 +351,7 @@ struct _zend_op_array {
int last_var; int last_var;
uint32_t T; uint32_t T;
zend_string **vars; zend_string **vars;
uint32_t *T_liveliness;
int last_brk_cont; int last_brk_cont;
int last_try_catch; 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 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 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 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_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_user_class_data(zend_class_entry *ce);
ZEND_API void zend_cleanup_internal_class_data(zend_class_entry *ce); ZEND_API void zend_cleanup_internal_class_data(zend_class_entry *ce);

View file

@ -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) /* {{{ */ 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; 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))) { if (UNEXPECTED(EX(call))) {
zend_execute_data *call = EX(call); zend_execute_data *call = EX(call);
zend_op *opline = EX(func)->op_array.opcodes + op_num; 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) { if (!catch_op_num || catch_op_num >= brk_cont->brk) {
zend_op *brk_opline = &EX(func)->op_array.opcodes[brk_cont->brk]; zend_op *brk_opline = &EX(func)->op_array.opcodes[brk_cont->brk];
if (brk_opline->opcode == ZEND_FREE) { /* ZEND_FREE opcodes are handled by temporary variable freeing */
zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var)); if (brk_opline->opcode == ZEND_FE_FREE) {
} else if (brk_opline->opcode == ZEND_FE_FREE) {
zval *var = EX_VAR(brk_opline->op1.var); zval *var = EX_VAR(brk_opline->op1.var);
if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) { if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) {
zend_hash_iterator_del(Z_FE_ITER_P(var)); zend_hash_iterator_del(Z_FE_ITER_P(var));

View file

@ -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->vars = NULL;
op_array->T = 0; op_array->T = 0;
op_array->T_liveliness = NULL;
op_array->function_name = NULL; op_array->function_name = NULL;
op_array->filename = zend_get_compiled_filename(); 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) { if (op_array->try_catch_array) {
efree(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) { 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); 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; fast_call_var = op_array->opcodes[op_array->try_catch_array[i].finally_end].op1.var;
/* generate a FAST_CALL to finally block */ /* 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 = get_next_op(op_array);
opline->opcode = ZEND_FAST_CALL; 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); op_array->literals = (zval*)erealloc(op_array->literals, sizeof(zval) * op_array->last_literal);
CG(context).literals_size = 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; opline = op_array->opcodes;
end = opline + op_array->last; end = opline + op_array->last;
while (opline < end) { while (opline < end) {
@ -840,6 +847,133 @@ int pass_two_wrapper(zval *el)
return pass_two((zend_op_array *) Z_PTR_P(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) int print_class(zend_class_entry *class_entry)
{ {
printf("Class %s:\n", ZSTR_VAL(class_entry->name)); printf("Class %s:\n", ZSTR_VAL(class_entry->name));

View file

@ -482,6 +482,8 @@ static void zend_accel_optimize(zend_op_array *op_array,
{ {
zend_op *opline, *end; zend_op *opline, *end;
efree(op_array->T_liveliness);
/* Revert pass_two() */ /* Revert pass_two() */
opline = op_array->opcodes; opline = op_array->opcodes;
end = opline + op_array->last; end = opline + op_array->last;
@ -527,15 +529,39 @@ static void zend_accel_optimize(zend_op_array *op_array,
/* Do actual optimizations */ /* Do actual optimizations */
zend_optimize(op_array, ctx); 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() */ /* Redo pass_two() */
opline = op_array->opcodes; opline = op_array->opcodes;
end = opline + op_array->last; end = opline + op_array->last;
while (opline < end) { while (opline < end) {
if (opline->op1_type == IS_CONST) { if (opline->op1_type == IS_CONST) {
ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline->op1); 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) { if (opline->op2_type == IS_CONST) {
ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline->op2); 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) { switch (opline->opcode) {
case ZEND_JMP: case ZEND_JMP:

View file

@ -464,6 +464,7 @@ static void zend_file_cache_serialize_op_array(zend_op_array *op_arra
SERIALIZE_STR(op_array->doc_comment); SERIALIZE_STR(op_array->doc_comment);
SERIALIZE_PTR(op_array->try_catch_array); SERIALIZE_PTR(op_array->try_catch_array);
SERIALIZE_PTR(op_array->prototype); 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_STR(op_array->doc_comment);
UNSERIALIZE_PTR(op_array->try_catch_array); UNSERIALIZE_PTR(op_array->try_catch_array);
UNSERIALIZE_PTR(op_array->prototype); UNSERIALIZE_PTR(op_array->prototype);
UNSERIALIZE_PTR(op_array->T_liveliness);
} }
} }

View file

@ -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); 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 (op_array->vars) {
if (already_stored) { if (already_stored) {
persist_ptr = zend_shared_alloc_get_xlat_entry(op_array->vars); persist_ptr = zend_shared_alloc_get_xlat_entry(op_array->vars);

View file

@ -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); 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) { if (op_array->vars) {
int i; int i;