ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_jit_profile_helper(ZEND_OPCODE_HANDLER_ARGS) { zend_op_array *op_array = (zend_op_array*)EX(func); zend_vm_opcode_handler_t handler = (zend_vm_opcode_handler_t)ZEND_FUNC_INFO(op_array); uintptr_t counter = (uintptr_t)ZEND_COUNTER_INFO(op_array); ZEND_COUNTER_INFO(op_array) = (void*)(counter + 1); ++zend_jit_profile_counter; ZEND_OPCODE_TAIL_CALL(handler); }
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) { i = b->start; b->start = target; while (i <= b->end) { 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) && (op_array->opcodes[i-1].opcode == ZEND_IS_IDENTICAL || op_array->opcodes[i-1].opcode == ZEND_IS_NOT_IDENTICAL || op_array->opcodes[i-1].opcode == ZEND_IS_EQUAL || op_array->opcodes[i-1].opcode == ZEND_IS_NOT_EQUAL || op_array->opcodes[i-1].opcode == ZEND_IS_SMALLER || op_array->opcodes[i-1].opcode == ZEND_IS_SMALLER_OR_EQUAL || op_array->opcodes[i-1].opcode == ZEND_CASE || op_array->opcodes[i-1].opcode == ZEND_ISSET_ISEMPTY_VAR || op_array->opcodes[i-1].opcode == ZEND_ISSET_ISEMPTY_STATIC_PROP || op_array->opcodes[i-1].opcode == ZEND_ISSET_ISEMPTY_DIM_OBJ || op_array->opcodes[i-1].opcode == ZEND_ISSET_ISEMPTY_PROP_OBJ || op_array->opcodes[i-1].opcode == ZEND_INSTANCEOF || op_array->opcodes[i-1].opcode == ZEND_TYPE_CHECK || op_array->opcodes[i-1].opcode == ZEND_DEFINED))) { if (i != target) { op_array->opcodes[target] = op_array->opcodes[i]; ssa->ops[target] = ssa->ops[i]; shiftlist[i] = i - target; } target++; } i++; } if (b->end != target - 1) { zend_op *opline; zend_op *new_opline; opline = op_array->opcodes + b->end; b->end = target - 1; new_opline = op_array->opcodes + b->end; 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; } } } } 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) { zend_op *opline = op_array->opcodes + b->end; 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; } } } /* 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); }
int zend_dfa_optimize_calls(zend_op_array *op_array, zend_ssa *ssa) { zend_func_info *func_info = ZEND_FUNC_INFO(op_array); int removed_ops = 0; if (func_info->callee_info) { zend_call_info *call_info = func_info->callee_info; do { if (call_info->caller_call_opline->opcode == ZEND_DO_ICALL && call_info->callee_func && ZSTR_LEN(call_info->callee_func->common.function_name) == sizeof("in_array")-1 && memcmp(ZSTR_VAL(call_info->callee_func->common.function_name), "in_array", sizeof("in_array")-1) == 0 && (call_info->caller_init_opline->extended_value == 2 || (call_info->caller_init_opline->extended_value == 3 && (call_info->caller_call_opline - 1)->opcode == ZEND_SEND_VAL && (call_info->caller_call_opline - 1)->op1_type == IS_CONST))) { zend_op *send_array; zend_op *send_needly; zend_bool strict = 0; if (call_info->caller_init_opline->extended_value == 2) { send_array = call_info->caller_call_opline - 1; send_needly = call_info->caller_call_opline - 2; } else { if (zend_is_true(CT_CONSTANT_EX(op_array, (call_info->caller_call_opline - 1)->op1.constant))) { strict = 1; } send_array = call_info->caller_call_opline - 2; send_needly = call_info->caller_call_opline - 3; } if (send_array->opcode == ZEND_SEND_VAL && send_array->op1_type == IS_CONST && Z_TYPE_P(CT_CONSTANT_EX(op_array, send_array->op1.constant)) == IS_ARRAY && (send_needly->opcode == ZEND_SEND_VAL || send_needly->opcode == ZEND_SEND_VAR) ) { int ok = 1; HashTable *src = Z_ARRVAL_P(CT_CONSTANT_EX(op_array, send_array->op1.constant)); HashTable *dst; zval *val, tmp; zend_ulong idx; ZVAL_TRUE(&tmp); dst = zend_new_array(zend_hash_num_elements(src)); if (strict) { ZEND_HASH_FOREACH_VAL(src, val) { if (Z_TYPE_P(val) == IS_STRING) { zend_hash_add(dst, Z_STR_P(val), &tmp); } else if (Z_TYPE_P(val) == IS_LONG) { zend_hash_index_add(dst, Z_LVAL_P(val), &tmp); } else { zend_array_destroy(dst); ok = 0; break; } } ZEND_HASH_FOREACH_END(); } else { ZEND_HASH_FOREACH_VAL(src, val) { if (Z_TYPE_P(val) != IS_STRING || ZEND_HANDLE_NUMERIC(Z_STR_P(val), idx)) { zend_array_destroy(dst); ok = 0; break; } zend_hash_add(dst, Z_STR_P(val), &tmp); } ZEND_HASH_FOREACH_END(); } if (ok) { uint32_t op_num = send_needly - op_array->opcodes; zend_ssa_op *ssa_op = ssa->ops + op_num; if (ssa_op->op1_use >= 0) { /* Reconstruct SSA */ int var_num = ssa_op->op1_use; zend_ssa_var *var = ssa->vars + var_num; ZEND_ASSERT(ssa_op->op1_def < 0); zend_ssa_unlink_use_chain(ssa, op_num, ssa_op->op1_use); ssa_op->op1_use = -1; ssa_op->op1_use_chain = -1; op_num = call_info->caller_call_opline - op_array->opcodes; ssa_op = ssa->ops + op_num; ssa_op->op1_use = var_num; ssa_op->op1_use_chain = var->use_chain; var->use_chain = op_num; } ZVAL_ARR(&tmp, dst); /* Update opcode */ call_info->caller_call_opline->opcode = ZEND_IN_ARRAY; call_info->caller_call_opline->extended_value = strict; call_info->caller_call_opline->op1_type = send_needly->op1_type; call_info->caller_call_opline->op1.num = send_needly->op1.num; call_info->caller_call_opline->op2_type = IS_CONST; call_info->caller_call_opline->op2.constant = zend_optimizer_add_literal(op_array, &tmp); if (call_info->caller_init_opline->extended_value == 3) { MAKE_NOP(call_info->caller_call_opline - 1); } MAKE_NOP(call_info->caller_init_opline); MAKE_NOP(send_needly); MAKE_NOP(send_array); removed_ops++; } }
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); }
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); }