614 lines
17 KiB
Java
614 lines
17 KiB
Java
/******************************************************************************
|
|
* Copyright (c) 2002 - 2006 IBM Corporation.
|
|
* All rights reserved. This program and the accompanying materials
|
|
* are made available under the terms of the Eclipse Public License v1.0
|
|
* which accompanies this distribution, and is available at
|
|
* http://www.eclipse.org/legal/epl-v10.html
|
|
*
|
|
* Contributors:
|
|
* IBM Corporation - initial API and implementation
|
|
*****************************************************************************/
|
|
package com.ibm.wala.cast.ir.ssa;
|
|
|
|
import java.util.HashMap;
|
|
import java.util.HashSet;
|
|
import java.util.Iterator;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
import java.util.Stack;
|
|
|
|
import com.ibm.wala.cast.ir.ssa.AstIRFactory.AstIR;
|
|
import com.ibm.wala.cast.ir.ssa.analysis.LiveAnalysis;
|
|
import com.ibm.wala.cast.loader.AstMethod;
|
|
import com.ibm.wala.cast.loader.AstMethod.DebuggingInformation;
|
|
import com.ibm.wala.cast.loader.AstMethod.LexicalInformation;
|
|
import com.ibm.wala.shrikeBT.IInstruction;
|
|
import com.ibm.wala.ssa.SSACFG;
|
|
import com.ibm.wala.ssa.SSAInstruction;
|
|
import com.ibm.wala.ssa.SSAOptions;
|
|
import com.ibm.wala.ssa.SSAPhiInstruction;
|
|
import com.ibm.wala.ssa.SymbolTable;
|
|
import com.ibm.wala.ssa.IR.SSA2LocalMap;
|
|
import com.ibm.wala.util.debug.Assertions;
|
|
import com.ibm.wala.util.debug.Trace;
|
|
import com.ibm.wala.util.intset.BitVector;
|
|
import com.ibm.wala.util.intset.BitVectorIntSet;
|
|
import com.ibm.wala.util.intset.IntSet;
|
|
import com.ibm.wala.util.intset.MutableIntSet;
|
|
|
|
/**
|
|
* @author Julian Dolby
|
|
*
|
|
* Standard SSA conversion for local value numbers.
|
|
*/
|
|
public class SSAConversion extends AbstractSSAConversion {
|
|
|
|
public static boolean DEBUG = false;
|
|
public static boolean DEBUG_UNDO = true;
|
|
public static boolean DEBUG_NAMES = true;
|
|
public static boolean DUMP = false;
|
|
|
|
private final AstIR ir;
|
|
|
|
private int nextSSAValue;
|
|
|
|
private final DebuggingInformation debugInfo;
|
|
|
|
private final LexicalInformation lexicalInfo;
|
|
|
|
private final SymbolTable symtab;
|
|
|
|
private final LiveAnalysis.Result liveness;
|
|
|
|
private SSA2LocalMap computedLocalMap;
|
|
|
|
//
|
|
// Copy propagation history
|
|
//
|
|
|
|
private final Map copyPropagationMap;
|
|
private final Stack R[];
|
|
|
|
private static class UseRecord {
|
|
final int instructionIndex;
|
|
final int useNumber;
|
|
|
|
private UseRecord(int instructionIndex, int useNumber) {
|
|
this.useNumber = useNumber;
|
|
this.instructionIndex = instructionIndex;
|
|
}
|
|
|
|
public int hashCode() {
|
|
return useNumber*instructionIndex;
|
|
}
|
|
|
|
public boolean equals(Object o) {
|
|
return (o instanceof UseRecord) &&
|
|
instructionIndex==((UseRecord)o).instructionIndex &&
|
|
useNumber==((UseRecord)o).useNumber;
|
|
}
|
|
}
|
|
|
|
private class PhiUseRecord {
|
|
final int BBnumber;
|
|
final int phiNumber;
|
|
final int useNumber;
|
|
|
|
private PhiUseRecord(int BBnumber, int phiNumber, int useNumber) {
|
|
this.BBnumber = BBnumber;
|
|
this.phiNumber = phiNumber;
|
|
this.useNumber = useNumber;
|
|
}
|
|
|
|
public int hashCode() {
|
|
return phiNumber*BBnumber*useNumber;
|
|
}
|
|
|
|
public boolean equals(Object o) {
|
|
return (o instanceof PhiUseRecord) &&
|
|
BBnumber==((PhiUseRecord)o).BBnumber &&
|
|
phiNumber==((PhiUseRecord)o).phiNumber &&
|
|
useNumber==((PhiUseRecord)o).useNumber;
|
|
}
|
|
}
|
|
|
|
private class CopyPropagationRecord {
|
|
final int lhs;
|
|
final int rhs;
|
|
final int instructionIndex;
|
|
final Set renamedUses = new HashSet(2);
|
|
private final Set childRecords = new HashSet(1);
|
|
|
|
public int hashCode() {
|
|
return instructionIndex;
|
|
}
|
|
|
|
public boolean equals(Object o) {
|
|
return (o instanceof CopyPropagationRecord) &&
|
|
instructionIndex==((CopyPropagationRecord)o).instructionIndex;
|
|
}
|
|
|
|
private CopyPropagationRecord(int instructionIndex, int lhs, int rhs) {
|
|
if (DEBUG_UNDO) Trace.println("new copy record for instruction #" + instructionIndex + ", rhs value is " + rhs);
|
|
this.lhs = lhs;
|
|
this.rhs = rhs;
|
|
this.instructionIndex = instructionIndex;
|
|
}
|
|
|
|
private void addChild(CopyPropagationRecord rec) {
|
|
if (DEBUG_UNDO) Trace.println("("+rec.instructionIndex+","+rec.rhs+") is a child of ("+instructionIndex+","+rhs+")");
|
|
childRecords.add(rec);
|
|
}
|
|
|
|
private void addUse(int instructionIndex, int use) {
|
|
if (DEBUG_UNDO) Trace.println("propagated use of (" + this.instructionIndex + "," + this.rhs + ") at use #" + use + " of instruction #" + instructionIndex);
|
|
UseRecord rec = new UseRecord(instructionIndex, use);
|
|
copyPropagationMap.put(rec, this);
|
|
renamedUses.add( rec );
|
|
}
|
|
|
|
private void addUse(int BB, int phiNumber, int use) {
|
|
PhiUseRecord rec = new PhiUseRecord(BB, phiNumber, use);
|
|
copyPropagationMap.put(rec, this);
|
|
renamedUses.add( rec );
|
|
}
|
|
|
|
private SSAInstruction undo(SSAInstruction inst, int use, int val) {
|
|
int c = getNumberOfUses(inst);
|
|
int[] newUses = new int[ c ];
|
|
for(int i = 0; i < c; i++) {
|
|
if (i == use)
|
|
newUses[i] = val;
|
|
else
|
|
newUses[i] = getUse(inst, i);
|
|
}
|
|
|
|
return inst.copyForSSA(null, newUses);
|
|
}
|
|
|
|
private void undo(int rhs) {
|
|
int lhs = symtab.newSymbol();
|
|
|
|
instructions[ instructionIndex ] = new AssignInstruction(lhs, rhs);
|
|
|
|
if (DEBUG_UNDO) Trace.println("recreating assignment at " + instructionIndex + " as " + lhs + " = " + rhs);
|
|
|
|
for(Iterator uses = renamedUses.iterator(); uses.hasNext(); ) {
|
|
Object x = uses.next();
|
|
if (x instanceof UseRecord) {
|
|
UseRecord use = (UseRecord)x;
|
|
int idx = use.instructionIndex;
|
|
SSAInstruction inst = instructions[ idx ];
|
|
|
|
if (DEBUG_UNDO) Trace.println("Changing use #" + use.useNumber + " of inst #" + idx + " to val " + lhs);
|
|
|
|
if (use.useNumber >= 0) {
|
|
instructions[idx] = undo(inst, use.useNumber, lhs);
|
|
} else {
|
|
lexicalInfo.getExposedUses(idx)[-use.useNumber - 1] = lhs;
|
|
}
|
|
copyPropagationMap.remove( use );
|
|
} else {
|
|
PhiUseRecord use = (PhiUseRecord)x;
|
|
int bb = use.BBnumber;
|
|
int phi = use.phiNumber;
|
|
SSACFG.BasicBlock BB = (SSACFG.BasicBlock)CFG.getNode(bb);
|
|
BB.addPhiForLocal(phi, (SSAPhiInstruction)undo(BB.getPhiForLocal(phi), use.useNumber, lhs));
|
|
copyPropagationMap.remove( use );
|
|
}
|
|
}
|
|
|
|
for(Iterator cs = childRecords.iterator(); cs.hasNext(); ) {
|
|
((CopyPropagationRecord)cs.next()).undo( lhs );
|
|
}
|
|
}
|
|
|
|
public void undo() {
|
|
undo( this.rhs );
|
|
copyPropagationMap.remove( new UseRecord(instructionIndex, rhs) );
|
|
}
|
|
}
|
|
|
|
public static void undoCopyPropagation(AstIR ir, int instruction, int use) {
|
|
SSAInformation info = (SSAInformation) ir.getLocalMap();
|
|
info.undoCopyPropagation(instruction, use);
|
|
}
|
|
|
|
public static void copyUse(AstIR ir, int fromInst, int fromUse, int toInst, int toUse) {
|
|
SSAInformation info = (SSAInformation) ir.getLocalMap();
|
|
info.copyUse(fromInst, fromUse, toInst, toUse);
|
|
}
|
|
|
|
|
|
//
|
|
// SSA2LocalMap implementation for SSAConversion
|
|
//
|
|
private class SSAInformation implements com.ibm.wala.ssa.IR.SSA2LocalMap {
|
|
|
|
public String[] getLocalNames(int pc, int vn) {
|
|
int v = skip(vn)||vn>=valueMap.length? vn: valueMap[vn];
|
|
String[][] namesData = debugInfo.getSourceNamesForValues();
|
|
if (namesData == null || namesData.length <= v)
|
|
return new String[0];
|
|
else
|
|
return namesData[v];
|
|
}
|
|
|
|
private void undoCopyPropagation(int instructionIndex, int useNumber) {
|
|
|
|
if (DEBUG_UNDO)
|
|
Trace.println("undoing for use #" + useNumber + " of inst #" + instructionIndex);
|
|
|
|
UseRecord use = new UseRecord(instructionIndex, useNumber);
|
|
if (copyPropagationMap.containsKey(use)) {
|
|
((CopyPropagationRecord)copyPropagationMap.get(use)).undo();
|
|
}
|
|
}
|
|
|
|
private void copyUse(int fromInst, int fromUse, int toInst, int toUse) {
|
|
UseRecord use = new UseRecord(fromInst, fromUse);
|
|
if (copyPropagationMap.containsKey(use)) {
|
|
((CopyPropagationRecord)copyPropagationMap.get(use)).addUse(toInst, toUse);
|
|
}
|
|
}
|
|
|
|
private Map getCopyHistory() {
|
|
return copyPropagationMap;
|
|
}
|
|
}
|
|
|
|
private CopyPropagationRecord topR(int v) {
|
|
if (R[v] != null && !R[v].isEmpty()) {
|
|
CopyPropagationRecord rec = (CopyPropagationRecord)R[v].peek();
|
|
if (top(v) == rec.rhs) {
|
|
return rec;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
|
|
//
|
|
// implementation of AbstractSSAConversion hooks
|
|
//
|
|
|
|
protected int getNumberOfDefs(SSAInstruction inst) {
|
|
return inst.getNumberOfDefs();
|
|
}
|
|
|
|
protected int getDef(SSAInstruction inst, int index) {
|
|
return inst.getDef(index);
|
|
}
|
|
|
|
protected int getNumberOfUses(SSAInstruction inst) {
|
|
return inst.getNumberOfUses();
|
|
}
|
|
|
|
protected int getUse(SSAInstruction inst, int index) {
|
|
return inst.getUse(index);
|
|
}
|
|
|
|
protected boolean isAssignInstruction(SSAInstruction inst) {
|
|
return inst instanceof AssignInstruction;
|
|
}
|
|
|
|
protected int getMaxValueNumber() {
|
|
return symtab.getMaxValueNumber();
|
|
}
|
|
|
|
protected boolean skip(int vn) {
|
|
return false;
|
|
}
|
|
|
|
protected boolean isLive(SSACFG.BasicBlock Y, int V) {
|
|
return (liveness.isLiveEntry(Y, V));
|
|
}
|
|
|
|
private void addPhi(SSACFG.BasicBlock BB, SSAPhiInstruction phi) {
|
|
BB.addPhiForLocal(phiCounts[BB.getGraphNodeId()], phi);
|
|
}
|
|
|
|
protected void placeNewPhiAt(int value, SSACFG.BasicBlock Y) {
|
|
int[] params = new int[CFG.getPredNodeCount(Y)];
|
|
for (int i = 0; i < params.length; i++)
|
|
params[i] = value;
|
|
|
|
SSAPhiInstruction phi = new SSAPhiInstruction(value, params);
|
|
|
|
if (DEBUG)
|
|
Trace.println("Placing " + phi + " at " + Y);
|
|
|
|
addPhi(Y, phi);
|
|
}
|
|
|
|
protected SSAPhiInstruction getPhi(SSACFG.BasicBlock B, int index) {
|
|
return B.getPhiForLocal(index);
|
|
}
|
|
|
|
protected void setPhi(SSACFG.BasicBlock B, int index, SSAPhiInstruction inst) {
|
|
B.addPhiForLocal(index, inst);
|
|
}
|
|
|
|
protected SSAPhiInstruction repairPhiDefs(SSAPhiInstruction phi, int[] newDefs) {
|
|
return (SSAPhiInstruction)phi.copyForSSA(newDefs, null);
|
|
}
|
|
|
|
protected void repairPhiUse(SSACFG.BasicBlock BB, int phiIndex, int rvalIndex, int newRval) {
|
|
SSAPhiInstruction phi = getPhi(BB, phiIndex);
|
|
|
|
int[] newUses = new int[getNumberOfUses(phi)];
|
|
for (int v = 0; v < newUses.length; v++) {
|
|
int oldUse = getUse(phi, v);
|
|
int newUse = (v==rvalIndex)? newRval: oldUse;
|
|
newUses[v] = newUse;
|
|
|
|
if (v==rvalIndex && topR(oldUse) != null) {
|
|
topR(oldUse).addUse(BB.getGraphNodeId(), phiIndex, v);
|
|
}
|
|
}
|
|
|
|
phi.setValues(newUses);
|
|
}
|
|
|
|
protected void pushAssignment(SSAInstruction inst, int index, int newRhs) {
|
|
int lhs = getDef(inst, 0);
|
|
int rhs = getUse(inst, 0);
|
|
|
|
copyNames(rhs, lhs);
|
|
|
|
CopyPropagationRecord rec = new CopyPropagationRecord(index, lhs, newRhs);
|
|
R[lhs].push( rec );
|
|
if (topR(rhs) != null) {
|
|
topR(rhs).addChild( rec );
|
|
}
|
|
}
|
|
|
|
protected void repairInstructionUses(SSAInstruction inst, int index, int[] newUses){
|
|
for (int j = 0; j < getNumberOfUses(inst); j++) {
|
|
if (topR(getUse(inst, j)) != null) {
|
|
topR(getUse(inst, j)).addUse(index, j);
|
|
}
|
|
}
|
|
|
|
int[] lexicalUses = lexicalInfo.getExposedUses(index);
|
|
if (lexicalUses != null) {
|
|
for(int j = 0; j < lexicalUses.length; j++) {
|
|
int lexicalUse = lexicalUses[j];
|
|
if (lexicalUse != -1 && !skip(lexicalUse)) {
|
|
if (S[lexicalUse].isEmpty()) {
|
|
lexicalUses[j] = -1;
|
|
} else {
|
|
int newUse = top(lexicalUse);
|
|
|
|
lexicalUses[j] = newUse;
|
|
|
|
if (topR(lexicalUse) != null) {
|
|
topR(lexicalUse).addUse(index, -j - 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
protected void repairInstructionDefs(SSAInstruction inst, int index, int[] newDefs, int[] newUses) {
|
|
instructions[index] = inst.copyForSSA(newDefs, newUses);
|
|
}
|
|
|
|
protected void popAssignment(SSAInstruction inst, int index) {
|
|
instructions[index] = null;
|
|
}
|
|
|
|
protected boolean isConstant(int valueNumber) {
|
|
return symtab.isConstant(valueNumber);
|
|
}
|
|
|
|
protected boolean skipRepair(SSAInstruction inst, int index) {
|
|
if (! super.skipRepair(inst, index)) {
|
|
return false;
|
|
}
|
|
|
|
if (index == -1) return true;
|
|
|
|
int[] lexicalUses = lexicalInfo.getExposedUses(index);
|
|
if (lexicalUses != null) {
|
|
for(int j = 0; j < lexicalUses.length; j++) {
|
|
if (! skip(lexicalUses[j])) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @param ir
|
|
* @param options
|
|
*/
|
|
private SSAConversion(AstMethod M, AstIR ir, SSAOptions options) {
|
|
super(ir, options);
|
|
this.copyPropagationMap =
|
|
(ir.getLocalMap() instanceof SSAInformation)?
|
|
((SSAInformation)ir.getLocalMap()).getCopyHistory():
|
|
new HashMap();
|
|
|
|
this.ir = ir;
|
|
this.debugInfo = M.debugInfo;
|
|
this.lexicalInfo = M.lexicalInfo;
|
|
this.symtab = ir.getSymbolTable();
|
|
this.R = new Stack[ir.getSymbolTable().getMaxValueNumber() + 1];
|
|
|
|
for(int i = 0; i < CFG.getNumberOfNodes(); i++) {
|
|
SSACFG.BasicBlock bb = (SSACFG.BasicBlock) CFG.getNode(i);
|
|
if (bb.hasPhi()) {
|
|
int n = 0;
|
|
for(Iterator X = bb.iteratePhis(); X.hasNext(); n++) X.next();
|
|
phiCounts[i] = n;
|
|
}
|
|
}
|
|
|
|
this.nextSSAValue = ir.getNumberOfParameters()+1;
|
|
|
|
int[] exitLive = lexicalInfo.getExitExposedUses();
|
|
BitVector v = new BitVector();
|
|
if (exitLive != null)
|
|
for(int i = 0; i < exitLive.length; i++)
|
|
v.set( exitLive[i] );
|
|
this.liveness = LiveAnalysis.perform(CFG, symtab, v);
|
|
|
|
if (DEBUG) {
|
|
Trace.println(liveness);
|
|
}
|
|
}
|
|
|
|
protected int getNextNewValueNumber() {
|
|
while (symtab.isConstant(nextSSAValue) || skip(nextSSAValue))
|
|
++nextSSAValue;
|
|
symtab.ensureSymbol( nextSSAValue );
|
|
return nextSSAValue++;
|
|
}
|
|
|
|
private void copyNames(int to, int from) {
|
|
String[][] namesData = debugInfo.getSourceNamesForValues();
|
|
if (namesData != null &&
|
|
namesData.length > from &&
|
|
namesData[from] != null)
|
|
{
|
|
if (namesData[to] == null) {
|
|
namesData[to] = namesData[from];
|
|
} else {
|
|
String[] newNames = new String[ namesData[from].length+namesData[to].length ];
|
|
System.arraycopy(namesData[from], 0, newNames, 0, namesData[from].length);
|
|
System.arraycopy(namesData[to], 0, newNames, namesData[from].length, namesData[to].length);
|
|
namesData[to] = newNames;
|
|
}
|
|
}
|
|
}
|
|
|
|
protected void initializeVariables() {
|
|
for (int V = 1; V <= getMaxValueNumber(); V++) {
|
|
if (! skip(V)) {
|
|
R[V] = new Stack();
|
|
}
|
|
}
|
|
|
|
int[] params = symtab.getParameterValueNumbers();
|
|
for (int i = 0; i < params.length; i++) {
|
|
if (! skip( params[i] )) {
|
|
S[params[i]].push( params[i] );
|
|
valueMap[ params[i] ] = params[i];
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
protected void repairExit() {
|
|
int[] exitLives = lexicalInfo.getExitExposedUses();
|
|
if (exitLives != null) {
|
|
for(int i = 0; i < exitLives.length; i++) {
|
|
if (! skip(exitLives[i])) {
|
|
Assertions._assert(! S[exitLives[i]].isEmpty());
|
|
exitLives[i] = top(exitLives[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Global control.
|
|
//
|
|
|
|
protected void fail(int v) {
|
|
System.err.println( "during SSA conversion of the following IR:" );
|
|
System.err.println( ir );
|
|
super.fail(v);
|
|
}
|
|
|
|
public SSA2LocalMap getComputedLocalMap() {
|
|
return computedLocalMap;
|
|
}
|
|
|
|
public void perform() {
|
|
super.perform();
|
|
|
|
if (DUMP) {
|
|
Trace.println( ir );
|
|
if (lexicalInfo != null) {
|
|
for(int i = 0; i < instructions.length; i++) {
|
|
int[] lexicalUses = lexicalInfo.getExposedUses(i);
|
|
if (lexicalUses != null) {
|
|
Trace.print("extra uses for " + instructions[i] + ": ");
|
|
for(int j = 0; j < lexicalUses.length; j++) {
|
|
Trace.print( new Integer(lexicalUses[j]).toString() + " " );
|
|
}
|
|
Trace.println("");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
computedLocalMap = new SSAInformation();
|
|
}
|
|
|
|
private static IntSet valuesToConvert(AstIR ir) {
|
|
IInstruction[] insts = ir.getInstructions();
|
|
MutableIntSet foundOne = new BitVectorIntSet();
|
|
MutableIntSet foundTwo = new BitVectorIntSet();
|
|
for(int i = 0; i < insts.length; i++) {
|
|
SSAInstruction inst = (SSAInstruction) insts[i];
|
|
if (inst != null) {
|
|
for(int j = 0; j < inst.getNumberOfDefs(); j++) {
|
|
int def = inst.getDef(j);
|
|
if (def != -1) {
|
|
if (foundOne.contains(def) ||
|
|
ir.getSymbolTable().isConstant(def) ||
|
|
def <= ir.getNumberOfParameters() ||
|
|
inst instanceof AssignInstruction)
|
|
{
|
|
foundTwo.add(def);
|
|
} else {
|
|
foundOne.add(def);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return foundTwo;
|
|
}
|
|
|
|
public static SSA2LocalMap convert(AstMethod M, AstIR ir, SSAOptions options) {
|
|
return convert(M, ir, options, valuesToConvert(ir));
|
|
}
|
|
|
|
public static SSA2LocalMap convert(AstMethod M,
|
|
final AstIR ir,
|
|
SSAOptions options,
|
|
final IntSet values)
|
|
{
|
|
try {
|
|
if (DEBUG) {
|
|
Trace.println("starting conversion for " + values);
|
|
Trace.println( ir );
|
|
}
|
|
if (DEBUG_UNDO) Trace.println(">>> starting " + ir.getMethod());
|
|
SSAConversion ssa = new SSAConversion(M, ir, options) {
|
|
final int limit = ir.getSymbolTable().getMaxValueNumber();
|
|
protected boolean skip(int i) {
|
|
return (i >= 0) && (i <= limit) && (!values.contains(i));
|
|
}
|
|
};
|
|
ssa.perform();
|
|
if (DEBUG_UNDO) Trace.println("<<< done " + ir.getMethod());
|
|
return ssa.getComputedLocalMap();
|
|
} catch (RuntimeException e) {
|
|
Trace.println("exception " + e + " while converting:");
|
|
Trace.println(ir);
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
}
|