/*
* @(#)SSA.java
*/
package org.jf.dexlib.Code.Analysis.ssa;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jf.dexlib.Code.Analysis.ssa.dom.Dominators;
import org.jf.dexlib.Code.Analysis.ssa.graphs.AbstractNode;
import org.jf.dexlib.Code.Analysis.ssa.graphs.CFG;
import org.jf.dexlib.Code.Analysis.ssa.graphs.InstructionNode;
import org.jf.dexlib.Code.Analysis.ssa.graphs.Node;
import org.jgrapht.DirectedGraph;
/**
*
* @param <V>
* @param <E>
* @author Patrick Kuhn
*/
public final class SSA<V, E> {
protected final CFG cfg;
protected final Dominators<V, E> dom;
protected final Map<V, Set<V>> domFrontier;
protected final SSAGraph<V> cdgTree;
protected final SSABlockTree<V> cdgBlockTree;
// place part
protected Set<Integer> variables = new HashSet<Integer>();
// rename part
private final Map<Integer, Integer> count;
private final Map<Integer, LinkedList<Integer>> stack;
private Set<SSABlockNode<V>> visited;
@SuppressWarnings("unchecked")
public static <Y, Z> SSA<Y, Z> compute(DirectedGraph<Y, Z> graph, Dominators<Y, Z> dom) {
if (graph == null || dom == null) {
throw new IllegalArgumentException();
}
SSA<Y, Z> cdg = new SSA<Y, Z>(graph, dom);
cdg.place();
cdg.rename();
return cdg;
}
@SuppressWarnings("unchecked")
protected SSA(DirectedGraph<V, E> graph, Dominators<V, E> dom) {
if (!(graph instanceof CFG)) {
throw new IllegalArgumentException();
}
this.cfg = (CFG) graph;
this.dom = dom;
this.domFrontier = dom.getDF();
this.cdgTree = new SSAGraph<V>();
for (V v : graph.vertexSet()) {
cdgTree.addVertex(v);
}
for (E e : graph.edgeSet()) {
cdgTree.addEdge(graph.getEdgeSource(e), graph.getEdgeTarget(e));
}
this.cdgBlockTree = new SSABlockTree<V>(this.cdgTree, dom.getStart());
// gather variables
discoverVariables();
//this.count = new int[variables.size()];
this.count = new HashMap<Integer, Integer>(variables.size());
this.stack = new HashMap<Integer, LinkedList<Integer>>(variables.size());
// for each variable a
for (int i : variables) {
//count[i] = 0;
count.put(i, 0);
stack.put(i, new LinkedList<Integer>());
stack.get(i).add(0); // push 0 onto Stack[a]
}
}
/**
* Get the SSA CFG.
* @return SSA
*/
public SSAGraph<V> getSSAGraph() {
return cdgTree;
}
/**
* Get the SSA CFG with blocks instead of single instructions.
* If the algorithm for placing and renaming was not yet started etc. an
* <tt>IllegalStateException</tt> is thrown.
* @return SSA w/ blocks
*/
public SSABlockTree<V> getSSAGraphBlocks() {
return cdgBlockTree;
}
/**
* Find all variables in the CFG and return as Set
* @return Set of variables
*/
private void discoverVariables() {
variables = new HashSet<Integer>();
for (Node n : cfg.vertexSet()) {
if (n.isInstruction()) {
String ss = ((InstructionNode) n).getDestinationRegister();
if (ss != null) {
try {
variables.add(Integer.parseInt(ss));
} catch (NumberFormatException ex) {
throw new AssertionError(ex);
}
}
String[] parameters = ((InstructionNode) n).getSourceRegisters();
if (parameters != null) {
for (String s : parameters) {
variables.add(Integer.parseInt(s));
}
}
}
}
}
private void place() {
final Map<Integer, Set<SSABlockNode<V>>> defsites = new HashMap<Integer, Set<SSABlockNode<V>>>();
// variables defined in n
final Map<SSABlockNode<V>, Set<Integer>> a_orig = new HashMap<SSABlockNode<V>, Set<Integer>>();
for (SSABlockNode<V> block : cdgBlockTree.vertexSet()) {
Set<Integer> s = new HashSet<Integer>();
for (V v : block) {
if (v instanceof InstructionNode) {
final InstructionNode in = (InstructionNode) v;
String destReg = in.getDestinationRegister();
if (destReg != null) {
s.add(Integer.parseInt(destReg));
}
}
}
a_orig.put(block, s);
}
final Map<SSABlockNode<V>, Set<Integer>> a_phi = new HashMap<SSABlockNode<V>, Set<Integer>>();
// for each block node n
for (SSABlockNode<V> n : cdgBlockTree.vertexSet()) {
// for each variable a in Aorig[n]
for (Integer a : a_orig.get(n)) {
Set<SSABlockNode<V>> defA = defsites.get(a);
if (defA == null) {
defA = new HashSet<SSABlockNode<V>>();
defsites.put(a, defA);
}
defA.add(n);
}
}
for (int a : variables) {
LinkedList<SSABlockNode<V>> w = new LinkedList<SSABlockNode<V>>();
if (defsites.get(a) != null) {
w.addAll(defsites.get(a));
}
while (!w.isEmpty()) {
// remove some node n from W
SSABlockNode<V> n = w.pop();
// for each y in DF[n]
Set<V> dfs = new HashSet<V>();
for (V v : n) {
if (domFrontier.get(v) != null) {
dfs.addAll(domFrontier.get(v));
}
}
for (V v : dfs) {
// avoid exit node
if (!((Node) v).isExit()) {
SSABlockNode<V> y = cdgBlockTree.getBlockContainingNode(v);
// if a not in A_phi[y]
if (a_phi.get(y) == null || !a_phi.get(y).contains(a)) {
int numberOfParas = cdgBlockTree.inDegreeOf(y);
PHI phi = new PHI(a, numberOfParas);
@SuppressWarnings("unchecked")
V p = (V) new PHINode(phi);
insertFirstInBlock(y, p);
if (a_phi.get(y) == null) {
a_phi.put(y, new HashSet<Integer>());
}
a_phi.get(y).add(a);
if (!a_orig.get(y).contains(a)) {
w.add(y);
}
}
}
}
}
}
}
private void insertFirstInBlock(final SSABlockNode<V> block, final V node) {
// insert in cdg and cdgblocktree to avoid rebuilding
// SSABlockTree
block.addFirst(node);
// SSAGraph
V first = block.getFirstNotPhi();
Set<SSAEdge> inEdges = cdgTree.incomingEdgesOf(first);
Set<V> predecessors = new HashSet<V>();
for (SSAEdge e : inEdges) {
predecessors.add(cdgTree.getEdgeSource(e));
}
cdgTree.addVertex(node);
cdgTree.addEdge(node, first);
for (V v : predecessors) {
cdgTree.addEdge(v, node);
cdgTree.removeEdge(v, first);
}
}
private void rename() {
visited = new HashSet<SSABlockNode<V>>();
// EXIT node must be removed.
V end = null;
SSABlockNode<V> endBlock = null;
for (V n : cdgTree.vertexSet()) {
if (((Node) n).isExit()) {
end = n;
endBlock = SSABlockNode.getInstance(end);
break;
}
}
Set<V> pre = new HashSet<V>();
Set<SSABlockNode<V>> preBlock = new HashSet<SSABlockNode<V>>();
if (end != null) {
for (SSAEdge e : cdgTree.incomingEdgesOf(end)) {
pre.add(cdgTree.getEdgeSource(e));
}
for (SSAEdge e : cdgBlockTree.incomingEdgesOf(endBlock)) {
preBlock.add(cdgBlockTree.getEdgeSource(e));
}
cdgTree.removeVertex(end);
cdgBlockTree.removeVertex(endBlock);
}
// rename
rename(cdgBlockTree.getBlockContainingNode(dom.getStart()));
// add EXIT node again
cdgTree.addVertex(end);
cdgBlockTree.addVertex(endBlock);
for (V v : pre) {
cdgTree.addEdge(v, end);
}
for (SSABlockNode<V> v : preBlock) {
cdgBlockTree.addEdge(v, endBlock);
}
}
private void rename(SSABlockNode<V> n) {
for (V s : n) {
Node sNode = (Node) s;
// if S is not a phi function
if (sNode.isInstruction()) {
InstructionNode ins = (InstructionNode) s;
// for each use of some variable x in S
if (ins.getSourceRegisters() != null) {
for (String xStr : ins.getSourceRegisters()) {
// XXX: check
try {
int x = Integer.parseInt(xStr);
int i = stack.get(x).peek();
ins.changeVariableName(x, x + "_" + i);
} catch (NumberFormatException ex) {
// TODO
}
}
}
}
if (sNode.isInstruction() || sNode.isPHI()) {
// for each definition of some variable a in S
String aStr = null;
AbstractNode ins = null;
if (sNode.isInstruction()) {
ins = (InstructionNode) s;
aStr = ((InstructionNode) s).getDestinationRegister();
} else if (sNode.isPHI()) {
ins = (PHINode) s;
aStr = ((PHINode) s).getPHI().getDestination();
}
if (aStr != null && ins != null) {
try {
int a = Integer.parseInt(aStr);
//++count[a];
count.put(a, count.get(a) + 1);
//int i = count[a];
int i = count.get(a);
stack.get(a).push(i);
ins.changeDefinitionName(a + "_" + i);
} catch (NumberFormatException ex) {
// TODO
}
}
}
}
// for each successor Y of block n
for (SSAEdge e : cdgBlockTree.outgoingEdgesOf(n)) {
final SSABlockNode<V> succY = cdgBlockTree.getEdgeTarget(e);
// find j
Set<SSAEdge> edges = cdgBlockTree.incomingEdgesOf(succY);
int j = 0;
for (SSAEdge edge : edges) {
SSABlockNode<V> source = cdgBlockTree.getEdgeSource(edge);
if (source.equals(n)) {
break;
}
++j;
}
for (V v : succY) {
if (((Node) v).isPHI()) {
PHINode phi = (PHINode) v;
String s = phi.getOperands()[j];
try {
Integer i = stack.get(Integer.parseInt(s)).peek();
if (i != null) {
phi.changeVariableName(j, phi.getOperands()[j] + "_" + i);
}
} catch (NumberFormatException ex) {
Logger.getLogger(SSA.class.toString()).log(Level.FINE, null, ex);
}
}
}
}
// for each child X of n
// TODO: should be faster with dominators
// XXX: child of n are nodes whose idom is n
for (SSAEdge e : cdgBlockTree.outgoingEdgesOf(n)) {
final SSABlockNode<V> childX = cdgBlockTree.getEdgeTarget(e);
visited.add(n);
if (!visited.contains(childX)) {
rename(childX);
}
}
// for each statement S in block n
for (V s : n) {
Node sNode = (Node) s;
if (sNode.isInstruction()) {
InstructionNode ins = (InstructionNode) s;
String dest = ins.getDestinationRegister();
if (dest != null) {
dest = dest.substring(0, dest.indexOf('_'));
try {
int a = Integer.parseInt(dest);
if (!stack.get(a).isEmpty()) {
stack.get(a).pop();
}
} catch (NumberFormatException ex) {
Logger.getLogger(SSA.class.toString()).log(Level.FINE, null, ex);
System.err.println(ex);
}
}
}
}
}
/**
* Remove duplicates of registers in PHINodes.
*/
@Deprecated
private void cleanUp() {
for (V v : cdgTree.vertexSet()) {
if (((Node) v).isPHI()) {
((PHINode) v).cleanup();
}
}
}
}