zend_op *def_opline = &op_array->opcodes[start], *orig_def_opline = def_opline;
zend_op *use_opline = &op_array->opcodes[end];
zend_live_range *range;
- uint32_t kind = ZEND_LIVE_TMPVAR;
+ uint32_t kind;
switch (def_opline->opcode) {
/* These should never be the first def. */
return;
case ZEND_BEGIN_SILENCE:
kind = ZEND_LIVE_SILENCE;
+ start++;
break;
case ZEND_ROPE_INIT:
kind = ZEND_LIVE_ROPE;
case ZEND_FE_RESET_R:
case ZEND_FE_RESET_RW:
kind = ZEND_LIVE_LOOP;
+ start++;
break;
/* Objects created via ZEND_NEW are only fully initialized
* after the DO_FCALL (constructor call). */
int level = 0;
while (def_opline + 1 < use_opline) {
def_opline++;
+ start++;
if (def_opline->opcode == ZEND_DO_FCALL) {
if (level == 0) {
break;
}
}
}
+ if (start + 1 == end) {
+ /* Trivial live-range, no need to store it. */
+ return;
+ }
+ /* break missing intentionally */
+ default:
+ start++;
+ kind = ZEND_LIVE_TMPVAR;
break;
}
}
- start = def_opline + 1 - op_array->opcodes;
- if (start == end) {
- /* Trivial live-range, no need to store it. */
- return;
- }
-
/* Check hook to determine whether a live range is necessary, e.g. based on type info. */
if (needs_live_range && !needs_live_range(op_array, orig_def_opline)) {
return;
return a->start - b->start;
}
static void swap_live_range(zend_live_range *a, zend_live_range *b) {
- zend_live_range tmp = *a;
- *a = *b;
- *b = tmp;
+ uint32_t tmp;
+ tmp = a->var;
+ a->var = b->var;
+ b->var = tmp;
+ tmp = a->start;
+ a->start = b->start;
+ b->start = tmp;
+ tmp = a->end;
+ a->end = b->end;
+ b->end = tmp;
}
static void zend_calc_live_ranges(
zend_op_array *op_array, zend_needs_live_range_cb needs_live_range) {
- zend_op *opline = &op_array->opcodes[op_array->last - 1];
- zend_op *begin = op_array->opcodes;
+ uint32_t opnum = op_array->last;
+ zend_op *opline = &op_array->opcodes[opnum];
ALLOCA_FLAG(use_heap)
uint32_t var_offset = op_array->last_var;
uint32_t *last_use = do_alloca(sizeof(uint32_t) * op_array->T, use_heap);
memset(last_use, -1, sizeof(uint32_t) * op_array->T);
ZEND_ASSERT(!op_array->live_range);
- for (; opline >= begin; opline--) {
- uint32_t opnum = opline - begin;
-
- if (opline->opcode == ZEND_OP_DATA) {
- /* OP_DATA is really part of the previous opcode. */
- opnum--;
- }
+ while (opnum > 0) {
+ opnum--;
+ opline--;
if ((opline->result_type & (IS_TMP_VAR|IS_VAR)) && !is_fake_def(opline)) {
uint32_t var_num = EX_VAR_TO_NUM(opline->result.var) - var_offset;
* which case the last one starts the live range. As such, we can simply ignore
* missing uses here. */
if (last_use[var_num] != (uint32_t) -1) {
- emit_live_range(op_array, var_num, opnum, last_use[var_num], needs_live_range);
+ /* Skip trivial live-range */
+ if (opnum + 1 != last_use[var_num]) {
+ uint32_t num;
+
+#if 1
+ /* OP_DATA uses only op1 operand */
+ ZEND_ASSERT(opline->opcode != ZEND_OP_DATA);
+ num = opnum;
+#else
+ /* OP_DATA is really part of the previous opcode. */
+ num = opnum - (opline->opcode == ZEND_OP_DATA);
+#endif
+ emit_live_range(op_array, var_num, num, last_use[var_num], needs_live_range);
+ }
last_use[var_num] = (uint32_t) -1;
}
}
if ((opline->op1_type & (IS_TMP_VAR|IS_VAR)) && !keeps_op1_alive(opline)) {
uint32_t var_num = EX_VAR_TO_NUM(opline->op1.var) - var_offset;
if (last_use[var_num] == (uint32_t) -1) {
- last_use[var_num] = opnum;
+ /* OP_DATA is really part of the previous opcode. */
+ last_use[var_num] = opnum - (opline->opcode == ZEND_OP_DATA);
}
}
if (opline->op2_type & (IS_TMP_VAR|IS_VAR)) {
uint32_t var_num = EX_VAR_TO_NUM(opline->op2.var) - var_offset;
if (last_use[var_num] == (uint32_t) -1) {
+#if 1
+ /* OP_DATA uses only op1 operand */
+ ZEND_ASSERT(opline->opcode != ZEND_OP_DATA);
last_use[var_num] = opnum;
+#else
+ /* OP_DATA is really part of the previous opcode. */
+ last_use[var_num] = opnum - (opline->opcode == ZEND_OP_DATA);
+#endif
}
}
}
- if (op_array->live_range) {
- zend_sort(op_array->live_range, op_array->last_live_range, sizeof(zend_live_range),
- (compare_func_t) cmp_live_range, (swap_func_t) swap_live_range);
+ if (op_array->last_live_range > 1) {
+ zend_live_range *r1 = op_array->live_range;
+ zend_live_range *r2 = r1 + op_array->last_live_range - 1;
+
+ /* In most cases we need just revert the array */
+ while (r1 < r2) {
+ swap_live_range(r1, r2);
+ r1++;
+ r2--;
+ }
+
+ r1 = op_array->live_range;
+ r2 = r1 + op_array->last_live_range - 1;
+ while (r1 < r2) {
+ if (r1->start > (r1+1)->start) {
+ zend_sort(r1, r2 - r1 + 1, sizeof(zend_live_range),
+ (compare_func_t) cmp_live_range, (swap_func_t) swap_live_range);
+ break;
+ }
+ r1++;
+ }
}
free_alloca(last_use, use_heap);
ZEND_API void zend_recalc_live_ranges(
zend_op_array *op_array, zend_needs_live_range_cb needs_live_range) {
/* We assume that we never create live-ranges where there were none before. */
- if (!op_array->live_range) {
- return;
- }
-
+ ZEND_ASSERT(op_array->live_range);
efree(op_array->live_range);
op_array->live_range = NULL;
op_array->last_live_range = 0;
/* Redo pass_two() */
zend_redo_pass_two(op_array);
- zend_recalc_live_ranges(op_array, NULL);
+ if (op_array->live_range) {
+ zend_recalc_live_ranges(op_array, NULL);
+ }
}
static void zend_adjust_fcall_stack_size(zend_op_array *op_array, zend_optimizer_ctx *ctx)
}
for (i = 0; i < call_graph.op_arrays_count; i++) {
- func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
+ op_array = call_graph.op_arrays[i];
+ func_info = ZEND_FUNC_INFO(op_array);
if (func_info && func_info->ssa.var_info) {
- zend_redo_pass_two_ex(call_graph.op_arrays[i], &func_info->ssa);
- zend_recalc_live_ranges(call_graph.op_arrays[i], needs_live_range);
+ zend_redo_pass_two_ex(op_array, &func_info->ssa);
+ if (op_array->live_range) {
+ zend_recalc_live_ranges(op_array, needs_live_range);
+ }
} else {
- zend_redo_pass_two(call_graph.op_arrays[i]);
- zend_recalc_live_ranges(call_graph.op_arrays[i], NULL);
+ zend_redo_pass_two(op_array);
+ if (op_array->live_range) {
+ zend_recalc_live_ranges(op_array, NULL);
+ }
}
}