1
0
mirror of https://github.com/php/php-src.git synced 2026-04-17 21:11:02 +02:00

Revert "cleanup" (wrong commit)

This reverts commit 5db5f71f28.
This commit is contained in:
Dmitry Stogov
2020-04-07 21:34:18 +03:00
parent 5db5f71f28
commit cff7703a61

View File

@@ -803,11 +803,6 @@ static int is_checked_guard(const zend_ssa *tssa, const zend_op **ssa_opcodes, u
return 0;
}
typedef struct _zend_tssa {
zend_ssa ssa;
const zend_op **tssa_opcodes;
} zend_tssa;
static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uint32_t parent_trace, uint32_t exit_num, zend_script *script, const zend_op_array **op_arrays, int *num_op_arrays_ptr)
{
zend_ssa *tssa;
@@ -918,7 +913,7 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin
*num_op_arrays_ptr = num_op_arrays;
/* 2. Construct TSSA */
tssa = zend_arena_calloc(&CG(arena), 1, sizeof(zend_tssa));
tssa = zend_arena_calloc(&CG(arena), 1, sizeof(zend_ssa));
tssa->cfg.blocks = zend_arena_calloc(&CG(arena), 2, sizeof(zend_basic_block));
tssa->blocks = zend_arena_calloc(&CG(arena), 2, sizeof(zend_ssa_block));
tssa->cfg.predecessors = zend_arena_calloc(&CG(arena), 2, sizeof(int));
@@ -926,7 +921,6 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin
memset(ssa_ops, -1, ssa_ops_count * sizeof(zend_ssa_op));
ssa_opcodes = zend_arena_calloc(&CG(arena), ssa_ops_count, sizeof(zend_op*));
JIT_G(current_frame) = frame = (zend_jit_trace_stack_frame*)((char*)zend_arena_alloc(&CG(arena), stack_bottom + stack_size) + stack_bottom);
((zend_tssa*)tssa)->tssa_opcodes = ssa_opcodes;
if (trace_buffer->stop == ZEND_JIT_TRACE_STOP_LOOP) {
tssa->cfg.blocks_count = 2;
@@ -1684,784 +1678,6 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin
return tssa;
}
static int zend_jit_trace_try_allocate_free_reg(zend_jit_trace_rec *trace_buffer, const zend_op **ssa_opcodes, zend_ssa *ssa, zend_lifetime_interval *current, zend_regset available, zend_regset *hints, zend_lifetime_interval *active, zend_lifetime_interval *inactive, zend_lifetime_interval **list, zend_lifetime_interval **free)
{
zend_lifetime_interval *it;
uint32_t freeUntilPos[ZREG_NUM];
uint32_t pos, pos2;
zend_reg i, reg, reg2;
zend_reg hint = ZREG_NONE;
zend_regset low_priority_regs;
zend_life_range *range;
if ((ssa->var_info[current->ssa_var].type & MAY_BE_ANY) == MAY_BE_DOUBLE) {
available = ZEND_REGSET_INTERSECTION(available, ZEND_REGSET_FP);
} else {
available = ZEND_REGSET_INTERSECTION(available, ZEND_REGSET_GP);
}
/* TODO: Allow usage of preserved registers ???
* Their values have to be stored in prologuee and restored in epilogue
*/
available = ZEND_REGSET_DIFFERENCE(available, ZEND_REGSET_PRESERVED);
if (ZEND_REGSET_IS_EMPTY(available)) {
return 0;
}
/* Set freeUntilPos of all physical registers to maxInt */
for (i = 0; i < ZREG_NUM; i++) {
freeUntilPos[i] = 0xffffffff;
}
/* for each interval it in active do */
/* freeUntilPos[it.reg] = 0 */
it = active;
if (ssa->vars[current->ssa_var].definition == current->range.start) {
while (it) {
if (current->range.start != zend_interval_end(it)) {
freeUntilPos[it->reg] = 0;
} else if (zend_jit_may_reuse_reg(ssa_opcodes[current->range.start], ssa->ops + current->range.start, ssa, current->ssa_var, it->ssa_var)) {
if (!ZEND_REGSET_IN(*hints, it->reg) &&
/* TODO: Avoid most often scratch registers. Find a better way ??? */
(!current->used_as_hint ||
(it->reg != ZREG_R0 && it->reg != ZREG_R1 && it->reg != ZREG_XMM0 && it->reg != ZREG_XMM1))) {
hint = it->reg;
}
} else {
freeUntilPos[it->reg] = 0;
}
it = it->list_next;
}
} else {
while (it) {
freeUntilPos[it->reg] = 0;
it = it->list_next;
}
}
if (current->hint) {
hint = current->hint->reg;
if (hint != ZREG_NONE && current->hint->used_as_hint == current) {
ZEND_REGSET_EXCL(*hints, hint);
}
}
/* See "Linear Scan Register Allocation on SSA Form", Christian Wimmer and
Michael Franz, CGO'10 (2010), Figure 6. */
if (current->split) {
/* for each interval it in inactive intersecting with current do */
/* freeUntilPos[it.reg] = next intersection of it with current */
it = inactive;
while (it) {
uint32_t next = zend_interval_intersection(current, it);
//ZEND_ASSERT(next != 0xffffffff && !current->split);
if (next < freeUntilPos[it->reg]) {
freeUntilPos[it->reg] = next;
}
it = it->list_next;
}
}
/* Handle Scratch Registers */
/* TODO: Optimize ??? */
range = &current->range;
do {
uint32_t line = range->start;
zend_regset regset;
zend_reg reg;
if (ssa->ops[line].op1_def == current->ssa_var ||
ssa->ops[line].op2_def == current->ssa_var ||
ssa->ops[line].result_def == current->ssa_var) {
line++;
}
while (line <= range->end) {
regset = zend_jit_get_scratch_regset(
ssa_opcodes[line],
ssa->ops + line,
// TODO: Support for nested call frames ???
trace_buffer->op_array, ssa, current->ssa_var);
ZEND_REGSET_FOREACH(regset, reg) {
if (line < freeUntilPos[reg]) {
freeUntilPos[reg] = line;
}
} ZEND_REGSET_FOREACH_END();
line++;
}
range = range->next;
} while (range);
#if 0
/* Coalesing */
if (ssa->vars[current->ssa_var].definition == current->start) {
zend_op *opline = op_array->opcodes + current->start;
int hint = -1;
switch (opline->opcode) {
case ZEND_ASSIGN:
hint = ssa->ops[current->start].op2_use;
case ZEND_QM_ASSIGN:
hint = ssa->ops[current->start].op1_use;
break;
case ZEND_ADD:
case ZEND_SUB:
case ZEND_MUL:
hint = ssa->ops[current->start].op1_use;
break;
case ZEND_ASSIGN_OP:
if (opline->extended_value == ZEND_ADD
|| opline->extended_value == ZEND_SUB
|| opline->extended_value == ZEND_MUL) {
hint = ssa->ops[current->start].op1_use;
}
break;
}
if (hint >= 0) {
}
}
#endif
if (hint != ZREG_NONE && freeUntilPos[hint] > zend_interval_end(current)) {
current->reg = hint;
if (current->used_as_hint) {
ZEND_REGSET_INCL(*hints, hint);
}
return 1;
}
pos = 0; reg = ZREG_NONE;
pos2 = 0; reg2 = ZREG_NONE;
low_priority_regs = *hints;
if (current->used_as_hint) {
/* TODO: Avoid most often scratch registers. Find a better way ??? */
ZEND_REGSET_INCL(low_priority_regs, ZREG_R0);
ZEND_REGSET_INCL(low_priority_regs, ZREG_R1);
ZEND_REGSET_INCL(low_priority_regs, ZREG_XMM0);
ZEND_REGSET_INCL(low_priority_regs, ZREG_XMM1);
}
ZEND_REGSET_FOREACH(available, i) {
if (ZEND_REGSET_IN(low_priority_regs, i)) {
if (freeUntilPos[i] > pos2) {
reg2 = i;
pos2 = freeUntilPos[i];
}
} else if (freeUntilPos[i] > pos) {
reg = i;
pos = freeUntilPos[i];
}
} ZEND_REGSET_FOREACH_END();
if (reg == ZREG_NONE) {
if (reg2 != ZREG_NONE) {
reg = reg2;
pos = pos2;
reg2 = ZREG_NONE;
}
}
if (reg == ZREG_NONE) {
/* no register available without spilling */
return 0;
} else if (zend_interval_end(current) < pos) {
/* register available for the whole interval */
current->reg = reg;
if (current->used_as_hint) {
ZEND_REGSET_INCL(*hints, reg);
}
return 1;
#if 0
// TODO: allow low prioirity register usage
} else if (reg2 != ZREG_NONE && zend_interval_end(current) < pos2) {
/* register available for the whole interval */
current->reg = reg2;
if (current->used_as_hint) {
ZEND_REGSET_INCL(*hints, reg2);
}
return 1;
#endif
} else {
/* TODO: enable interval splitting ??? */
/* register available for the first part of the interval */
if (1 || zend_jit_split_interval(current, pos, list, free) != SUCCESS) {
return 0;
}
current->reg = reg;
if (current->used_as_hint) {
ZEND_REGSET_INCL(*hints, reg);
}
return 1;
}
}
static zend_lifetime_interval* zend_jit_trace_linear_scan(zend_jit_trace_rec *trace_buffer, const zend_op **ssa_opcodes, zend_ssa *ssa, zend_lifetime_interval *list)
{
zend_lifetime_interval *unhandled, *active, *inactive, *handled, *free;
zend_lifetime_interval *current, **p, *q;
uint32_t position;
zend_regset available = ZEND_REGSET_UNION(ZEND_REGSET_GP, ZEND_REGSET_FP);
zend_regset hints = ZEND_REGSET_EMPTY;
unhandled = list;
/* active = inactive = handled = free = {} */
active = inactive = handled = free = NULL;
while (unhandled != NULL) {
current = unhandled;
unhandled = unhandled->list_next;
position = current->range.start;
p = &active;
while (*p) {
uint32_t end = zend_interval_end(*p);
q = *p;
if (end < position) {
/* move ival from active to handled */
ZEND_REGSET_INCL(available, q->reg);
*p = q->list_next;
q->list_next = handled;
handled = q;
} else if (!zend_interval_covers(q, position)) {
/* move ival from active to inactive */
ZEND_REGSET_INCL(available, q->reg);
*p = q->list_next;
q->list_next = inactive;
inactive = q;
} else {
p = &q->list_next;
}
}
p = &inactive;
while (*p) {
uint32_t end = zend_interval_end(*p);
q = *p;
if (end < position) {
/* move ival from inactive to handled */
*p = q->list_next;
q->list_next = handled;
handled = q;
} else if (zend_interval_covers(q, position)) {
/* move ival from inactive to active */
ZEND_REGSET_EXCL(available, q->reg);
*p = q->list_next;
q->list_next = active;
active = q;
} else {
p = &q->list_next;
}
}
if (zend_jit_trace_try_allocate_free_reg(trace_buffer, ssa_opcodes, ssa, current, available, &hints, active, inactive, &unhandled, &free) ||
zend_jit_allocate_blocked_reg()) {
ZEND_REGSET_EXCL(available, current->reg);
current->list_next = active;
active = current;
} else {
current->list_next = free;
free = current;
}
}
/* move active to handled */
while (active) {
current = active;
active = active->list_next;
current->list_next = handled;
handled = current;
}
/* move inactive to handled */
while (inactive) {
current = inactive;
inactive = inactive->list_next;
current->list_next = handled;
handled = current;
}
return handled;
}
static zend_lifetime_interval** zend_jit_trace_allocate_registers(zend_jit_trace_rec *trace_buffer, zend_ssa *ssa)
{
const zend_op **ssa_opcodes = ((zend_tssa*)ssa)->tssa_opcodes;
zend_jit_trace_rec *p;
const zend_op_array *op_array;
const zend_ssa_op *ssa_op;
int i, j, idx, count, level;
int *start, *end;
zend_lifetime_interval **intervals, *list;
void *checkpoint;
ALLOCA_FLAG(use_heap);
ZEND_ASSERT(ssa->var_info != NULL);
start =do_alloca(sizeof(int) * ssa->vars_count * 2, use_heap);
if (!start) {
return NULL;
}
end = start + ssa->vars_count;
memset(start, -1, sizeof(int) * ssa->vars_count * 2);
op_array = trace_buffer->op_array;
count = 0;
i = 0;
j = op_array->last_var;
if (trace_buffer->start != ZEND_JIT_TRACE_START_ENTER) {
j += op_array->T;
}
while (i < j) {
/* We don't start intervals for variables used in Phi */
if ((ssa->vars[i].use_chain >= 0 /*|| ssa->vars[i].phi_use_chain*/)
&& zend_jit_var_supports_reg(ssa, i)) {
start[i] = 0;
count++;
}
i++;
}
if (trace_buffer->stop == ZEND_JIT_TRACE_STOP_LOOP) {
zend_ssa_phi *phi = ssa->blocks[1].phis;
while (phi) {
if (ssa->vars[phi->ssa_var].use_chain >= 0
&& zend_jit_var_supports_reg(ssa, phi->ssa_var)) {
start[phi->ssa_var] = 0;
count++;
}
phi = phi->next;
}
}
p = trace_buffer + ZEND_JIT_TRACE_START_REC_SIZE;
level = 0;
ssa_op = ssa->ops;
idx = 0;
for (;;p++) {
if (p->op == ZEND_JIT_TRACE_VM) {
const zend_op *opline = p->opline;
int len;
zend_bool support_opline;
support_opline =
zend_jit_opline_supports_reg(op_array, ssa, opline, ssa_op);
if (support_opline) {
if (ssa_op->op1_use >= 0
&& start[ssa_op->op1_use] >= 0
&& !zend_ssa_is_no_val_use(opline, ssa_op, ssa_op->op1_use)) {
end[ssa_op->op1_use] = idx;
}
if (ssa_op->op2_use >= 0
&& start[ssa_op->op2_use] >= 0
&& !zend_ssa_is_no_val_use(opline, ssa_op, ssa_op->op2_use)) {
end[ssa_op->op2_use] = idx;
}
if (ssa_op->result_use >= 0
&& start[ssa_op->result_use] >= 0
&& !zend_ssa_is_no_val_use(opline, ssa_op, ssa_op->result_use)) {
end[ssa_op->result_use] = idx;
}
if (ssa_op->result_def >= 0
&& (ssa->vars[ssa_op->result_def].use_chain >= 0
|| ssa->vars[ssa_op->result_def].phi_use_chain)
&& zend_jit_var_supports_reg(ssa, ssa_op->result_def)) {
start[ssa_op->result_def] = idx;
count++;
}
if (ssa_op->op1_def >= 0
&& (ssa->vars[ssa_op->op1_def].use_chain >= 0
|| ssa->vars[ssa_op->op1_def].phi_use_chain)
&& zend_jit_var_supports_reg(ssa, ssa_op->op1_def)) {
start[ssa_op->op1_def] = idx;
count++;
}
if (ssa_op->op2_def >= 0
&& (ssa->vars[ssa_op->op2_def].use_chain >= 0
|| ssa->vars[ssa_op->op2_def].phi_use_chain)
&& zend_jit_var_supports_reg(ssa, ssa_op->op2_def)) {
start[ssa_op->op2_def] = idx;
count++;
}
} else {
if (ssa_op->op1_use >= 0
&& start[ssa_op->op1_use] >= 0
&& !zend_ssa_is_no_val_use(opline, ssa_op, ssa_op->op1_use)) {
start[ssa_op->op1_use] = -1;
end[ssa_op->op1_use] = -1;
count--;
}
if (ssa_op->op2_use >= 0
&& start[ssa_op->op2_use] >= 0
&& !zend_ssa_is_no_val_use(opline, ssa_op, ssa_op->op2_use)) {
start[ssa_op->op2_use] = -1;
end[ssa_op->op2_use] = -1;
count--;
}
if (ssa_op->result_use >= 0
&& start[ssa_op->result_use] >= 0
&& !zend_ssa_is_no_val_use(opline, ssa_op, ssa_op->result_use)) {
start[ssa_op->result_use] = -1;
end[ssa_op->result_use] = -1;
count--;
}
}
len = zend_jit_trace_op_len(opline);
switch (opline->opcode) {
case ZEND_ASSIGN_DIM:
case ZEND_ASSIGN_OBJ:
case ZEND_ASSIGN_STATIC_PROP:
case ZEND_ASSIGN_DIM_OP:
case ZEND_ASSIGN_OBJ_OP:
case ZEND_ASSIGN_STATIC_PROP_OP:
case ZEND_ASSIGN_OBJ_REF:
case ZEND_ASSIGN_STATIC_PROP_REF:
/* OP_DATA */
ssa_op++;
opline++;
if (support_opline) {
if (ssa_op->op1_use >= 0
&& start[ssa_op->op1_use] >= 0
&& !zend_ssa_is_no_val_use(opline, ssa_op, ssa_op->op1_use)) {
end[ssa_op->op1_use] = idx;
}
if (ssa_op->op1_def >= 0
&& (ssa->vars[ssa_op->op1_def].use_chain >= 0
|| ssa->vars[ssa_op->op1_def].phi_use_chain)
&& zend_jit_var_supports_reg(ssa, ssa_op->op1_def)) {
start[ssa_op->op1_def] = idx;
count++;
}
} else {
if (ssa_op->op1_use >= 0
&& start[ssa_op->op1_use] >= 0
&& !zend_ssa_is_no_val_use(opline, ssa_op, ssa_op->op1_use)) {
start[ssa_op->op1_use] = -1;
end[ssa_op->op1_use] = -1;
count--;
}
}
ssa_op++;
opline++;
idx+=2;
break;
case ZEND_RECV_INIT:
ssa_op++;
opline++;
idx++;
while (opline->opcode == ZEND_RECV_INIT) {
/* RECV_INIT doesn't support registers */
ssa_op++;
opline++;
idx++;
}
break;
case ZEND_BIND_GLOBAL:
ssa_op++;
opline++;
idx++;
while (opline->opcode == ZEND_BIND_GLOBAL) {
/* BIND_GLOBAL doesn't support registers */
ssa_op++;
opline++;
idx++;
}
break;
default:
ssa_op += len;
idx += len;
break;
}
} else if (p->op == ZEND_JIT_TRACE_ENTER) {
op_array = p->op_array;
/* New call frames */
i = op_array->last_var;
j = p->first_ssa_var;
while (i) {
if (ssa->vars[j].use_chain >= 0
&& zend_jit_var_supports_reg(ssa, j)) {
start[j] = idx;
count++;
}
j++;
i--;
}
level++;
} else if (p->op == ZEND_JIT_TRACE_BACK) {
// TODO: Close exiting call frames ???
op_array = p->op_array;
if (level == 0) {
/* New return frames */
i = op_array->last_var + op_array->T;
j = p->first_ssa_var;
while (i) {
if (ssa->vars[j].use_chain >= 0
&& zend_jit_var_supports_reg(ssa, j)) {
start[j] = idx;
count++;
}
j++;
i--;
}
} else {
level--;
}
} else if (p->op == ZEND_JIT_TRACE_END) {
break;
}
}
if (trace_buffer->stop == ZEND_JIT_TRACE_STOP_LOOP) {
zend_ssa_phi *phi = ssa->blocks[1].phis;
while (phi) {
if (start[phi->sources[1]] >= 0) {
end[phi->sources[1]] = idx - 1;
}
phi = phi->next;
}
}
if (!count) {
free_alloca(start, use_heap);
return NULL;
}
checkpoint = zend_arena_checkpoint(CG(arena));
intervals = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_lifetime_interval));
memset(intervals, 0, sizeof(zend_lifetime_interval*) * ssa->vars_count);
list = zend_arena_alloc(&CG(arena), sizeof(zend_lifetime_interval) * count);
j = 0;
//fprintf(stderr, "(%d)\n", count);
for (i = 0; i < ssa->vars_count; i++) {
if (start[i] >= 0) {
if (end[i] < 0) {
// TODO: ???
end[i] = idx;
// continue;
}
//fprintf(stderr, "#%d: %d..%d\n", i, start[i], end[i]);
ZEND_ASSERT(j < count);
intervals[i] = &list[j];
list[j].ssa_var = i;
list[j].reg = ZREG_NONE;
list[j].split = 0;
list[j].store = 0;
list[j].load = 0;
list[j].range.start = start[i];
list[j].range.end = end[i];
list[j].range.next = NULL;
list[j].hint = NULL;
list[j].used_as_hint = NULL;
list[j].list_next = NULL;
j++;
}
}
ZEND_ASSERT(j == count);
free_alloca(start, use_heap);
start = end = NULL;
/* Add hints */
if (trace_buffer->stop == ZEND_JIT_TRACE_STOP_LOOP) {
zend_ssa_phi *phi = ssa->blocks[1].phis;
while (phi) {
if (intervals[phi->ssa_var]) {
if (intervals[phi->sources[1]]) {
intervals[phi->sources[1]]->hint = intervals[phi->ssa_var];
}
}
phi = phi->next;
}
}
for (i = 0; i < ssa->vars_count; i++) {
if (intervals[i] && !intervals[i]->hint) {
if (ssa->vars[i].definition >= 0) {
uint32_t line = ssa->vars[i].definition;
const zend_op *opline = ssa_opcodes[line];
switch (opline->opcode) {
case ZEND_QM_ASSIGN:
case ZEND_POST_INC:
case ZEND_POST_DEC:
if (ssa->ops[line].op1_use >= 0 &&
intervals[ssa->ops[line].op1_use] &&
(i == ssa->ops[line].op1_def ||
(i == ssa->ops[line].result_def &&
(ssa->ops[line].op1_def < 0 ||
!intervals[ssa->ops[line].op1_def])))) {
zend_jit_add_hint(intervals, i, ssa->ops[line].op1_use);
}
break;
case ZEND_SEND_VAR:
case ZEND_PRE_INC:
case ZEND_PRE_DEC:
if (i == ssa->ops[line].op1_def &&
ssa->ops[line].op1_use >= 0 &&
intervals[ssa->ops[line].op1_use]) {
zend_jit_add_hint(intervals, i, ssa->ops[line].op1_use);
}
break;
case ZEND_ASSIGN:
if (ssa->ops[line].op2_use >= 0 &&
intervals[ssa->ops[line].op2_use] &&
(i == ssa->ops[line].op2_def ||
(i == ssa->ops[line].op1_def &&
(ssa->ops[line].op2_def < 0 ||
!intervals[ssa->ops[line].op2_def])) ||
(i == ssa->ops[line].result_def &&
(ssa->ops[line].op2_def < 0 ||
!intervals[ssa->ops[line].op2_def]) &&
(ssa->ops[line].op1_def < 0 ||
!intervals[ssa->ops[line].op1_def])))) {
zend_jit_add_hint(intervals, i, ssa->ops[line].op2_use);
}
break;
}
}
}
}
list = zend_jit_sort_intervals(intervals, ssa->vars_count);
if (list) {
zend_lifetime_interval *ival = list;
while (ival) {
if (ival->hint) {
ival->hint->used_as_hint = ival;
}
ival = ival->list_next;
}
if (ZCG(accel_directives).jit_debug & ZEND_JIT_DEBUG_REG_ALLOC) {
// TODO: Support for nested call frames ???
op_array = trace_buffer->op_array;
ival = list;
while (ival) {
zend_life_range *range;
int var_num = ssa->vars[ival->ssa_var].var;
fprintf(stderr, "#%d.", ival->ssa_var);
// TODO: Support for nested call frames ???
zend_dump_var(op_array, (var_num < op_array->last_var ? IS_CV : 0), var_num);
fprintf(stderr, ": %u-%u", ival->range.start, ival->range.end);
range = ival->range.next;
while (range) {
fprintf(stderr, ", %u-%u", range->start, range->end);
range = range->next;
}
if (ival->load) {
fprintf(stderr, " load");
}
if (ival->store) {
fprintf(stderr, " store");
}
if (ival->hint) {
var_num = ssa->vars[ival->hint->ssa_var].var;
fprintf(stderr, " hint=#%d.", ival->hint->ssa_var);
// TODO: Support for nested call frames ???
zend_dump_var(op_array, (var_num < op_array->last_var ? IS_CV : 0), var_num);
}
fprintf(stderr, "\n");
ival = ival->list_next;
}
fprintf(stderr, "\n");
}
}
/* Linear Scan Register Allocation */
list = zend_jit_trace_linear_scan(trace_buffer, ssa_opcodes, ssa, list);
if (list) {
zend_lifetime_interval *ival, *next;
memset(intervals, 0, ssa->vars_count * sizeof(zend_lifetime_interval*));
ival = list;
while (ival != NULL) {
ZEND_ASSERT(ival->reg != ZREG_NONE);
next = ival->list_next;
ival->list_next = intervals[ival->ssa_var];
intervals[ival->ssa_var] = ival;
ival = next;
}
/* SSA resolution */
if (trace_buffer->stop == ZEND_JIT_TRACE_STOP_LOOP) {
zend_ssa_phi *phi = ssa->blocks[1].phis;
while (phi) {
int def = phi->ssa_var;
int use = phi->sources[1];
if (intervals[def]) {
if (!intervals[use]) {
intervals[def]->load = 1;
} else if (intervals[def]->reg != intervals[use]->reg) {
intervals[def]->load = 1;
intervals[use]->store = 1;
}
} else if (intervals[use]) {
intervals[use]->store = 1;
}
phi = phi->next;
}
}
// Remove useless register allocation ???
// Remove intervals used once ???
if (ZCG(accel_directives).jit_debug & ZEND_JIT_DEBUG_REG_ALLOC) {
// TODO: Support for nested call frames ???
fprintf(stderr, "---- TRACE %d Allocated Live Ranges\n", ZEND_JIT_TRACE_NUM);
for (i = 0; i < ssa->vars_count; i++) {
ival = intervals[i];
while (ival) {
zend_life_range *range;
int var_num = ssa->vars[ival->ssa_var].var;
fprintf(stderr, "#%d.", ival->ssa_var);
zend_dump_var(op_array, (var_num < op_array->last_var ? IS_CV : 0), var_num);
fprintf(stderr, ": %u-%u", ival->range.start, ival->range.end);
range = ival->range.next;
while (range) {
fprintf(stderr, ", %u-%u", range->start, range->end);
range = range->next;
}
fprintf(stderr, " (%s)", zend_reg_name[ival->reg]);
if (ival->load) {
fprintf(stderr, " load");
}
if (ival->store) {
fprintf(stderr, " store");
}
if (ival->hint) {
var_num = ssa->vars[ival->hint->ssa_var].var;
fprintf(stderr, " hint=#%d.", ival->hint->ssa_var);
zend_dump_var(op_array, (var_num < op_array->last_var ? IS_CV : 0), var_num);
if (ival->hint->reg != ZREG_NONE) {
fprintf(stderr, " (%s)", zend_reg_name[ival->hint->reg]);
}
}
fprintf(stderr, "\n");
ival = ival->list_next;
}
}
fprintf(stderr, "\n");
}
return intervals;
}
zend_arena_release(&CG(arena), checkpoint); //???
return NULL;
}
static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t parent_trace, uint32_t exit_num)
{
const void *handler = NULL;
@@ -2495,11 +1711,6 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
ssa = zend_jit_trace_build_tssa(trace_buffer, parent_trace, exit_num, script, op_arrays, &num_op_arrays);
/* Register allocation */
if (zend_jit_reg_alloc) {
ra = zend_jit_trace_allocate_registers(trace_buffer, ssa);
}
p = trace_buffer;
ZEND_ASSERT(p->op == ZEND_JIT_TRACE_START);
op_array = p->op_array;
@@ -2561,6 +1772,8 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
(zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(op_array);
op_array_ssa = &jit_extension->func_info.ssa;
// TODO: register allocation ???
dasm_growpc(&dasm_state, 1); /* trace needs just one global label for loop */
zend_jit_align_func(&dasm_state);
@@ -2604,14 +1817,6 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
SET_STACK_TYPE(stack, i, concrete_type(info));
}
}
if (ra) {
zend_ssa_phi *phi = ssa->vars[i].phi_use_chain;
if (phi && ra[phi->ssa_var] && !ra[phi->ssa_var]->load) {
if (!zend_jit_load_var(&dasm_state, ssa->var_info[i].type, i, ra[phi->ssa_var]->reg)) {
goto jit_failure;
}
}
}
}
}