Esempio n. 1
0
File: scdf.c Progetto: jhdxr/php-src
void scdf_mark_edge_feasible(scdf_ctx *scdf, int from, int to) {
	uint32_t edge = scdf_edge(&scdf->ssa->cfg, from, to);

	if (zend_bitset_in(scdf->feasible_edges, edge)) {
		/* We already handled this edge */
		return;
	}

	DEBUG_PRINT("Marking edge %d->%d feasible\n", from, to);
	zend_bitset_incl(scdf->feasible_edges, edge);

	if (!zend_bitset_in(scdf->executable_blocks, to)) {
		if (!zend_bitset_in(scdf->block_worklist, to)) {
			DEBUG_PRINT("Adding block %d to worklist\n", to);
		}
		zend_bitset_incl(scdf->block_worklist, to);
	} else {
		/* Block is already executable, only a new edge became feasible.
		 * Reevaluate phi nodes to account for changed source operands. */
		zend_ssa_block *ssa_block = &scdf->ssa->blocks[to];
		zend_ssa_phi *phi;
		for (phi = ssa_block->phis; phi; phi = phi->next) {
			zend_bitset_excl(scdf->phi_var_worklist, phi->ssa_var);
			scdf->handlers.visit_phi(scdf, phi);
		}
	}
}
Esempio n. 2
0
static zend_always_inline void add_to_worklists(context *ctx, int var_num, int check) {
	zend_ssa_var *var = &ctx->ssa->vars[var_num];
	if (var->definition >= 0) {
		if (!check || zend_bitset_in(ctx->instr_dead, var->definition)) {
			zend_bitset_incl(ctx->instr_worklist, var->definition);
		}
	} else if (var->definition_phi) {
		if (!check || zend_bitset_in(ctx->phi_dead, var_num)) {
			zend_bitset_incl(ctx->phi_worklist, var_num);
		}
	}
}
Esempio n. 3
0
File: scdf.c Progetto: jhdxr/php-src
/* Removes unreachable blocks. This will remove both the instructions (and phis) in the
 * blocks, as well as remove them from the successor / predecessor lists and mark them
 * unreachable. Blocks already marked unreachable are not removed. */
int scdf_remove_unreachable_blocks(scdf_ctx *scdf) {
	zend_ssa *ssa = scdf->ssa;
	int i;
	int removed_ops = 0;

	for (i = 0; i < ssa->cfg.blocks_count; i++) {
		if (!zend_bitset_in(scdf->executable_blocks, i)
				&& (ssa->cfg.blocks[i].flags & ZEND_BB_REACHABLE)
				&& !kept_alive_by_live_range(scdf, i)) {
			removed_ops += ssa->cfg.blocks[i].len;
			zend_ssa_remove_block(scdf->op_array, ssa, i);
		}
	}
	return removed_ops;
}
Esempio n. 4
0
File: scdf.c Progetto: jhdxr/php-src
/* If a live range starts in a reachable block and ends in an unreachable block, we should
 * not eliminate the latter. While it cannot be reached, the FREE opcode of the loop var
 * is necessary for the correctness of temporary compaction. */
static zend_bool kept_alive_by_live_range(scdf_ctx *scdf, uint32_t block) {
	uint32_t i;
	const zend_op_array *op_array = scdf->op_array;
	const zend_cfg *cfg = &scdf->ssa->cfg;
	for (i = 0; i < op_array->last_live_range; i++) {
		zend_live_range *live_range = &op_array->live_range[i];
		uint32_t start_block = cfg->map[live_range->start];
		uint32_t end_block = cfg->map[live_range->end];

		if (end_block == block && start_block != block
				&& zend_bitset_in(scdf->executable_blocks, start_block)) {
			return 1;
		}
	}
	return 0;
}
Esempio n. 5
0
/* If a live range starts in a reachable block and ends in an unreachable block, we should
 * not eliminate the latter. While it cannot be reached, the FREE opcode of the loop var
 * is necessary for the correctness of temporary compaction. */
static zend_bool kept_alive_by_loop_var_free(scdf_ctx *scdf, uint32_t block_idx) {
	uint32_t i;
	const zend_op_array *op_array = scdf->op_array;
	const zend_cfg *cfg = &scdf->ssa->cfg;
	const zend_basic_block *block = &cfg->blocks[block_idx];
	for (i = block->start; i < block->start + block->len; i++) {
		zend_op *opline = &op_array->opcodes[i];
		if (opline->opcode == ZEND_FE_FREE ||
				(opline->opcode == ZEND_FREE && opline->extended_value == ZEND_FREE_SWITCH)) {
			int ssa_var = scdf->ssa->ops[i].op1_use;
			if (ssa_var >= 0) {
				int op_num = scdf->ssa->vars[ssa_var].definition;
				uint32_t def_block;
				ZEND_ASSERT(op_num >= 0);
				def_block = cfg->map[op_num];
				if (zend_bitset_in(scdf->executable_blocks, def_block)) {
					return 1;
				}
			}
		}
	}
	return 0;
}
Esempio n. 6
0
int zend_build_ssa(zend_arena **arena, const zend_op_array *op_array, uint32_t build_flags, zend_ssa *ssa, uint32_t *func_flags) /* {{{ */
{
	zend_basic_block *blocks = ssa->cfg.blocks;
	zend_ssa_block *ssa_blocks;
	int blocks_count = ssa->cfg.blocks_count;
	uint32_t set_size;
	zend_bitset tmp, gen, in;
	int *var = NULL;
	int i, j, k, changed;
	zend_dfg dfg;
	ALLOCA_FLAG(dfg_use_heap);
	ALLOCA_FLAG(var_use_heap);

	ssa->rt_constants = (build_flags & ZEND_RT_CONSTANTS);
	ssa_blocks = zend_arena_calloc(arena, blocks_count, sizeof(zend_ssa_block));
	if (!ssa_blocks) {
		return FAILURE;
	}
	ssa->blocks = ssa_blocks;

	/* Compute Variable Liveness */
	dfg.vars = op_array->last_var + op_array->T;
	dfg.size = set_size = zend_bitset_len(dfg.vars);
	dfg.tmp = do_alloca((set_size * sizeof(zend_ulong)) * (blocks_count * 5 + 1), dfg_use_heap);
	memset(dfg.tmp, 0, (set_size * sizeof(zend_ulong)) * (blocks_count * 5 + 1));
	dfg.gen = dfg.tmp + set_size;
	dfg.def = dfg.gen + set_size * blocks_count;
	dfg.use = dfg.def + set_size * blocks_count;
	dfg.in  = dfg.use + set_size * blocks_count;
	dfg.out = dfg.in  + set_size * blocks_count;

	if (zend_build_dfg(op_array, &ssa->cfg, &dfg, build_flags) != SUCCESS) {
		free_alloca(dfg.tmp, dfg_use_heap);
		return FAILURE;
	}

	if (build_flags & ZEND_SSA_DEBUG_LIVENESS) {
		zend_dump_dfg(op_array, &ssa->cfg, &dfg);
	}

	tmp = dfg.tmp;
	gen = dfg.gen;
	in  = dfg.in;

	/* SSA construction, Step 1: Propagate "gen" sets in merge points */
	do {
		changed = 0;
		for (j = 0; j < blocks_count; j++) {
			if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
				continue;
			}
			if (j >= 0 && (blocks[j].predecessors_count > 1 || j == 0)) {
				zend_bitset_copy(tmp, gen + (j * set_size), set_size);
				for (k = 0; k < blocks[j].predecessors_count; k++) {
					i = ssa->cfg.predecessors[blocks[j].predecessor_offset + k];
					while (i != -1 && i != blocks[j].idom) {
						zend_bitset_union_with_intersection(tmp, tmp, gen + (i * set_size), in + (j * set_size), set_size);
						i = blocks[i].idom;
					}
				}
				if (!zend_bitset_equal(gen + (j * set_size), tmp, set_size)) {
					zend_bitset_copy(gen + (j * set_size), tmp, set_size);
					changed = 1;
				}
			}
		}
	} while (changed);

	/* SSA construction, Step 2: Phi placement based on Dominance Frontiers */
	var = do_alloca(sizeof(int) * (op_array->last_var + op_array->T), var_use_heap);
	if (!var) {
		free_alloca(dfg.tmp, dfg_use_heap);
		return FAILURE;
	}
	zend_bitset_clear(tmp, set_size);

	for (j = 0; j < blocks_count; j++) {
		if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
			continue;
		}
		if (blocks[j].predecessors_count > 1) {
			zend_bitset_clear(tmp, set_size);
			if (blocks[j].flags & ZEND_BB_IRREDUCIBLE_LOOP) {
				/* Prevent any values from flowing into irreducible loops by
				   replacing all incoming values with explicit phis.  The
				   register allocator depends on this property.  */
				zend_bitset_copy(tmp, in + (j * set_size), set_size);
			} else {
				for (k = 0; k < blocks[j].predecessors_count; k++) {
					i = ssa->cfg.predecessors[blocks[j].predecessor_offset + k];
					while (i != -1 && i != blocks[j].idom) {
						zend_bitset_union_with_intersection(tmp, tmp, gen + (i * set_size), in + (j * set_size), set_size);
						i = blocks[i].idom;
					}
				}
			}

			if (!zend_bitset_empty(tmp, set_size)) {
				i = op_array->last_var + op_array->T;
				while (i > 0) {
					i--;
					if (zend_bitset_in(tmp, i)) {
						zend_ssa_phi *phi = zend_arena_calloc(arena, 1,
							sizeof(zend_ssa_phi) +
							sizeof(int) * blocks[j].predecessors_count +
							sizeof(void*) * blocks[j].predecessors_count);

						if (!phi) {
							goto failure;
						}
						phi->sources = (int*)(((char*)phi) + sizeof(zend_ssa_phi));
						memset(phi->sources, 0xff, sizeof(int) * blocks[j].predecessors_count);
						phi->use_chains = (zend_ssa_phi**)(((char*)phi->sources) + sizeof(int) * ssa->cfg.blocks[j].predecessors_count);

					    phi->pi = -1;
						phi->var = i;
						phi->ssa_var = -1;
						phi->next = ssa_blocks[j].phis;
						ssa_blocks[j].phis = phi;
					}
				}
			}
		}
	}

	place_essa_pis(arena, op_array, build_flags, ssa, &dfg);

	/* SSA construction, Step ?: Phi after Pi placement based on Dominance Frontiers */
	for (j = 0; j < blocks_count; j++) {
		if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
			continue;
		}
		if (blocks[j].predecessors_count > 1) {
			zend_bitset_clear(tmp, set_size);
			if (blocks[j].flags & ZEND_BB_IRREDUCIBLE_LOOP) {
				/* Prevent any values from flowing into irreducible loops by
				   replacing all incoming values with explicit phis.  The
				   register allocator depends on this property.  */
				zend_bitset_copy(tmp, in + (j * set_size), set_size);
			} else {
				for (k = 0; k < blocks[j].predecessors_count; k++) {
					i = ssa->cfg.predecessors[blocks[j].predecessor_offset + k];
					while (i != -1 && i != blocks[j].idom) {
						zend_ssa_phi *p = ssa_blocks[i].phis;
						while (p) {
							if (p) {
								if (p->pi >= 0) {
									if (zend_bitset_in(in + (j * set_size), p->var) &&
									    !zend_bitset_in(gen + (i * set_size), p->var)) {
										zend_bitset_incl(tmp, p->var);
									}
								} else {
									zend_bitset_excl(tmp, p->var);
								}
							}
							p = p->next;
						}
						i = blocks[i].idom;
					}
				}
			}

			if (!zend_bitset_empty(tmp, set_size)) {
				i = op_array->last_var + op_array->T;
				while (i > 0) {
					i--;
					if (zend_bitset_in(tmp, i)) {
						zend_ssa_phi **pp = &ssa_blocks[j].phis;
						while (*pp) {
							if ((*pp)->pi <= 0 && (*pp)->var == i) {
								break;
							}
							pp = &(*pp)->next;
						}
						if (*pp == NULL) {
							zend_ssa_phi *phi = zend_arena_calloc(arena, 1,
								sizeof(zend_ssa_phi) +
								sizeof(int) * blocks[j].predecessors_count +
								sizeof(void*) * blocks[j].predecessors_count);

							if (!phi) {
								goto failure;
							}
							phi->sources = (int*)(((char*)phi) + sizeof(zend_ssa_phi));
							memset(phi->sources, 0xff, sizeof(int) * blocks[j].predecessors_count);
							phi->use_chains = (zend_ssa_phi**)(((char*)phi->sources) + sizeof(int) * ssa->cfg.blocks[j].predecessors_count);

						    phi->pi = -1;
							phi->var = i;
							phi->ssa_var = -1;
							phi->next = NULL;
							*pp = phi;
						}
					}
				}
			}
		}
	}

	if (build_flags & ZEND_SSA_DEBUG_PHI_PLACEMENT) {
		zend_dump_phi_placement(op_array, ssa);
	}

	/* SSA construction, Step 3: Renaming */
	ssa->ops = zend_arena_calloc(arena, op_array->last, sizeof(zend_ssa_op));
	memset(ssa->ops, 0xff, op_array->last * sizeof(zend_ssa_op));
	memset(var, 0xff, (op_array->last_var + op_array->T) * sizeof(int));
	/* Create uninitialized SSA variables for each CV */
	for (j = 0; j < op_array->last_var; j++) {
		var[j] = j;
	}
	ssa->vars_count = op_array->last_var;
	if (zend_ssa_rename(op_array, build_flags, ssa, var, 0) != SUCCESS) {
failure:
		free_alloca(var, var_use_heap);
		free_alloca(dfg.tmp, dfg_use_heap);
		return FAILURE;
	}

	free_alloca(var, var_use_heap);
	free_alloca(dfg.tmp, dfg_use_heap);

	return SUCCESS;
}
Esempio n. 7
0
void zend_optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *ctx)
{
	int T = op_array->T;
	int offset = op_array->last_var;
	uint32_t bitset_len;
	zend_bitset taken_T;	/* T index in use */
	zend_op **start_of_T;	/* opline where T is first used */
	zend_bitset valid_T;	/* Is the map_T valid */
	int *map_T;				/* Map's the T to its new index */
	zend_op *opline, *end;
	int currT;
	int i;
	int max = -1;
	int var_to_free = -1;
	void *checkpoint = zend_arena_checkpoint(ctx->arena);

	bitset_len = zend_bitset_len(T);
	taken_T = (zend_bitset) zend_arena_alloc(&ctx->arena, bitset_len * ZEND_BITSET_ELM_SIZE);
	start_of_T = (zend_op **) zend_arena_alloc(&ctx->arena, T * sizeof(zend_op *));
	valid_T = (zend_bitset) zend_arena_alloc(&ctx->arena, bitset_len * ZEND_BITSET_ELM_SIZE);
	map_T = (int *) zend_arena_alloc(&ctx->arena, T * sizeof(int));

    end = op_array->opcodes;
    opline = &op_array->opcodes[op_array->last - 1];

    /* Find T definition points */
    while (opline >= end) {
        if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR)) {
			start_of_T[VAR_NUM(ZEND_RESULT(opline).var) - offset] = opline;
		}
		opline--;
	}

	zend_bitset_clear(valid_T, bitset_len);
	zend_bitset_clear(taken_T, bitset_len);

    end = op_array->opcodes;
    opline = &op_array->opcodes[op_array->last - 1];

    while (opline >= end) {
		if ((ZEND_OP1_TYPE(opline) & (IS_VAR | IS_TMP_VAR))) {
			currT = VAR_NUM(ZEND_OP1(opline).var) - offset;
			if (opline->opcode == ZEND_ROPE_END) {
				int num = (((opline->extended_value + 1) * sizeof(zend_string*)) + (sizeof(zval) - 1)) / sizeof(zval);
				int var;

				var = max;
				while (var >= 0 && !zend_bitset_in(taken_T, var)) {
					var--;
				}
				max = MAX(max, var + num);
				var = var + 1;
				map_T[currT] = var;
				zend_bitset_incl(valid_T, currT);
				zend_bitset_incl(taken_T, var);
				ZEND_OP1(opline).var = NUM_VAR(var + offset);
				while (num > 1) {
					num--;
					zend_bitset_incl(taken_T, var + num);
				}
			} else {
				if (!zend_bitset_in(valid_T, currT)) {
					int use_new_var = 0;

					/* Code in "finally" blocks may modify temorary variables.
					 * We allocate new temporaries for values that need to
					 * relive FAST_CALLs.
					 */
					if ((op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) &&
					    (opline->opcode == ZEND_RETURN ||
					     opline->opcode == ZEND_GENERATOR_RETURN ||
					     opline->opcode == ZEND_RETURN_BY_REF ||
					     opline->opcode == ZEND_FREE ||
					     opline->opcode == ZEND_FE_FREE)) {
						zend_op *curr = opline;

						while (--curr >= end) {
							if (curr->opcode == ZEND_FAST_CALL) {
								use_new_var = 1;
								break;
							} else if (curr->opcode != ZEND_FREE &&
							           curr->opcode != ZEND_FE_FREE &&
							           curr->opcode != ZEND_VERIFY_RETURN_TYPE &&
							           curr->opcode != ZEND_DISCARD_EXCEPTION) {
								break;
							}
						}
					}
					if (use_new_var) {
						i = ++max;
						zend_bitset_incl(taken_T, i);
					} else {
						GET_AVAILABLE_T();
					}
					map_T[currT] = i;
					zend_bitset_incl(valid_T, currT);
				}
				ZEND_OP1(opline).var = NUM_VAR(map_T[currT] + offset);
			}
		}

		if ((ZEND_OP2_TYPE(opline) & (IS_VAR | IS_TMP_VAR))) {
			currT = VAR_NUM(ZEND_OP2(opline).var) - offset;
			if (!zend_bitset_in(valid_T, currT)) {
				GET_AVAILABLE_T();
				map_T[currT] = i;
				zend_bitset_incl(valid_T, currT);
			}
			ZEND_OP2(opline).var = NUM_VAR(map_T[currT] + offset);
		}

		if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR)) {
			currT = VAR_NUM(ZEND_RESULT(opline).var) - offset;
			if (zend_bitset_in(valid_T, currT)) {
				if (start_of_T[currT] == opline) {
					/* ZEND_FAST_CALL can not share temporary var with others
					 * since the fast_var could also be set by ZEND_HANDLE_EXCEPTION
					 * which could be ahead of it */
					if (opline->opcode != ZEND_FAST_CALL) {
						zend_bitset_excl(taken_T, map_T[currT]);
					}
				}
				ZEND_RESULT(opline).var = NUM_VAR(map_T[currT] + offset);
				if (opline->opcode == ZEND_ROPE_INIT) {
					if (start_of_T[currT] == opline) {
						uint32_t num = ((opline->extended_value * sizeof(zend_string*)) + (sizeof(zval) - 1)) / sizeof(zval);
						while (num > 1) {
							num--;
							zend_bitset_excl(taken_T, map_T[currT]+num);
						}
					}
				}
			} else {
				/* Code which gets here is using a wrongly built opcode such as RECV() */
				GET_AVAILABLE_T();
				map_T[currT] = i;
				zend_bitset_incl(valid_T, currT);
				ZEND_RESULT(opline).var = NUM_VAR(i + offset);
			}
		}

		if (var_to_free >= 0) {
			zend_bitset_excl(taken_T, var_to_free);
			var_to_free = -1;
		}

		opline--;
	}

	if (op_array->live_range) {
		for (i = 0; i < op_array->last_live_range; i++) {
			op_array->live_range[i].var =
				NUM_VAR(map_T[VAR_NUM(op_array->live_range[i].var & ~ZEND_LIVE_MASK) - offset] + offset) |
				(op_array->live_range[i].var & ZEND_LIVE_MASK);
		}
	}

	zend_arena_release(&ctx->arena, checkpoint);
	op_array->T = max + 1;
}
Esempio n. 8
0
/* This pass removes all CVs and temporaries that are completely unused. It does *not* merge any CVs or TMPs.
 * This pass does not operate on SSA form anymore. */
void zend_optimizer_compact_vars(zend_op_array *op_array) {
	int i;

	ALLOCA_FLAG(use_heap1);
	ALLOCA_FLAG(use_heap2);
	uint32_t used_vars_len = zend_bitset_len(op_array->last_var + op_array->T);
	zend_bitset used_vars = ZEND_BITSET_ALLOCA(used_vars_len, use_heap1);
	uint32_t *vars_map = do_alloca((op_array->last_var + op_array->T) * sizeof(uint32_t), use_heap2);
	uint32_t num_cvs, num_tmps;

	/* Determine which CVs are used */
	zend_bitset_clear(used_vars, used_vars_len);
	for (i = 0; i < op_array->last; i++) {
		zend_op *opline = &op_array->opcodes[i];
		if (opline->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
			zend_bitset_incl(used_vars, VAR_NUM(opline->op1.var));
		}
		if (opline->op2_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
			zend_bitset_incl(used_vars, VAR_NUM(opline->op2.var));
		}
		if (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
			zend_bitset_incl(used_vars, VAR_NUM(opline->result.var));
			if (opline->opcode == ZEND_ROPE_INIT) {
				uint32_t num = ((opline->extended_value * sizeof(zend_string*)) + (sizeof(zval) - 1)) / sizeof(zval);
				while (num > 1) {
					num--;
					zend_bitset_incl(used_vars, VAR_NUM(opline->result.var) + num);
				}
			}
		}
	}

	num_cvs = 0;
	for (i = 0; i < op_array->last_var; i++) {
		if (zend_bitset_in(used_vars, i)) {
			vars_map[i] = num_cvs++;
		} else {
			vars_map[i] = (uint32_t) -1;
		}
	}

	num_tmps = 0;
	for (i = op_array->last_var; i < op_array->last_var + op_array->T; i++) {
		if (zend_bitset_in(used_vars, i)) {
			vars_map[i] = num_cvs + num_tmps++;
		} else {
			vars_map[i] = (uint32_t) -1;
		}
	}

	free_alloca(used_vars, use_heap1);
	if (num_cvs == op_array->last_var && num_tmps == op_array->T) {
		free_alloca(vars_map, use_heap2);
		return;
	}

	ZEND_ASSERT(num_cvs <= op_array->last_var);
	ZEND_ASSERT(num_tmps <= op_array->T);

	/* Update CV and TMP references in opcodes */
	for (i = 0; i < op_array->last; i++) {
		zend_op *opline = &op_array->opcodes[i];
		if (opline->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
			opline->op1.var = NUM_VAR(vars_map[VAR_NUM(opline->op1.var)]);
		}
		if (opline->op2_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
			opline->op2.var = NUM_VAR(vars_map[VAR_NUM(opline->op2.var)]);
		}
		if (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
			opline->result.var = NUM_VAR(vars_map[VAR_NUM(opline->result.var)]);
		}
	}

	/* Update TMP references in live ranges */
	if (op_array->live_range) {
		for (i = 0; i < op_array->last_live_range; i++) {
			op_array->live_range[i].var =
				(op_array->live_range[i].var & ZEND_LIVE_MASK) |
				NUM_VAR(vars_map[VAR_NUM(op_array->live_range[i].var & ~ZEND_LIVE_MASK)]);
		}
	}

	/* Update CV name table */
	if (num_cvs != op_array->last_var) {
		zend_string **names = safe_emalloc(sizeof(zend_string *), num_cvs, 0);
		for (i = 0; i < op_array->last_var; i++) {
			if (vars_map[i] != (uint32_t) -1) {
				names[vars_map[i]] = op_array->vars[i];
			} else {
				zend_string_release(op_array->vars[i]);
			}
		}
		efree(op_array->vars);
		op_array->vars = names;
	}

	op_array->last_var = num_cvs;
	op_array->T = num_tmps;

	free_alloca(vars_map, use_heap2);
}
Esempio n. 9
0
File: scdf.c Progetto: jhdxr/php-src
void scdf_solve(scdf_ctx *scdf, const char *name) {
	zend_ssa *ssa = scdf->ssa;
	DEBUG_PRINT("Start SCDF solve (%s)\n", name);
	while (!zend_bitset_empty(scdf->instr_worklist, scdf->instr_worklist_len)
		|| !zend_bitset_empty(scdf->phi_var_worklist, scdf->phi_var_worklist_len)
		|| !zend_bitset_empty(scdf->block_worklist, scdf->block_worklist_len)
	) {
		int i;
		while ((i = zend_bitset_pop_first(scdf->phi_var_worklist, scdf->phi_var_worklist_len)) >= 0) {
			zend_ssa_phi *phi = ssa->vars[i].definition_phi;
			ZEND_ASSERT(phi);
			if (zend_bitset_in(scdf->executable_blocks, phi->block)) {
				scdf->handlers.visit_phi(scdf, phi);
			}
		}

		while ((i = zend_bitset_pop_first(scdf->instr_worklist, scdf->instr_worklist_len)) >= 0) {
			int block_num = ssa->cfg.map[i];
			if (zend_bitset_in(scdf->executable_blocks, block_num)) {
				zend_basic_block *block = &ssa->cfg.blocks[block_num];
				zend_op *opline = &scdf->op_array->opcodes[i];
				zend_ssa_op *ssa_op = &ssa->ops[i];
				if (opline->opcode == ZEND_OP_DATA) {
					opline--;
					ssa_op--;
				}
				scdf->handlers.visit_instr(scdf, opline, ssa_op);
				if (i == block->start + block->len - 1) {
					if (block->successors_count == 1) {
						scdf_mark_edge_feasible(scdf, block_num, block->successors[0]);
					} else if (block->successors_count > 1) {
						scdf->handlers.mark_feasible_successors(scdf, block_num, block, opline, ssa_op);
					}
				}
			}
		}

		while ((i = zend_bitset_pop_first(scdf->block_worklist, scdf->block_worklist_len)) >= 0) {
			/* This block is now live. Interpret phis and instructions in it. */
			zend_basic_block *block = &ssa->cfg.blocks[i];
			zend_ssa_block *ssa_block = &ssa->blocks[i];

			DEBUG_PRINT("Pop block %d from worklist\n", i);
			zend_bitset_incl(scdf->executable_blocks, i);

			{
				zend_ssa_phi *phi;
				for (phi = ssa_block->phis; phi; phi = phi->next) {
					zend_bitset_excl(scdf->phi_var_worklist, phi->ssa_var);
					scdf->handlers.visit_phi(scdf, phi);
				}
			}

			if (block->len == 0) {
				/* Zero length blocks don't have a last instruction that would normally do this */
				scdf_mark_edge_feasible(scdf, i, block->successors[0]);
			} else {
				zend_op *opline;
				int j, end = block->start + block->len;
				for (j = block->start; j < end; j++) {
					opline = &scdf->op_array->opcodes[j];
					zend_bitset_excl(scdf->instr_worklist, j);
					if (opline->opcode != ZEND_OP_DATA) {
						scdf->handlers.visit_instr(scdf, opline, &ssa->ops[j]);
					}
				}
				if (block->successors_count == 1) {
					scdf_mark_edge_feasible(scdf, i, block->successors[0]);
				} else if (block->successors_count > 1) {
					if (opline->opcode == ZEND_OP_DATA) {
						opline--;
						j--;
					}
					scdf->handlers.mark_feasible_successors(scdf, i, block, opline, &ssa->ops[j-1]);
				}
			}
		}
	}
}
Esempio n. 10
0
static inline void add_to_phi_worklist_no_val(context *ctx, int var_num) {
	zend_ssa_var *var = &ctx->ssa->vars[var_num];
	if (var->definition_phi && zend_bitset_in(ctx->phi_dead, var_num)) {
		zend_bitset_incl(ctx->phi_worklist_no_val, var_num);
	}
}