return use < 0 || zend_ssa_is_no_val_use(op_array->opcodes + use, ssa->ops + use, var);
}
+static zend_bool zend_ival_is_last_use(const zend_lifetime_interval *ival, int use)
+{
+ if (ival->flags & ZREG_LAST_USE) {
+ const zend_life_range *range = &ival->range;
+
+ while (range->next) {
+ range = range->next;
+ }
+ return range->end == use;
+ }
+ return 0;
+}
+
static zend_bool zend_is_commutative(zend_uchar opcode)
{
return
}
ival->ssa_var = var;
ival->reg = ZREG_NONE;
- ival->split = 0;
- ival->store = 0;
- ival->load = 0;
+ ival->flags = 0;
ival->range.start = from;
ival->range.end = to;
ival->range.next = NULL;
}
}
- current->store = 1;
+ current->flags |= ZREG_STORE;
ival->ssa_var = current->ssa_var;
ival->reg = ZREG_NONE;
- ival->split = 1;
- ival->store = 0;
- ival->load = 1;
+ ival->flags |= ZREG_SPLIT | ZREG_LOAD;
+ ival->flags &= ZREG_STORE;
ival->hint = NULL;
do {
/* See "Linear Scan Register Allocation on SSA Form", Christian Wimmer and
Michael Franz, CGO'10 (2010), Figure 6. */
- if (current->split) {
+ if (current->flags & ZREG_SPLIT) {
/* for each interval it in inactive intersecting with current do */
/* freeUntilPos[it.reg] = next intersection of it with current */
it = inactive;
range = ¤t->range;
do {
uint32_t line = range->start;
+ uint32_t last_use_line = (uint32_t)-1;
zend_regset regset;
zend_reg reg;
+ if ((current->flags & ZREG_LAST_USE) && !range->next) {
+ last_use_line = range->end;
+ }
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) {
regset = zend_jit_get_scratch_regset(
op_array->opcodes + line,
ssa->ops + line,
- op_array, ssa, current->ssa_var);
+ op_array, ssa, current->ssa_var, line == last_use_line);
ZEND_REGSET_FOREACH(regset, reg) {
if (line < freeUntilPos[reg]) {
freeUntilPos[reg] = line;
goto failure;
}
+ if (list) {
+ /* Set ZREG_LAST_USE flags */
+ ival = list;
+ while (ival) {
+ zend_life_range *range = &ival->range;
+
+ while (range->next) {
+ range = range->next;
+ }
+ if (zend_ssa_is_last_use(op_array, ssa, ival->ssa_var, range->end)) {
+ ival->flags |= ZREG_LAST_USE;
+ }
+ ival = ival->list_next;
+ }
+ }
+
if (list) {
if (ZCG(accel_directives).jit_debug & ZEND_JIT_DEBUG_REG_ALLOC) {
fprintf(stderr, "Live Ranges \"%s\"\n", op_array->function_name ? ZSTR_VAL(op_array->function_name) : "[main]");
fprintf(stderr, ", %u-%u", range->start, range->end);
range = range->next;
}
- if (ival->load) {
+ if (ival->flags & ZREG_LOAD) {
fprintf(stderr, " load");
}
- if (ival->store) {
+ if (ival->flags & ZREG_STORE) {
fprintf(stderr, " store");
}
if (ival->hint) {
src = phi->sources[0];
if (intervals[i]) {
if (!intervals[src]) {
- intervals[i]->load = 1;
+ intervals[i]->flags |= ZREG_LOAD;
} else if (intervals[i]->reg != intervals[src]->reg) {
- intervals[i]->load = 1;
- intervals[src]->store = 1;
+ intervals[i]->flags |= ZREG_LOAD;
+ intervals[src]->flags |= ZREG_STORE;
}
} else if (intervals[src]) {
- intervals[src]->store = 1;
+ intervals[src]->flags |= ZREG_STORE;
}
}
} else {
}
if (need_move) {
if (intervals[i]) {
- intervals[i]->load = 1;
+ intervals[i]->flags |= ZREG_LOAD;
}
for (k = 0; k < ssa->cfg.blocks[phi->block].predecessors_count; k++) {
src = phi->sources[k];
src = ssa->vars[src].definition_phi->sources[0];
}
if (intervals[src]) {
- intervals[src]->store = 1;
+ intervals[src]->flags |= ZREG_STORE;
}
}
}
/* Remove useless register allocation */
for (i = 0; i < ssa->vars_count; i++) {
if (intervals[i] &&
- (intervals[i]->load ||
- (intervals[i]->store && ssa->vars[i].definition >= 0)) &&
+ ((intervals[i]->flags & ZREG_LOAD) ||
+ ((intervals[i]->flags & ZREG_STORE) && ssa->vars[i].definition >= 0)) &&
ssa->vars[i].use_chain < 0) {
zend_bool may_remove = 1;
zend_ssa_phi *phi = ssa->vars[i].phi_use_chain;
while (phi) {
if (intervals[phi->ssa_var] &&
- !intervals[phi->ssa_var]->load) {
+ !(intervals[phi->ssa_var]->flags & ZREG_LOAD)) {
may_remove = 0;
break;
}
/* Remove intervals used once */
for (i = 0; i < ssa->vars_count; i++) {
if (intervals[i] &&
- intervals[i]->load &&
- intervals[i]->store &&
+ (intervals[i]->flags & ZREG_LOAD) &&
+ (intervals[i]->flags & ZREG_STORE) &&
(ssa->vars[i].use_chain < 0 ||
zend_ssa_next_use(ssa->ops, i, ssa->vars[i].use_chain) < 0)) {
zend_bool may_remove = 1;
while (phi) {
if (intervals[phi->ssa_var] &&
- !intervals[phi->ssa_var]->load) {
+ !(intervals[phi->ssa_var]->flags & ZREG_LOAD)) {
may_remove = 0;
break;
}
range = range->next;
}
fprintf(stderr, " (%s)", zend_reg_name[ival->reg]);
- if (ival->load) {
+ if (ival->flags & ZREG_LOAD) {
fprintf(stderr, " load");
}
- if (ival->store) {
+ if (ival->flags & ZREG_STORE) {
fprintf(stderr, " store");
}
if (ival->hint) {
zend_lifetime_interval *ival = ra[phi->ssa_var];
if (ival) {
- if (ival->load) {
+ if (ival->flags & ZREG_LOAD) {
ZEND_ASSERT(ival->reg != ZREG_NONE);
if (!zend_jit_load_var(&dasm_state, ssa->var_info[phi->ssa_var].type, ssa->vars[phi->ssa_var].var, ival->reg)) {
goto jit_failure;
}
- } else if (ival->store) {
+ } else if (ival->flags & ZREG_STORE) {
ZEND_ASSERT(ival->reg != ZREG_NONE);
if (!zend_jit_store_var(&dasm_state, ssa->var_info[phi->ssa_var].type, ssa->vars[phi->ssa_var].var, ival->reg)) {
return 0;
}
-static zend_regset zend_jit_get_scratch_regset(const zend_op *opline, const zend_ssa_op *ssa_op, const zend_op_array *op_array, zend_ssa *ssa, int current_var)
+static zend_regset zend_jit_get_scratch_regset(const zend_op *opline, const zend_ssa_op *ssa_op, const zend_op_array *op_array, zend_ssa *ssa, int current_var, zend_bool last_use)
{
uint32_t op1_info, op2_info, res_info;
zend_regset regset = ZEND_REGSET_SCRATCH;
regset = ZEND_REGSET_EMPTY;
if ((op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG)) {
if (ssa_op->result_def != current_var &&
- (ssa_op->op1_use != current_var || !zend_ssa_is_last_use(op_array, ssa, current_var, ssa_op - ssa->ops))) {
+ (ssa_op->op1_use != current_var || !last_use)) {
ZEND_REGSET_INCL(regset, ZREG_R0);
}
res_info = OP1_INFO();
} else {
ZEND_REGSET_INCL(regset, ZREG_XMM0);
if (ssa_op->result_def != current_var &&
- (ssa_op->op1_use != current_var || !zend_ssa_is_last_use(op_array, ssa, current_var, ssa_op - ssa->ops))) {
+ (ssa_op->op1_use != current_var || !last_use)) {
ZEND_REGSET_INCL(regset, ZREG_XMM1);
}
}
}
if ((op1_info & MAY_BE_DOUBLE) && (op2_info & MAY_BE_DOUBLE)) {
if (ssa_op->result_def != current_var &&
- (ssa_op->op1_use != current_var || !zend_ssa_is_last_use(op_array, ssa, current_var, ssa_op - ssa->ops))) {
+ (ssa_op->op1_use != current_var || !last_use)) {
ZEND_REGSET_INCL(regset, ZREG_XMM0);
}
}
!(op2_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-MAY_BE_LONG))) {
regset = ZEND_REGSET_EMPTY;
if (ssa_op->result_def != current_var &&
- (ssa_op->op1_use != current_var || !zend_ssa_is_last_use(op_array, ssa, current_var, ssa_op - ssa->ops))) {
+ (ssa_op->op1_use != current_var || !last_use)) {
ZEND_REGSET_INCL(regset, ZREG_R0);
}
}
!(op2_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-MAY_BE_LONG))) {
regset = ZEND_REGSET_EMPTY;
if (ssa_op->result_def != current_var &&
- (ssa_op->op1_use != current_var || !zend_ssa_is_last_use(op_array, ssa, current_var, ssa_op - ssa->ops))) {
+ (ssa_op->op1_use != current_var || !last_use)) {
ZEND_REGSET_INCL(regset, ZREG_R0);
}
if (opline->op2_type != IS_CONST && ssa_op->op2_use != current_var) {
Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) == IS_LONG &&
zend_long_is_power_of_two(Z_LVAL_P(RT_CONSTANT(opline, opline->op2)))) {
if (ssa_op->result_def != current_var &&
- (ssa_op->op1_use != current_var || !zend_ssa_is_last_use(op_array, ssa, current_var, ssa_op - ssa->ops))) {
+ (ssa_op->op1_use != current_var || !last_use)) {
ZEND_REGSET_INCL(regset, ZREG_R0);
}
} else {