static void zend_mark_reachable(zend_op *opcodes, zend_basic_block *blocks, zend_basic_block *b) /* {{{ */ { zend_uchar opcode; zend_basic_block *b0; int successor_0, successor_1; while (1) { 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, blocks, b0); } opcode = opcodes[b->end].opcode; b = blocks + successor_1; if (opcode == ZEND_JMPZNZ) { b->flags |= ZEND_BB_TARGET; } else { b->flags |= ZEND_BB_FOLLOW; } } else { opcode = opcodes[b->end].opcode; b = blocks + successor_0; if (opcode == ZEND_JMP) { b->flags |= ZEND_BB_TARGET; } else { b->flags |= ZEND_BB_FOLLOW; //TODO: support for stackless CFG??? if (0/*stackless*/) { if (opcode == ZEND_INCLUDE_OR_EVAL || 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 (b->flags & ZEND_BB_REACHABLE) return; } else { b->flags |= ZEND_BB_EXIT; return; } } }
static void zend_mark_reachable_blocks(const zend_op_array *op_array, zend_cfg *cfg, int start) /* {{{ */ { zend_basic_block *blocks = cfg->blocks; blocks[start].flags = ZEND_BB_START; zend_mark_reachable(op_array->opcodes, blocks, blocks + start); if (op_array->last_live_range || op_array->last_try_catch) { zend_basic_block *b; int j, changed; uint32_t *block_map = cfg->map; do { changed = 0; /* Add brk/cont paths */ for (j = 0; j < op_array->last_live_range; j++) { if (op_array->live_range[j].var == (uint32_t)-1) { /* this live range already removed */ continue; } b = blocks + block_map[op_array->live_range[j].start]; if (b->flags & ZEND_BB_REACHABLE) { while (op_array->opcodes[b->start].opcode == ZEND_NOP && b->start != b->end) { b->start++; } if (op_array->opcodes[b->start].opcode == ZEND_NOP && b->start == b->end && b->successors[0] == block_map[op_array->live_range[j].end]) { /* mark as removed (empty live range) */ op_array->live_range[j].var = (uint32_t)-1; continue; } b->flags |= ZEND_BB_GEN_VAR; b = blocks + block_map[op_array->live_range[j].end]; b->flags |= ZEND_BB_KILL_VAR; if (!(b->flags & ZEND_BB_REACHABLE)) { changed = 1; zend_mark_reachable(op_array->opcodes, blocks, b); } } else { ZEND_ASSERT(!(blocks[block_map[op_array->live_range[j].end]].flags & ZEND_BB_REACHABLE)); } } /* Add exception paths */ for (j = 0; j < op_array->last_try_catch; j++) { /* check for jumps into the middle of try block */ b = blocks + block_map[op_array->try_catch_array[j].try_op]; if (!(b->flags & ZEND_BB_REACHABLE)) { zend_basic_block *end; if (op_array->try_catch_array[j].catch_op) { end = blocks + block_map[op_array->try_catch_array[j].catch_op]; while (b != end) { if (b->flags & ZEND_BB_REACHABLE) { op_array->try_catch_array[j].try_op = b->start; break; } b++; } } b = blocks + block_map[op_array->try_catch_array[j].try_op]; if (!(b->flags & ZEND_BB_REACHABLE)) { if (op_array->try_catch_array[j].finally_op) { end = blocks + block_map[op_array->try_catch_array[j].finally_op]; while (b != end) { if (b->flags & ZEND_BB_REACHABLE) { op_array->try_catch_array[j].try_op = op_array->try_catch_array[j].catch_op; changed = 1; zend_mark_reachable(op_array->opcodes, blocks, blocks + block_map[op_array->try_catch_array[j].try_op]); break; } b++; } } } } b = blocks + block_map[op_array->try_catch_array[j].try_op]; if (b->flags & ZEND_BB_REACHABLE) { b->flags |= ZEND_BB_TRY; if (op_array->try_catch_array[j].catch_op) { b = blocks + block_map[op_array->try_catch_array[j].catch_op]; b->flags |= ZEND_BB_CATCH; if (!(b->flags & ZEND_BB_REACHABLE)) { changed = 1; zend_mark_reachable(op_array->opcodes, blocks, b); } } if (op_array->try_catch_array[j].finally_op) { b = blocks + block_map[op_array->try_catch_array[j].finally_op]; b->flags |= ZEND_BB_FINALLY; if (!(b->flags & ZEND_BB_REACHABLE)) { changed = 1; zend_mark_reachable(op_array->opcodes, blocks, b); } } if (op_array->try_catch_array[j].finally_end) { b = blocks + block_map[op_array->try_catch_array[j].finally_end]; b->flags |= ZEND_BB_FINALLY_END; if (!(b->flags & ZEND_BB_REACHABLE)) { changed = 1; zend_mark_reachable(op_array->opcodes, blocks, b); } } } else { if (op_array->try_catch_array[j].catch_op) { ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].catch_op]].flags & ZEND_BB_REACHABLE)); } if (op_array->try_catch_array[j].finally_op) { ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].finally_op]].flags & ZEND_BB_REACHABLE)); } if (op_array->try_catch_array[j].finally_end) { ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].finally_end]].flags & ZEND_BB_REACHABLE)); } } } } while (changed); } }
static void zend_mark_reachable_blocks(zend_op_array *op_array, zend_cfg *cfg, int start) { zend_basic_block *blocks = cfg->blocks; blocks[start].flags = ZEND_BB_START; zend_mark_reachable(op_array->opcodes, blocks, blocks + start); if (op_array->last_live_range || op_array->last_try_catch) { zend_basic_block *b; int j, changed; uint32_t *block_map = cfg->map; do { changed = 0; /* Add brk/cont paths */ for (j = 0; j < op_array->last_live_range; j++) { b = blocks + block_map[op_array->live_range[j].start]; if (b->flags & ZEND_BB_REACHABLE) { b->flags |= ZEND_BB_GEN_VAR; b = blocks + block_map[op_array->live_range[j].end]; b->flags |= ZEND_BB_KILL_VAR; if (!(b->flags & ZEND_BB_REACHABLE)) { changed = 1; zend_mark_reachable(op_array->opcodes, blocks, b); } } else { ZEND_ASSERT(!(blocks[block_map[op_array->live_range[j].end]].flags & ZEND_BB_REACHABLE)); } } /* Add exception paths */ for (j = 0; j < op_array->last_try_catch; j++) { /* check for jumps into the middle of try block */ b = blocks + block_map[op_array->try_catch_array[j].try_op]; if (!(b->flags & ZEND_BB_REACHABLE)) { zend_basic_block *end; if (op_array->try_catch_array[j].catch_op) { end = blocks + block_map[op_array->try_catch_array[j].catch_op]; while (b != end) { if (b->flags & ZEND_BB_REACHABLE) { op_array->try_catch_array[j].try_op = b->start; break; } b++; } } b = blocks + block_map[op_array->try_catch_array[j].try_op]; if (!(b->flags & ZEND_BB_REACHABLE)) { if (op_array->try_catch_array[j].finally_op) { end = blocks + block_map[op_array->try_catch_array[j].finally_op]; while (b != end) { if (b->flags & ZEND_BB_REACHABLE) { op_array->try_catch_array[j].try_op = op_array->try_catch_array[j].catch_op; changed = 1; zend_mark_reachable(op_array->opcodes, blocks, blocks + block_map[op_array->try_catch_array[j].try_op]); break; } b++; } } } } b = blocks + block_map[op_array->try_catch_array[j].try_op]; if (b->flags & ZEND_BB_REACHABLE) { b->flags |= ZEND_BB_TRY; if (op_array->try_catch_array[j].catch_op) { b = blocks + block_map[op_array->try_catch_array[j].catch_op]; b->flags |= ZEND_BB_CATCH; if (!(b->flags & ZEND_BB_REACHABLE)) { changed = 1; zend_mark_reachable(op_array->opcodes, blocks, b); } } if (op_array->try_catch_array[j].finally_op) { b = blocks + block_map[op_array->try_catch_array[j].finally_op]; b->flags |= ZEND_BB_FINALLY; if (!(b->flags & ZEND_BB_REACHABLE)) { changed = 1; zend_mark_reachable(op_array->opcodes, blocks, b); } } if (op_array->try_catch_array[j].finally_end) { b = blocks + block_map[op_array->try_catch_array[j].finally_end]; b->flags |= ZEND_BB_FINALLY_END; if (!(b->flags & ZEND_BB_REACHABLE)) { changed = 1; zend_mark_reachable(op_array->opcodes, blocks, b); } } } else { if (op_array->try_catch_array[j].catch_op) { ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].catch_op]].flags & ZEND_BB_REACHABLE)); } if (op_array->try_catch_array[j].finally_op) { ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].finally_op]].flags & ZEND_BB_REACHABLE)); } if (op_array->try_catch_array[j].finally_end) { ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].finally_end]].flags & ZEND_BB_REACHABLE)); } } } } while (changed); } }