package soottocfg.cfg.optimization;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import soottocfg.cfg.expression.Expression;
import soottocfg.cfg.expression.IdentifierExpression;
import soottocfg.cfg.method.CfgBlock;
import soottocfg.cfg.method.Method;
import soottocfg.cfg.statement.AssignStatement;
import soottocfg.cfg.statement.Statement;
import soottocfg.cfg.variable.Variable;
public class ExpressionInliner extends CfgUpdater {
public ExpressionInliner() {
}
private Variable toReplace;
private Expression replaceWith;
protected Variable getLhsVariable(AssignStatement s) {
Expression lhs = s.getLeft();
// DSN - I Assume that it can only be a variable.
assert (lhs instanceof IdentifierExpression);
return ((IdentifierExpression) lhs).getVariable();
}
// Given an assignment statement, replace the readers with the rhs of the
// statement
// TODO make this boolean
public void inlineExpression(AssignStatement s, Method m) {
toReplace = getLhsVariable(s);
replaceWith = s.getRight();
Set<Statement> definingStmts = computeDefiningStatements(m).get(toReplace);
if (definingStmts.size() != 1) {
throw new RuntimeException(
"Cannot inline: Variable is defined in more than one place: " + toReplace + "\n" + definingStmts);
}
// We know that this is the only defn for the variable, so its clearly
// safe to override.
for (CfgBlock b : computeBlocksContainingVariable(m).get(toReplace)) {
processCfgBlock(b);
}
}
@Override
public boolean updateMethod(Method m) {
return inlineAllCandidates(m);
}
public boolean inlineAllCandidates(Method m) {
changed = false;
for (AssignStatement s : candidatesForInlining(m)) {
inlineExpression(s, m);
}
return changed;
}
public boolean inlineIfUsesLessThan(Method m, int maxUses) {
changed = false;
Map<Variable, Set<Statement>> usingStmts = computeStatementsContainingVariable(m);
for (AssignStatement s : candidatesForInlining(m)) {
Variable v = getLhsVariable(s);
if (usingStmts.get(v).size() < maxUses) {
inlineExpression(s, m);
}
}
return changed;
}
public Set<AssignStatement> candidatesForInlining(Method m) {
Set<AssignStatement> rval = new HashSet<AssignStatement>();
for (Entry<Variable, Set<Statement>> entry : computeDefiningStatements(m).entrySet()) {
if (entry.getValue().size() == 1) {
Statement s = entry.getValue().iterator().next();
if (s instanceof AssignStatement) {
rval.add((AssignStatement) s);
}
}
}
return rval;
}
@Override
protected Statement processStatement(AssignStatement s) {
// We don't process the lhs of the assign statement, because otherwise
// we'd get
// b:= e
// turns into
// e := e
Expression lhs = s.getLeft();
Expression rhs = processExpression(s.getRight());
return new AssignStatement(s.getSourceLocation(), lhs, rhs);
}
@Override
protected Expression processExpression(IdentifierExpression e) {
if (e.getVariable().equals(toReplace)) {
changed = true;
return replaceWith;
} else {
return e;
}
}
// Really simple for now: Just get all blocks that define each variable.
// Don't worry too much about
// dominators, etc
// TODO worry about dominators etc
protected Map<Variable, Set<CfgBlock>> computeDefiningBlocks(Method m) {
Map<Variable, Set<CfgBlock>> rval = new HashMap<Variable, Set<CfgBlock>>();
for (CfgBlock b : m.vertexSet()) {
for (Variable v : b.getDefVariables()) {
Set<CfgBlock> definingBlocks = rval.get(v);
if (definingBlocks == null) {
definingBlocks = new HashSet<CfgBlock>();
rval.put(v, definingBlocks);
}
definingBlocks.add(b);
}
}
return rval;
}
protected Map<Variable, Set<Statement>> computeDefiningStatements(Method m) {
Map<Variable, Set<Statement>> rval = new HashMap<Variable, Set<Statement>>();
for (Map.Entry<Variable, Set<CfgBlock>> entry : computeDefiningBlocks(m).entrySet()) {
Variable v = entry.getKey();
Set<Statement> set = new HashSet<Statement>();
for (CfgBlock b : entry.getValue()) {
for (Statement s : b.getStatements()) {
if (s.getDefVariables().contains(v)) {
set.add(s);
}
}
}
rval.put(v, set);
}
return rval;
}
protected Map<Variable, Set<Statement>> computeUsingStatements(Method m) {
Map<Variable, Set<Statement>> rval = new HashMap<Variable, Set<Statement>>();
for (Map.Entry<Variable, Set<CfgBlock>> entry : computeUsingBlocks(m).entrySet()) {
Variable v = entry.getKey();
Set<Statement> set = new HashSet<Statement>();
for (CfgBlock b : entry.getValue()) {
for (Statement s : b.getStatements()) {
if (s.getUseVariables().contains(v)) {
set.add(s);
}
}
}
rval.put(v, set);
}
return rval;
}
// Really simple for now: Just get all blocks that define each variable.
// Don't worry too much about
// dominators, etc
// TODO worry about dominators etc
protected Map<Variable, Set<CfgBlock>> computeUsingBlocks(Method m) {
Map<Variable, Set<CfgBlock>> rval = new HashMap<Variable, Set<CfgBlock>>();
for (CfgBlock b : m.vertexSet()) {
for (Variable v : b.getUseVariables()) {
Set<CfgBlock> usingBlocks = rval.get(v);
if (usingBlocks == null) {
usingBlocks = new HashSet<CfgBlock>();
rval.put(v, usingBlocks);
}
usingBlocks.add(b);
}
}
return rval;
}
/**
* compute the set of statements that either use or def a variable
* @param m
* @return
*/
protected Map<Variable, Set<Statement>> computeStatementsContainingVariable(Method m) {
Map<Variable, Set<Statement>> rval = new HashMap<Variable, Set<Statement>>();
rval.putAll(computeDefiningStatements(m));
for (Entry<Variable, Set<Statement>> entry : computeUsingStatements(m).entrySet()) {
if (!rval.containsKey(entry.getKey())) {
rval.put(entry.getKey(), entry.getValue());
} else {
rval.get(entry.getKey()).addAll(entry.getValue());
}
}
return rval;
}
/**
* compute the set of blocks that either use or def a variable
* @param m
* @return
*/
protected Map<Variable, Set<CfgBlock>> computeBlocksContainingVariable(Method m) {
Map<Variable, Set<CfgBlock>> rval = new HashMap<Variable, Set<CfgBlock>>();
rval.putAll(computeDefiningBlocks(m));
for (Entry<Variable, Set<CfgBlock>> entry : computeUsingBlocks(m).entrySet()) {
if (!rval.containsKey(entry.getKey())) {
rval.put(entry.getKey(), entry.getValue());
} else {
rval.get(entry.getKey()).addAll(entry.getValue());
}
}
return rval;
}
}