コード例 #1
0
ファイル: zend_ssa.c プロジェクト: Ashroyal/php-src
/* We can interpret $a + 5 == 0 as $a = 0 - 5, i.e. shift the adjustment to the other operand.
 * This negated adjustment is what is written into the "adjustment" parameter. */
static int find_adjusted_tmp_var(const zend_op_array *op_array, uint32_t build_flags, zend_op *opline, uint32_t var_num, zend_long *adjustment)
{
	zend_op *op = opline;
	while (op != op_array->opcodes) {
		op--;
		if (op->result_type != IS_TMP_VAR || op->result.var != var_num) {
			continue;
		}

		if (op->opcode == ZEND_POST_DEC) {
			if (op->op1_type == IS_CV) {
				*adjustment = -1;
				return EX_VAR_TO_NUM(op->op1.var);
			}
		} else if (op->opcode == ZEND_POST_INC) {
			if (op->op1_type == IS_CV) {
				*adjustment = 1;
				return EX_VAR_TO_NUM(op->op1.var);
			}
		} else if (op->opcode == ZEND_ADD) {
			if (op->op1_type == IS_CV &&
				op->op2_type == IS_CONST &&
				Z_TYPE_P(CRT_CONSTANT(op->op2)) == IS_LONG &&
				Z_LVAL_P(CRT_CONSTANT(op->op2)) != ZEND_LONG_MIN) {
				*adjustment = -Z_LVAL_P(CRT_CONSTANT(op->op2));
				return EX_VAR_TO_NUM(op->op1.var);
			} else if (op->op2_type == IS_CV &&
					   op->op1_type == IS_CONST &&
					   Z_TYPE_P(CRT_CONSTANT(op->op1)) == IS_LONG &&
					   Z_LVAL_P(CRT_CONSTANT(op->op1)) != ZEND_LONG_MIN) {
				*adjustment = -Z_LVAL_P(CRT_CONSTANT(op->op1));
				return EX_VAR_TO_NUM(op->op2.var);
			}
		} else if (op->opcode == ZEND_SUB) {
			if (op->op1_type == IS_CV &&
				op->op2_type == IS_CONST &&
				Z_TYPE_P(CRT_CONSTANT(op->op2)) == IS_LONG) {
				*adjustment = Z_LVAL_P(CRT_CONSTANT(op->op2));
				return EX_VAR_TO_NUM(op->op1.var);
			}
		}
		break;
	}
	return -1;
}
コード例 #2
0
ファイル: zend_ssa.c プロジェクト: Ashroyal/php-src
/* e-SSA construction: Pi placement (Pi is actually a Phi with single
 * source and constraint).
 * Order of Phis is importent, Pis must be placed before Phis
 */
static void place_essa_pis(
		zend_arena **arena, const zend_op_array *op_array, uint32_t build_flags, zend_ssa *ssa,
		zend_dfg *dfg) {
	zend_basic_block *blocks = ssa->cfg.blocks;
	int j, blocks_count = ssa->cfg.blocks_count;
	for (j = 0; j < blocks_count; j++) {
		zend_ssa_phi *pi;
		zend_op *opline = op_array->opcodes + ssa->cfg.blocks[j].end;
		int bt; /* successor block number if a condition is true */
		int bf; /* successor block number if a condition is false */

		if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
			continue;
		}
		/* the last instruction of basic block is conditional branch,
		 * based on comparison of CV(s)
		 */
		switch (opline->opcode) {
			case ZEND_JMPZ:
			case ZEND_JMPZNZ:
				bf = ssa->cfg.blocks[j].successors[0];
				bt = ssa->cfg.blocks[j].successors[1];
				break;
			case ZEND_JMPNZ:
				bt = ssa->cfg.blocks[j].successors[0];
				bf = ssa->cfg.blocks[j].successors[1];
				break;
			default:
				continue;
		}
		if (opline->op1_type == IS_TMP_VAR &&
		    ((opline-1)->opcode == ZEND_IS_EQUAL ||
		     (opline-1)->opcode == ZEND_IS_NOT_EQUAL ||
		     (opline-1)->opcode == ZEND_IS_SMALLER ||
		     (opline-1)->opcode == ZEND_IS_SMALLER_OR_EQUAL) &&
		    opline->op1.var == (opline-1)->result.var) {
			int  var1 = -1;
			int  var2 = -1;
			zend_long val1 = 0;
			zend_long val2 = 0;
//			long val = 0;

			if ((opline-1)->op1_type == IS_CV) {
				var1 = EX_VAR_TO_NUM((opline-1)->op1.var);
			} else if ((opline-1)->op1_type == IS_TMP_VAR) {
				var1 = find_adjusted_tmp_var(
					op_array, build_flags, opline, (opline-1)->op1.var, &val2);
			}

			if ((opline-1)->op2_type == IS_CV) {
				var2 = EX_VAR_TO_NUM((opline-1)->op2.var);
			} else if ((opline-1)->op2_type == IS_TMP_VAR) {
				var2 = find_adjusted_tmp_var(
					op_array, build_flags, opline, (opline-1)->op2.var, &val1);
			}

			if (var1 >= 0 && var2 >= 0) {
				if (!sub_will_overflow(val1, val2) && !sub_will_overflow(val2, val1)) {
					zend_long tmp = val1;
					val1 -= val2;
					val2 -= tmp;
				} else {
					var1 = -1;
					var2 = -1;
				}
			} else if (var1 >= 0 && var2 < 0) {
				zend_long add_val2 = 0;
				if ((opline-1)->op2_type == IS_CONST &&
				    Z_TYPE_P(CRT_CONSTANT((opline-1)->op2)) == IS_LONG) {
					add_val2 = Z_LVAL_P(CRT_CONSTANT((opline-1)->op2));
				} else if ((opline-1)->op2_type == IS_CONST &&
				    Z_TYPE_P(CRT_CONSTANT((opline-1)->op2)) == IS_FALSE) {
					add_val2 = 0;
				} else if ((opline-1)->op2_type == IS_CONST &&
				    Z_TYPE_P(CRT_CONSTANT((opline-1)->op2)) == IS_TRUE) {
					add_val2 = 1;
				} else {
					var1 = -1;
				}
				if (!add_will_overflow(val2, add_val2)) {
					val2 += add_val2;
				} else {
					var1 = -1;
				}
			} else if (var1 < 0 && var2 >= 0) {
				zend_long add_val1 = 0;
				if ((opline-1)->op1_type == IS_CONST &&
				    Z_TYPE_P(CRT_CONSTANT((opline-1)->op1)) == IS_LONG) {
					add_val1 = Z_LVAL_P(CRT_CONSTANT((opline-1)->op1));
				} else if ((opline-1)->op1_type == IS_CONST &&
				    Z_TYPE_P(CRT_CONSTANT((opline-1)->op1)) == IS_FALSE) {
					add_val1 = 0;
				} else if ((opline-1)->op1_type == IS_CONST &&
				    Z_TYPE_P(CRT_CONSTANT((opline-1)->op1)) == IS_TRUE) {
					add_val1 = 1;
				} else {
					var2 = -1;
				}
				if (!add_will_overflow(val1, add_val1)) {
					val1 += add_val1;
				} else {
					var2 = -1;
				}
			}

			if (var1 >= 0) {
				if ((opline-1)->opcode == ZEND_IS_EQUAL) {
					if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var1))) {
						pi_range_equals(pi, var2, val2);
					}
					if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var1))) {
						pi_range_not_equals(pi, var2, val2);
					}
				} else if ((opline-1)->opcode == ZEND_IS_NOT_EQUAL) {
					if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var1))) {
						pi_range_equals(pi, var2, val2);
					}
					if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var1))) {
						pi_range_not_equals(pi, var2, val2);
					}
				} else if ((opline-1)->opcode == ZEND_IS_SMALLER) {
					if (val2 > ZEND_LONG_MIN) {
						if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var1))) {
							pi_range_max(pi, var2, val2-1);
						}
					}
					if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var1))) {
						pi_range_min(pi, var2, val2);
					}
				} else if ((opline-1)->opcode == ZEND_IS_SMALLER_OR_EQUAL) {
					if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var1))) {
						pi_range_max(pi, var2, val2);
					}
					if (val2 < ZEND_LONG_MAX) {
						if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var1))) {
							pi_range_min(pi, var2, val2+1);
						}
					}
				}
			}
			if (var2 >= 0) {
				if((opline-1)->opcode == ZEND_IS_EQUAL) {
					if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var2))) {
						pi_range_equals(pi, var1, val1);
					}
					if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var2))) {
						pi_range_not_equals(pi, var1, val1);
					}
				} else if ((opline-1)->opcode == ZEND_IS_NOT_EQUAL) {
					if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var2))) {
						pi_range_equals(pi, var1, val1);
					}
					if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var2))) {
						pi_range_not_equals(pi, var1, val1);
					}
				} else if ((opline-1)->opcode == ZEND_IS_SMALLER) {
					if (val1 < ZEND_LONG_MAX) {
						if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var2))) {
							pi_range_min(pi, var1, val1+1);
						}
					}
					if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var2))) {
						pi_range_max(pi, var1, val1);
					}
				} else if ((opline-1)->opcode == ZEND_IS_SMALLER_OR_EQUAL) {
					if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var2))) {
						pi_range_min(pi, var1, val1);
					}
					if (val1 > ZEND_LONG_MIN) {
						if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var2))) {
							pi_range_max(pi, var1, val1-1);
						}
					}
				}
			}
		} else if (opline->op1_type == IS_TMP_VAR &&
		           ((opline-1)->opcode == ZEND_POST_INC ||
		            (opline-1)->opcode == ZEND_POST_DEC) &&
		           opline->op1.var == (opline-1)->result.var &&
		           (opline-1)->op1_type == IS_CV) {
			int var = EX_VAR_TO_NUM((opline-1)->op1.var);

			if ((opline-1)->opcode == ZEND_POST_DEC) {
				if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var))) {
					pi_range_equals(pi, -1, -1);
				}
				if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var))) {
					pi_range_not_equals(pi, -1, -1);
				}
			} else if ((opline-1)->opcode == ZEND_POST_INC) {
				if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var))) {
					pi_range_equals(pi, -1, 1);
				}
				if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var))) {
					pi_range_not_equals(pi, -1, 1);
				}
			}
		} else if (opline->op1_type == IS_VAR &&
		           ((opline-1)->opcode == ZEND_PRE_INC ||
		            (opline-1)->opcode == ZEND_PRE_DEC) &&
		           opline->op1.var == (opline-1)->result.var &&
		           (opline-1)->op1_type == IS_CV) {
			int var = EX_VAR_TO_NUM((opline-1)->op1.var);

			if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var))) {
				pi_range_equals(pi, -1, 0);
			}
			/* speculative */
			if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var))) {
				pi_range_not_equals(pi, -1, 0);
			}
		} else if (opline->op1_type == IS_TMP_VAR && (opline-1)->opcode == ZEND_TYPE_CHECK &&
				   opline->op1.var == (opline-1)->result.var && (opline-1)->op1_type == IS_CV) {
			int var = EX_VAR_TO_NUM((opline-1)->op1.var);
			uint32_t type = (opline-1)->extended_value;
			if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var))) {
				pi_type_mask(pi, mask_for_type_check(type));
			}
			if (type != IS_OBJECT && type != IS_RESOURCE) {
				/* is_object() and is_resource() may return false, even though the value is
				 * an object/resource. */
				if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var))) {
					pi_not_type_mask(pi, mask_for_type_check(type));
				}
			}
		} else if (opline->op1_type == IS_TMP_VAR &&
				   ((opline-1)->opcode == ZEND_IS_IDENTICAL
					|| (opline-1)->opcode == ZEND_IS_NOT_IDENTICAL) &&
				   opline->op1.var == (opline-1)->result.var) {
			int var;
			zval *val;
			uint32_t type_mask;
			if ((opline-1)->op1_type == IS_CV && (opline-1)->op2_type == IS_CONST) {
				var = EX_VAR_TO_NUM((opline-1)->op1.var);
				val = CRT_CONSTANT((opline-1)->op2);
			} else if ((opline-1)->op1_type == IS_CONST && (opline-1)->op2_type == IS_CV) {
				var = EX_VAR_TO_NUM((opline-1)->op2.var);
				val = CRT_CONSTANT((opline-1)->op1);
			} else {
				continue;
			}

			/* We're interested in === null/true/false comparisons here, because they eliminate
			 * a type in the false-branch. Other === VAL comparisons are unlikely to be useful. */
			if (Z_TYPE_P(val) != IS_NULL && Z_TYPE_P(val) != IS_TRUE && Z_TYPE_P(val) != IS_FALSE) {
				continue;
			}

			type_mask = _const_op_type(val);
			if ((opline-1)->opcode == ZEND_IS_IDENTICAL) {
				if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var))) {
					pi_type_mask(pi, type_mask);
				}
				if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var))) {
					pi_not_type_mask(pi, type_mask);
				}
			} else {
				if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var))) {
					pi_type_mask(pi, type_mask);
				}
				if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var))) {
					pi_not_type_mask(pi, type_mask);
				}
			}
		}
	}
}
コード例 #3
0
ファイル: zend_cfg.c プロジェクト: CooCoooo/php-src
int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t build_flags, 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);
	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:
				flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
			case ZEND_YIELD:
			case ZEND_YIELD_FROM:
				if (build_flags & ZEND_CFG_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 (build_flags & ZEND_CFG_STACKLESS) {
					BB_START(i + 1);
				}
				break;
			case ZEND_DO_ICALL:
				flags |= ZEND_FUNC_HAS_CALLS;
				break;
			case ZEND_INIT_FCALL:
			case ZEND_INIT_NS_FCALL_BY_NAME:
				zv = CRT_CONSTANT(opline->op2);
				if (opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME) {
					/* The third literal is the lowercased unqualified name */
					zv += 2;
				}
				if ((fn = zend_hash_find_ptr(EG(function_table), Z_STR_P(zv))) != NULL) {
					if (fn->type == ZEND_INTERNAL_FUNCTION) {
						if (zend_string_equals_literal(Z_STR_P(zv), "extract")) {
							flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
						} else if (zend_string_equals_literal(Z_STR_P(zv), "compact")) {
							flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
						} else if (zend_string_equals_literal(Z_STR_P(zv), "parse_str") &&
						           opline->extended_value == 1) {
							flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
						} else if (zend_string_equals_literal(Z_STR_P(zv), "mb_parse_str") &&
						           opline->extended_value == 1) {
							flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
						} else if (zend_string_equals_literal(Z_STR_P(zv), "get_defined_vars")) {
							flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
						} else if (zend_string_equals_literal(Z_STR_P(zv), "func_num_args")) {
							flags |= ZEND_FUNC_VARARG;
						} else if (zend_string_equals_literal(Z_STR_P(zv), "func_get_arg")) {
							flags |= ZEND_FUNC_VARARG;
						} else if (zend_string_equals_literal(Z_STR_P(zv), "func_get_args")) {
							flags |= ZEND_FUNC_VARARG;
						}
					}
				}
				break;
			case ZEND_FAST_CALL:
				BB_START(OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes);
				BB_START(i + 1);
				break;
			case ZEND_FAST_RET:
				if (i + 1 < op_array->last) {
					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:
				if (!opline->result.num) {
					BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
				}
				BB_START(i + 1);
				break;
			case ZEND_DECLARE_ANON_CLASS:
			case ZEND_DECLARE_ANON_INHERITED_CLASS:
			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_UNSET_VAR:
			case ZEND_ISSET_ISEMPTY_VAR:
				if (((opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_LOCAL) &&
				    !(opline->extended_value & ZEND_QUICK_SET)) {
					flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
				} 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_INDIRECT_VAR_ACCESS;
				}
				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_INDIRECT_VAR_ACCESS;
				} 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_INDIRECT_VAR_ACCESS;
				}
				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_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_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);
				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;
}