mirror of
https://github.com/php/php-src.git
synced 2024-11-24 10:24:11 +08:00
Improved algorithm. It's actually the same algorithm with second loop removed and simpler temporary data structures. The only difference may be in "re-defined" vatriable handling. Now live-range in that case started from the seconnd definition (this must be more safe).
This commit is contained in:
parent
9a16810f7a
commit
a81c4bb8c6
@ -842,40 +842,63 @@ 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;
|
||||
|
||||
/* The following liveliness analyzing algorithm assumes that
|
||||
* 1) temporary variables are defined before use
|
||||
* 2) they have linear live-ranges without "holes"
|
||||
* 3) Opcodes never use and define the same temorary variables
|
||||
*/
|
||||
typedef struct _op_var_info {
|
||||
struct _op_var_info *next;
|
||||
uint32_t var;
|
||||
} op_var_info;
|
||||
|
||||
/* The following liveliness analyzing algorithm assumes that
|
||||
* 1) temporary variables are defined before use
|
||||
* 2) they have linear live-ranges without "holes"
|
||||
*/
|
||||
static zend_always_inline uint32_t liveliness_kill_var(zend_op_array *op_array, zend_op *cur_op, uint32_t var, uint32_t *Tstart, op_var_info **opTs)
|
||||
{
|
||||
uint32_t start = Tstart[var];
|
||||
uint32_t end = cur_op - op_array->opcodes;
|
||||
uint32_t count = 0;
|
||||
uint32_t var_offset, j;
|
||||
|
||||
Tstart[var] = -1;
|
||||
if (cur_op->opcode == ZEND_OP_DATA) {
|
||||
end--;
|
||||
}
|
||||
start++;
|
||||
if (op_array->opcodes[start].opcode == ZEND_OP_DATA
|
||||
|| op_array->opcodes[start].opcode == ZEND_FE_FETCH_R
|
||||
|| op_array->opcodes[start].opcode == ZEND_FE_FETCH_RW) {
|
||||
start++;
|
||||
}
|
||||
if (start < end) {
|
||||
var_offset = (uint32_t)(zend_intptr_t)ZEND_CALL_VAR_NUM(NULL, op_array->last_var + var);
|
||||
if (op_array->opcodes[end].opcode == ZEND_ROPE_END) {
|
||||
var_offset |= ZEND_LIVE_ROPE;
|
||||
} else if (op_array->opcodes[end].opcode == ZEND_END_SILENCE) {
|
||||
var_offset |= ZEND_LIVE_SILENCE;
|
||||
} else if (op_array->opcodes[end].opcode == ZEND_FE_FREE) {
|
||||
var_offset |= ZEND_LIVE_LOOP;
|
||||
}
|
||||
for (j = start; j < end; j++) {
|
||||
op_var_info *newOpTs = zend_arena_alloc(&CG(arena), sizeof(op_var_info));
|
||||
newOpTs->next = opTs[j];
|
||||
newOpTs->var = var_offset;
|
||||
opTs[j] = newOpTs;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
ZEND_API uint32_t *generate_var_liveliness_info(zend_op_array *op_array)
|
||||
{
|
||||
uint32_t i, op_live_total = 0;
|
||||
void *checkpoint = zend_arena_checkpoint(CG(arena));
|
||||
uint32_t *info, info_off = op_array->last + 1;
|
||||
var_live_info **Ts = zend_arena_alloc(&CG(arena), sizeof(var_live_info *) * op_array->T);
|
||||
uint32_t *Tstart = zend_arena_alloc(&CG(arena), sizeof(uint32_t) * op_array->T);
|
||||
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++) {
|
||||
Ts[i] = zend_arena_alloc(&CG(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] = zend_arena_alloc(&CG(arena), sizeof(op_var_info));
|
||||
opTs[i]->var = -1;
|
||||
opTs[i]->next = NULL;
|
||||
}
|
||||
memset(Tstart, -1, sizeof(uint32_t) * op_array->T);
|
||||
memset(opTs, 0, sizeof(op_var_info *) * (op_array->last + 1));
|
||||
|
||||
zend_op *end_op = op_array->opcodes + op_array->last;
|
||||
zend_op *cur_op = op_array->opcodes;
|
||||
@ -885,112 +908,61 @@ ZEND_API uint32_t *generate_var_liveliness_info(zend_op_array *op_array)
|
||||
&& cur_op->opcode != ZEND_BOOL /* the following opcodes are used in inline branching (and anyway always bool, so no need to free) and may not be defined depending on the taken branch */
|
||||
&& cur_op->opcode != ZEND_JMPZ_EX
|
||||
&& cur_op->opcode != ZEND_JMPNZ_EX
|
||||
&& (cur_op->opcode != ZEND_QM_ASSIGN || (cur_op + 1)->opcode != ZEND_JMP)) { /* these two consecutive ops appear on ternary; the result of true branch is undefined for false branch */
|
||||
var_live_info *T = Ts[cur_op->result.var];
|
||||
if (T->end != (uint32_t) -1) {
|
||||
Ts[cur_op->result.var] = zend_arena_alloc(&CG(arena), sizeof(var_live_info));
|
||||
Ts[cur_op->result.var]->next = T;
|
||||
T = Ts[cur_op->result.var];
|
||||
T->start = T->end = -1;
|
||||
}
|
||||
if (T->start == -1 /* we need the exceptions *here* and not around the whole branch, else temporary ranges may be accidentally reused. If any of these opcodes reuses a temporary, we must mark the previous range as definitely terminated first */
|
||||
&& cur_op->opcode != ZEND_CASE /* exception for opcache, it might nowhere use the temporary (anyway bool, so no need to free) */
|
||||
&& cur_op->opcode != ZEND_ROPE_ADD /* the following opcodes 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) {
|
||||
/* 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;
|
||||
}
|
||||
&& (cur_op->opcode != ZEND_QM_ASSIGN || (cur_op + 1)->opcode != ZEND_JMP) /* these two consecutive ops appear on ternary; the result of true branch is undefined for false branch */
|
||||
&& cur_op->opcode != ZEND_CASE /* exception for opcache, it might nowhere use the temporary (anyway bool, so no need to free) */
|
||||
&& cur_op->opcode != ZEND_ROPE_ADD /* the following opcodes 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) {
|
||||
/* Objects created via ZEND_NEW are only fully initialized after the DO_FCALL (constructor call) */
|
||||
if (cur_op->opcode == ZEND_NEW) {
|
||||
Tstart[cur_op->result.var] = cur_op->op2.opline_num - 1;
|
||||
} else {
|
||||
Tstart[cur_op->result.var] = cur_op - op_array->opcodes;
|
||||
}
|
||||
}
|
||||
if ((cur_op->op1_type & (IS_VAR | IS_TMP_VAR))
|
||||
&& cur_op->opcode != ZEND_ROPE_ADD) { /* the following opcodes don't free TMP */
|
||||
var_live_info *T = Ts[cur_op->op1.var];
|
||||
if (T->start != (uint32_t) -1) {
|
||||
T->end = cur_op - op_array->opcodes;
|
||||
if (cur_op->opcode == ZEND_OP_DATA) {
|
||||
T->end--;
|
||||
}
|
||||
}
|
||||
&& Tstart[cur_op->op1.var] != (uint32_t)-1
|
||||
&& cur_op->opcode != ZEND_ROPE_ADD /* the following opcodes don't free TMP */
|
||||
&& cur_op->opcode != ZEND_FETCH_LIST
|
||||
&& cur_op->opcode != ZEND_CASE
|
||||
&& cur_op->opcode != ZEND_FE_FETCH_R
|
||||
&& cur_op->opcode != ZEND_FE_FETCH_RW) {
|
||||
op_live_total += liveliness_kill_var(op_array, cur_op, cur_op->op1.var, Tstart, opTs);
|
||||
}
|
||||
if (cur_op->op2_type & (IS_VAR | IS_TMP_VAR)) {
|
||||
var_live_info *T = Ts[cur_op->op2.var];
|
||||
if (T->start != (uint32_t) -1) {
|
||||
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)
|
||||
&& Tstart[cur_op->op2.var] != (uint32_t)-1) {
|
||||
op_live_total += liveliness_kill_var(op_array, cur_op, cur_op->op2.var, Tstart, opTs);
|
||||
}
|
||||
}
|
||||
|
||||
#if ZEND_DEBUG
|
||||
/* Check that all TMP variable live-ranges are closed */
|
||||
for (i = 0; i < op_array->T; i++) {
|
||||
int j;
|
||||
var_live_info *T = Ts[i];
|
||||
|
||||
do {
|
||||
if (T->start == (uint32_t) -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* if one lands here, some opcode range isn't properly terminated or part of the exceptions */
|
||||
ZEND_ASSERT(T->end != (uint32_t) -1);
|
||||
|
||||
j = T->start + 1;
|
||||
if (op_array->opcodes[j].opcode == ZEND_OP_DATA
|
||||
|| op_array->opcodes[j].opcode == ZEND_FE_FETCH_R
|
||||
|| op_array->opcodes[j].opcode == ZEND_FE_FETCH_RW) {
|
||||
/* On exception TMP variable is destroyed by current opcode */
|
||||
j++;
|
||||
}
|
||||
|
||||
for (; j < T->end; j++) {
|
||||
op_var_info *opT = opTs[j];
|
||||
if (opT->var != (uint32_t) -1) {
|
||||
opTs[j] = zend_arena_alloc(&CG(arena), sizeof(op_var_info));
|
||||
opTs[j]->next = opT;
|
||||
opT = opTs[j];
|
||||
}
|
||||
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;
|
||||
} else if (op_array->opcodes[T->end].opcode == ZEND_FE_FREE) {
|
||||
opT->var |= ZEND_LIVE_LOOP;
|
||||
}
|
||||
op_live_total++;
|
||||
}
|
||||
} while ((T = T->next));
|
||||
ZEND_ASSERT(Tstart[i] == (uint32_t)-1);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!op_live_total) {
|
||||
zend_arena_release(&CG(arena), checkpoint);
|
||||
return NULL;
|
||||
}
|
||||
info = NULL;
|
||||
} else {
|
||||
info = emalloc(op_live_total * sizeof(uint32_t) + (op_array->last + 1) * 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 = opTs[i];
|
||||
info[i] = info_off;
|
||||
if (opT->var == (uint32_t) -1) {
|
||||
continue;
|
||||
for (i = 0; i < op_array->last; i++) {
|
||||
op_var_info *opT = opTs[i];
|
||||
info[i] = info_off;
|
||||
while (opT) {
|
||||
info[info_off++] = opT->var;
|
||||
opT = opT->next;
|
||||
}
|
||||
}
|
||||
do {
|
||||
info[info_off++] = opT->var;
|
||||
} while ((opT = opT->next));
|
||||
info[i] = info_off;
|
||||
}
|
||||
info[i] = info_off;
|
||||
|
||||
zend_arena_release(&CG(arena), checkpoint);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user