last_op->op1.constant = zend_optimizer_add_literal(op_array, &zv);
}
DEL_SOURCE(block, block->successors[0]);
- block->successors[0] = -1;
+ block->successors_count = 0;
#if 0
/* Temporarily disabled - see bug #0025274 */
} else if (0&& block->op1_to != block &&
/* JMPNZ(true) -> JMP */
last_op->opcode = ZEND_JMP;
DEL_SOURCE(block, block->successors[1]);
- block->successors[1] = -1;
+ block->successors_count = 1;
} else {
/* JMPNZ(false) -> NOP */
MAKE_NOP(last_op);
DEL_SOURCE(block, block->successors[0]);
+ block->successors_count = 1;
block->successors[0] = block->successors[1];
- block->successors[1] = -1;
}
break;
}
} else {
MAKE_NOP(last_op);
}
- block->successors[1] = -1;
+ block->successors_count = 1;
break;
}
last_op->opcode = ZEND_QM_ASSIGN;
SET_UNUSED(last_op->op2);
DEL_SOURCE(block, block->successors[0]);
+ block->successors_count = 1;
block->successors[0] = block->successors[1];
- block->successors[1] = -1;
}
break;
}
SET_UNUSED(last_op->op1);
SET_UNUSED(last_op->op2);
DEL_SOURCE(block, block->successors[1]);
- block->successors[1] = -1;
+ block->successors_count = 1;
} else {
/* JMPZNZ(true,L1,L2) -> JMP(L2) */
literal_dtor(&ZEND_OP1_LITERAL(last_op));
SET_UNUSED(last_op->op1);
SET_UNUSED(last_op->op2);
DEL_SOURCE(block, block->successors[0]);
+ block->successors_count = 1;
block->successors[0] = block->successors[1];
- block->successors[1] = -1;
}
} else if (block->successors[0] == block->successors[1]) {
/* both goto the same one - it's JMP */
last_op->opcode = ZEND_JMP;
SET_UNUSED(last_op->op1);
SET_UNUSED(last_op->op2);
- block->successors[1] = -1;
+ block->successors_count = 1;
}
} else if (block->successors[0] == next) {
/* jumping to next on Z - can follow to it and jump only on NZ */
(next_block->flags & ZEND_BB_TARGET)) {
/* Skip continuation of "extended" BB */
zend_bitset_copy(usage, used_ext, bitset_len);
- } else if (block->successors[1] != -1) {
+ } else if (block->successors_count > 1) {
zend_bitset_union(usage, used_ext, bitset_len);
}
next_block = block;
if (b->flags & ZEND_BB_REACHABLE) {
if ((b->flags & ZEND_BB_FOLLOW) &&
!(b->flags & (ZEND_BB_TARGET | ZEND_BB_PROTECTED)) &&
- prev &&
- prev->successors[0] == i && prev->successors[1] == -1)
+ prev && prev->successors_count == 1 && prev->successors[0] == i)
{
zend_op *last_op = op_array->opcodes + prev->start + prev->len - 1;
if (prev->len != 0 && last_op->opcode == ZEND_JMP) {
/* re-link */
prev->flags |= (b->flags & ZEND_BB_EXIT);
prev->len = b->start + b->len - prev->start;
- prev->successors[0] = b->successors[0];
- prev->successors[1] = b->successors[1];
+ prev->successors_count = b->successors_count;
+ memcpy(prev->successors, b->successors, b->successors_count * sizeof(int));
/* unlink & make block empty and unreachable */
b->flags = 0;
b->len = 0;
- b->successors[0] = -1;
- b->successors[1] = -1;
+ b->successors_count = 0;
} else {
prev = b;
}
static void zend_mark_reachable(zend_op *opcodes, zend_cfg *cfg, zend_basic_block *b) /* {{{ */
{
- zend_uchar opcode;
- zend_basic_block *b0;
- int successor_0, successor_1;
zend_basic_block *blocks = cfg->blocks;
while (1) {
+ int i;
+
b->flags |= ZEND_BB_REACHABLE;
- successor_0 = b->successors[0];
- if (successor_0 >= 0) {
- successor_1 = b->successors[1];
- if (successor_1 >= 0) {
- b0 = blocks + successor_0;
- b0->flags |= ZEND_BB_TARGET;
- if (!(b0->flags & ZEND_BB_REACHABLE)) {
- zend_mark_reachable(opcodes, cfg, b0);
- }
+ if (b->successors_count == 0) {
+ b->flags |= ZEND_BB_EXIT;
+ return;
+ }
- ZEND_ASSERT(b->len != 0);
- opcode = opcodes[b->start + b->len - 1].opcode;
- b = blocks + successor_1;
- if (opcode == ZEND_JMPZNZ) {
- b->flags |= ZEND_BB_TARGET;
- } else {
- b->flags |= ZEND_BB_FOLLOW;
- }
- } else if (b->len != 0) {
- opcode = opcodes[b->start + b->len - 1].opcode;
- b = blocks + successor_0;
- if (opcode == ZEND_JMP) {
- b->flags |= ZEND_BB_TARGET;
- } else {
- b->flags |= ZEND_BB_FOLLOW;
-
- if (cfg->split_at_calls) {
- if (opcode == ZEND_INCLUDE_OR_EVAL ||
- opcode == ZEND_GENERATOR_CREATE ||
- opcode == ZEND_YIELD ||
- opcode == ZEND_YIELD_FROM ||
- opcode == ZEND_DO_FCALL ||
- opcode == ZEND_DO_UCALL ||
- opcode == ZEND_DO_FCALL_BY_NAME) {
- b->flags |= ZEND_BB_ENTRY;
+ for (i = 0; i < b->successors_count; i++) {
+ zend_basic_block *succ = blocks + b->successors[i];
+
+ if (b->len != 0) {
+ zend_uchar opcode = opcodes[b->start + b->len - 1].opcode;
+ if (b->successors_count == 1) {
+ if (opcode == ZEND_JMP) {
+ succ->flags |= ZEND_BB_TARGET;
+
+ if (cfg->split_at_calls) {
+ if (opcode == ZEND_INCLUDE_OR_EVAL ||
+ opcode == ZEND_GENERATOR_CREATE ||
+ opcode == ZEND_YIELD ||
+ opcode == ZEND_YIELD_FROM ||
+ opcode == ZEND_DO_FCALL ||
+ opcode == ZEND_DO_UCALL ||
+ opcode == ZEND_DO_FCALL_BY_NAME) {
+ b->flags |= ZEND_BB_ENTRY;
+ }
}
- }
- if (cfg->split_at_recv) {
- if (opcode == ZEND_RECV ||
- opcode == ZEND_RECV_INIT) {
- b->flags |= ZEND_BB_RECV_ENTRY;
+ if (cfg->split_at_recv) {
+ if (opcode == ZEND_RECV ||
+ opcode == ZEND_RECV_INIT) {
+ b->flags |= ZEND_BB_RECV_ENTRY;
+ }
}
+ } else {
+ succ->flags |= ZEND_BB_FOLLOW;
+ }
+ } else if (b->successors_count == 2) {
+ if (i == 0 || opcode == ZEND_JMPZNZ) {
+ succ->flags |= ZEND_BB_TARGET;
+ } else {
+ succ->flags |= ZEND_BB_FOLLOW;
}
+ } else {
+ ZEND_ASSERT(0);
}
} else {
- b = blocks + successor_0;
- b->flags |= ZEND_BB_FOLLOW;
+ succ->flags |= ZEND_BB_FOLLOW;
+ }
+
+ if (i == b->successors_count - 1) {
+ /* Tail call optimization */
+ if (succ->flags & ZEND_BB_REACHABLE) {
+ return;
+ }
+
+ b = succ;
+ break;
+ } else {
+ /* Recusively check reachability */
+ if (!(succ->flags & ZEND_BB_REACHABLE)) {
+ zend_mark_reachable(opcodes, cfg, succ);
+ }
}
- if (b->flags & ZEND_BB_REACHABLE) return;
- } else {
- b->flags |= ZEND_BB_EXIT;
- return;
}
}
}
}
/* }}} */
-static void record_successor(zend_basic_block *blocks, int pred, int n, int succ)
-{
- blocks[pred].successors[n] = succ;
-}
-
static void initialize_block(zend_basic_block *block) {
block->flags = 0;
- block->successors[0] = -1;
- block->successors[1] = -1;
+ block->successors = block->successors_storage;
+ block->successors_count = 0;
block->predecessors_count = 0;
block->predecessor_offset = -1;
block->idom = -1;
/* Build CFG, Step 3: Calculate successors */
for (j = 0; j < blocks_count; j++) {
+ zend_basic_block *block = &blocks[j];
zend_op *opline;
- if (blocks[j].len == 0) {
- record_successor(blocks, j, 0, j + 1);
+ if (block->len == 0) {
+ block->successors_count = 1;
+ block->successors[0] = j + 1;
continue;
}
- opline = op_array->opcodes + blocks[j].start + blocks[j].len - 1;
+ opline = op_array->opcodes + block->start + block->len - 1;
switch (opline->opcode) {
case ZEND_FAST_RET:
case ZEND_RETURN:
case ZEND_THROW:
break;
case ZEND_JMP:
- record_successor(blocks, j, 0, block_map[OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes]);
+ block->successors_count = 1;
+ block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes];
break;
case ZEND_JMPZNZ:
- record_successor(blocks, j, 0, block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes]);
- record_successor(blocks, j, 1, block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]);
+ block->successors_count = 2;
+ block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes];
+ block->successors[1] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)];
break;
case ZEND_JMPZ:
case ZEND_JMPNZ:
case ZEND_JMP_SET:
case ZEND_COALESCE:
case ZEND_ASSERT_CHECK:
- record_successor(blocks, j, 0, block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes]);
- record_successor(blocks, j, 1, j + 1);
+ block->successors_count = 2;
+ block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes];
+ block->successors[1] = j + 1;
break;
case ZEND_CATCH:
if (!opline->result.num) {
- record_successor(blocks, j, 0, block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]);
- record_successor(blocks, j, 1, j + 1);
+ block->successors_count = 2;
+ block->successors[0] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)];
+ block->successors[1] = j + 1;
} else {
- record_successor(blocks, j, 0, j + 1);
+ block->successors_count = 1;
+ block->successors[0] = j + 1;
}
break;
case ZEND_DECLARE_ANON_CLASS:
case ZEND_DECLARE_ANON_INHERITED_CLASS:
case ZEND_FE_FETCH_R:
case ZEND_FE_FETCH_RW:
- record_successor(blocks, j, 0, block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]);
- record_successor(blocks, j, 1, j + 1);
+ block->successors_count = 2;
+ block->successors[0] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)];
+ block->successors[1] = j + 1;
break;
case ZEND_FE_RESET_R:
case ZEND_FE_RESET_RW:
- record_successor(blocks, j, 0, block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes]);
- record_successor(blocks, j, 1, j + 1);
+ block->successors_count = 2;
+ block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes];
+ block->successors[1] = j + 1;
break;
case ZEND_FAST_CALL:
- record_successor(blocks, j, 0, block_map[OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes]);
- record_successor(blocks, j, 1, j + 1);
+ block->successors_count = 2;
+ block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes];
+ block->successors[1] = j + 1;
break;
default:
- record_successor(blocks, j, 0, j + 1);
+ block->successors_count = 1;
+ block->successors[0] = j + 1;
break;
}
}
int zend_cfg_build_predecessors(zend_arena **arena, zend_cfg *cfg) /* {{{ */
{
- int j, edges;
+ int j, s, edges;
zend_basic_block *b;
zend_basic_block *blocks = cfg->blocks;
zend_basic_block *end = blocks + cfg->blocks_count;
}
for (b = blocks; b < end; b++) {
if (!(b->flags & ZEND_BB_REACHABLE)) {
- b->successors[0] = -1;
- b->successors[1] = -1;
+ b->successors_count = 0;
b->predecessors_count = 0;
} else {
- if (b->successors[0] >= 0) {
+ for (s = 0; s < b->successors_count; s++) {
edges++;
- blocks[b->successors[0]].predecessors_count++;
- if (b->successors[1] >= 0 && b->successors[1] != b->successors[0]) {
- edges++;
- blocks[b->successors[1]].predecessors_count++;
- }
+ blocks[b->successors[s]].predecessors_count++;
}
}
}
for (j = 0; j < cfg->blocks_count; j++) {
if (blocks[j].flags & ZEND_BB_REACHABLE) {
- if (blocks[j].successors[0] >= 0) {
- zend_basic_block *b = blocks + blocks[j].successors[0];
+ for (s = 0; s < blocks[j].successors_count; s++) {
+ zend_basic_block *b = blocks + blocks[j].successors[s];
predecessors[b->predecessor_offset + b->predecessors_count] = j;
b->predecessors_count++;
- if (blocks[j].successors[1] >= 0
- && blocks[j].successors[1] != blocks[j].successors[0]) {
- zend_basic_block *b = blocks + blocks[j].successors[1];
- predecessors[b->predecessor_offset + b->predecessors_count] = j;
- b->predecessors_count++;
- }
}
}
}
static void compute_postnum_recursive(
int *postnum, int *cur, const zend_cfg *cfg, int block_num) /* {{{ */
{
+ int s;
zend_basic_block *block = &cfg->blocks[block_num];
if (postnum[block_num] != -1) {
return;
}
postnum[block_num] = -2; /* Marker for "currently visiting" */
- if (block->successors[0] >= 0) {
- compute_postnum_recursive(postnum, cur, cfg, block->successors[0]);
- if (block->successors[1] >= 0) {
- compute_postnum_recursive(postnum, cur, cfg, block->successors[1]);
- }
+ for (s = 0; s < block->successors_count; s++) {
+ compute_postnum_recursive(postnum, cur, cfg, block->successors[s]);
}
postnum[block_num] = (*cur)++;
}
}
}
/* Visit join edges. */
- for (j = 0; j < 2; j++) {
+ for (j = 0; j < blocks[i].successors_count; j++) {
int succ = blocks[i].successors[j];
- if (succ < 0) {
- continue;
- } else if (blocks[succ].idom == i) {
+ if (blocks[succ].idom == i) {
continue;
} else if (zend_worklist_push(&work, succ)) {
goto next;
/* Check that the other successor of the from block does not dominate all other predecessors.
* If it does, we'd probably end up annihilating a positive+negative pi assertion. */
from_block = &ssa->cfg.blocks[from];
+ ZEND_ASSERT(from_block->successors_count == 2);
other_successor = from_block->successors[0] == to
? from_block->successors[1] : from_block->successors[0];
return !dominates_other_predecessors(&ssa->cfg, to_block, other_successor, from);
}
}
- for (i = 0; i < 2; i++) {
+ for (i = 0; i < blocks[n].successors_count; i++) {
int succ = blocks[n].successors[i];
- if (succ >= 0) {
- zend_ssa_phi *p;
- for (p = ssa_blocks[succ].phis; p; p = p->next) {
- if (p->pi == n) {
- /* e-SSA Pi */
- if (p->has_range_constraint) {
- if (p->constraint.range.min_var >= 0) {
- p->constraint.range.min_ssa_var = var[p->constraint.range.min_var];
- }
- if (p->constraint.range.max_var >= 0) {
- p->constraint.range.max_ssa_var = var[p->constraint.range.max_var];
- }
+ zend_ssa_phi *p;
+ for (p = ssa_blocks[succ].phis; p; p = p->next) {
+ if (p->pi == n) {
+ /* e-SSA Pi */
+ if (p->has_range_constraint) {
+ if (p->constraint.range.min_var >= 0) {
+ p->constraint.range.min_ssa_var = var[p->constraint.range.min_var];
}
- for (j = 0; j < blocks[succ].predecessors_count; j++) {
- p->sources[j] = var[p->var];
+ if (p->constraint.range.max_var >= 0) {
+ p->constraint.range.max_ssa_var = var[p->constraint.range.max_var];
}
- if (p->ssa_var < 0) {
- p->ssa_var = ssa_vars_count;
- ssa_vars_count++;
- }
- } else if (p->pi < 0) {
- /* Normal Phi */
- for (j = 0; j < blocks[succ].predecessors_count; j++)
- if (ssa->cfg.predecessors[blocks[succ].predecessor_offset + j] == n) {
- break;
- }
- ZEND_ASSERT(j < blocks[succ].predecessors_count);
+ }
+ for (j = 0; j < blocks[succ].predecessors_count; j++) {
p->sources[j] = var[p->var];
}
+ if (p->ssa_var < 0) {
+ p->ssa_var = ssa_vars_count;
+ ssa_vars_count++;
+ }
+ } else if (p->pi < 0) {
+ /* Normal Phi */
+ for (j = 0; j < blocks[succ].predecessors_count; j++)
+ if (ssa->cfg.predecessors[blocks[succ].predecessor_offset + j] == n) {
+ break;
+ }
+ ZEND_ASSERT(j < blocks[succ].predecessors_count);
+ p->sources[j] = var[p->var];
}
- for (p = ssa_blocks[succ].phis; p && (p->pi >= 0); p = p->next) {
- if (p->pi == n) {
- zend_ssa_phi *q = p->next;
- while (q) {
- if (q->pi < 0 && q->var == p->var) {
- for (j = 0; j < blocks[succ].predecessors_count; j++) {
- if (ssa->cfg.predecessors[blocks[succ].predecessor_offset + j] == n) {
- break;
- }
+ }
+ for (p = ssa_blocks[succ].phis; p && (p->pi >= 0); p = p->next) {
+ if (p->pi == n) {
+ zend_ssa_phi *q = p->next;
+ while (q) {
+ if (q->pi < 0 && q->var == p->var) {
+ for (j = 0; j < blocks[succ].predecessors_count; j++) {
+ if (ssa->cfg.predecessors[blocks[succ].predecessor_offset + j] == n) {
+ break;
}
- ZEND_ASSERT(j < blocks[succ].predecessors_count);
- q->sources[j] = p->ssa_var;
}
- q = q->next;
+ ZEND_ASSERT(j < blocks[succ].predecessors_count);
+ q->sources[j] = p->ssa_var;
}
+ q = q->next;
}
}
}