static void zend_ssa_remove_nops(zend_op_array *op_array, zend_ssa *ssa) { zend_basic_block *blocks = ssa->cfg.blocks; zend_basic_block *end = blocks + ssa->cfg.blocks_count; zend_basic_block *b; zend_func_info *func_info; int j; uint32_t i; uint32_t target = 0; uint32_t *shiftlist; ALLOCA_FLAG(use_heap); shiftlist = (uint32_t *)do_alloca(sizeof(uint32_t) * op_array->last, use_heap); memset(shiftlist, 0, sizeof(uint32_t) * op_array->last); for (b = blocks; b < end; b++) { if (b->flags & (ZEND_BB_REACHABLE|ZEND_BB_UNREACHABLE_FREE)) { uint32_t end; if (b->flags & ZEND_BB_UNREACHABLE_FREE) { /* Only keep the FREE for the loop var */ ZEND_ASSERT(op_array->opcodes[b->start].opcode == ZEND_FREE || op_array->opcodes[b->start].opcode == ZEND_FE_FREE); b->len = 1; } end = b->start + b->len; i = b->start; b->start = target; while (i < end) { shiftlist[i] = i - target; if (EXPECTED(op_array->opcodes[i].opcode != ZEND_NOP) || /*keep NOP to support ZEND_VM_SMART_BRANCH */ (i > 0 && i + 1 < op_array->last && (op_array->opcodes[i+1].opcode == ZEND_JMPZ || op_array->opcodes[i+1].opcode == ZEND_JMPNZ) && zend_is_smart_branch(op_array->opcodes + i - 1))) { if (i != target) { op_array->opcodes[target] = op_array->opcodes[i]; ssa->ops[target] = ssa->ops[i]; } target++; } i++; } if (target != end && b->len != 0) { zend_op *opline; zend_op *new_opline; b->len = target - b->start; opline = op_array->opcodes + end - 1; if (opline->opcode == ZEND_NOP) { continue; } new_opline = op_array->opcodes + target - 1; zend_optimizer_migrate_jump(op_array, new_opline, opline); } } } if (target != op_array->last) { /* reset rest opcodes */ for (i = target; i < op_array->last; i++) { MAKE_NOP(op_array->opcodes + i); } /* update SSA variables */ for (j = 0; j < ssa->vars_count; j++) { if (ssa->vars[j].definition >= 0) { ssa->vars[j].definition -= shiftlist[ssa->vars[j].definition]; } if (ssa->vars[j].use_chain >= 0) { ssa->vars[j].use_chain -= shiftlist[ssa->vars[j].use_chain]; } } for (i = 0; i < op_array->last; i++) { if (ssa->ops[i].op1_use_chain >= 0) { ssa->ops[i].op1_use_chain -= shiftlist[ssa->ops[i].op1_use_chain]; } if (ssa->ops[i].op2_use_chain >= 0) { ssa->ops[i].op2_use_chain -= shiftlist[ssa->ops[i].op2_use_chain]; } if (ssa->ops[i].res_use_chain >= 0) { ssa->ops[i].res_use_chain -= shiftlist[ssa->ops[i].res_use_chain]; } } /* update branch targets */ 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; zend_optimizer_shift_jump(op_array, opline, shiftlist); } } /* update brk/cont array */ for (j = 0; j < op_array->last_live_range; j++) { op_array->live_range[j].start -= shiftlist[op_array->live_range[j].start]; op_array->live_range[j].end -= shiftlist[op_array->live_range[j].end]; } /* update try/catch array */ for (j = 0; j < op_array->last_try_catch; j++) { op_array->try_catch_array[j].try_op -= shiftlist[op_array->try_catch_array[j].try_op]; op_array->try_catch_array[j].catch_op -= shiftlist[op_array->try_catch_array[j].catch_op]; if (op_array->try_catch_array[j].finally_op) { op_array->try_catch_array[j].finally_op -= shiftlist[op_array->try_catch_array[j].finally_op]; op_array->try_catch_array[j].finally_end -= shiftlist[op_array->try_catch_array[j].finally_end]; } } /* update early binding list */ if (op_array->early_binding != (uint32_t)-1) { uint32_t *opline_num = &op_array->early_binding; do { *opline_num -= shiftlist[*opline_num]; opline_num = &ZEND_RESULT(&op_array->opcodes[*opline_num]).opline_num; } while (*opline_num != (uint32_t)-1); } /* update call graph */ func_info = ZEND_FUNC_INFO(op_array); if (func_info) { zend_call_info *call_info = func_info->callee_info; while (call_info) { call_info->caller_init_opline -= shiftlist[call_info->caller_init_opline - op_array->opcodes]; call_info->caller_call_opline -= shiftlist[call_info->caller_call_opline - op_array->opcodes]; call_info = call_info->next_callee; } } op_array->last = target; } free_alloca(shiftlist, use_heap); }
static void zend_ssa_remove_nops(zend_op_array *op_array, zend_ssa *ssa, zend_optimizer_ctx *ctx) { zend_basic_block *blocks = ssa->cfg.blocks; zend_basic_block *end = blocks + ssa->cfg.blocks_count; zend_basic_block *b; zend_func_info *func_info; int j; uint32_t i = 0; uint32_t target = 0; uint32_t *shiftlist; ALLOCA_FLAG(use_heap); shiftlist = (uint32_t *)do_alloca(sizeof(uint32_t) * op_array->last, use_heap); memset(shiftlist, 0, sizeof(uint32_t) * op_array->last); /* remove empty callee_info */ func_info = ZEND_FUNC_INFO(op_array); if (func_info) { zend_call_info **call_info = &func_info->callee_info; while ((*call_info)) { if ((*call_info)->caller_init_opline->opcode == ZEND_NOP) { *call_info = (*call_info)->next_callee; } else { call_info = &(*call_info)->next_callee; } } } for (b = blocks; b < end; b++) { if (b->flags & (ZEND_BB_REACHABLE|ZEND_BB_UNREACHABLE_FREE)) { uint32_t end; if (b->len) { while (i < b->start) { shiftlist[i] = i - target; i++; } if (b->flags & ZEND_BB_UNREACHABLE_FREE) { /* Only keep the FREE for the loop var */ ZEND_ASSERT(op_array->opcodes[b->start].opcode == ZEND_FREE || op_array->opcodes[b->start].opcode == ZEND_FE_FREE); b->len = 1; } end = b->start + b->len; b->start = target; while (i < end) { shiftlist[i] = i - target; if (EXPECTED(op_array->opcodes[i].opcode != ZEND_NOP) || /* Keep NOP to support ZEND_VM_SMART_BRANCH. Using "target-1" instead of * "i-1" here to check the last non-NOP instruction. */ (target > 0 && i + 1 < op_array->last && (op_array->opcodes[i+1].opcode == ZEND_JMPZ || op_array->opcodes[i+1].opcode == ZEND_JMPNZ) && zend_is_smart_branch(op_array->opcodes + target - 1))) { if (i != target) { op_array->opcodes[target] = op_array->opcodes[i]; ssa->ops[target] = ssa->ops[i]; ssa->cfg.map[target] = b - blocks; } target++; } i++; } if (target != end) { zend_op *opline; zend_op *new_opline; b->len = target - b->start; opline = op_array->opcodes + end - 1; if (opline->opcode == ZEND_NOP) { continue; } new_opline = op_array->opcodes + target - 1; zend_optimizer_migrate_jump(op_array, new_opline, opline); } } else { b->start = target; } } else { b->start = target; b->len = 0; } } if (target != op_array->last) { /* reset rest opcodes */ for (i = target; i < op_array->last; i++) { MAKE_NOP(op_array->opcodes + i); } /* update SSA variables */ for (j = 0; j < ssa->vars_count; j++) { if (ssa->vars[j].definition >= 0) { ssa->vars[j].definition -= shiftlist[ssa->vars[j].definition]; } if (ssa->vars[j].use_chain >= 0) { ssa->vars[j].use_chain -= shiftlist[ssa->vars[j].use_chain]; } } for (i = 0; i < op_array->last; i++) { if (ssa->ops[i].op1_use_chain >= 0) { ssa->ops[i].op1_use_chain -= shiftlist[ssa->ops[i].op1_use_chain]; } if (ssa->ops[i].op2_use_chain >= 0) { ssa->ops[i].op2_use_chain -= shiftlist[ssa->ops[i].op2_use_chain]; } if (ssa->ops[i].res_use_chain >= 0) { ssa->ops[i].res_use_chain -= shiftlist[ssa->ops[i].res_use_chain]; } } /* update branch targets */ 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; zend_optimizer_shift_jump(op_array, opline, shiftlist); } } /* update try/catch array */ for (j = 0; j < op_array->last_try_catch; j++) { op_array->try_catch_array[j].try_op -= shiftlist[op_array->try_catch_array[j].try_op]; op_array->try_catch_array[j].catch_op -= shiftlist[op_array->try_catch_array[j].catch_op]; if (op_array->try_catch_array[j].finally_op) { op_array->try_catch_array[j].finally_op -= shiftlist[op_array->try_catch_array[j].finally_op]; op_array->try_catch_array[j].finally_end -= shiftlist[op_array->try_catch_array[j].finally_end]; } } /* update early binding list */ if (op_array->fn_flags & ZEND_ACC_EARLY_BINDING) { uint32_t *opline_num = &ctx->script->first_early_binding_opline; ZEND_ASSERT(op_array == &ctx->script->main_op_array); do { *opline_num -= shiftlist[*opline_num]; opline_num = &op_array->opcodes[*opline_num].result.opline_num; } while (*opline_num != (uint32_t)-1); } /* update call graph */ if (func_info) { zend_call_info *call_info = func_info->callee_info; while (call_info) { call_info->caller_init_opline -= shiftlist[call_info->caller_init_opline - op_array->opcodes]; call_info->caller_call_opline -= shiftlist[call_info->caller_call_opline - op_array->opcodes]; call_info = call_info->next_callee; } } op_array->last = target; } free_alloca(shiftlist, use_heap); }
void zend_optimizer_nop_removal(zend_op_array *op_array, zend_optimizer_ctx *ctx) { zend_op *end, *opline; uint32_t new_count, i, shift; int j; uint32_t *shiftlist; ALLOCA_FLAG(use_heap); shiftlist = (uint32_t *)do_alloca(sizeof(uint32_t) * op_array->last, use_heap); i = new_count = shift = 0; end = op_array->opcodes + op_array->last; for (opline = op_array->opcodes; opline < end; opline++) { /* Kill JMP-over-NOP-s */ if (opline->opcode == ZEND_JMP && ZEND_OP1_JMP_ADDR(opline) > op_array->opcodes + i) { /* check if there are only NOPs under the branch */ zend_op *target = ZEND_OP1_JMP_ADDR(opline) - 1; while (target->opcode == ZEND_NOP) { target--; } if (target == opline) { /* only NOPs */ opline->opcode = ZEND_NOP; } } shiftlist[i++] = shift; if (opline->opcode == ZEND_NOP) { shift++; } else { if (shift) { zend_op *new_opline = op_array->opcodes + new_count; *new_opline = *opline; zend_optimizer_migrate_jump(op_array, new_opline, opline); } new_count++; } } if (shift) { op_array->last = new_count; end = op_array->opcodes + op_array->last; /* update JMPs */ for (opline = op_array->opcodes; opline<end; opline++) { zend_optimizer_shift_jump(op_array, opline, shiftlist); } /* update brk/cont array */ for (j = 0; j < op_array->last_live_range; j++) { op_array->live_range[j].start -= shiftlist[op_array->live_range[j].start]; op_array->live_range[j].end -= shiftlist[op_array->live_range[j].end]; } /* update try/catch array */ for (j = 0; j < op_array->last_try_catch; j++) { op_array->try_catch_array[j].try_op -= shiftlist[op_array->try_catch_array[j].try_op]; op_array->try_catch_array[j].catch_op -= shiftlist[op_array->try_catch_array[j].catch_op]; if (op_array->try_catch_array[j].finally_op) { op_array->try_catch_array[j].finally_op -= shiftlist[op_array->try_catch_array[j].finally_op]; op_array->try_catch_array[j].finally_end -= shiftlist[op_array->try_catch_array[j].finally_end]; } } /* update early binding list */ if (op_array->fn_flags & ZEND_ACC_EARLY_BINDING) { uint32_t *opline_num = &ctx->script->first_early_binding_opline; ZEND_ASSERT(op_array == &ctx->script->main_op_array); do { *opline_num -= shiftlist[*opline_num]; opline_num = &op_array->opcodes[*opline_num].result.opline_num; } while (*opline_num != (uint32_t)-1); } } free_alloca(shiftlist, use_heap); }