package cd.transform.optimizers;

import cd.ir.BasicBlock;
import cd.ir.CompileTimeEvaluator;
import cd.ir.ControlFlowGraph;

import java.util.HashSet;
import java.util.Optional;
import java.util.Set;

import static cd.Config.TRUE;

/**
 * this class eliminates branches if a block condition evaluates to false at compile time
 */
public class BranchElimination implements IOptimizer {
	@Override
	public boolean go(ControlFlowGraph cfg) {
		CompileTimeEvaluator cte = new CompileTimeEvaluator();
		Set<BasicBlock> removedSet = new HashSet<>();
		boolean changed = false;

		for (BasicBlock block : cfg.allBlocks) {
			// Removed blocks and blocks without condition don't matter
			if (block.condition == null || removedSet.contains(block))
				continue;

			// If the condition cannot be computed, skip block
			Optional<Integer> condition = cte.calc(block.condition);
			if (!condition.isPresent()) continue;

			changed = true;
			// Remove all connections to unreachable block
			BasicBlock eliminated;
			if (condition.get() == TRUE)
				eliminated = block.falseSuccessor();
			else
				eliminated = block.trueSuccessor();
			block.successors.remove(eliminated);
			block.condition = null;
			for (BasicBlock b : eliminated.successors)
				b.predecessors.remove(eliminated);
			removedSet.add(eliminated);
		}

		// Can't edit the list while we are iterating over it
		cfg.allBlocks.removeAll(removedSet);
		return changed;
	}
}
