package bixie.transformation.loopunwinding;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import util.Log;
import bixie.checker.GlobalsCache;
import boogie.ProgramFactory;
import boogie.ast.Attribute;
import boogie.controlflow.BasicBlock;
import boogie.controlflow.CfgProcedure;
import boogie.controlflow.expression.CfgBooleanLiteral;
import boogie.controlflow.statement.CfgAssertStatement;
import boogie.controlflow.util.LoopInfo;
/**
* @author schaef
*/
public abstract class AbstractLoopUnwinding {
public static void unwindeLoops(CfgProcedure proc) {
AbstractLoopUnwinding unwinder = null;
unwinder = new FmsdUnwinding(proc);
unwinder.unwind();
}
/**
* Boogie procedure
*/
protected CfgProcedure proc;
protected int maxUnwinding = 0;
protected boolean dontVerifyClones = false;
/**
* C-tor
*
* @param proc
* Boogie procedure
*/
protected AbstractLoopUnwinding(CfgProcedure proc) {
this.proc = proc;
}
/**
* Unwinds the loops
*/
public abstract void unwind();
/**
* Eliminates a loop by removing all BasicBlocks that must reach the loop
* head. This works for all sorts of loops but probably generates much
* larger programs than the typical loop elimination that we would do for
* normal while loops (namely just removing the enitre body).
*
* @param loop
*/
private void eliminate(LoopInfo loop) {
// identify all blocks that can exit the loop.
LinkedList<BasicBlock> todo = new LinkedList<BasicBlock>();
for (BasicBlock b : loop.loopExit) {
for (BasicBlock pre : b.getPredecessors()) {
if (loop.loopBody.contains(pre)) {
todo.add(pre);
}
}
}
// now find all blocks that connect the
// loop entry to a loop exit
LinkedList<BasicBlock> connectedToExit = new LinkedList<BasicBlock>();
while (!todo.isEmpty()) {
BasicBlock b = todo.pop();
connectedToExit.add(b);
if (b != loop.loopHead) {
for (BasicBlock pre : b.getPredecessors()) {
if (!todo.contains(pre)) {
todo.add(pre);
}
}
}
}
HashSet<BasicBlock> mustset = mustBeReachedByLoopHead(loop);
for (BasicBlock b : loop.loopBody) {
if (connectedToExit.contains(b)) {
//ensure that blocks inside the loop are not reported.
if (dontVerifyClones
&& (!mustset.contains(b)
|| loop.isNestedLoop)) {
// if (dontVerifyClones ) {
//TODO: check if it makes sense to not mark the loop head.
markAsClone(b);
}
for (BasicBlock s : new HashSet<BasicBlock>(b.getSuccessors())) {
if (!loop.loopExit.contains(s)
&& !connectedToExit.contains(s)) {
b.disconnectFromSuccessor(s);
}
}
} else {
for (BasicBlock s : new HashSet<BasicBlock>(b.getSuccessors())) {
b.disconnectFromSuccessor(s);
}
}
}
}
private HashSet<BasicBlock> mustBeReachedByLoopHead (LoopInfo loop) {
if (loop.loopExit.size()==0) {
//TODO: the code below does not work for loops without exit
//so we simply return the empty set
return new HashSet<BasicBlock>();
}
LinkedList<BasicBlock> todo = new LinkedList<BasicBlock>();
HashSet<BasicBlock> done = new HashSet<BasicBlock>();
HashMap<BasicBlock, HashSet<BasicBlock>> mustReach = new HashMap<BasicBlock, HashSet<BasicBlock>>();
HashSet<BasicBlock> end_nodes = new HashSet<BasicBlock>();
for (BasicBlock b : loop.loopingPred) {
if (b!=loop.loopHead) {
end_nodes.add(b);
}
}
for (BasicBlock b : loop.loopExit) {
for (BasicBlock pre : b.getPredecessors()) {
if (loop.loopBody.contains(pre) && pre!=loop.loopHead) {
end_nodes.add(pre);
}
}
}
todo.addAll(end_nodes);
while (!todo.isEmpty()) {
BasicBlock current = todo.removeLast();
//check if all predecessors have been processed already.
//if not, add the block to the end of the cue and start over
boolean allGood = true;
for (BasicBlock prev : current.getSuccessors()) {
if (!loop.loopBody.contains(prev) || prev==loop.loopHead) continue;
if (!done.contains(prev)) {
allGood=false;
break;
}
}
if (!allGood){
todo.addFirst(current);
continue;
}
//if all predecessors have been processed,
//we can compute the dominators list by
//intersecting the list of all predecessors
HashSet<BasicBlock> currentDom = null;
if (!end_nodes.contains(current)) {
for (BasicBlock prev : current.getSuccessors()) {
if (!loop.loopBody.contains(prev) || prev==loop.loopHead) continue;
if (currentDom == null) {
currentDom = new HashSet<BasicBlock>(mustReach.get(prev));
} else {
//retainAll computes the intersection of the two sets.
currentDom.retainAll(mustReach.get(prev));
}
}
}
//special case, only occurs for the root/sink
if (currentDom == null) {
currentDom = new HashSet<BasicBlock>();
}
currentDom.add(current); //of course, a block dominates itself
mustReach.put(current, currentDom);
done.add(current);
if (current!=loop.loopHead) {
for (BasicBlock next : current.getPredecessors()) {
if (!todo.contains(next) && !done.contains(next)) {
if (!loop.loopBody.contains(next)) continue;
todo.addLast(next);
}
}
}
}
// System.err.println("MUST REACH");
// for (BasicBlock b : mustReach.get(loop.loopHead)) {
// System.err.println("\t" + b.getLabel());
// }
if (mustReach.get(loop.loopHead)==null) {
Log.error("Could not compute the dominator relation for loop at "+loop.loopHead.getLabel());
return new HashSet<BasicBlock>();
}
return mustReach.get(loop.loopHead);
}
protected void unwind(LoopInfo loop, int unwindings) {
for (LoopInfo nest : new LinkedList<LoopInfo>(loop.nestedLoops)) {
loop.nestedLoopHeads.remove(nest.loopHead);
loop.nestedLoops.remove(nest);
unwind(nest, unwindings);
}
loop.refreshLoopBody(); //TODO: test
HashSet<BasicBlock> mustset = mustBeReachedByLoopHead(loop);
if (unwindings <= 0) {
eliminate(loop);
return;
}
// first unwind the nested loops.
// clone the loop once and add it before the actual loop.
HashMap<BasicBlock, BasicBlock> clonemap = new HashMap<BasicBlock, BasicBlock>();
// clone the loopbody
BasicBlock cloneHead = null;
for (BasicBlock b : loop.loopBody) {
BasicBlock clone = b.clone("iter"
+ (this.maxUnwinding - unwindings));
if (b == loop.loopHead) {
cloneHead = clone;
}
// TODO: not sure if this is the right condition. Eventually,
// if we want to do abstract unwinding, we have to make
// the clones NoCode blocks.
if (dontVerifyClones && (!mustset.contains(b)
|| loop.isNestedLoop)) {
markAsClone(clone);
}
clonemap.put(b, clone);
}
if (cloneHead == null) {
throw new RuntimeException("No loop head found during unwinding.");
}
// now redirect the predecessors of the loop head to the
// head of the clone.
for (BasicBlock b : loop.loopEntries) {
b.disconnectFromSuccessor(loop.loopHead);
b.connectToSuccessor(cloneHead);
}
// fix the successors of the cloned nodes.
for (BasicBlock b : loop.loopBody) {
BasicBlock clone = clonemap.get(b);
for (BasicBlock s : new HashSet<BasicBlock>(b.getSuccessors())) {
if (clonemap.containsKey(s)) {
clone.connectToSuccessor(clonemap.get(s));
} else {
// add the missing edge that was not cloned
clone.connectToSuccessor(s);
}
}
}
// now redirect all back edges to the original loop head.
for (BasicBlock b : loop.loopingPred) {
if (!clonemap.containsKey(b)) {
Log.error("something fishy with that loop!");
continue;
}
BasicBlock clone = clonemap.get(b);
clone.disconnectFromSuccessor(cloneHead);
clone.connectToSuccessor(loop.loopHead);
}
loop.UpdateLoopEntries();
unwind(loop, unwindings - 1);
}
private void markAsClone(BasicBlock clone) {
ProgramFactory pf = GlobalsCache.v().getProgramFactory();
clone.addStatement(new CfgAssertStatement(null,
new Attribute[] { pf.mkCustomAttribute(ProgramFactory.Cloned) },
new CfgBooleanLiteral(null, pf.getBoolType(), true)));
}
}