From: Nikita Popov Date: Fri, 17 Mar 2017 20:37:57 +0000 (+0100) Subject: Support more than two successors in opcache CFG X-Git-Tag: php-7.2.0alpha1~141 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=d1a012b60270b751449de79235028295696145b2;p=php Support more than two successors in opcache CFG --- diff --git a/ext/opcache/Optimizer/block_pass.c b/ext/opcache/Optimizer/block_pass.c index 767fb672a1..7436c559c1 100644 --- a/ext/opcache/Optimizer/block_pass.c +++ b/ext/opcache/Optimizer/block_pass.c @@ -1079,7 +1079,7 @@ static void zend_jmp_optimization(zend_basic_block *block, zend_op_array *op_arr 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 && @@ -1152,13 +1152,13 @@ static void zend_jmp_optimization(zend_basic_block *block, zend_op_array *op_arr /* 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; } @@ -1175,7 +1175,7 @@ static void zend_jmp_optimization(zend_basic_block *block, zend_op_array *op_arr } else { MAKE_NOP(last_op); } - block->successors[1] = -1; + block->successors_count = 1; break; } @@ -1304,8 +1304,8 @@ next_target: 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; } @@ -1404,7 +1404,7 @@ next_target_ex: 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)); @@ -1412,8 +1412,8 @@ next_target_ex: 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 */ @@ -1422,7 +1422,7 @@ next_target_ex: 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 */ @@ -1606,7 +1606,7 @@ static void zend_t_usage(zend_cfg *cfg, zend_op_array *op_array, zend_bitset use (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; @@ -1724,8 +1724,7 @@ static void zend_merge_blocks(zend_op_array *op_array, zend_cfg *cfg) 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) { @@ -1752,14 +1751,13 @@ static void zend_merge_blocks(zend_op_array *op_array, zend_cfg *cfg) /* 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; } diff --git a/ext/opcache/Optimizer/zend_cfg.c b/ext/opcache/Optimizer/zend_cfg.c index 0f0cabb258..c4394757b8 100644 --- a/ext/opcache/Optimizer/zend_cfg.c +++ b/ext/opcache/Optimizer/zend_cfg.c @@ -26,65 +26,73 @@ 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; } } } @@ -251,15 +259,10 @@ void zend_cfg_remark_reachable_blocks(const zend_op_array *op_array, zend_cfg *c } /* }}} */ -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; @@ -484,13 +487,15 @@ int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t b /* 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: @@ -500,11 +505,13 @@ int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t b 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: @@ -513,35 +520,42 @@ int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t b 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; } } @@ -559,7 +573,7 @@ int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t b 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; @@ -571,17 +585,12 @@ int zend_cfg_build_predecessors(zend_arena **arena, zend_cfg *cfg) /* {{{ */ } 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++; } } } @@ -599,16 +608,10 @@ int zend_cfg_build_predecessors(zend_arena **arena, zend_cfg *cfg) /* {{{ */ 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++; - } } } } @@ -621,17 +624,15 @@ int zend_cfg_build_predecessors(zend_arena **arena, zend_cfg *cfg) /* {{{ */ 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)++; } @@ -788,11 +789,9 @@ int zend_cfg_identify_loops(const zend_op_array *op_array, zend_cfg *cfg, uint32 } } /* 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; diff --git a/ext/opcache/Optimizer/zend_cfg.h b/ext/opcache/Optimizer/zend_cfg.h index d24bbd910d..315b93ef3a 100644 --- a/ext/opcache/Optimizer/zend_cfg.h +++ b/ext/opcache/Optimizer/zend_cfg.h @@ -42,10 +42,11 @@ #define ZEND_BB_PROTECTED (ZEND_BB_ENTRY|ZEND_BB_RECV_ENTRY|ZEND_BB_TRY|ZEND_BB_CATCH|ZEND_BB_FINALLY|ZEND_BB_FINALLY_END|ZEND_BB_GEN_VAR|ZEND_BB_KILL_VAR) typedef struct _zend_basic_block { + int *successors; /* successor block indices */ uint32_t flags; uint32_t start; /* first opcode number */ uint32_t len; /* number of opcodes */ - int successors[2]; /* up to 2 successor blocks */ + int successors_count; /* number of successors */ int predecessors_count; /* number of predecessors */ int predecessor_offset; /* offset of 1-st predecessor */ int idom; /* immediate dominator block */ @@ -53,6 +54,7 @@ typedef struct _zend_basic_block { int level; /* steps away from the entry in the dom. tree */ int children; /* list of dominated blocks */ int next_child; /* next dominated block */ + int successors_storage[2]; /* up to 2 successor blocks */ } zend_basic_block; /* diff --git a/ext/opcache/Optimizer/zend_dfg.c b/ext/opcache/Optimizer/zend_dfg.c index 6e6997b2a5..e51c3a583a 100644 --- a/ext/opcache/Optimizer/zend_dfg.c +++ b/ext/opcache/Optimizer/zend_dfg.c @@ -220,10 +220,10 @@ op2_use: if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) { continue; } - if (blocks[j].successors[0] >= 0) { + if (blocks[j].successors_count != 0) { zend_bitset_copy(DFG_BITSET(out, set_size, j), DFG_BITSET(in, set_size, blocks[j].successors[0]), set_size); - if (blocks[j].successors[1] >= 0) { - zend_bitset_union(DFG_BITSET(out, set_size, j), DFG_BITSET(in, set_size, blocks[j].successors[1]), set_size); + for (k = 1; k < blocks[j].successors_count; k++) { + zend_bitset_union(DFG_BITSET(out, set_size, j), DFG_BITSET(in, set_size, blocks[j].successors[k]), set_size); } } else { zend_bitset_clear(DFG_BITSET(out, set_size, j), set_size); diff --git a/ext/opcache/Optimizer/zend_ssa.c b/ext/opcache/Optimizer/zend_ssa.c index 4c3361b5c6..54bf985d40 100644 --- a/ext/opcache/Optimizer/zend_ssa.c +++ b/ext/opcache/Optimizer/zend_ssa.c @@ -61,6 +61,7 @@ static zend_bool needs_pi(const zend_op_array *op_array, zend_dfg *dfg, zend_ssa /* 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); @@ -779,53 +780,51 @@ static int zend_ssa_rename(const zend_op_array *op_array, uint32_t build_flags, } } - 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; } } }