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++; } }
void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx, zend_ssa *ssa) { if (ctx->debug_level & ZEND_DUMP_BEFORE_DFA_PASS) { zend_dump_op_array(op_array, ZEND_DUMP_SSA, "before dfa pass", ssa); } if (ssa->var_info) { int op_1; int v; int remove_nops = 0; zend_op *opline; zval tmp; for (v = op_array->last_var; v < ssa->vars_count; v++) { op_1 = ssa->vars[v].definition; if (op_1 < 0) { continue; } opline = op_array->opcodes + op_1; /* Convert LONG constants to DOUBLE */ if (ssa->var_info[v].use_as_double) { if (opline->opcode == ZEND_ASSIGN && opline->op2_type == IS_CONST && ssa->ops[op_1].op1_def == v && !RETURN_VALUE_USED(opline) ) { // op_1: ASSIGN ? -> #v [use_as_double], long(?) => ASSIGN ? -> #v, double(?) zval *zv = CT_CONSTANT_EX(op_array, opline->op2.constant); ZEND_ASSERT(Z_TYPE_INFO_P(zv) == IS_LONG); ZVAL_DOUBLE(&tmp, zval_get_double(zv)); opline->op2.constant = zend_optimizer_add_literal(op_array, &tmp); } else if (opline->opcode == ZEND_QM_ASSIGN && opline->op1_type == IS_CONST ) { // op_1: QM_ASSIGN #v [use_as_double], long(?) => QM_ASSIGN #v, double(?) zval *zv = CT_CONSTANT_EX(op_array, opline->op1.constant); ZEND_ASSERT(Z_TYPE_INFO_P(zv) == IS_LONG); ZVAL_DOUBLE(&tmp, zval_get_double(zv)); opline->op1.constant = zend_optimizer_add_literal(op_array, &tmp); } } else { if (opline->opcode == ZEND_ADD || opline->opcode == ZEND_SUB || opline->opcode == ZEND_MUL || opline->opcode == ZEND_IS_EQUAL || opline->opcode == ZEND_IS_NOT_EQUAL || opline->opcode == ZEND_IS_SMALLER || opline->opcode == ZEND_IS_SMALLER_OR_EQUAL ) { if (opline->op1_type == IS_CONST && opline->op2_type != IS_CONST && (OP2_INFO() & MAY_BE_ANY) == MAY_BE_DOUBLE && Z_TYPE_INFO_P(CT_CONSTANT_EX(op_array, opline->op1.constant)) == IS_LONG ) { // op_1: #v.? = ADD long(?), #?.? [double] => #v.? = ADD double(?), #?.? [double] zval *zv = CT_CONSTANT_EX(op_array, opline->op1.constant); ZVAL_DOUBLE(&tmp, zval_get_double(zv)); opline->op1.constant = zend_optimizer_add_literal(op_array, &tmp); } else if (opline->op1_type != IS_CONST && opline->op2_type == IS_CONST && (OP1_INFO() & MAY_BE_ANY) == MAY_BE_DOUBLE && Z_TYPE_INFO_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == IS_LONG ) { // op_1: #v.? = ADD #?.? [double], long(?) => #v.? = ADD #?.? [double], double(?) zval *zv = CT_CONSTANT_EX(op_array, opline->op2.constant); ZVAL_DOUBLE(&tmp, zval_get_double(zv)); opline->op2.constant = zend_optimizer_add_literal(op_array, &tmp); } } } if (ssa->vars[v].var >= op_array->last_var) { /* skip TMP and VAR */ continue; } if (opline->opcode == ZEND_ASSIGN && ssa->ops[op_1].op1_def == v && !RETURN_VALUE_USED(opline) ) { int orig_var = ssa->ops[op_1].op1_use; if (orig_var >= 0 && !(ssa->var_info[orig_var].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) ) { int src_var = ssa->ops[op_1].op2_use; if ((opline->op2_type & (IS_TMP_VAR|IS_VAR)) && src_var >= 0 && !(ssa->var_info[src_var].type & MAY_BE_REF) && ssa->vars[src_var].definition >= 0 && ssa->ops[ssa->vars[src_var].definition].result_def == src_var && ssa->ops[ssa->vars[src_var].definition].result_use < 0 && ssa->vars[src_var].use_chain == op_1 && ssa->ops[op_1].op2_use_chain < 0 && !ssa->vars[src_var].phi_use_chain && !ssa->vars[src_var].sym_use_chain /* see Zend/tests/generators/aborted_yield_during_new.phpt */ && op_array->opcodes[ssa->vars[src_var].definition].opcode != ZEND_NEW ) { int op_2 = ssa->vars[src_var].definition; // op_2: #src_var.T = OP ... => #v.CV = OP ... // op_1: ASSIGN #orig_var.CV [undef,scalar] -> #v.CV, #src_var.T NOP if (zend_ssa_unlink_use_chain(ssa, op_1, orig_var)) { /* Reconstruct SSA */ ssa->vars[v].definition = op_2; ssa->ops[op_2].result_def = v; ssa->vars[src_var].definition = -1; ssa->vars[src_var].use_chain = -1; ssa->ops[op_1].op1_use = -1; ssa->ops[op_1].op2_use = -1; ssa->ops[op_1].op1_def = -1; ssa->ops[op_1].op1_use_chain = -1; /* Update opcodes */ op_array->opcodes[op_2].result_type = opline->op1_type; op_array->opcodes[op_2].result.var = opline->op1.var; MAKE_NOP(opline); remove_nops = 1; } } else if (opline->op2_type == IS_CONST || ((opline->op2_type & (IS_TMP_VAR|IS_VAR|IS_CV)) && ssa->ops[op_1].op2_use >= 0 && ssa->ops[op_1].op2_def < 0) ) { // op_1: ASSIGN #orig_var.CV [undef,scalar] -> #v.CV, CONST|TMPVAR => QM_ASSIGN v.CV, CONST|TMPVAR if (zend_ssa_unlink_use_chain(ssa, op_1, orig_var)) { /* Reconstruct SSA */ ssa->ops[op_1].result_def = v; ssa->ops[op_1].op1_def = -1; ssa->ops[op_1].op1_use = ssa->ops[op_1].op2_use; ssa->ops[op_1].op1_use_chain = ssa->ops[op_1].op2_use_chain; ssa->ops[op_1].op2_use = -1; ssa->ops[op_1].op2_use_chain = -1; /* Update opcode */ opline->result_type = opline->op1_type; opline->result.var = opline->op1.var; opline->op1_type = opline->op2_type; opline->op1.var = opline->op2.var; opline->op2_type = IS_UNUSED; opline->op2.var = 0; opline->opcode = ZEND_QM_ASSIGN; } } } } else if (opline->opcode == ZEND_ASSIGN_ADD && opline->extended_value == 0 && ssa->ops[op_1].op1_def == v && opline->op2_type == IS_CONST && Z_TYPE_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == IS_LONG && Z_LVAL_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == 1 && ssa->ops[op_1].op1_use >= 0 && !(ssa->var_info[ssa->ops[op_1].op1_use].type & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) { // op_1: ASSIGN_ADD #?.CV [undef,null,int,foat] ->#v.CV, int(1) => PRE_INC #?.CV ->#v.CV opline->opcode = ZEND_PRE_INC; SET_UNUSED(opline->op2); } else if (opline->opcode == ZEND_ASSIGN_SUB && opline->extended_value == 0 && ssa->ops[op_1].op1_def == v && opline->op2_type == IS_CONST && Z_TYPE_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == IS_LONG && Z_LVAL_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == 1 && ssa->ops[op_1].op1_use >= 0 && !(ssa->var_info[ssa->ops[op_1].op1_use].type & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) { // op_1: ASSIGN_SUB #?.CV [undef,null,int,foat] -> #v.CV, int(1) => PRE_DEC #?.CV ->#v.CV opline->opcode = ZEND_PRE_DEC; SET_UNUSED(opline->op2); } else if (opline->opcode == ZEND_VERIFY_RETURN_TYPE && ssa->ops[op_1].op1_def == v && ssa->ops[op_1].op1_use >= 0 && ssa->ops[op_1].op1_use_chain == -1 && ssa->vars[v].use_chain >= 0 && (ssa->var_info[ssa->ops[op_1].op1_use].type & (MAY_BE_ANY|MAY_BE_UNDEF)) == (ssa->var_info[ssa->ops[op_1].op1_def].type & MAY_BE_ANY)) { // op_1: VERIFY_RETURN_TYPE #orig_var.CV [T] -> #v.CV [T] => NOP int orig_var = ssa->ops[op_1].op1_use; int ret = ssa->vars[v].use_chain; ssa->vars[orig_var].use_chain = ret; ssa->ops[ret].op1_use = orig_var; ssa->vars[v].definition = -1; ssa->vars[v].use_chain = -1; ssa->ops[op_1].op1_def = -1; ssa->ops[op_1].op1_use = -1; MAKE_NOP(opline); remove_nops = 1; } else if (ssa->ops[op_1].op1_def == v && !RETURN_VALUE_USED(opline) && ssa->ops[op_1].op1_use >= 0 && !(ssa->var_info[ssa->ops[op_1].op1_use].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) && (opline->opcode == ZEND_ASSIGN_ADD || opline->opcode == ZEND_ASSIGN_SUB || opline->opcode == ZEND_ASSIGN_MUL || opline->opcode == ZEND_ASSIGN_DIV || opline->opcode == ZEND_ASSIGN_MOD || opline->opcode == ZEND_ASSIGN_SL || opline->opcode == ZEND_ASSIGN_SR || opline->opcode == ZEND_ASSIGN_BW_OR || opline->opcode == ZEND_ASSIGN_BW_AND || opline->opcode == ZEND_ASSIGN_BW_XOR) && opline->extended_value == 0) { // op_1: ASSIGN_ADD #orig_var.CV [undef,null,bool,int,double] -> #v.CV, ? => #v.CV = ADD #orig_var.CV, ? /* Reconstruct SSA */ ssa->ops[op_1].result_def = ssa->ops[op_1].op1_def; ssa->ops[op_1].op1_def = -1; /* Update opcode */ opline->opcode -= (ZEND_ASSIGN_ADD - ZEND_ADD); opline->result_type = opline->op1_type; opline->result.var = opline->op1.var; } } if (remove_nops) { zend_ssa_remove_nops(op_array, ssa); } } if (ctx->debug_level & ZEND_DUMP_AFTER_DFA_PASS) { zend_dump_op_array(op_array, ZEND_DUMP_SSA, "after dfa pass", ssa); } }
int zend_build_cfg(zend_arena **arena, zend_op_array *op_array, int rt_constants, int stackless, zend_cfg *cfg, uint32_t *func_flags) /* {{{ */ { uint32_t flags = 0; uint32_t i; int j; uint32_t *block_map; zend_function *fn; int blocks_count = 0; zend_basic_block *blocks; zval *zv; cfg->map = block_map = zend_arena_calloc(arena, op_array->last, sizeof(uint32_t)); if (!block_map) { return FAILURE; } /* Build CFG, Step 1: Find basic blocks starts, calculate number of blocks */ BB_START(0); if ((op_array->fn_flags & ZEND_ACC_CLOSURE) && op_array->static_variables) { // FIXME: Really we should try to perform variable initialization flags |= ZEND_FUNC_TOO_DYNAMIC; } for (i = 0; i < op_array->last; i++) { zend_op *opline = op_array->opcodes + i; switch(opline->opcode) { case ZEND_RETURN: case ZEND_RETURN_BY_REF: case ZEND_GENERATOR_RETURN: case ZEND_EXIT: case ZEND_THROW: if (i + 1 < op_array->last) { BB_START(i + 1); } break; case ZEND_INCLUDE_OR_EVAL: case ZEND_YIELD: case ZEND_YIELD_FROM: flags |= ZEND_FUNC_TOO_DYNAMIC; if (stackless) { BB_START(i + 1); } break; case ZEND_DO_FCALL: case ZEND_DO_UCALL: case ZEND_DO_FCALL_BY_NAME: flags |= ZEND_FUNC_HAS_CALLS; if (stackless) { BB_START(i + 1); } break; case ZEND_DO_ICALL: flags |= ZEND_FUNC_HAS_CALLS; break; case ZEND_INIT_FCALL: if (rt_constants) { zv = RT_CONSTANT(op_array, opline->op2); } else { zv = CT_CONSTANT_EX(op_array, opline->op2.constant); } if ((fn = zend_hash_find_ptr(EG(function_table), Z_STR_P(zv))) != NULL) { if (fn->type == ZEND_INTERNAL_FUNCTION) { if (Z_STRLEN_P(zv) == sizeof("extract")-1 && memcmp(Z_STRVAL_P(zv), "extract", sizeof("extract")-1) == 0) { flags |= ZEND_FUNC_TOO_DYNAMIC; } else if (Z_STRLEN_P(zv) == sizeof("compact")-1 && memcmp(Z_STRVAL_P(zv), "compact", sizeof("compact")-1) == 0) { flags |= ZEND_FUNC_TOO_DYNAMIC; } else if (Z_STRLEN_P(zv) == sizeof("parse_str")-1 && memcmp(Z_STRVAL_P(zv), "parse_str", sizeof("parse_str")-1) == 0) { flags |= ZEND_FUNC_TOO_DYNAMIC; } else if (Z_STRLEN_P(zv) == sizeof("mb_parse_str")-1 && memcmp(Z_STRVAL_P(zv), "mb_parse_str", sizeof("mb_parse_str")-1) == 0) { flags |= ZEND_FUNC_TOO_DYNAMIC; } else if (Z_STRLEN_P(zv) == sizeof("get_defined_vars")-1 && memcmp(Z_STRVAL_P(zv), "get_defined_vars", sizeof("get_defined_vars")-1) == 0) { flags |= ZEND_FUNC_TOO_DYNAMIC; } else if (Z_STRLEN_P(zv) == sizeof("func_num_args")-1 && memcmp(Z_STRVAL_P(zv), "func_num_args", sizeof("func_num_args")-1) == 0) { flags |= ZEND_FUNC_VARARG; } else if (Z_STRLEN_P(zv) == sizeof("func_get_arg")-1 && memcmp(Z_STRVAL_P(zv), "func_get_arg", sizeof("func_get_arg")-1) == 0) { flags |= ZEND_FUNC_VARARG; } else if (Z_STRLEN_P(zv) == sizeof("func_get_args")-1 && memcmp(Z_STRVAL_P(zv), "func_get_args", sizeof("func_get_args")-1) == 0) { flags |= ZEND_FUNC_VARARG; } } } break; case ZEND_FAST_CALL: flags |= ZEND_FUNC_TOO_DYNAMIC; BB_START(OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes); BB_START(i + 1); break; case ZEND_FAST_RET: flags |= ZEND_FUNC_TOO_DYNAMIC; if (i + 1 < op_array->last) { BB_START(i + 1); } break; case ZEND_DECLARE_ANON_CLASS: case ZEND_DECLARE_ANON_INHERITED_CLASS: BB_START(OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes); BB_START(i + 1); break; case ZEND_JMP: BB_START(OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes); if (i + 1 < op_array->last) { BB_START(i + 1); } break; case ZEND_JMPZNZ: BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes); BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)); if (i + 1 < op_array->last) { BB_START(i + 1); } break; case ZEND_JMPZ: case ZEND_JMPNZ: case ZEND_JMPZ_EX: case ZEND_JMPNZ_EX: case ZEND_JMP_SET: case ZEND_COALESCE: case ZEND_ASSERT_CHECK: BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes); BB_START(i + 1); break; case ZEND_CATCH: flags |= ZEND_FUNC_TOO_DYNAMIC; if (!opline->result.num) { BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)); } BB_START(i + 1); break; case ZEND_FE_FETCH_R: case ZEND_FE_FETCH_RW: BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)); BB_START(i + 1); break; case ZEND_FE_RESET_R: case ZEND_FE_RESET_RW: case ZEND_NEW: BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes); BB_START(i + 1); break; case ZEND_DECLARE_LAMBDA_FUNCTION: { //??? zend_op_array *lambda_op_array; //??? //??? if (rt_constants) { //??? zv = RT_CONSTANT(op_array, opline->op1); //??? } else { //??? zv = CT_CONSTANT_EX(op_array, opline->op1.constant); //??? } //??? if (ctx->main_script && //??? (lambda_op_array = zend_hash_find_ptr(&ctx->main_script->function_table, Z_STR_P(zv))) != NULL) { //??? if (lambda_op_array->type == ZEND_USER_FUNCTION && //??? lambda_op_array->static_variables) { //??? // FIXME: Really we should try to perform alias //??? // analysis on variables used by the closure //??? info->flags |= ZEND_FUNC_TOO_DYNAMIC; //??? } //??? } else { //??? // FIXME: how to find the lambda function? flags |= ZEND_FUNC_TOO_DYNAMIC; //??? } } break; case ZEND_UNSET_VAR: if (!(opline->extended_value & ZEND_QUICK_SET)) { flags |= ZEND_FUNC_TOO_DYNAMIC; } break; case ZEND_FETCH_R: case ZEND_FETCH_W: case ZEND_FETCH_RW: case ZEND_FETCH_FUNC_ARG: case ZEND_FETCH_IS: case ZEND_FETCH_UNSET: if ((opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_LOCAL) { flags |= ZEND_FUNC_TOO_DYNAMIC; } else if (((opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_GLOBAL || (opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_GLOBAL_LOCK) && !op_array->function_name) { flags |= ZEND_FUNC_TOO_DYNAMIC; } break; } } for (j = 0; j < op_array->last_live_range; j++) { BB_START(op_array->live_range[j].start); BB_START(op_array->live_range[j].end); } if (op_array->last_try_catch) { for (j = 0; j < op_array->last_try_catch; j++) { BB_START(op_array->try_catch_array[j].try_op); if (op_array->try_catch_array[j].catch_op) { BB_START(op_array->try_catch_array[j].catch_op); } if (op_array->try_catch_array[j].finally_op) { BB_START(op_array->try_catch_array[j].finally_op); } if (op_array->try_catch_array[j].finally_end) { BB_START(op_array->try_catch_array[j].finally_end); } } } cfg->blocks_count = blocks_count; /* Build CFG, Step 2: Build Array of Basic Blocks */ cfg->blocks = blocks = zend_arena_calloc(arena, sizeof(zend_basic_block), blocks_count); if (!blocks) { return FAILURE; } for (i = 0, blocks_count = -1; i < op_array->last; i++) { if (block_map[i]) { if (blocks_count >= 0) { blocks[blocks_count].end = i - 1; } blocks_count++; blocks[blocks_count].flags = 0; blocks[blocks_count].start = i; blocks[blocks_count].successors[0] = -1; blocks[blocks_count].successors[1] = -1; blocks[blocks_count].predecessors_count = 0; blocks[blocks_count].predecessor_offset = -1; blocks[blocks_count].idom = -1; blocks[blocks_count].loop_header = -1; blocks[blocks_count].level = -1; blocks[blocks_count].children = -1; blocks[blocks_count].next_child = -1; block_map[i] = blocks_count; } else { block_map[i] = (uint32_t)-1; } } blocks[blocks_count].end = i - 1; blocks_count++; /* Build CFG, Step 3: Calculate successors */ for (j = 0; j < blocks_count; j++) { zend_op *opline = op_array->opcodes + blocks[j].end; switch(opline->opcode) { case ZEND_FAST_RET: case ZEND_RETURN: case ZEND_RETURN_BY_REF: case ZEND_GENERATOR_RETURN: case ZEND_EXIT: case ZEND_THROW: break; case ZEND_DECLARE_ANON_CLASS: case ZEND_DECLARE_ANON_INHERITED_CLASS: record_successor(blocks, j, 0, block_map[OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes]); record_successor(blocks, j, 1, j + 1); break; case ZEND_JMP: record_successor(blocks, j, 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)]); break; case ZEND_JMPZ: case ZEND_JMPNZ: case ZEND_JMPZ_EX: case ZEND_JMPNZ_EX: 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); 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); } else { record_successor(blocks, j, 0, j + 1); } break; 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); break; case ZEND_FE_RESET_R: case ZEND_FE_RESET_RW: case ZEND_NEW: record_successor(blocks, j, 0, block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes]); record_successor(blocks, j, 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); break; default: record_successor(blocks, j, 0, j + 1); break; } } /* Build CFG, Step 4, Mark Reachable Basic Blocks */ zend_mark_reachable_blocks(op_array, cfg, 0); if (func_flags) { *func_flags = flags; } return SUCCESS; }