package soottocfg.cfg.optimization.dataflow;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import soottocfg.cfg.LiveVars;
import soottocfg.cfg.expression.Expression;
import soottocfg.cfg.expression.literal.BooleanLiteral;
import soottocfg.cfg.method.CfgBlock;
import soottocfg.cfg.method.CfgEdge;
import soottocfg.cfg.method.Method;
import soottocfg.cfg.optimization.ExpressionEvaluator;
import soottocfg.cfg.optimization.UnreachableNodeRemover;
import soottocfg.cfg.statement.AssignStatement;
import soottocfg.cfg.statement.PullStatement;
import soottocfg.cfg.statement.Statement;
import soottocfg.util.SetOperations;
public class DeadCodeElimination {
// Given a method, eliminate the dead code in it
public DeadCodeElimination() {
}
public static boolean eliminateDeadCode(Method m) {
LiveVars<CfgBlock> blockLiveVars = m.computeBlockLiveVariables();
boolean changed = false;
for (CfgBlock block : m.vertexSet()) {
changed = eliminateDeadStatements(m, block, blockLiveVars) ? true : changed;
//TODO: this currently doesn't affect the value of changed
//because its not finished yet.
eliminateDeadConditions(m, block);
}
UnreachableNodeRemover.pruneUnreachableNodes(m, m.getSource());
return changed;
}
protected static boolean isDead(Statement stmt, LiveVars<Statement> liveVars) {
if (!(stmt instanceof AssignStatement) && !(stmt instanceof PullStatement)) {
// only assignments and pulls can be dead.
return false;
}
// If a statement writes to only variables that are not live, we can
// remove it!
// I.e. if intersection s.lvals, s.live is empty
return SetOperations.intersect(stmt.getDefVariables(), liveVars.liveOut.get(stmt)).isEmpty();
}
protected static boolean eliminateDeadStatements(Method method, CfgBlock block, LiveVars<CfgBlock> blockLiveVars) {
boolean changed = false;
List<Statement> rval = new LinkedList<Statement>();
LiveVars<Statement> stmtLiveVars = block.computeLiveVariables(blockLiveVars);
for (Statement s : block.getStatements()) {
if (isDead(s, stmtLiveVars)) {
// If the statements is dead, just remove it from the list
changed = true;
} else {
// otherwise, it stays in the list
rval.add(s.deepCopy());
}
}
if (changed) {
//only replace statements if something changed.
block.setStatements(rval);
}
return changed;
}
/**
* Simplify the expressions on the edge labels. If an expression simplifies
* to True, we can remove the lable. If it simplifies to false, we can remove
* the edge.
* @param method
* @param block
* @return
*/
protected static boolean eliminateDeadConditions(Method method, CfgBlock block) {
boolean changed = false;
Set<CfgEdge> toRemove = new HashSet<CfgEdge>();
for (CfgEdge edge : method.outgoingEdgesOf(block)) {
if (edge.getLabel().isPresent()) {
Expression simpleLabel = ExpressionEvaluator.simplify(edge.getLabel().get());
if (simpleLabel instanceof BooleanLiteral) {
if (((BooleanLiteral)simpleLabel).getValue()) {
//then we can remove the label.
edge.removeLabel();
} else {
//then we can remove the edge.
toRemove.add(edge);
}
} else {
//TODO : do not set the label for now because it causes
//princess to fail during type checking.
// edge.setLabel(simpleLabel);
}
//
// Optional<Object> res = ExpressionEvaluator.eval(edge.getLabel().get());
// if (res.isPresent()) {
// if (!(Boolean)res.get()) {
// // condition false, remove edge
// toRemove.add(edge);
// } else {
// // condition true, remove all other edges?
// }
// } else {
// // TODO?
// }
}
}
if (!toRemove.isEmpty()) {
method.removeAllEdges(toRemove);
changed = true;
}
return changed;
}
}