From 2d33fb7f462f42ddb6ff9b0ce36b654d93964224 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Fri, 17 Mar 2017 21:37:57 +0100 Subject: [PATCH 1/4] Support more than two successors in opcache CFG --- ext/opcache/Optimizer/block_pass.c | 28 ++-- ext/opcache/Optimizer/zend_cfg.c | 197 ++++++++++++++--------------- ext/opcache/Optimizer/zend_cfg.h | 4 +- ext/opcache/Optimizer/zend_dfg.c | 6 +- ext/opcache/Optimizer/zend_ssa.c | 75 ++++++----- 5 files changed, 154 insertions(+), 156 deletions(-) diff --git a/ext/opcache/Optimizer/block_pass.c b/ext/opcache/Optimizer/block_pass.c index a7c8897c3fbd1..d3fb6153231eb 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; } @@ -1172,7 +1172,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; } @@ -1301,8 +1301,8 @@ static void zend_jmp_optimization(zend_basic_block *block, zend_op_array *op_arr 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; } @@ -1401,7 +1401,7 @@ static void zend_jmp_optimization(zend_basic_block *block, zend_op_array *op_arr 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)); @@ -1409,8 +1409,8 @@ static void zend_jmp_optimization(zend_basic_block *block, zend_op_array *op_arr 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 */ @@ -1419,7 +1419,7 @@ static void zend_jmp_optimization(zend_basic_block *block, zend_op_array *op_arr 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 */ @@ -1603,7 +1603,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; @@ -1721,8 +1721,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) { @@ -1749,14 +1748,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 0f0cabb2587cd..c4394757b86f7 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 d24bbd910d736..315b93ef3a4d1 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 6e6997b2a5a58..e51c3a583af5d 100644 --- a/ext/opcache/Optimizer/zend_dfg.c +++ b/ext/opcache/Optimizer/zend_dfg.c @@ -220,10 +220,10 @@ int zend_build_dfg(const zend_op_array *op_array, const zend_cfg *cfg, zend_dfg 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 4c3361b5c6412..54bf985d40ca1 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; } } } From 81001ec65ab6ee9e7e2ecc7436868cbe27cfb87e Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Fri, 17 Mar 2017 23:45:05 +0100 Subject: [PATCH 2/4] Implement ZEND_SWITCH opcode --- Zend/tests/switch_on_numeric_strings.phpt | 29 ++++ Zend/zend_compile.c | 86 +++++++++- Zend/zend_opcode.c | 12 ++ Zend/zend_vm_def.h | 33 ++++ Zend/zend_vm_execute.h | 199 ++++++++++++++++++---- Zend/zend_vm_opcodes.c | 6 +- Zend/zend_vm_opcodes.h | 3 +- 7 files changed, 327 insertions(+), 41 deletions(-) create mode 100644 Zend/tests/switch_on_numeric_strings.phpt diff --git a/Zend/tests/switch_on_numeric_strings.phpt b/Zend/tests/switch_on_numeric_strings.phpt new file mode 100644 index 0000000000000..b436cefe41a84 --- /dev/null +++ b/Zend/tests/switch_on_numeric_strings.phpt @@ -0,0 +1,29 @@ +--TEST-- +Switch on numeric strings +--FILE-- + +--EXPECT-- +string(2) "01" +string(2) " 2" +string(4) "10.0" diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index ab6e23d7dfd44..8ff37c2127342 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -4603,6 +4603,47 @@ void zend_compile_if(zend_ast *ast) /* {{{ */ } /* }}} */ +static zend_uchar determine_switch_jumptable_type(zend_ast_list *cases) { + uint32_t i; + zend_uchar common_type = IS_UNDEF; + for (i = 0; i < cases->children; i++) { + zend_ast *case_ast = cases->child[i]; + zend_ast **cond_ast = &case_ast->child[0]; + zval *cond_zv; + if (!case_ast->child[0]) { + /* Skip default clause */ + continue; + } + + zend_eval_const_expr(cond_ast); + if ((*cond_ast)->kind != ZEND_AST_ZVAL) { + /* Non-constant case */ + return IS_UNDEF; + } + + cond_zv = zend_ast_get_zval(case_ast->child[0]); + if (Z_TYPE_P(cond_zv) != IS_LONG && Z_TYPE_P(cond_zv) != IS_STRING) { + /* We only optimize switched on integers and strings */ + return IS_UNDEF; + } + + if (common_type == IS_UNDEF) { + common_type = Z_TYPE_P(cond_zv); + } else if (common_type != Z_TYPE_P(cond_zv)) { + /* Non-uniform case types */ + return IS_UNDEF; + } + + if (Z_TYPE_P(cond_zv) == IS_STRING + && is_numeric_string(Z_STRVAL_P(cond_zv), Z_STRLEN_P(cond_zv), NULL, NULL, 0)) { + /* Numeric strings cannot be compared with a simple hash lookup */ + return IS_UNDEF; + } + } + + return common_type; +} + void zend_compile_switch(zend_ast *ast) /* {{{ */ { zend_ast *expr_ast = ast->child[0]; @@ -4613,7 +4654,9 @@ void zend_compile_switch(zend_ast *ast) /* {{{ */ znode expr_node, case_node; zend_op *opline; - uint32_t *jmpnz_opnums, opnum_default_jmp; + uint32_t *jmpnz_opnums, opnum_default_jmp, opnum_switch; + zend_uchar jumptable_type; + HashTable *jumptable = NULL; zend_compile_expr(&expr_node, expr_ast); @@ -4622,6 +4665,23 @@ void zend_compile_switch(zend_ast *ast) /* {{{ */ case_node.op_type = IS_TMP_VAR; case_node.u.op.var = get_temporary_variable(CG(active_op_array)); + jumptable_type = determine_switch_jumptable_type(cases); + if (jumptable_type != IS_UNDEF && cases->children >= 4) { + znode jumptable_op; + + ALLOC_HASHTABLE(jumptable); + zend_hash_init(jumptable, cases->children, NULL, NULL, 0); + jumptable_op.op_type = IS_CONST; + ZVAL_ARR(&jumptable_op.u.constant, jumptable); + + opline = zend_emit_op(NULL, ZEND_SWITCH, &expr_node, &jumptable_op); + if (opline->op1_type == IS_CONST) { + zval_copy_ctor(CT_CONSTANT(opline->op1)); + } + opline->result.num = jumptable_type; + opnum_switch = opline - CG(active_op_array)->opcodes; + } + jmpnz_opnums = safe_emalloc(sizeof(uint32_t), cases->children, 0); for (i = 0; i < cases->children; ++i) { zend_ast *case_ast = cases->child[i]; @@ -4666,8 +4726,27 @@ void zend_compile_switch(zend_ast *ast) /* {{{ */ if (cond_ast) { zend_update_jump_target_to_next(jmpnz_opnums[i]); + + if (jumptable) { + zval *cond_zv = zend_ast_get_zval(cond_ast); + zval jmp_target; + ZVAL_LONG(&jmp_target, get_next_op_number(CG(active_op_array))); + + ZEND_ASSERT(Z_TYPE_P(cond_zv) == jumptable_type); + if (Z_TYPE_P(cond_zv) == IS_LONG) { + zend_hash_index_add(jumptable, Z_LVAL_P(cond_zv), &jmp_target); + } else { + ZEND_ASSERT(Z_TYPE_P(cond_zv) == IS_STRING); + zend_hash_add(jumptable, Z_STR_P(cond_zv), &jmp_target); + } + } } else { zend_update_jump_target_to_next(opnum_default_jmp); + + if (jumptable) { + opline = &CG(active_op_array)->opcodes[opnum_switch]; + opline->extended_value = get_next_op_number(CG(active_op_array)); + } } zend_compile_stmt(stmt_ast); @@ -4675,6 +4754,11 @@ void zend_compile_switch(zend_ast *ast) /* {{{ */ if (!has_default_case) { zend_update_jump_target_to_next(opnum_default_jmp); + + if (jumptable) { + opline = &CG(active_op_array)->opcodes[opnum_switch]; + opline->extended_value = get_next_op_number(CG(active_op_array)); + } } zend_end_loop(get_next_op_number(CG(active_op_array)), &expr_node); diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index 7c79703338370..85e902970841d 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -670,6 +670,18 @@ ZEND_API int pass_two(zend_op_array *op_array) opline->opcode = ZEND_GENERATOR_RETURN; } break; + case ZEND_SWITCH: + { + /* absolute indexes to relative offsets */ + HashTable *jumptable = Z_ARRVAL_P(CT_CONSTANT(opline->op2)); + zval *zv; + ZEND_HASH_FOREACH_VAL(jumptable, zv) { + Z_LVAL_P(zv) = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, Z_LVAL_P(zv)); + } ZEND_HASH_FOREACH_END(); + + opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, opline->extended_value); + break; + } } if (opline->op1_type == IS_CONST) { ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline->op1); diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index d80f1e6b56ec8..7873759528e61 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -8043,6 +8043,39 @@ ZEND_VM_HANDLER(51, ZEND_MAKE_REF, VAR|CV, UNUSED) ZEND_VM_NEXT_OPCODE(); } +ZEND_VM_HANDLER(187, ZEND_SWITCH, CONST|TMPVAR|CV, CONST, JMP_ADDR) +{ + USE_OPLINE + zend_free_op free_op1, free_op2; + zval *op, *table_op, *jump_zv; + HashTable *ht; + + op = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R); + table_op = GET_OP2_ZVAL_PTR(BP_VAR_R); + ht = Z_ARRVAL_P(table_op); + + if (Z_TYPE_P(op) != opline->result.num) { + /* Wrong type, fall back to ZEND_CASE chain */ + ZEND_VM_NEXT_OPCODE(); + } + + if (opline->result.num == IS_LONG) { + jump_zv = zend_hash_index_find(ht, Z_LVAL_P(op)); + } else { + ZEND_ASSERT(opline->result.num == IS_STRING); + jump_zv = zend_hash_find(ht, Z_STR_P(op)); + } + + if (jump_zv != NULL) { + ZEND_VM_SET_RELATIVE_OPCODE(opline, Z_LVAL_P(jump_zv)); + ZEND_VM_CONTINUE(); + } else { + /* default */ + ZEND_VM_SET_RELATIVE_OPCODE(opline, opline->extended_value); + ZEND_VM_CONTINUE(); + } +} + ZEND_VM_TYPE_SPEC_HANDLER(ZEND_ADD, (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG), ZEND_ADD_LONG_NO_OVERFLOW, CONST|TMPVARCV, CONST|TMPVARCV, SPEC(NO_CONST_CONST,COMMUTATIVE)) { USE_OPLINE diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index bc369ae9b0778..bdaac6e3343a2 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -6409,6 +6409,39 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_CONST_HANDLER ZEND_VM_RETURN(); } +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SWITCH_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + + zval *op, *table_op, *jump_zv; + HashTable *ht; + + op = EX_CONSTANT(opline->op1); + table_op = EX_CONSTANT(opline->op2); + ht = Z_ARRVAL_P(table_op); + + if (Z_TYPE_P(op) != opline->result.num) { + /* Wrong type, fall back to ZEND_CASE chain */ + ZEND_VM_NEXT_OPCODE(); + } + + if (opline->result.num == IS_LONG) { + jump_zv = zend_hash_index_find(ht, Z_LVAL_P(op)); + } else { + ZEND_ASSERT(opline->result.num == IS_STRING); + jump_zv = zend_hash_find(ht, Z_STR_P(op)); + } + + if (jump_zv != NULL) { + ZEND_VM_SET_RELATIVE_OPCODE(opline, Z_LVAL_P(jump_zv)); + ZEND_VM_CONTINUE(); + } else { + /* default */ + ZEND_VM_SET_RELATIVE_OPCODE(opline, opline->extended_value); + ZEND_VM_CONTINUE(); + } +} + static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -38305,6 +38338,39 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BIND_STATIC_SPEC_CV_CONST_HAND ZEND_VM_NEXT_OPCODE(); } +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SWITCH_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + + zval *op, *table_op, *jump_zv; + HashTable *ht; + + op = _get_zval_ptr_cv_undef(execute_data, opline->op1.var); + table_op = EX_CONSTANT(opline->op2); + ht = Z_ARRVAL_P(table_op); + + if (Z_TYPE_P(op) != opline->result.num) { + /* Wrong type, fall back to ZEND_CASE chain */ + ZEND_VM_NEXT_OPCODE(); + } + + if (opline->result.num == IS_LONG) { + jump_zv = zend_hash_index_find(ht, Z_LVAL_P(op)); + } else { + ZEND_ASSERT(opline->result.num == IS_STRING); + jump_zv = zend_hash_find(ht, Z_STR_P(op)); + } + + if (jump_zv != NULL) { + ZEND_VM_SET_RELATIVE_OPCODE(opline, Z_LVAL_P(jump_zv)); + ZEND_VM_CONTINUE(); + } else { + /* default */ + ZEND_VM_SET_RELATIVE_OPCODE(opline, opline->extended_value); + ZEND_VM_CONTINUE(); + } +} + static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_R_INDEX_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -49767,6 +49833,39 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INSTANCEOF_SPEC_TMPVAR_CONST_H ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SWITCH_SPEC_TMPVAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + zend_free_op free_op1; + zval *op, *table_op, *jump_zv; + HashTable *ht; + + op = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); + table_op = EX_CONSTANT(opline->op2); + ht = Z_ARRVAL_P(table_op); + + if (Z_TYPE_P(op) != opline->result.num) { + /* Wrong type, fall back to ZEND_CASE chain */ + ZEND_VM_NEXT_OPCODE(); + } + + if (opline->result.num == IS_LONG) { + jump_zv = zend_hash_index_find(ht, Z_LVAL_P(op)); + } else { + ZEND_ASSERT(opline->result.num == IS_STRING); + jump_zv = zend_hash_find(ht, Z_STR_P(op)); + } + + if (jump_zv != NULL) { + ZEND_VM_SET_RELATIVE_OPCODE(opline, Z_LVAL_P(jump_zv)); + ZEND_VM_CONTINUE(); + } else { + /* default */ + ZEND_VM_SET_RELATIVE_OPCODE(opline, opline->extended_value); + ZEND_VM_CONTINUE(); + } +} + static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -57752,6 +57851,31 @@ void zend_init_opcodes_handlers(void) ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, + ZEND_SWITCH_SPEC_CONST_CONST_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_SWITCH_SPEC_TMPVAR_CONST_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_SWITCH_SPEC_TMPVAR_CONST_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_SWITCH_SPEC_CV_CONST_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV_HANDLER, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV_HANDLER, @@ -58820,7 +58944,7 @@ void zend_init_opcodes_handlers(void) 2257 | SPEC_RULE_OP1 | SPEC_RULE_OP2, 2282 | SPEC_RULE_OP1 | SPEC_RULE_OP2, 2307 | SPEC_RULE_OP1 | SPEC_RULE_OP2, - 4596, + 4621, 2332, 2333, 2334, @@ -58905,9 +59029,10 @@ void zend_init_opcodes_handlers(void) 3531 | SPEC_RULE_OP1 | SPEC_RULE_OP2, 3556 | SPEC_RULE_OP1 | SPEC_RULE_OP2, 3581 | SPEC_RULE_OP1 | SPEC_RULE_OP2, - 4596, + 4621, 3606 | SPEC_RULE_OP1 | SPEC_RULE_OP2, - 4596 + 3631 | SPEC_RULE_OP1 | SPEC_RULE_OP2, + 4621 }; zend_opcode_handlers = labels; zend_handlers_count = sizeof(labels) / sizeof(void*); @@ -59014,7 +59139,7 @@ ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3631 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 3656 | SPEC_RULE_OP1 | SPEC_RULE_OP2; if (op->op1_type > op->op2_type) { zend_swap_operands(op); } @@ -59022,7 +59147,7 @@ ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3656 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 3681 | SPEC_RULE_OP1 | SPEC_RULE_OP2; if (op->op1_type > op->op2_type) { zend_swap_operands(op); } @@ -59030,7 +59155,7 @@ ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3681 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 3706 | SPEC_RULE_OP1 | SPEC_RULE_OP2; if (op->op1_type > op->op2_type) { zend_swap_operands(op); } @@ -59041,17 +59166,17 @@ ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3706 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 3731 | SPEC_RULE_OP1 | SPEC_RULE_OP2; } else if (op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3731 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 3756 | SPEC_RULE_OP1 | SPEC_RULE_OP2; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3756 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 3781 | SPEC_RULE_OP1 | SPEC_RULE_OP2; } break; case ZEND_MUL: @@ -59059,7 +59184,7 @@ ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3781 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 3806 | SPEC_RULE_OP1 | SPEC_RULE_OP2; if (op->op1_type > op->op2_type) { zend_swap_operands(op); } @@ -59067,7 +59192,7 @@ ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3806 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 3831 | SPEC_RULE_OP1 | SPEC_RULE_OP2; if (op->op1_type > op->op2_type) { zend_swap_operands(op); } @@ -59075,7 +59200,7 @@ ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3831 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 3856 | SPEC_RULE_OP1 | SPEC_RULE_OP2; if (op->op1_type > op->op2_type) { zend_swap_operands(op); } @@ -59086,7 +59211,7 @@ ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3856 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; + spec = 3881 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; if (op->op1_type > op->op2_type) { zend_swap_operands(op); } @@ -59094,7 +59219,7 @@ ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3931 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; + spec = 3956 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; if (op->op1_type > op->op2_type) { zend_swap_operands(op); } @@ -59105,7 +59230,7 @@ ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 4006 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; + spec = 4031 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; if (op->op1_type > op->op2_type) { zend_swap_operands(op); } @@ -59113,7 +59238,7 @@ ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 4081 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; + spec = 4106 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; if (op->op1_type > op->op2_type) { zend_swap_operands(op); } @@ -59124,12 +59249,12 @@ ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 4156 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; + spec = 4181 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 4231 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; + spec = 4256 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; } break; case ZEND_IS_SMALLER_OR_EQUAL: @@ -59137,70 +59262,70 @@ ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 4306 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; + spec = 4331 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 4381 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; + spec = 4406 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; } break; case ZEND_QM_ASSIGN: if (op1_info == MAY_BE_DOUBLE) { - spec = 4546 | SPEC_RULE_OP1; + spec = 4571 | SPEC_RULE_OP1; } else if (!(op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE)))) { - spec = 4551 | SPEC_RULE_OP1; + spec = 4576 | SPEC_RULE_OP1; } break; case ZEND_PRE_INC: if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) { - spec = 4456 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL; + spec = 4481 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL; } else if (op1_info == MAY_BE_LONG) { - spec = 4466 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL; + spec = 4491 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL; } else if (op1_info == (MAY_BE_LONG|MAY_BE_DOUBLE)) { - spec = 4476 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL; + spec = 4501 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL; } break; case ZEND_PRE_DEC: if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) { - spec = 4486 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL; + spec = 4511 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL; } else if (op1_info == MAY_BE_LONG) { - spec = 4496 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL; + spec = 4521 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL; } else if (op1_info == (MAY_BE_LONG|MAY_BE_DOUBLE)) { - spec = 4506 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL; + spec = 4531 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL; } break; case ZEND_POST_INC: if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) { - spec = 4516 | SPEC_RULE_OP1; + spec = 4541 | SPEC_RULE_OP1; } else if (op1_info == MAY_BE_LONG) { - spec = 4521 | SPEC_RULE_OP1; + spec = 4546 | SPEC_RULE_OP1; } else if (op1_info == (MAY_BE_LONG|MAY_BE_DOUBLE)) { - spec = 4526 | SPEC_RULE_OP1; + spec = 4551 | SPEC_RULE_OP1; } break; case ZEND_POST_DEC: if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) { - spec = 4531 | SPEC_RULE_OP1; + spec = 4556 | SPEC_RULE_OP1; } else if (op1_info == MAY_BE_LONG) { - spec = 4536 | SPEC_RULE_OP1; + spec = 4561 | SPEC_RULE_OP1; } else if (op1_info == (MAY_BE_LONG|MAY_BE_DOUBLE)) { - spec = 4541 | SPEC_RULE_OP1; + spec = 4566 | SPEC_RULE_OP1; } break; case ZEND_SEND_VAR_EX: if ((op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0) { - spec = 4586 | SPEC_RULE_OP1 | SPEC_RULE_QUICK_ARG; + spec = 4611 | SPEC_RULE_OP1 | SPEC_RULE_QUICK_ARG; } break; case ZEND_FETCH_DIM_R: if (!(op2_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) { - spec = 4556 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 4581 | SPEC_RULE_OP1 | SPEC_RULE_OP2; } break; case ZEND_SEND_VAR: if ((op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0) { - spec = 4581 | SPEC_RULE_OP1; + spec = 4606 | SPEC_RULE_OP1; } break; default: diff --git a/Zend/zend_vm_opcodes.c b/Zend/zend_vm_opcodes.c index 07b43f7f2b5c5..5d2c4ecd7d5c9 100644 --- a/Zend/zend_vm_opcodes.c +++ b/Zend/zend_vm_opcodes.c @@ -21,7 +21,7 @@ #include #include -static const char *zend_vm_opcodes_names[187] = { +static const char *zend_vm_opcodes_names[188] = { "ZEND_NOP", "ZEND_ADD", "ZEND_SUB", @@ -209,9 +209,10 @@ static const char *zend_vm_opcodes_names[187] = { "ZEND_FETCH_THIS", NULL, "ZEND_ISSET_ISEMPTY_THIS", + "ZEND_SWITCH", }; -static uint32_t zend_vm_opcodes_flags[187] = { +static uint32_t zend_vm_opcodes_flags[188] = { 0x00000000, 0x00000707, 0x00000707, @@ -399,6 +400,7 @@ static uint32_t zend_vm_opcodes_flags[187] = { 0x00000101, 0x00000000, 0x00000101, + 0x03000307, }; ZEND_API const char* zend_get_opcode_name(zend_uchar opcode) { diff --git a/Zend/zend_vm_opcodes.h b/Zend/zend_vm_opcodes.h index db0fdd10ec1b6..2044b2f746a6a 100644 --- a/Zend/zend_vm_opcodes.h +++ b/Zend/zend_vm_opcodes.h @@ -252,7 +252,8 @@ END_EXTERN_C() #define ZEND_BIND_STATIC 183 #define ZEND_FETCH_THIS 184 #define ZEND_ISSET_ISEMPTY_THIS 186 +#define ZEND_SWITCH 187 -#define ZEND_VM_LAST_OPCODE 186 +#define ZEND_VM_LAST_OPCODE 187 #endif From 63fac3fc51366ed5b0551817bf73147cb73ba515 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Sat, 18 Mar 2017 00:39:07 +0100 Subject: [PATCH 3/4] Opcache support for ZEND_SWITCH --- Zend/zend_compile.c | 1 + ext/opcache/Optimizer/block_pass.c | 58 +++++++++++- ext/opcache/Optimizer/dfa_pass.c | 61 +------------ ext/opcache/Optimizer/nop_removal.c | 60 +------------ ext/opcache/Optimizer/zend_cfg.c | 35 +++++++- ext/opcache/Optimizer/zend_dump.c | 34 +++++-- ext/opcache/Optimizer/zend_optimizer.c | 89 ++++++++++++++++++- .../Optimizer/zend_optimizer_internal.h | 2 + ext/opcache/zend_file_cache.c | 2 + ext/opcache/zend_persist.c | 1 + 10 files changed, 215 insertions(+), 128 deletions(-) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 8ff37c2127342..82d5a3c303191 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -2081,6 +2081,7 @@ static void zend_check_live_ranges(zend_op *opline) /* {{{ */ } else if (opline->opcode == ZEND_FAST_RET) { /* fast_calls don't have to be destroyed */ } else if (opline->opcode == ZEND_CASE || + opline->opcode == ZEND_SWITCH || opline->opcode == ZEND_FE_FETCH_R || opline->opcode == ZEND_FE_FETCH_RW || opline->opcode == ZEND_FE_FREE || diff --git a/ext/opcache/Optimizer/block_pass.c b/ext/opcache/Optimizer/block_pass.c index d3fb6153231eb..8dabec45034f6 100644 --- a/ext/opcache/Optimizer/block_pass.c +++ b/ext/opcache/Optimizer/block_pass.c @@ -140,6 +140,26 @@ static void strip_nops(zend_op_array *op_array, zend_basic_block *b) } } +static int get_const_switch_target(zend_cfg *cfg, zend_op_array *op_array, zend_basic_block *block, zend_op *opline, zval *val) { + HashTable *jumptable = Z_ARRVAL(ZEND_OP2_LITERAL(opline)); + zval *zv; + if (Z_TYPE_P(val) != opline->result.num) { + /* fallback to next block */ + return block->successors[block->successors_count - 1]; + } + if (Z_TYPE_P(val) == IS_LONG) { + zv = zend_hash_index_find(jumptable, Z_LVAL_P(val)); + } else { + ZEND_ASSERT(Z_TYPE_P(val) == IS_STRING); + zv = zend_hash_find(jumptable, Z_STR_P(val)); + } + if (!zv) { + /* default */ + return block->successors[block->successors_count - 2]; + } + return cfg->map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv))]; +} + static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array, zend_bitset used_ext, zend_cfg *cfg, zend_op **Tsource) { zend_op *opline, *src; @@ -344,6 +364,24 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array } break; + case ZEND_SWITCH: + if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) { + /* SWITCH variable will be deleted later by FREE, so we can't optimize it */ + Tsource[VAR_NUM(opline->op1.var)] = NULL; + break; + } + if (opline->op1_type == IS_CONST) { + int target = get_const_switch_target(cfg, op_array, block, opline, &ZEND_OP1_LITERAL(opline)); + literal_dtor(&ZEND_OP1_LITERAL(opline)); + literal_dtor(&ZEND_OP2_LITERAL(opline)); + opline->opcode = ZEND_JMP; + opline->op1_type = IS_UNUSED; + opline->op2_type = IS_UNUSED; + block->successors_count = 1; + block->successors[0] = target; + } + break; + case ZEND_CASE: if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) { /* CASE variable will be deleted later by FREE, so we can't optimize it */ @@ -886,6 +924,19 @@ static void assemble_code_blocks(zend_cfg *cfg, zend_op_array *op_array) case ZEND_FE_FETCH_RW: opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, new_opcodes + blocks[b->successors[0]].start); break; + case ZEND_SWITCH: + { + HashTable *jumptable = Z_ARRVAL(ZEND_OP2_LITERAL(opline)); + zval *zv; + uint32_t s = 0; + ZEND_ASSERT(b->successors_count == 2 + zend_hash_num_elements(jumptable)); + + ZEND_HASH_FOREACH_VAL(jumptable, zv) { + Z_LVAL_P(zv) = ZEND_OPLINE_TO_OFFSET(opline, new_opcodes + blocks[b->successors[s++]].start); + } ZEND_HASH_FOREACH_END(); + opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, new_opcodes + blocks[b->successors[s++]].start); + break; + } } } @@ -1749,7 +1800,12 @@ static void zend_merge_blocks(zend_op_array *op_array, zend_cfg *cfg) prev->flags |= (b->flags & ZEND_BB_EXIT); prev->len = b->start + b->len - prev->start; prev->successors_count = b->successors_count; - memcpy(prev->successors, b->successors, b->successors_count * sizeof(int)); + if (b->successors != b->successors_storage) { + prev->successors = b->successors; + b->successors = b->successors_storage; + } else { + memcpy(prev->successors, b->successors, b->successors_count * sizeof(int)); + } /* unlink & make block empty and unreachable */ b->flags = 0; diff --git a/ext/opcache/Optimizer/dfa_pass.c b/ext/opcache/Optimizer/dfa_pass.c index 63f7fafa93df4..20fe1d6d21dac 100644 --- a/ext/opcache/Optimizer/dfa_pass.c +++ b/ext/opcache/Optimizer/dfa_pass.c @@ -165,37 +165,7 @@ static void zend_ssa_remove_nops(zend_op_array *op_array, zend_ssa *ssa) opline = op_array->opcodes + end - 1; b->len = target - b->start; new_opline = op_array->opcodes + target - 1; - switch (new_opline->opcode) { - case ZEND_JMP: - case ZEND_FAST_CALL: - ZEND_SET_OP_JMP_ADDR(new_opline, new_opline->op1, ZEND_OP1_JMP_ADDR(opline)); - break; - case ZEND_JMPZNZ: - new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)); - /* break missing intentionally */ - case ZEND_JMPZ: - case ZEND_JMPNZ: - case ZEND_JMPZ_EX: - case ZEND_JMPNZ_EX: - case ZEND_FE_RESET_R: - case ZEND_FE_RESET_RW: - case ZEND_JMP_SET: - case ZEND_COALESCE: - case ZEND_ASSERT_CHECK: - ZEND_SET_OP_JMP_ADDR(new_opline, new_opline->op2, ZEND_OP2_JMP_ADDR(opline)); - break; - case ZEND_CATCH: - if (!opline->result.num) { - new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)); - } - break; - case ZEND_DECLARE_ANON_CLASS: - case ZEND_DECLARE_ANON_INHERITED_CLASS: - case ZEND_FE_FETCH_R: - case ZEND_FE_FETCH_RW: - new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)); - break; - } + zend_optimizer_migrate_jump(op_array, new_opline, opline); } } } @@ -231,34 +201,7 @@ static void zend_ssa_remove_nops(zend_op_array *op_array, zend_ssa *ssa) for (b = blocks; b < end; b++) { if ((b->flags & ZEND_BB_REACHABLE) && b->len != 0) { zend_op *opline = op_array->opcodes + b->start + b->len - 1; - - switch (opline->opcode) { - case ZEND_JMP: - case ZEND_FAST_CALL: - ZEND_SET_OP_JMP_ADDR(opline, opline->op1, ZEND_OP1_JMP_ADDR(opline) - shiftlist[ZEND_OP1_JMP_ADDR(opline) - op_array->opcodes]); - break; - case ZEND_JMPZNZ: - opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) - shiftlist[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]); - /* break missing intentionally */ - case ZEND_JMPZ: - case ZEND_JMPNZ: - case ZEND_JMPZ_EX: - case ZEND_JMPNZ_EX: - case ZEND_FE_RESET_R: - case ZEND_FE_RESET_RW: - case ZEND_JMP_SET: - case ZEND_COALESCE: - case ZEND_ASSERT_CHECK: - ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP2_JMP_ADDR(opline) - shiftlist[ZEND_OP2_JMP_ADDR(opline) - op_array->opcodes]); - break; - case ZEND_DECLARE_ANON_CLASS: - case ZEND_DECLARE_ANON_INHERITED_CLASS: - case ZEND_FE_FETCH_R: - case ZEND_FE_FETCH_RW: - case ZEND_CATCH: - opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) - shiftlist[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]); - break; - } + zend_optimizer_shift_jump(op_array, opline, shiftlist); } } diff --git a/ext/opcache/Optimizer/nop_removal.c b/ext/opcache/Optimizer/nop_removal.c index c7ff73a61ba94..da2d4610a3ee9 100644 --- a/ext/opcache/Optimizer/nop_removal.c +++ b/ext/opcache/Optimizer/nop_removal.c @@ -66,37 +66,7 @@ void zend_optimizer_nop_removal(zend_op_array *op_array) zend_op *new_opline = op_array->opcodes + new_count; *new_opline = *opline; - switch (new_opline->opcode) { - case ZEND_JMP: - case ZEND_FAST_CALL: - ZEND_SET_OP_JMP_ADDR(new_opline, new_opline->op1, ZEND_OP1_JMP_ADDR(opline)); - break; - case ZEND_JMPZNZ: - new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)); - /* break missing intentionally */ - case ZEND_JMPZ: - case ZEND_JMPNZ: - case ZEND_JMPZ_EX: - case ZEND_JMPNZ_EX: - case ZEND_FE_RESET_R: - case ZEND_FE_RESET_RW: - case ZEND_JMP_SET: - case ZEND_COALESCE: - case ZEND_ASSERT_CHECK: - ZEND_SET_OP_JMP_ADDR(new_opline, new_opline->op2, ZEND_OP2_JMP_ADDR(opline)); - break; - case ZEND_CATCH: - if (!opline->result.num) { - new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)); - } - break; - case ZEND_DECLARE_ANON_CLASS: - case ZEND_DECLARE_ANON_INHERITED_CLASS: - case ZEND_FE_FETCH_R: - case ZEND_FE_FETCH_RW: - new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)); - break; - } + zend_optimizer_migrate_jump(op_array, new_opline, opline); } new_count++; } @@ -108,33 +78,7 @@ void zend_optimizer_nop_removal(zend_op_array *op_array) /* update JMPs */ for (opline = op_array->opcodes; oplineopcode) { - case ZEND_JMP: - case ZEND_FAST_CALL: - ZEND_SET_OP_JMP_ADDR(opline, opline->op1, ZEND_OP1_JMP_ADDR(opline) - shiftlist[ZEND_OP1_JMP_ADDR(opline) - op_array->opcodes]); - break; - case ZEND_JMPZNZ: - opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) - shiftlist[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]); - /* break missing intentionally */ - case ZEND_JMPZ: - case ZEND_JMPNZ: - case ZEND_JMPZ_EX: - case ZEND_JMPNZ_EX: - case ZEND_FE_RESET_R: - case ZEND_FE_RESET_RW: - case ZEND_JMP_SET: - case ZEND_COALESCE: - case ZEND_ASSERT_CHECK: - ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP2_JMP_ADDR(opline) - shiftlist[ZEND_OP2_JMP_ADDR(opline) - op_array->opcodes]); - break; - case ZEND_DECLARE_ANON_CLASS: - case ZEND_DECLARE_ANON_INHERITED_CLASS: - case ZEND_FE_FETCH_R: - case ZEND_FE_FETCH_RW: - case ZEND_CATCH: - opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) - shiftlist[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]); - break; - } + zend_optimizer_shift_jump(op_array, opline, shiftlist); } /* update brk/cont array */ diff --git a/ext/opcache/Optimizer/zend_cfg.c b/ext/opcache/Optimizer/zend_cfg.c index c4394757b86f7..dd5e3cd551d99 100644 --- a/ext/opcache/Optimizer/zend_cfg.c +++ b/ext/opcache/Optimizer/zend_cfg.c @@ -73,7 +73,12 @@ static void zend_mark_reachable(zend_op *opcodes, zend_cfg *cfg, zend_basic_bloc succ->flags |= ZEND_BB_FOLLOW; } } else { - ZEND_ASSERT(0); + ZEND_ASSERT(opcode == ZEND_SWITCH); + if (i == b->successors_count) { + succ->flags |= ZEND_BB_FOLLOW; + } else { + succ->flags |= ZEND_BB_TARGET; + } } } else { succ->flags |= ZEND_BB_FOLLOW; @@ -399,6 +404,17 @@ int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t b BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes); BB_START(i + 1); break; + case ZEND_SWITCH: + { + HashTable *jumptable = Z_ARRVAL_P(CRT_CONSTANT(opline->op2)); + zval *zv; + ZEND_HASH_FOREACH_VAL(jumptable, zv) { + BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv))); + } ZEND_HASH_FOREACH_END(); + BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)); + BB_START(i + 1); + break; + } case ZEND_UNSET_VAR: case ZEND_ISSET_ISEMPTY_VAR: if (((opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_LOCAL) && @@ -553,6 +569,23 @@ int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t b block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes]; block->successors[1] = j + 1; break; + case ZEND_SWITCH: + { + HashTable *jumptable = Z_ARRVAL_P(CRT_CONSTANT(opline->op2)); + zval *zv; + uint32_t s = 0; + + block->successors_count = 2 + zend_hash_num_elements(jumptable); + block->successors = zend_arena_calloc(arena, block->successors_count, sizeof(int)); + + ZEND_HASH_FOREACH_VAL(jumptable, zv) { + block->successors[s++] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv))]; + } ZEND_HASH_FOREACH_END(); + + block->successors[s++] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]; + block->successors[s++] = j + 1; + break; + } default: block->successors_count = 1; block->successors[0] = j + 1; diff --git a/ext/opcache/Optimizer/zend_dump.c b/ext/opcache/Optimizer/zend_dump.c index 2167fa6e6b316..92b861f176db2 100644 --- a/ext/opcache/Optimizer/zend_dump.c +++ b/ext/opcache/Optimizer/zend_dump.c @@ -581,7 +581,28 @@ static void zend_dump_op(const zend_op_array *op_array, const zend_basic_block * } if (opline->op2_type == IS_CONST) { - zend_dump_const(CRT_CONSTANT_EX(op_array, opline->op2, (dump_flags & ZEND_DUMP_RT_CONSTANTS))); + zval *op = CRT_CONSTANT_EX(op_array, opline->op2, (dump_flags & ZEND_DUMP_RT_CONSTANTS)); + if (opline->opcode == ZEND_SWITCH) { + HashTable *jumptable = Z_ARRVAL_P(op); + zend_string *key; + zend_ulong num_key; + zval *zv; + ZEND_HASH_FOREACH_KEY_VAL(jumptable, num_key, key, zv) { + if (key) { + fprintf(stderr, " \"%s\":", ZSTR_VAL(key)); + } else { + fprintf(stderr, " " ZEND_LONG_FMT ":", num_key); + } + if (b) { + fprintf(stderr, " BB%d,", b->successors[n++]); + } else { + fprintf(stderr, " L%u,", (uint32_t)ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv))); + } + } ZEND_HASH_FOREACH_END(); + fprintf(stderr, " default:"); + } else { + zend_dump_const(op); + } } else if (opline->op2_type & (IS_CV|IS_VAR|IS_TMP_VAR)) { if (ssa && ssa->ops) { int ssa_var_num = ssa->ops[opline - op_array->opcodes].op2_use; @@ -654,7 +675,6 @@ static void zend_dump_op(const zend_op_array *op_array, const zend_basic_block * static void zend_dump_block_info(const zend_cfg *cfg, int n, uint32_t dump_flags) { zend_basic_block *b = cfg->blocks + n; - int printed = 0; fprintf(stderr, "BB%d:", n); if (b->flags & ZEND_BB_START) { @@ -717,14 +737,12 @@ static void zend_dump_block_info(const zend_cfg *cfg, int n, uint32_t dump_flags fprintf(stderr, ")\n"); } - if (b->successors[0] != -1) { + if (b->successors_count > 0) { + int s; fprintf(stderr, " ; to=(BB%d", b->successors[0]); - printed = 1; - if (b->successors[1] != -1) { - fprintf(stderr, ", BB%d", b->successors[1]); + for (s = 1; s < b->successors_count; s++) { + fprintf(stderr, ", BB%d", b->successors[s]); } - } - if (printed) { fprintf(stderr, ")\n"); } diff --git a/ext/opcache/Optimizer/zend_optimizer.c b/ext/opcache/Optimizer/zend_optimizer.c index 1015ea498ca42..a02471579d89f 100644 --- a/ext/opcache/Optimizer/zend_optimizer.c +++ b/ext/opcache/Optimizer/zend_optimizer.c @@ -470,6 +470,7 @@ int zend_optimizer_replace_by_const(zend_op_array *op_array, zend_optimizer_remove_live_range(op_array, var); return 1; } + case ZEND_SWITCH: case ZEND_CASE: case ZEND_FREE: { zend_op *m, *n; @@ -501,7 +502,7 @@ int zend_optimizer_replace_by_const(zend_op_array *op_array, while (m < n) { if (ZEND_OP1_TYPE(m) == type && ZEND_OP1(m).var == var) { - if (m->opcode == ZEND_CASE) { + if (m->opcode == ZEND_CASE || m->opcode == ZEND_SWITCH) { zval old_val; ZVAL_COPY_VALUE(&old_val, val); zval_copy_ctor(val); @@ -562,6 +563,92 @@ int zend_optimizer_replace_by_const(zend_op_array *op_array, return 1; } +/* Update jump offsets after a jump was migrated to another opline */ +void zend_optimizer_migrate_jump(zend_op_array *op_array, zend_op *new_opline, zend_op *opline) { + switch (new_opline->opcode) { + case ZEND_JMP: + case ZEND_FAST_CALL: + ZEND_SET_OP_JMP_ADDR(new_opline, new_opline->op1, ZEND_OP1_JMP_ADDR(opline)); + break; + case ZEND_JMPZNZ: + new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)); + /* break missing intentionally */ + case ZEND_JMPZ: + case ZEND_JMPNZ: + case ZEND_JMPZ_EX: + case ZEND_JMPNZ_EX: + case ZEND_FE_RESET_R: + case ZEND_FE_RESET_RW: + case ZEND_JMP_SET: + case ZEND_COALESCE: + case ZEND_ASSERT_CHECK: + ZEND_SET_OP_JMP_ADDR(new_opline, new_opline->op2, ZEND_OP2_JMP_ADDR(opline)); + break; + case ZEND_CATCH: + if (!opline->result.num) { + new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)); + } + break; + case ZEND_DECLARE_ANON_CLASS: + case ZEND_DECLARE_ANON_INHERITED_CLASS: + case ZEND_FE_FETCH_R: + case ZEND_FE_FETCH_RW: + new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)); + break; + case ZEND_SWITCH: + { + HashTable *jumptable = Z_ARRVAL(ZEND_OP2_LITERAL(opline)); + zval *zv; + ZEND_HASH_FOREACH_VAL(jumptable, zv) { + Z_LVAL_P(zv) = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv))); + } ZEND_HASH_FOREACH_END(); + new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)); + break; + } + } +} + +/* Shift jump offsets based on shiftlist */ +void zend_optimizer_shift_jump(zend_op_array *op_array, zend_op *opline, uint32_t *shiftlist) { + switch (opline->opcode) { + case ZEND_JMP: + case ZEND_FAST_CALL: + ZEND_SET_OP_JMP_ADDR(opline, opline->op1, ZEND_OP1_JMP_ADDR(opline) - shiftlist[ZEND_OP1_JMP_ADDR(opline) - op_array->opcodes]); + break; + case ZEND_JMPZNZ: + opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) - shiftlist[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]); + /* break missing intentionally */ + case ZEND_JMPZ: + case ZEND_JMPNZ: + case ZEND_JMPZ_EX: + case ZEND_JMPNZ_EX: + case ZEND_FE_RESET_R: + case ZEND_FE_RESET_RW: + case ZEND_JMP_SET: + case ZEND_COALESCE: + case ZEND_ASSERT_CHECK: + ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP2_JMP_ADDR(opline) - shiftlist[ZEND_OP2_JMP_ADDR(opline) - op_array->opcodes]); + break; + case ZEND_DECLARE_ANON_CLASS: + case ZEND_DECLARE_ANON_INHERITED_CLASS: + case ZEND_FE_FETCH_R: + case ZEND_FE_FETCH_RW: + case ZEND_CATCH: + opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) - shiftlist[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]); + break; + case ZEND_SWITCH: + { + HashTable *jumptable = Z_ARRVAL(ZEND_OP2_LITERAL(opline)); + zval *zv; + ZEND_HASH_FOREACH_VAL(jumptable, zv) { + Z_LVAL_P(zv) = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv)) - shiftlist[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv))]); + } ZEND_HASH_FOREACH_END(); + opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) - shiftlist[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]); + break; + } + } +} + static zend_class_entry *get_class_entry_from_op1( zend_script *script, zend_op_array *op_array, zend_op *opline, zend_bool rt_constants) { if (opline->op1_type == IS_CONST) { diff --git a/ext/opcache/Optimizer/zend_optimizer_internal.h b/ext/opcache/Optimizer/zend_optimizer_internal.h index 90297ad816aba..710181317eaa6 100644 --- a/ext/opcache/Optimizer/zend_optimizer_internal.h +++ b/ext/opcache/Optimizer/zend_optimizer_internal.h @@ -109,5 +109,7 @@ int zend_optimizer_is_disabled_func(const char *name, size_t len); zend_function *zend_optimizer_get_called_func( zend_script *script, zend_op_array *op_array, zend_op *opline, zend_bool rt_constants); uint32_t zend_optimizer_classify_function(zend_string *name, uint32_t num_args); +void zend_optimizer_migrate_jump(zend_op_array *op_array, zend_op *new_opline, zend_op *opline); +void zend_optimizer_shift_jump(zend_op_array *op_array, zend_op *opline, uint32_t *shiftlist); #endif diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c index 8747ac3787c53..5d64ea19d5a66 100644 --- a/ext/opcache/zend_file_cache.c +++ b/ext/opcache/zend_file_cache.c @@ -431,6 +431,7 @@ static void zend_file_cache_serialize_op_array(zend_op_array *op_arra case ZEND_DECLARE_ANON_INHERITED_CLASS: case ZEND_FE_FETCH_R: case ZEND_FE_FETCH_RW: + case ZEND_SWITCH: /* relative extended_value don't have to be changed */ break; } @@ -1030,6 +1031,7 @@ static void zend_file_cache_unserialize_op_array(zend_op_array *op_arr case ZEND_DECLARE_ANON_INHERITED_CLASS: case ZEND_FE_FETCH_R: case ZEND_FE_FETCH_RW: + case ZEND_SWITCH: /* relative extended_value don't have to be changed */ break; } diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index 59f3765fea5e9..77d107fdd8985 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -446,6 +446,7 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc case ZEND_DECLARE_ANON_INHERITED_CLASS: case ZEND_FE_FETCH_R: case ZEND_FE_FETCH_RW: + case ZEND_SWITCH: /* relative extended_value don't have to be changed */ break; } From d75ef8929b14ed167979ec478853a67d1dbf2f18 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Mon, 10 Apr 2017 11:33:00 +0200 Subject: [PATCH 4/4] Split ZEND_SWITCH into LONG and STRING opcodes --- Zend/zend_compile.c | 21 +- Zend/zend_opcode.c | 3 +- Zend/zend_vm_def.h | 49 +++-- Zend/zend_vm_execute.h | 255 +++++++++++++++++-------- Zend/zend_vm_opcodes.c | 8 +- Zend/zend_vm_opcodes.h | 5 +- ext/opcache/Optimizer/block_pass.c | 9 +- ext/opcache/Optimizer/zend_cfg.c | 8 +- ext/opcache/Optimizer/zend_dump.c | 2 +- ext/opcache/Optimizer/zend_optimizer.c | 13 +- ext/opcache/zend_file_cache.c | 6 +- ext/opcache/zend_persist.c | 3 +- 12 files changed, 269 insertions(+), 113 deletions(-) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 82d5a3c303191..e760676e6f6fe 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -2081,7 +2081,8 @@ static void zend_check_live_ranges(zend_op *opline) /* {{{ */ } else if (opline->opcode == ZEND_FAST_RET) { /* fast_calls don't have to be destroyed */ } else if (opline->opcode == ZEND_CASE || - opline->opcode == ZEND_SWITCH || + opline->opcode == ZEND_SWITCH_LONG || + opline->opcode == ZEND_SWITCH_STRING || opline->opcode == ZEND_FE_FETCH_R || opline->opcode == ZEND_FE_FETCH_RW || opline->opcode == ZEND_FE_FREE || @@ -4645,6 +4646,17 @@ static zend_uchar determine_switch_jumptable_type(zend_ast_list *cases) { return common_type; } +static zend_bool should_use_jumptable(zend_ast_list *cases, zend_uchar jumptable_type) { + /* Thresholds are chosen based on when the average switch time for equidistributed + * input becomes smaller when using the jumptable optimization. */ + if (jumptable_type == IS_LONG) { + return cases->children >= 5; + } else { + ZEND_ASSERT(jumptable_type == IS_STRING); + return cases->children >= 2; + } +} + void zend_compile_switch(zend_ast *ast) /* {{{ */ { zend_ast *expr_ast = ast->child[0]; @@ -4667,7 +4679,7 @@ void zend_compile_switch(zend_ast *ast) /* {{{ */ case_node.u.op.var = get_temporary_variable(CG(active_op_array)); jumptable_type = determine_switch_jumptable_type(cases); - if (jumptable_type != IS_UNDEF && cases->children >= 4) { + if (jumptable_type != IS_UNDEF && should_use_jumptable(cases, jumptable_type)) { znode jumptable_op; ALLOC_HASHTABLE(jumptable); @@ -4675,11 +4687,12 @@ void zend_compile_switch(zend_ast *ast) /* {{{ */ jumptable_op.op_type = IS_CONST; ZVAL_ARR(&jumptable_op.u.constant, jumptable); - opline = zend_emit_op(NULL, ZEND_SWITCH, &expr_node, &jumptable_op); + opline = zend_emit_op(NULL, + jumptable_type == IS_LONG ? ZEND_SWITCH_LONG : ZEND_SWITCH_STRING, + &expr_node, &jumptable_op); if (opline->op1_type == IS_CONST) { zval_copy_ctor(CT_CONSTANT(opline->op1)); } - opline->result.num = jumptable_type; opnum_switch = opline - CG(active_op_array)->opcodes; } diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index 85e902970841d..e8764e964a4cd 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -670,7 +670,8 @@ ZEND_API int pass_two(zend_op_array *op_array) opline->opcode = ZEND_GENERATOR_RETURN; } break; - case ZEND_SWITCH: + case ZEND_SWITCH_LONG: + case ZEND_SWITCH_STRING: { /* absolute indexes to relative offsets */ HashTable *jumptable = Z_ARRVAL_P(CT_CONSTANT(opline->op2)); diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 7873759528e61..d486d1514ee5d 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -8043,29 +8043,54 @@ ZEND_VM_HANDLER(51, ZEND_MAKE_REF, VAR|CV, UNUSED) ZEND_VM_NEXT_OPCODE(); } -ZEND_VM_HANDLER(187, ZEND_SWITCH, CONST|TMPVAR|CV, CONST, JMP_ADDR) +ZEND_VM_HANDLER(187, ZEND_SWITCH_LONG, CONST|TMPVAR|CV, CONST, JMP_ADDR) { USE_OPLINE zend_free_op free_op1, free_op2; - zval *op, *table_op, *jump_zv; - HashTable *ht; + zval *op, *jump_zv; + HashTable *jumptable; op = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R); - table_op = GET_OP2_ZVAL_PTR(BP_VAR_R); - ht = Z_ARRVAL_P(table_op); + jumptable = Z_ARRVAL_P(GET_OP2_ZVAL_PTR(BP_VAR_R)); - if (Z_TYPE_P(op) != opline->result.num) { - /* Wrong type, fall back to ZEND_CASE chain */ - ZEND_VM_NEXT_OPCODE(); + if (Z_TYPE_P(op) != IS_LONG) { + ZVAL_DEREF(op); + if (Z_TYPE_P(op) != IS_LONG) { + /* Wrong type, fall back to ZEND_CASE chain */ + ZEND_VM_NEXT_OPCODE(); + } } - if (opline->result.num == IS_LONG) { - jump_zv = zend_hash_index_find(ht, Z_LVAL_P(op)); + jump_zv = zend_hash_index_find(jumptable, Z_LVAL_P(op)); + if (jump_zv != NULL) { + ZEND_VM_SET_RELATIVE_OPCODE(opline, Z_LVAL_P(jump_zv)); + ZEND_VM_CONTINUE(); } else { - ZEND_ASSERT(opline->result.num == IS_STRING); - jump_zv = zend_hash_find(ht, Z_STR_P(op)); + /* default */ + ZEND_VM_SET_RELATIVE_OPCODE(opline, opline->extended_value); + ZEND_VM_CONTINUE(); + } +} + +ZEND_VM_HANDLER(188, ZEND_SWITCH_STRING, CONST|TMPVAR|CV, CONST, JMP_ADDR) +{ + USE_OPLINE + zend_free_op free_op1, free_op2; + zval *op, *jump_zv; + HashTable *jumptable; + + op = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R); + jumptable = Z_ARRVAL_P(GET_OP2_ZVAL_PTR(BP_VAR_R)); + + if (Z_TYPE_P(op) != IS_STRING) { + ZVAL_DEREF(op); + if (Z_TYPE_P(op) != IS_STRING) { + /* Wrong type, fall back to ZEND_CASE chain */ + ZEND_VM_NEXT_OPCODE(); + } } + jump_zv = zend_hash_find(jumptable, Z_STR_P(op)); if (jump_zv != NULL) { ZEND_VM_SET_RELATIVE_OPCODE(opline, Z_LVAL_P(jump_zv)); ZEND_VM_CONTINUE(); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index bdaac6e3343a2..7136aa8790851 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -6409,29 +6409,54 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_CONST_HANDLER ZEND_VM_RETURN(); } -static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SWITCH_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SWITCH_LONG_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE - zval *op, *table_op, *jump_zv; - HashTable *ht; + zval *op, *jump_zv; + HashTable *jumptable; op = EX_CONSTANT(opline->op1); - table_op = EX_CONSTANT(opline->op2); - ht = Z_ARRVAL_P(table_op); + jumptable = Z_ARRVAL_P(EX_CONSTANT(opline->op2)); - if (Z_TYPE_P(op) != opline->result.num) { - /* Wrong type, fall back to ZEND_CASE chain */ - ZEND_VM_NEXT_OPCODE(); + if (Z_TYPE_P(op) != IS_LONG) { + ZVAL_DEREF(op); + if (Z_TYPE_P(op) != IS_LONG) { + /* Wrong type, fall back to ZEND_CASE chain */ + ZEND_VM_NEXT_OPCODE(); + } } - if (opline->result.num == IS_LONG) { - jump_zv = zend_hash_index_find(ht, Z_LVAL_P(op)); + jump_zv = zend_hash_index_find(jumptable, Z_LVAL_P(op)); + if (jump_zv != NULL) { + ZEND_VM_SET_RELATIVE_OPCODE(opline, Z_LVAL_P(jump_zv)); + ZEND_VM_CONTINUE(); } else { - ZEND_ASSERT(opline->result.num == IS_STRING); - jump_zv = zend_hash_find(ht, Z_STR_P(op)); + /* default */ + ZEND_VM_SET_RELATIVE_OPCODE(opline, opline->extended_value); + ZEND_VM_CONTINUE(); } +} + +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SWITCH_STRING_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + zval *op, *jump_zv; + HashTable *jumptable; + + op = EX_CONSTANT(opline->op1); + jumptable = Z_ARRVAL_P(EX_CONSTANT(opline->op2)); + + if (Z_TYPE_P(op) != IS_STRING) { + ZVAL_DEREF(op); + if (Z_TYPE_P(op) != IS_STRING) { + /* Wrong type, fall back to ZEND_CASE chain */ + ZEND_VM_NEXT_OPCODE(); + } + } + + jump_zv = zend_hash_find(jumptable, Z_STR_P(op)); if (jump_zv != NULL) { ZEND_VM_SET_RELATIVE_OPCODE(opline, Z_LVAL_P(jump_zv)); ZEND_VM_CONTINUE(); @@ -38338,29 +38363,54 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BIND_STATIC_SPEC_CV_CONST_HAND ZEND_VM_NEXT_OPCODE(); } -static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SWITCH_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SWITCH_LONG_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE - zval *op, *table_op, *jump_zv; - HashTable *ht; + zval *op, *jump_zv; + HashTable *jumptable; op = _get_zval_ptr_cv_undef(execute_data, opline->op1.var); - table_op = EX_CONSTANT(opline->op2); - ht = Z_ARRVAL_P(table_op); + jumptable = Z_ARRVAL_P(EX_CONSTANT(opline->op2)); - if (Z_TYPE_P(op) != opline->result.num) { - /* Wrong type, fall back to ZEND_CASE chain */ - ZEND_VM_NEXT_OPCODE(); + if (Z_TYPE_P(op) != IS_LONG) { + ZVAL_DEREF(op); + if (Z_TYPE_P(op) != IS_LONG) { + /* Wrong type, fall back to ZEND_CASE chain */ + ZEND_VM_NEXT_OPCODE(); + } } - if (opline->result.num == IS_LONG) { - jump_zv = zend_hash_index_find(ht, Z_LVAL_P(op)); + jump_zv = zend_hash_index_find(jumptable, Z_LVAL_P(op)); + if (jump_zv != NULL) { + ZEND_VM_SET_RELATIVE_OPCODE(opline, Z_LVAL_P(jump_zv)); + ZEND_VM_CONTINUE(); } else { - ZEND_ASSERT(opline->result.num == IS_STRING); - jump_zv = zend_hash_find(ht, Z_STR_P(op)); + /* default */ + ZEND_VM_SET_RELATIVE_OPCODE(opline, opline->extended_value); + ZEND_VM_CONTINUE(); + } +} + +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SWITCH_STRING_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + + zval *op, *jump_zv; + HashTable *jumptable; + + op = _get_zval_ptr_cv_undef(execute_data, opline->op1.var); + jumptable = Z_ARRVAL_P(EX_CONSTANT(opline->op2)); + + if (Z_TYPE_P(op) != IS_STRING) { + ZVAL_DEREF(op); + if (Z_TYPE_P(op) != IS_STRING) { + /* Wrong type, fall back to ZEND_CASE chain */ + ZEND_VM_NEXT_OPCODE(); + } } + jump_zv = zend_hash_find(jumptable, Z_STR_P(op)); if (jump_zv != NULL) { ZEND_VM_SET_RELATIVE_OPCODE(opline, Z_LVAL_P(jump_zv)); ZEND_VM_CONTINUE(); @@ -49833,29 +49883,54 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INSTANCEOF_SPEC_TMPVAR_CONST_H ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } -static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SWITCH_SPEC_TMPVAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SWITCH_LONG_SPEC_TMPVAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE zend_free_op free_op1; - zval *op, *table_op, *jump_zv; - HashTable *ht; + zval *op, *jump_zv; + HashTable *jumptable; op = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); - table_op = EX_CONSTANT(opline->op2); - ht = Z_ARRVAL_P(table_op); + jumptable = Z_ARRVAL_P(EX_CONSTANT(opline->op2)); - if (Z_TYPE_P(op) != opline->result.num) { - /* Wrong type, fall back to ZEND_CASE chain */ - ZEND_VM_NEXT_OPCODE(); + if (Z_TYPE_P(op) != IS_LONG) { + ZVAL_DEREF(op); + if (Z_TYPE_P(op) != IS_LONG) { + /* Wrong type, fall back to ZEND_CASE chain */ + ZEND_VM_NEXT_OPCODE(); + } } - if (opline->result.num == IS_LONG) { - jump_zv = zend_hash_index_find(ht, Z_LVAL_P(op)); + jump_zv = zend_hash_index_find(jumptable, Z_LVAL_P(op)); + if (jump_zv != NULL) { + ZEND_VM_SET_RELATIVE_OPCODE(opline, Z_LVAL_P(jump_zv)); + ZEND_VM_CONTINUE(); } else { - ZEND_ASSERT(opline->result.num == IS_STRING); - jump_zv = zend_hash_find(ht, Z_STR_P(op)); + /* default */ + ZEND_VM_SET_RELATIVE_OPCODE(opline, opline->extended_value); + ZEND_VM_CONTINUE(); + } +} + +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SWITCH_STRING_SPEC_TMPVAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + zend_free_op free_op1; + zval *op, *jump_zv; + HashTable *jumptable; + + op = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); + jumptable = Z_ARRVAL_P(EX_CONSTANT(opline->op2)); + + if (Z_TYPE_P(op) != IS_STRING) { + ZVAL_DEREF(op); + if (Z_TYPE_P(op) != IS_STRING) { + /* Wrong type, fall back to ZEND_CASE chain */ + ZEND_VM_NEXT_OPCODE(); + } } + jump_zv = zend_hash_find(jumptable, Z_STR_P(op)); if (jump_zv != NULL) { ZEND_VM_SET_RELATIVE_OPCODE(opline, Z_LVAL_P(jump_zv)); ZEND_VM_CONTINUE(); @@ -57851,27 +57926,52 @@ void zend_init_opcodes_handlers(void) ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, - ZEND_SWITCH_SPEC_CONST_CONST_HANDLER, + ZEND_SWITCH_LONG_SPEC_CONST_CONST_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_SWITCH_LONG_SPEC_TMPVAR_CONST_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_SWITCH_LONG_SPEC_TMPVAR_CONST_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_SWITCH_LONG_SPEC_CV_CONST_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, - ZEND_SWITCH_SPEC_TMPVAR_CONST_HANDLER, + ZEND_SWITCH_STRING_SPEC_CONST_CONST_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, - ZEND_SWITCH_SPEC_TMPVAR_CONST_HANDLER, + ZEND_SWITCH_STRING_SPEC_TMPVAR_CONST_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, + ZEND_SWITCH_STRING_SPEC_TMPVAR_CONST_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, - ZEND_SWITCH_SPEC_CV_CONST_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_SWITCH_STRING_SPEC_CV_CONST_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, @@ -58944,7 +59044,7 @@ void zend_init_opcodes_handlers(void) 2257 | SPEC_RULE_OP1 | SPEC_RULE_OP2, 2282 | SPEC_RULE_OP1 | SPEC_RULE_OP2, 2307 | SPEC_RULE_OP1 | SPEC_RULE_OP2, - 4621, + 4646, 2332, 2333, 2334, @@ -59029,10 +59129,11 @@ void zend_init_opcodes_handlers(void) 3531 | SPEC_RULE_OP1 | SPEC_RULE_OP2, 3556 | SPEC_RULE_OP1 | SPEC_RULE_OP2, 3581 | SPEC_RULE_OP1 | SPEC_RULE_OP2, - 4621, + 4646, 3606 | SPEC_RULE_OP1 | SPEC_RULE_OP2, 3631 | SPEC_RULE_OP1 | SPEC_RULE_OP2, - 4621 + 3656 | SPEC_RULE_OP1 | SPEC_RULE_OP2, + 4646 }; zend_opcode_handlers = labels; zend_handlers_count = sizeof(labels) / sizeof(void*); @@ -59139,7 +59240,7 @@ ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3656 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 3681 | SPEC_RULE_OP1 | SPEC_RULE_OP2; if (op->op1_type > op->op2_type) { zend_swap_operands(op); } @@ -59147,7 +59248,7 @@ ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3681 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 3706 | SPEC_RULE_OP1 | SPEC_RULE_OP2; if (op->op1_type > op->op2_type) { zend_swap_operands(op); } @@ -59155,7 +59256,7 @@ ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3706 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 3731 | SPEC_RULE_OP1 | SPEC_RULE_OP2; if (op->op1_type > op->op2_type) { zend_swap_operands(op); } @@ -59166,17 +59267,17 @@ ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3731 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 3756 | SPEC_RULE_OP1 | SPEC_RULE_OP2; } else if (op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3756 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 3781 | SPEC_RULE_OP1 | SPEC_RULE_OP2; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3781 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 3806 | SPEC_RULE_OP1 | SPEC_RULE_OP2; } break; case ZEND_MUL: @@ -59184,7 +59285,7 @@ ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3806 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 3831 | SPEC_RULE_OP1 | SPEC_RULE_OP2; if (op->op1_type > op->op2_type) { zend_swap_operands(op); } @@ -59192,7 +59293,7 @@ ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3831 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 3856 | SPEC_RULE_OP1 | SPEC_RULE_OP2; if (op->op1_type > op->op2_type) { zend_swap_operands(op); } @@ -59200,7 +59301,7 @@ ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3856 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 3881 | SPEC_RULE_OP1 | SPEC_RULE_OP2; if (op->op1_type > op->op2_type) { zend_swap_operands(op); } @@ -59211,7 +59312,7 @@ ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3881 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; + spec = 3906 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; if (op->op1_type > op->op2_type) { zend_swap_operands(op); } @@ -59219,7 +59320,7 @@ ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3956 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; + spec = 3981 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; if (op->op1_type > op->op2_type) { zend_swap_operands(op); } @@ -59230,7 +59331,7 @@ ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 4031 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; + spec = 4056 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; if (op->op1_type > op->op2_type) { zend_swap_operands(op); } @@ -59238,7 +59339,7 @@ ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 4106 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; + spec = 4131 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; if (op->op1_type > op->op2_type) { zend_swap_operands(op); } @@ -59249,12 +59350,12 @@ ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 4181 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; + spec = 4206 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 4256 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; + spec = 4281 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; } break; case ZEND_IS_SMALLER_OR_EQUAL: @@ -59262,70 +59363,70 @@ ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 4331 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; + spec = 4356 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 4406 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; + spec = 4431 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; } break; case ZEND_QM_ASSIGN: if (op1_info == MAY_BE_DOUBLE) { - spec = 4571 | SPEC_RULE_OP1; + spec = 4596 | SPEC_RULE_OP1; } else if (!(op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE)))) { - spec = 4576 | SPEC_RULE_OP1; + spec = 4601 | SPEC_RULE_OP1; } break; case ZEND_PRE_INC: if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) { - spec = 4481 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL; + spec = 4506 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL; } else if (op1_info == MAY_BE_LONG) { - spec = 4491 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL; + spec = 4516 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL; } else if (op1_info == (MAY_BE_LONG|MAY_BE_DOUBLE)) { - spec = 4501 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL; + spec = 4526 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL; } break; case ZEND_PRE_DEC: if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) { - spec = 4511 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL; + spec = 4536 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL; } else if (op1_info == MAY_BE_LONG) { - spec = 4521 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL; + spec = 4546 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL; } else if (op1_info == (MAY_BE_LONG|MAY_BE_DOUBLE)) { - spec = 4531 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL; + spec = 4556 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL; } break; case ZEND_POST_INC: if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) { - spec = 4541 | SPEC_RULE_OP1; + spec = 4566 | SPEC_RULE_OP1; } else if (op1_info == MAY_BE_LONG) { - spec = 4546 | SPEC_RULE_OP1; + spec = 4571 | SPEC_RULE_OP1; } else if (op1_info == (MAY_BE_LONG|MAY_BE_DOUBLE)) { - spec = 4551 | SPEC_RULE_OP1; + spec = 4576 | SPEC_RULE_OP1; } break; case ZEND_POST_DEC: if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) { - spec = 4556 | SPEC_RULE_OP1; + spec = 4581 | SPEC_RULE_OP1; } else if (op1_info == MAY_BE_LONG) { - spec = 4561 | SPEC_RULE_OP1; + spec = 4586 | SPEC_RULE_OP1; } else if (op1_info == (MAY_BE_LONG|MAY_BE_DOUBLE)) { - spec = 4566 | SPEC_RULE_OP1; + spec = 4591 | SPEC_RULE_OP1; } break; case ZEND_SEND_VAR_EX: if ((op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0) { - spec = 4611 | SPEC_RULE_OP1 | SPEC_RULE_QUICK_ARG; + spec = 4636 | SPEC_RULE_OP1 | SPEC_RULE_QUICK_ARG; } break; case ZEND_FETCH_DIM_R: if (!(op2_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) { - spec = 4581 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 4606 | SPEC_RULE_OP1 | SPEC_RULE_OP2; } break; case ZEND_SEND_VAR: if ((op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0) { - spec = 4606 | SPEC_RULE_OP1; + spec = 4631 | SPEC_RULE_OP1; } break; default: diff --git a/Zend/zend_vm_opcodes.c b/Zend/zend_vm_opcodes.c index 5d2c4ecd7d5c9..315f6e4a8ea66 100644 --- a/Zend/zend_vm_opcodes.c +++ b/Zend/zend_vm_opcodes.c @@ -21,7 +21,7 @@ #include #include -static const char *zend_vm_opcodes_names[188] = { +static const char *zend_vm_opcodes_names[189] = { "ZEND_NOP", "ZEND_ADD", "ZEND_SUB", @@ -209,10 +209,11 @@ static const char *zend_vm_opcodes_names[188] = { "ZEND_FETCH_THIS", NULL, "ZEND_ISSET_ISEMPTY_THIS", - "ZEND_SWITCH", + "ZEND_SWITCH_LONG", + "ZEND_SWITCH_STRING", }; -static uint32_t zend_vm_opcodes_flags[188] = { +static uint32_t zend_vm_opcodes_flags[189] = { 0x00000000, 0x00000707, 0x00000707, @@ -401,6 +402,7 @@ static uint32_t zend_vm_opcodes_flags[188] = { 0x00000000, 0x00000101, 0x03000307, + 0x03000307, }; ZEND_API const char* zend_get_opcode_name(zend_uchar opcode) { diff --git a/Zend/zend_vm_opcodes.h b/Zend/zend_vm_opcodes.h index 2044b2f746a6a..2a0eb93aeb7c0 100644 --- a/Zend/zend_vm_opcodes.h +++ b/Zend/zend_vm_opcodes.h @@ -252,8 +252,9 @@ END_EXTERN_C() #define ZEND_BIND_STATIC 183 #define ZEND_FETCH_THIS 184 #define ZEND_ISSET_ISEMPTY_THIS 186 -#define ZEND_SWITCH 187 +#define ZEND_SWITCH_LONG 187 +#define ZEND_SWITCH_STRING 188 -#define ZEND_VM_LAST_OPCODE 187 +#define ZEND_VM_LAST_OPCODE 188 #endif diff --git a/ext/opcache/Optimizer/block_pass.c b/ext/opcache/Optimizer/block_pass.c index 8dabec45034f6..13e681a656921 100644 --- a/ext/opcache/Optimizer/block_pass.c +++ b/ext/opcache/Optimizer/block_pass.c @@ -143,7 +143,8 @@ static void strip_nops(zend_op_array *op_array, zend_basic_block *b) static int get_const_switch_target(zend_cfg *cfg, zend_op_array *op_array, zend_basic_block *block, zend_op *opline, zval *val) { HashTable *jumptable = Z_ARRVAL(ZEND_OP2_LITERAL(opline)); zval *zv; - if (Z_TYPE_P(val) != opline->result.num) { + if ((opline->opcode == ZEND_SWITCH_LONG && Z_TYPE_P(val) != IS_LONG) + || (opline->opcode == ZEND_SWITCH_STRING && Z_TYPE_P(val) != IS_STRING)) { /* fallback to next block */ return block->successors[block->successors_count - 1]; } @@ -364,7 +365,8 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array } break; - case ZEND_SWITCH: + case ZEND_SWITCH_LONG: + case ZEND_SWITCH_STRING: if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) { /* SWITCH variable will be deleted later by FREE, so we can't optimize it */ Tsource[VAR_NUM(opline->op1.var)] = NULL; @@ -924,7 +926,8 @@ static void assemble_code_blocks(zend_cfg *cfg, zend_op_array *op_array) case ZEND_FE_FETCH_RW: opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, new_opcodes + blocks[b->successors[0]].start); break; - case ZEND_SWITCH: + case ZEND_SWITCH_LONG: + case ZEND_SWITCH_STRING: { HashTable *jumptable = Z_ARRVAL(ZEND_OP2_LITERAL(opline)); zval *zv; diff --git a/ext/opcache/Optimizer/zend_cfg.c b/ext/opcache/Optimizer/zend_cfg.c index dd5e3cd551d99..5524bff19156b 100644 --- a/ext/opcache/Optimizer/zend_cfg.c +++ b/ext/opcache/Optimizer/zend_cfg.c @@ -73,7 +73,7 @@ static void zend_mark_reachable(zend_op *opcodes, zend_cfg *cfg, zend_basic_bloc succ->flags |= ZEND_BB_FOLLOW; } } else { - ZEND_ASSERT(opcode == ZEND_SWITCH); + ZEND_ASSERT(opcode == ZEND_SWITCH_LONG || opcode == ZEND_SWITCH_STRING); if (i == b->successors_count) { succ->flags |= ZEND_BB_FOLLOW; } else { @@ -404,7 +404,8 @@ int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t b BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes); BB_START(i + 1); break; - case ZEND_SWITCH: + case ZEND_SWITCH_LONG: + case ZEND_SWITCH_STRING: { HashTable *jumptable = Z_ARRVAL_P(CRT_CONSTANT(opline->op2)); zval *zv; @@ -569,7 +570,8 @@ int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t b block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes]; block->successors[1] = j + 1; break; - case ZEND_SWITCH: + case ZEND_SWITCH_LONG: + case ZEND_SWITCH_STRING: { HashTable *jumptable = Z_ARRVAL_P(CRT_CONSTANT(opline->op2)); zval *zv; diff --git a/ext/opcache/Optimizer/zend_dump.c b/ext/opcache/Optimizer/zend_dump.c index 92b861f176db2..49d579721cc7b 100644 --- a/ext/opcache/Optimizer/zend_dump.c +++ b/ext/opcache/Optimizer/zend_dump.c @@ -582,7 +582,7 @@ static void zend_dump_op(const zend_op_array *op_array, const zend_basic_block * if (opline->op2_type == IS_CONST) { zval *op = CRT_CONSTANT_EX(op_array, opline->op2, (dump_flags & ZEND_DUMP_RT_CONSTANTS)); - if (opline->opcode == ZEND_SWITCH) { + if (opline->opcode == ZEND_SWITCH_LONG || opline->opcode == ZEND_SWITCH_STRING) { HashTable *jumptable = Z_ARRVAL_P(op); zend_string *key; zend_ulong num_key; diff --git a/ext/opcache/Optimizer/zend_optimizer.c b/ext/opcache/Optimizer/zend_optimizer.c index a02471579d89f..5fd6484a44545 100644 --- a/ext/opcache/Optimizer/zend_optimizer.c +++ b/ext/opcache/Optimizer/zend_optimizer.c @@ -470,7 +470,8 @@ int zend_optimizer_replace_by_const(zend_op_array *op_array, zend_optimizer_remove_live_range(op_array, var); return 1; } - case ZEND_SWITCH: + case ZEND_SWITCH_LONG: + case ZEND_SWITCH_STRING: case ZEND_CASE: case ZEND_FREE: { zend_op *m, *n; @@ -502,7 +503,9 @@ int zend_optimizer_replace_by_const(zend_op_array *op_array, while (m < n) { if (ZEND_OP1_TYPE(m) == type && ZEND_OP1(m).var == var) { - if (m->opcode == ZEND_CASE || m->opcode == ZEND_SWITCH) { + if (m->opcode == ZEND_CASE + || m->opcode == ZEND_SWITCH_LONG + || m->opcode == ZEND_SWITCH_STRING) { zval old_val; ZVAL_COPY_VALUE(&old_val, val); zval_copy_ctor(val); @@ -595,7 +598,8 @@ void zend_optimizer_migrate_jump(zend_op_array *op_array, zend_op *new_opline, z case ZEND_FE_FETCH_RW: new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)); break; - case ZEND_SWITCH: + case ZEND_SWITCH_LONG: + case ZEND_SWITCH_STRING: { HashTable *jumptable = Z_ARRVAL(ZEND_OP2_LITERAL(opline)); zval *zv; @@ -636,7 +640,8 @@ void zend_optimizer_shift_jump(zend_op_array *op_array, zend_op *opline, uint32_ case ZEND_CATCH: opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) - shiftlist[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]); break; - case ZEND_SWITCH: + case ZEND_SWITCH_LONG: + case ZEND_SWITCH_STRING: { HashTable *jumptable = Z_ARRVAL(ZEND_OP2_LITERAL(opline)); zval *zv; diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c index 5d64ea19d5a66..fb6827a9fd707 100644 --- a/ext/opcache/zend_file_cache.c +++ b/ext/opcache/zend_file_cache.c @@ -431,7 +431,8 @@ static void zend_file_cache_serialize_op_array(zend_op_array *op_arra case ZEND_DECLARE_ANON_INHERITED_CLASS: case ZEND_FE_FETCH_R: case ZEND_FE_FETCH_RW: - case ZEND_SWITCH: + case ZEND_SWITCH_LONG: + case ZEND_SWITCH_STRING: /* relative extended_value don't have to be changed */ break; } @@ -1031,7 +1032,8 @@ static void zend_file_cache_unserialize_op_array(zend_op_array *op_arr case ZEND_DECLARE_ANON_INHERITED_CLASS: case ZEND_FE_FETCH_R: case ZEND_FE_FETCH_RW: - case ZEND_SWITCH: + case ZEND_SWITCH_LONG: + case ZEND_SWITCH_STRING: /* relative extended_value don't have to be changed */ break; } diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index 77d107fdd8985..30bbfe08d427c 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -446,7 +446,8 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc case ZEND_DECLARE_ANON_INHERITED_CLASS: case ZEND_FE_FETCH_R: case ZEND_FE_FETCH_RW: - case ZEND_SWITCH: + case ZEND_SWITCH_LONG: + case ZEND_SWITCH_STRING: /* relative extended_value don't have to be changed */ break; }