/*
* $Id$
*
* Copyright (C) 2003-2015 JNode.org
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; If not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jnode.vm.compiler.ir;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.jnode.util.ObjectArrayIterator;
import org.jnode.vm.bytecode.BytecodeParser;
import org.jnode.vm.classmgr.VmByteCode;
import org.jnode.vm.classmgr.VmInterpretedExceptionHandler;
import org.jnode.vm.compiler.ir.quad.AssignQuad;
import org.jnode.vm.compiler.ir.quad.CallAssignQuad;
import org.jnode.vm.compiler.ir.quad.NewAssignQuad;
import org.jnode.vm.compiler.ir.quad.NewMultiArrayAssignQuad;
import org.jnode.vm.compiler.ir.quad.NewObjectArrayAssignQuad;
import org.jnode.vm.compiler.ir.quad.NewPrimitiveArrayAssignQuad;
import org.jnode.vm.compiler.ir.quad.PhiAssignQuad;
import org.jnode.vm.compiler.ir.quad.Quad;
import org.jnode.vm.compiler.ir.quad.VariableRefAssignQuad;
import org.jnode.vm.objects.BootableArrayList;
/**
* @author Madhu Siddalingaiah
*/
//TODO simpify to use existing CFG from l1
public class IRControlFlowGraph<T> implements Iterable<IRBasicBlock<T>> {
private SSAStack<T>[] renumberArray;
private final IRBasicBlock<T>[] bblocks;
private List<IRBasicBlock<T>> postOrderList;
private IRBasicBlock<T> startBlock;
/**
* Create a new instance
*
* @param bytecode
*/
public IRControlFlowGraph(VmByteCode bytecode) {
// First determine the basic blocks
final IRBasicBlockFinder<T> bbf = new IRBasicBlockFinder<T>();
BytecodeParser.parse(bytecode, bbf);
this.bblocks = bbf.createBasicBlocks();
startBlock = bblocks[0];
computeDominance(bytecode);
}
//todo use set
public List<Variable<?>> computeLiveVariables() {
List<Variable<?>> liveVariables = new BootableArrayList<Variable<?>>();
for (IRBasicBlock<T> b : this) {
// System.out.println();
// System.out.println(b + ", stackOffset = " + b.getStackOffset());
for (Quad<T> q : b.getQuads()) {
if (!q.isDeadCode()) {
q.computeLiveness(liveVariables);
// System.out.println(q);
}
}
}
return liveVariables;
}
public void removeUnusedVars() {
Map<Variable, Integer> varUses = getVariableUsage();
boolean loop;
do {
loop = false;
for (Map.Entry<Variable, Integer> u : varUses.entrySet()) {
if (u.getValue() > 0 ||
u.getKey() instanceof MethodArgument ||
u.getKey().getAssignQuad().isDeadCode()) {
continue;
}
AssignQuad dq = u.getKey().getAssignQuad();
if (dq instanceof CallAssignQuad ||
dq instanceof NewAssignQuad ||
dq instanceof NewObjectArrayAssignQuad ||
dq instanceof NewPrimitiveArrayAssignQuad ||
dq instanceof NewMultiArrayAssignQuad) {
//todo optimize it, could be transformed to CallQuad
continue;
}
dq.setDeadCode(true);
Operand<T>[] refs = dq.getReferencedOps();
if (refs != null) {
for (Operand<T> ref : refs) {
if (ref instanceof Variable) {
Variable<T> r = (Variable<T>) ref;
Integer c = varUses.get(r);
if (c > 0) {
c--;
}
varUses.put(r, c);
}
}
}
loop = true;
break;
}
} while (loop);
}
public void removeDefUseChains() {
Map<Variable, Integer> varUses = getVariableUsage();
for (Map.Entry<Variable, Integer> u : varUses.entrySet()) {
Variable var = u.getKey();
if (u.getValue() == 1 && !(var instanceof MethodArgument) && !var.getAssignQuad().isDeadCode()) {
for (IRBasicBlock<T> b : this) {
for (Quad<T> q : b.getQuads()) {
if (!q.isDeadCode()) {
if (q instanceof VariableRefAssignQuad) {
VariableRefAssignQuad vq = (VariableRefAssignQuad) q;
if (vq.getRHS().equals(var) &&
vq.getBasicBlock().equals(var.getAssignQuad().getBasicBlock())) {
vq.setDeadCode(true);
var.getAssignQuad().setLHS(vq.getLHS());
}
}
}
}
}
}
}
}
private Map<Variable, Integer> getVariableUsage() {
Map<Variable, Integer> varUses = new HashMap<Variable, Integer>();
for (IRBasicBlock<T> b : this) {
for (Quad<T> q : b.getQuads()) {
if (!q.isDeadCode()) {
if (q instanceof AssignQuad) {
AssignQuad aq = (AssignQuad) q;
Variable v = aq.getLHS();
if (!varUses.containsKey(v)) {
varUses.put(v, 0);
}
}
Operand<T>[] refs = q.getReferencedOps();
if (refs != null) {
for (Operand<T> ref : refs) {
if (ref instanceof Variable) {
Variable<T> v = (Variable<T>) ref;
Integer c = varUses.get(v);
if (c == null) {
c = 0;
}
c++;
varUses.put(v, c);
}
}
}
}
}
}
return varUses;
}
/**
* Create an iterator to iterate over all basic blocks.
*
* @return An iterator that will return instances of IRBasicBlock.
*/
public Iterator<IRBasicBlock<T>> iterator() {
return new ObjectArrayIterator<IRBasicBlock<T>>(bblocks);
}
/**
* Gets the number of basic blocks in this graph
*
* @return count of basic blocks
*/
public int getBasicBlockCount() {
return bblocks.length;
}
/**
* Gets the basic block that contains the given address.
*
* @param pc
* @return the basic block or {@code null}.
*/
public IRBasicBlock getBasicBlock(int pc) {
final int max = bblocks.length;
for (int i = 0; i < max; i++) {
final IRBasicBlock bb = bblocks[i];
if (bb.contains(pc)) {
return bb;
}
}
return null;
}
public void computeDominance(VmByteCode bytecode) {
postOrderList = new BootableArrayList<IRBasicBlock<T>>();
startBlock.computePostOrder(postOrderList);
doComputeDominance(bytecode);
computeDominanceFrontier();
computeDominatedBlocks();
}
/*
for all nodes, b // initialize the dominators array
doms[b] = Undefined
doms[start_node] = start_node
Changed = true
while (Changed)
Changed = false
for all nodes, b, in reverse postorder (except start node)
new_idom = first (processed) predecessor of b // (pick one)
for all other predecessors, p, of b
if doms[p] != Undefined // i.e., if doms[p] already calculated
new_idom = intersect(p, new_idom)
if doms[b] != new_idom
doms[b] = new_idom
Changed = true
*/
private void doComputeDominance(VmByteCode bytecode) {
// This is critical, must be done in reverse postorder
startBlock.setIDominator(startBlock);
boolean changed = true;
while (changed) {
changed = false;
int i = postOrderList.size() - 1; // skip startBlock
while (i >= 0) {
IRBasicBlock<T> b = postOrderList.get(i--);
if (b == startBlock) {
continue;
}
Iterator<IRBasicBlock<T>> ip = b.getPredecessors().iterator();
if (!ip.hasNext()) {
throw new AssertionError(b + " has no predecessors!");
}
IRBasicBlock<T> newIdom = ip.next();
while (newIdom.getIDominator() == null && ip.hasNext()) {
newIdom = ip.next();
}
if (newIdom.getIDominator() == null) {
throw new AssertionError(newIdom + " has no dominator!");
}
while (ip.hasNext()) {
IRBasicBlock<T> p = ip.next();
if (p.getIDominator() != null) {
newIdom = intersect(p, newIdom);
}
}
if (b.getIDominator() != newIdom) {
b.setIDominator(newIdom);
changed = true;
}
}
}
startBlock.setIDominator(null);
for (VmInterpretedExceptionHandler eh : bytecode.getExceptionHandlers()) {
IRBasicBlock block = getBasicBlock(eh.getHandlerPC());
if (block != null && block.getIDominator() == null) {
IRBasicBlock pBlock = getBasicBlock(eh.getStartPC());
if (pBlock != null) {
block.setIDominator(pBlock.getIDominator());
}
}
}
}
/**
* @param b1
* @param b2
* @return
*/
/*
function intersect(b1, b2) returns node
finger1 = b1
finger2 = b2
while (finger1 != finger2)
while (finger1 < finger2)
finger1 = doms[finger1]
while (finger2 < finger1)
finger2 = doms[finger2]
return finger1
*/
private IRBasicBlock<T> intersect(IRBasicBlock<T> b1, IRBasicBlock<T> b2) {
while (b1 != b2) {
while (b1.getPostOrderNumber() < b2.getPostOrderNumber()) {
b1 = b1.getIDominator();
}
while (b2.getPostOrderNumber() < b1.getPostOrderNumber()) {
b2 = b2.getIDominator();
}
}
return b1;
}
/*
for all nodes, b
if the number of predecessors of b >= 2
for all predecessors, p, of b
runner = p
while runner != doms[b]
add b to runner\u2019s dominance frontier set
runner = doms[runner]
*/
private void computeDominanceFrontier() {
for (IRBasicBlock<T> b : postOrderList) {
List<IRBasicBlock<T>> predList = b.getPredecessors();
if (predList.size() >= 2) {
for (IRBasicBlock<T> runner : predList) {
while (runner != b.getIDominator()) {
runner.addDominanceFrontier(b);
runner = runner.getIDominator();
}
}
}
}
}
/**
*
*/
private void computeDominatedBlocks() {
for (IRBasicBlock<T> b : postOrderList) {
IRBasicBlock<T> idom = b.getIDominator();
if (idom != null) {
idom.addDominatedBlock(b);
idom = idom.getIDominator();
}
}
}
/**
*
*/
public void constructSSA() {
Variable<T>[] vars = startBlock.getVariables();
int nvars = vars.length;
renumberArray = new SSAStack[nvars];
// Push method arguments on the stack since they are not assigned
for (int i = 0; i < nvars; i += 1) {
Variable<T> vi = vars[i];
SSAStack<T> st = getStack(vi);
if (vi instanceof MethodArgument) {
st.getNewVariable();
}
}
placePhiFunctions();
renameVariables(startBlock);
}
/**
*
*/
public void optimize() {
for (IRBasicBlock<T> b : bblocks) {
for (Quad<T> q : b.getQuads()) {
q.doPass2();
}
}
}
public void optimize(Collection<Variable<T>> values) {
for (IRBasicBlock<T> b : bblocks) {
for (Quad<T> q : b.getQuads()) {
if (!q.isDeadCode()) {
q.doPass3(values);
}
}
}
}
public void deconstrucSSA() {
final List<PhiAssignQuad<T>> phiQuads = new BootableArrayList<PhiAssignQuad<T>>();
for (IRBasicBlock<T> b : bblocks) {
for (Quad<T> q : b.getQuads()) {
if (q instanceof PhiAssignQuad && !q.isDeadCode()) {
phiQuads.add((PhiAssignQuad<T>) q);
} else {
break;
}
}
}
Collections.sort(phiQuads, new Comparator<PhiAssignQuad<T>>() {
@Override
public int compare(PhiAssignQuad<T> o1, PhiAssignQuad<T> o2) {
int i = o2.getBasicBlock().getEndPC() - o1.getBasicBlock().getEndPC();
if (i == 0) {
i = o1.getLHS().getIndex() - o2.getLHS().getIndex();
}
return i;
}
});
for (PhiAssignQuad<T> paq : phiQuads) {
Variable<T> lhs = paq.getLHS();
IRBasicBlock<T> firstBlock = null;
AssignQuad<T> firstPhiMove = null;
for (Operand<T> o : paq.getPhiOperand().getSources()) {
Variable<T> rhs = (Variable<T>) o;
AssignQuad<T> assignQuad = rhs.getAssignQuad();
IRBasicBlock<T> ab;
if (assignQuad == null && rhs instanceof MethodArgument) {
ab = startBlock;
} else {
ab = assignQuad.getBasicBlock();
}
AssignQuad<T> phiMove;
phiMove = new VariableRefAssignQuad<T>(0, ab, lhs, rhs);
phiMove.doPass2();
ab.add(phiMove);
if (firstBlock == null || ab.getStartPC() < firstBlock.getStartPC()) {
firstBlock = ab;
firstPhiMove = phiMove;
}
}
lhs.setAssignQuad(firstPhiMove);
paq.setDeadCode(true);
}
}
public void deconstrucSSA(Collection<Variable<T>> liveVariables) {
final List<PhiAssignQuad<T>> phiQuads = new BootableArrayList<PhiAssignQuad<T>>();
for (IRBasicBlock<T> b : bblocks) {
for (Quad<T> q : b.getQuads()) {
if (q instanceof PhiAssignQuad) {
PhiAssignQuad<T> q1 = (PhiAssignQuad<T>) q;
if (liveVariables.contains(q1.getLHS())) {
phiQuads.add(q1);
} else {
q1.setDeadCode(true);
}
}
// else {
// break;
// }
}
}
for (PhiAssignQuad<T> paq : phiQuads) {
Variable<T> lhs = paq.getLHS();
IRBasicBlock<T> firstBlock = null;
VariableRefAssignQuad<T> firstPhiMove = null;
for (Operand<T> o : paq.getPhiOperand().getSources()) {
Variable<T> rhs = (Variable<T>) o;
IRBasicBlock<T> ab = rhs.getAssignQuad().getBasicBlock();
VariableRefAssignQuad<T> phiMove;
phiMove = new VariableRefAssignQuad<T>(0, ab, lhs, rhs);
ab.add(phiMove);
// fixupAddresses(); //todo possible optimisation to remove assignment chains
phiMove.doPass2();
if (firstBlock == null || ab.getStartPC() < firstBlock.getStartPC()) {
firstBlock = ab;
firstPhiMove = phiMove;
}
}
lhs.setAssignQuad(firstPhiMove);
paq.setDeadCode(true);
}
}
public void fixupAddresses() {
int address = 0;
for (IRBasicBlock<T> b : bblocks) {
b.setStartPC(address);
for (Quad<T> q : b.getQuads()) {
q.setAddress(address);
if (!q.isDeadCode()) {
address += 1;
}
}
b.setEndPC(address);
}
}
private void placePhiFunctions() {
for (IRBasicBlock<T> b : bblocks) {
for (Operand<T> def : b.getDefList()) {
for (IRBasicBlock<T> dfb : b.getDominanceFrontier()) {
dfb.add(new PhiAssignQuad<T>(dfb, ((Variable<T>) def).getIndex()));
}
}
}
}
/**
* @param block
*/
private void renameVariables(IRBasicBlock<T> block) {
doRenameVariables(block);
for (IRBasicBlock<T> b : block.getSuccessors()) {
rewritePhiParams(b);
}
if (block == startBlock) {
for (IRBasicBlock b : bblocks) {
if (b.getIDominator() == null && b != startBlock) {
renameVariables(b);
}
}
}
for (IRBasicBlock<T> b : block.getDominatedBlocks()) {
if (b != block) {
renameVariables(b);
}
}
popVariables(block);
}
/**
* @param block
*/
private void doRenameVariables(IRBasicBlock<T> block) {
for (Quad<T> q : block.getQuads()) {
Operand<T>[] refs = q.getReferencedOps();
if (refs != null) {
int n = refs.length;
for (int i = 0; i < n; i += 1) {
SSAStack<T> st = getStack(refs[i]);
if (st != null) {
Variable[] vars = block.getVariables();
Variable<T> peek = st.peek();
vars[((Variable) refs[i]).getIndex()] = peek;
refs[i] = peek;
}
}
}
if (q instanceof AssignQuad) {
AssignQuad<T> aq = (AssignQuad<T>) q;
SSAStack<T> st = getStack(aq.getLHS());
Variable var = aq.getLHS();
Variable[] vars = block.getVariables();
Variable<T> nvar = st.getNewVariable();
nvar.setType(var.getType());
vars[var.getIndex()] = nvar;
aq.setLHS(nvar);
}
}
if (block.isStartOfExceptionHandler()) {
if (block.getQuads().size() > 0) {
Quad q = block.getQuads().get(0);
Operand[] referencedOps = q.getReferencedOps();
for (int i = 0; i < referencedOps.length; i++) {
Operand op = referencedOps[i];
if (op instanceof StackVariable) {
StackVariable sv = (StackVariable) op;
if (sv.getIndex() == block.getStackOffset()) {
referencedOps[i] = new ExceptionArgument(Operand.REFERENCE, block.getStackOffset());
}
}
}
}
}
}
/**
* @param block
*/
private void rewritePhiParams(IRBasicBlock<T> block) {
if (block == null) {
return;
}
for (Quad<T> q : block.getQuads()) {
if (q instanceof PhiAssignQuad) {
PhiAssignQuad<T> aq = (PhiAssignQuad<T>) q;
if (!aq.isDeadCode()) {
SSAStack<T> st = getStack(aq.getLHS());
Variable<T> var = st.peek();
// If there was no incoming branch to this phi, I think it's dead...
if (var != null) {
PhiOperand<T> phi = aq.getPhiOperand();
phi.addSource(var);
} else {
aq.setDeadCode(true);
}
}
}
}
}
/**
* @param block
*/
private void popVariables(IRBasicBlock<T> block) {
for (Quad<T> q : block.getQuads()) {
if (q instanceof AssignQuad) {
AssignQuad<T> aq = (AssignQuad<T>) q;
SSAStack<T> st = getStack(aq.getLHS());
st.pop();
}
}
}
/**
* @param operand
* @return
*/
private SSAStack<T> getStack(Operand<T> operand) {
if (operand instanceof Variable) {
return getStack((Variable<T>) operand);
}
return null;
}
private SSAStack<T> getStack(Variable<T> var) {
int index = var.getIndex();
SSAStack<T> st = renumberArray[index];
if (st == null) {
st = new SSAStack<T>(var);
renumberArray[index] = st;
}
return st;
}
public String toString() {
StringBuilder sb = new StringBuilder();
for (IRBasicBlock<T> bb : this) {
sb.append(bb.toString());
sb.append(":\n predecessors:");
final List<IRBasicBlock<T>> pred = bb.getPredecessors();
for (IRBasicBlock<T> aPred : pred) {
sb.append("\n ");
sb.append(aPred.toString());
}
sb.append("\n successors:");
for (IRBasicBlock<T> succ : bb.getSuccessors()) {
sb.append("\n ");
sb.append(succ);
}
sb.append("\n idom: ");
sb.append(bb.getIDominator());
sb.append("\n DF:");
for (IRBasicBlock<T> dfb : bb.getDominanceFrontier()) {
sb.append(' ');
sb.append(dfb);
}
sb.append("\n\n");
}
return sb.toString();
}
}