/**
*
*/
package bixie.transformation;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map.Entry;
import util.Log;
import boogie.controlflow.BasicBlock;
import boogie.controlflow.CfgProcedure;
import boogie.controlflow.CfgVariable;
import boogie.controlflow.expression.CfgArrayAccessExpression;
import boogie.controlflow.expression.CfgArrayStoreExpression;
import boogie.controlflow.expression.CfgBinaryExpression;
import boogie.controlflow.expression.CfgBitVectorAccessExpression;
import boogie.controlflow.expression.CfgExpression;
import boogie.controlflow.expression.CfgFunctionApplication;
import boogie.controlflow.expression.CfgIdentifierExpression;
import boogie.controlflow.expression.CfgIfThenElseExpression;
import boogie.controlflow.expression.CfgQuantifierExpression;
import boogie.controlflow.expression.CfgUnaryExpression;
import boogie.controlflow.statement.CfgAssertStatement;
import boogie.controlflow.statement.CfgAssignStatement;
import boogie.controlflow.statement.CfgAssumeStatement;
import boogie.controlflow.statement.CfgCallStatement;
import boogie.controlflow.statement.CfgHavocStatement;
import boogie.controlflow.statement.CfgStatement;
/**
* @author schaef
*
*/
public class SingleStaticAssignment {
public SingleStaticAssignment() {
}
/**
* computes an SSA version of p. This step also introduces blocks
* for frame conditions and a unified exit.
* @param p
*/
public void computeSSA(CfgProcedure p) {
LinkedList<BasicBlock> todo = new LinkedList<BasicBlock>();
LinkedList<BasicBlock> done = new LinkedList<BasicBlock>();
//generate unified exit
{
Log.debug("------ create unified exit");
LinkedList<BasicBlock> sinks = new LinkedList<BasicBlock>();
todo.add(p.getRootNode());
while (!todo.isEmpty()) {
BasicBlock current = todo.removeLast();
done.add(current);
if (current.getSuccessors().size()>0) {
for (BasicBlock next : current.getSuccessors()) {
if (!todo.contains(next) && !done.contains(next)) {
todo.add(next);
}
}
} else {
sinks.add(current);
}
}
if (sinks.size()==0) {
throw new RuntimeException("Every procedure must have at least one sink: "+p.getProcedureName());
}
if (sinks.size()>1) {
BasicBlock unifiedExit = new BasicBlock(p.getLocation(), "$unifiedExit");
unifiedExit.returns = true;
for (BasicBlock b : sinks) {
b.returns = false;
b.connectToSuccessor(unifiedExit);
}
p.setExitNode(unifiedExit);
}
}
//generate inbetween block if needed
{
Log.debug("------ create block between");
todo = new LinkedList<BasicBlock>();
done = new LinkedList<BasicBlock>();
todo.add(p.getExitNode());
while (!todo.isEmpty()) {
BasicBlock current = todo.removeLast();
done.add(current);
for (BasicBlock next : current.getPredecessors()) {
if (!todo.contains(next) && !done.contains(next)) {
todo.add(next);
}
}
if (current.getPredecessors().size()>1) {
for (BasicBlock next : new HashSet<BasicBlock>(current.getPredecessors())) {
BasicBlock between = new BasicBlock(next.getLocationTag(), next.getLabel()+"$between$"+current.getLabel());
next.disconnectFromSuccessor(current);
next.connectToSuccessor(between);
between.connectToSuccessor(current);
}
}
}
}
updateBlockSSA(p);
//IMPORTANT: for some of the later analysis set we must be
//able to assume that the Exit node does not contain any
//statements. Thus, we create a fresh block here.
if (!p.getExitNode().getLabel().equals("$UnifiedExit")) {
BasicBlock finalUnifiedExit = new BasicBlock(p.getExitNode().getLocationTag(), "$UnifiedExit");
//connect each block that does not have a successor to the unified exit
for (BasicBlock b : done) {
if (b.getSuccessors().size() == 0) {
b.connectToSuccessor(finalUnifiedExit);
}
{
HashMap<CfgVariable, Integer> offset = new HashMap<CfgVariable, Integer>();
for (BasicBlock pred : finalUnifiedExit.getPredecessors()) {
mergeSSAOffsets(offset, pred.getLocalIncarnationMap());
}
for (BasicBlock pred : new HashSet<BasicBlock>(
finalUnifiedExit.getPredecessors())) {
addFrameCondition(pred, offset);
}
recomputLocalSSA(finalUnifiedExit, offset);
}
}
//Now do the SSA for the postcondition using the offset of
//the unified exit.
for (CfgExpression expr : p.getEnsures()) {
recomputLocalSSA(expr, finalUnifiedExit.getLocalIncarnationMap());
}
p.setExitNode(finalUnifiedExit);
}
return;
}
/**
* updates the SSA tables of each block.
* NOTE: use computeSSA instead if you are not sure if there is a unified exit
* of the frame conditions haven't been created yet.
* @param p
*/
public void updateBlockSSA(CfgProcedure p) {
Log.debug("------ recomute the ssa");
//Do the actual SSA
LinkedList<BasicBlock> todo = new LinkedList<BasicBlock>();
LinkedList<BasicBlock> done = new LinkedList<BasicBlock>();
if (p.getRootNode()==null) return;
todo.add(p.getRootNode());
//do SSA on the precondition first.
for (CfgExpression expr : p.getRequires()) {
recomputLocalSSA(expr, new HashMap<CfgVariable, Integer>());
}
while (!todo.isEmpty()) {
BasicBlock current = todo.removeLast();
/*
* Check if SSA has already been computed for all predecessors. If
* not, add current to the bottom of the todo stack and continue
* with the next Block.
*/
{
boolean allPredsDone = true;
if (current.getPredecessors()!=null) {
for (BasicBlock pred : current.getPredecessors()) {
if (!done.contains(pred)) {
allPredsDone = false;
break;
}
}
}
if (allPredsDone) {
done.add(current);
} else {
todo.addFirst(current);
continue;
}
}
// now compute the frame conditions for all predecessors
{
HashMap<CfgVariable, Integer> offset = new HashMap<CfgVariable, Integer>();
if (current.getPredecessors().size() > 0) {
for (BasicBlock pred : current.getPredecessors()) {
mergeSSAOffsets(offset, pred.getLocalIncarnationMap());
}
for (BasicBlock pred : new HashSet<BasicBlock>(
current.getPredecessors())) {
addFrameCondition(pred, offset);
}
}
recomputLocalSSA(current, offset);
}
for (BasicBlock next : current.getSuccessors()) {
if (!todo.contains(next) && !done.contains(next)) {
todo.addLast(next);
}
}
}
}
private void mergeSSAOffsets(HashMap<CfgVariable, Integer> successorOffset,
HashMap<CfgVariable, Integer> predecessorOffset) {
for (Entry<CfgVariable, Integer> entry : predecessorOffset.entrySet()) {
if (!successorOffset.containsKey(entry.getKey())) {
successorOffset.put(entry.getKey(), entry.getValue());
} else {
if (successorOffset.get(entry.getKey()) < entry.getValue()) {
successorOffset.put(entry.getKey(), entry.getValue());
}
}
}
}
private void addFrameCondition(BasicBlock b,
HashMap<CfgVariable, Integer> maxOffset) {
HashMap<CfgVariable, Integer> offset = b.getLocalIncarnationMap();
for (Entry<CfgVariable, Integer> entry : maxOffset.entrySet()) {
if (!offset.containsKey(entry.getKey())) {
} else {
if (offset.get(entry.getKey()) < entry.getValue()) {
CfgIdentifierExpression[] left = { new CfgIdentifierExpression(
b.getLocationTag(), entry.getKey(),
entry.getValue()) };
CfgIdentifierExpression[] right = { new CfgIdentifierExpression(
b.getLocationTag(), entry.getKey(),
offset.get(entry.getKey())) };
CfgAssignStatement assign = new CfgAssignStatement(
b.getLocationTag(), left, right);
b.addStatement(assign);
b.getLocalIncarnationMap().put(entry.getKey(), entry.getValue());//TODO: experiment
}
}
}
}
public void recomputLocalSSA(BasicBlock b, HashMap<CfgVariable, Integer> offset) {
b.localIncarnationMap = new HashMap<CfgVariable, Integer>(offset);
for (CfgStatement stmt : b.getStatements()) {
if (stmt instanceof CfgAssertStatement) {
CfgAssertStatement asrt = (CfgAssertStatement) stmt;
recomputLocalSSA(asrt.getCondition(), b.localIncarnationMap);
} else if (stmt instanceof CfgAssumeStatement) {
CfgAssumeStatement asum = (CfgAssumeStatement) stmt;
recomputLocalSSA(asum.getCondition(), b.localIncarnationMap);
} else if (stmt instanceof CfgAssignStatement) {
CfgAssignStatement asgn = (CfgAssignStatement) stmt;
recomputLocalSSA(asgn.getRight(), b.localIncarnationMap);
for (CfgIdentifierExpression id : asgn.getLeft()) {
if (!b.localIncarnationMap.containsKey(id.getVariable())) {
b.localIncarnationMap.put(id.getVariable(), 0);
}
b.localIncarnationMap.put(id.getVariable(),
b.localIncarnationMap.get(id.getVariable()) + 1);
id.setCurrentIncarnation(b.localIncarnationMap.get(id.getVariable()));
}
} else if (stmt instanceof CfgCallStatement) {
CfgCallStatement call = (CfgCallStatement) stmt;
recomputLocalSSA(call.getArguments(), b.localIncarnationMap);
for (CfgVariable v : call.getCallee().getModifies()) {
if (!b.localIncarnationMap.containsKey(v)) {
b.localIncarnationMap.put(v, 0);
}
b.localIncarnationMap.put(v, b.localIncarnationMap.get(v) + 1);
}
for (CfgIdentifierExpression id : call.getLeftHandSide()) {
if (!b.localIncarnationMap.containsKey(id.getVariable())) {
b.localIncarnationMap.put(id.getVariable(), 0);
}
b.localIncarnationMap.put(id.getVariable(),
b.localIncarnationMap.get(id.getVariable()) + 1);
id.setCurrentIncarnation(b.localIncarnationMap.get(id.getVariable()));
}
} else if (stmt instanceof CfgHavocStatement) {
for (CfgVariable v : ((CfgHavocStatement) stmt).getVariables()) {
if (!b.localIncarnationMap.containsKey(v)) {
b.localIncarnationMap.put(v, 0);
}
b.localIncarnationMap.put(v, b.localIncarnationMap.get(v) + 1);
}
}
}
}
private void recomputLocalSSA(CfgExpression[] exp,
HashMap<CfgVariable, Integer> offset) {
if (exp == null) {
return;
}
for (int i = 0; i < exp.length; i++) {
recomputLocalSSA(exp[i], offset);
}
}
private void recomputLocalSSA(CfgExpression exp,
HashMap<CfgVariable, Integer> offset) {
if (exp instanceof CfgArrayAccessExpression) {
CfgArrayAccessExpression aae = (CfgArrayAccessExpression) exp;
recomputLocalSSA(aae.getIndices(), offset);
recomputLocalSSA(aae.getBaseExpression(), offset);
} else if (exp instanceof CfgArrayStoreExpression) {
CfgArrayStoreExpression ase = (CfgArrayStoreExpression) exp;
recomputLocalSSA(ase.getValueExpression(), offset);
recomputLocalSSA(ase.getIndices(), offset);
recomputLocalSSA(ase.getBaseExpression(), offset);
} else if (exp instanceof CfgBinaryExpression) {
CfgBinaryExpression bexp = (CfgBinaryExpression) exp;
recomputLocalSSA(bexp.getLeftOp(), offset);
recomputLocalSSA(bexp.getRightOp(), offset);
} else if (exp instanceof CfgBitVectorAccessExpression) {
CfgBitVectorAccessExpression bva = (CfgBitVectorAccessExpression) exp;
recomputLocalSSA(bva.getBitvector(), offset);
} else if (exp instanceof CfgFunctionApplication) {
CfgFunctionApplication fa = (CfgFunctionApplication) exp;
recomputLocalSSA(fa.getArguments(), offset);
} else if (exp instanceof CfgIdentifierExpression) {
CfgIdentifierExpression id = (CfgIdentifierExpression) exp;
if (!offset.containsKey(id.getVariable())) {
offset.put(id.getVariable(), Integer.valueOf(0));
}
id.setCurrentIncarnation(offset.get(id.getVariable()));
} else if (exp instanceof CfgIfThenElseExpression) {
CfgIfThenElseExpression ite = (CfgIfThenElseExpression) exp;
recomputLocalSSA(ite.getCondition(), offset);
recomputLocalSSA(ite.getThenExpression(), offset);
recomputLocalSSA(ite.getElseExpression(), offset);
} else if (exp instanceof CfgQuantifierExpression) {
CfgQuantifierExpression qe = (CfgQuantifierExpression) exp;
//TODO: does that make sense?
for (CfgVariable v : qe.getParameters()) {
offset.put(v, 0);
}
recomputLocalSSA(((CfgQuantifierExpression) exp).getSubformula(), offset);
} else if (exp instanceof CfgUnaryExpression) {
CfgUnaryExpression uexp = (CfgUnaryExpression) exp;
recomputLocalSSA(uexp.getExpression(), offset);
}
}
}