package bixie.transformation.loopunwinding;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import util.Log;
import boogie.controlflow.BasicBlock;
import boogie.controlflow.CfgProcedure;
import boogie.controlflow.CfgVariable;
import boogie.controlflow.expression.CfgExpression;
import boogie.controlflow.expression.CfgIdentifierExpression;
import boogie.controlflow.statement.CfgAssignStatement;
import boogie.controlflow.statement.CfgCallStatement;
import boogie.controlflow.statement.CfgHavocStatement;
import boogie.controlflow.statement.CfgStatement;
import boogie.controlflow.util.LoopDetection;
import boogie.controlflow.util.LoopInfo;
/**
* @author schaef
*/
public class FmsdUnwinding extends AbstractLoopUnwinding {
/**
* C-tor
*
* @param proc
* Boogie procedure
*/
public FmsdUnwinding(CfgProcedure proc) {
super(proc);
this.proc = proc;
this.maxUnwinding=1;
this.dontVerifyClones=true;
}
@Override
public void unwind() {
BasicBlock root = proc.getRootNode();
LoopDetection detection = new LoopDetection();
List<LoopInfo> loops = detection.computeLoops(root);
for (LoopInfo loop : loops) {
abstractUnwinding(loop);
}
}
private void abstractUnwinding(LoopInfo loop) {
// havoc the nested loops first
this.maxUnwinding=0;
for (LoopInfo nest : new LinkedList<LoopInfo>(loop.nestedLoops)) {
loop.nestedLoopHeads.remove(nest.loopHead);
loop.nestedLoops.remove(nest);
havocLoop(nest);
}
loop.refreshLoopBody(); //TODO: test
loop.UpdateLoopEntries();
//TOOD: recompute the loop info because the body has changed
this.maxUnwinding=1;
for (BasicBlock b : loop.loopEntries) {
b.addStatement(computeHavocStatement(loop), false);
}
unwind(loop,this.maxUnwinding);
}
private void havocLoop(LoopInfo loop) {
// havoc the nested loops first
for (LoopInfo nest : new LinkedList<LoopInfo>(loop.nestedLoops)) {
loop.nestedLoopHeads.remove(nest.loopHead);
loop.nestedLoops.remove(nest);
havocLoop(nest);
}
loop.refreshLoopBody(); //TODO: test
loop.loopHead.addStatement(computeHavocStatement(loop), true);
for (BasicBlock b : loop.loopExit) {
b.addStatement(computeHavocStatement(loop), true);
}
if (loop.loopExit.size()==0 && bixie.Options.v().getDebugMode()) {
Log.error("Loop has no exit! LoopHead "+loop.loopHead.getLabel());
}
unwind(loop,0);
}
private CfgHavocStatement computeHavocStatement(LoopInfo l) {
HashSet<CfgVariable> havocedVars = new HashSet<CfgVariable>();
for (BasicBlock b : l.loopBody) {
for (CfgStatement s : b.getStatements()) {
if (s instanceof CfgAssignStatement) {
CfgAssignStatement ass = (CfgAssignStatement) s;
for (CfgExpression e : ass.getLeft()) {
CfgVariable v = expToCfgVariable(e);
if (v != null)
havocedVars.add(v);
}
} else if (s instanceof CfgHavocStatement) {
CfgHavocStatement havoc = (CfgHavocStatement) s;
for (CfgVariable v : havoc.getVariables()) {
havocedVars.add(v);
}
} else if (s instanceof CfgCallStatement) {
// CfgCallStatement ivk = (CfgCallStatement) s;
throw new RuntimeException(
"Call statements are assumed to be deleted before loop unwinding");
}
}
}
return new CfgHavocStatement(l.loopHead.getLocationTag(),
havocedVars.toArray(new CfgVariable[havocedVars.size()]));
}
private CfgVariable expToCfgVariable(CfgExpression e) {
if (e instanceof CfgIdentifierExpression) {
return ((CfgIdentifierExpression) e).getVariable();
} else {
throw new RuntimeException("Don't know what to do with " + e);
}
}
}