/****************************************************************************** * 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.Arrays; import java.util.HashMap; 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.cast.tree.CAstType.Array; import com.ibm.wala.ssa.IR.SSA2LocalMap; 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.util.collections.HashMapFactory; import com.ibm.wala.util.collections.HashSetFactory; import com.ibm.wala.util.collections.Pair; 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.IntSetUtil; 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 = false; public static boolean DEBUG_NAMES = false; 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; private Map<Integer,Integer> assignments = HashMapFactory.make(); // // Copy propagation history // private final Map<Object, CopyPropagationRecord> copyPropagationMap; private final Stack<CopyPropagationRecord> R[]; private static class UseRecord { final int instructionIndex; final int useNumber; private UseRecord(int instructionIndex, int useNumber) { this.useNumber = useNumber; this.instructionIndex = instructionIndex; } @Override public String toString() { return "[use " + useNumber + " of " + instructionIndex + "]"; } @Override public int hashCode() { return useNumber * instructionIndex; } @Override 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; } @Override public String toString() { return "[use " + useNumber + " of " + phiNumber + " of block " + BBnumber + "]"; } @Override public int hashCode() { return phiNumber * BBnumber * useNumber; } @Override 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 rhs; final int lhs; final int instructionIndex; final Set<Object> renamedUses = HashSetFactory.make(2); private final Set<CopyPropagationRecord> childRecords = HashSetFactory.make(1); @Override public String toString() { StringBuffer sb = new StringBuffer("<vn " + rhs + " at " + instructionIndex); for (CopyPropagationRecord c : childRecords) { sb.append("\n " + c.toString()); } sb.append(">"); return sb.toString(); } @Override public int hashCode() { return instructionIndex; } @Override public boolean equals(Object o) { return (o instanceof CopyPropagationRecord) && instructionIndex == ((CopyPropagationRecord) o).instructionIndex; } private CopyPropagationRecord(int instructionIndex, int lhs, int rhs) { if (DEBUG_UNDO) System.err.println(("new copy record for instruction #" + instructionIndex + ", rhs value is " + rhs)); this.rhs = rhs; this.lhs = lhs; this.instructionIndex = instructionIndex; } private void addChild(CopyPropagationRecord rec) { if (DEBUG_UNDO) System.err.println(("(" + rec.instructionIndex + "," + rec.rhs + ") is a child of (" + instructionIndex + "," + rhs + ")")); childRecords.add(rec); } private void addUse(int instructionIndex, int use) { if (DEBUG_UNDO) System.err.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(CFG.getMethod().getDeclaringClass().getClassLoader().getInstructionFactory(), null, newUses); } private void undo(int rhs) { int lhs = symtab.newSymbol(); instructions[instructionIndex] = new AssignInstruction(instructionIndex, lhs, rhs); if (DEBUG_UNDO) System.err.println(("recreating assignment at " + instructionIndex + " as " + lhs + " = " + rhs)); for (Iterator<Object> 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) System.err.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 = CFG.getNode(bb); BB.addPhiForLocal(phi, (SSAPhiInstruction) undo(BB.getPhiForLocal(phi), use.useNumber, lhs)); copyPropagationMap.remove(use); } } for (Iterator<CopyPropagationRecord> cs = childRecords.iterator(); cs.hasNext();) { 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 { private final String[][] computedNames = new String[valueMap.length][]; @Override public String[] getLocalNames(int pc, int vn) { if (computedNames[vn] != null) { return computedNames[vn]; } int v = skip(vn) || vn >= valueMap.length ? vn : valueMap[vn]; String[][] namesData = debugInfo.getSourceNamesForValues(); String[] vNames = namesData[v]; Set<String> x = HashSetFactory.make(); x.addAll(Arrays.asList(vNames)); MutableIntSet vals = IntSetUtil.make(); while (assignments.containsKey(v) && !vals.contains(v)) { vals.add(v); v = assignments.get(v); vNames = namesData[v]; x.addAll(Arrays.asList(vNames)); } return computedNames[vn] = x.toArray(new String[x.size()]); } private void undoCopyPropagation(int instructionIndex, int useNumber) { if (DEBUG_UNDO) System.err.println(("undoing for use #" + useNumber + " of inst #" + instructionIndex)); UseRecord use = new UseRecord(instructionIndex, useNumber); if (copyPropagationMap.containsKey(use)) { 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)) { copyPropagationMap.get(use).addUse(toInst, toUse); } } private Map<Object, CopyPropagationRecord> getCopyHistory() { return copyPropagationMap; } @Override public String toString() { StringBuffer sb = new StringBuffer( super.toString() ); for(Map.Entry<Object, CopyPropagationRecord> x : copyPropagationMap.entrySet()) { sb.append(x.getKey().toString() + " --> " + x.getValue().toString() + "\n"); } return sb.toString(); } } private CopyPropagationRecord topR(int v) { if (R[v] != null && !R[v].isEmpty()) { CopyPropagationRecord rec = R[v].peek(); if (top(v) == rec.rhs) { return rec; } } return null; } // // implementation of AbstractSSAConversion hooks // @Override protected int getNumberOfDefs(SSAInstruction inst) { return inst.getNumberOfDefs(); } @Override protected int getDef(SSAInstruction inst, int index) { return inst.getDef(index); } @Override protected int getNumberOfUses(SSAInstruction inst) { return inst.getNumberOfUses(); } @Override protected int getUse(SSAInstruction inst, int index) { return inst.getUse(index); } @Override protected boolean isAssignInstruction(SSAInstruction inst) { return inst instanceof AssignInstruction; } @Override protected int getMaxValueNumber() { return symtab.getMaxValueNumber(); } @Override protected boolean skip(int vn) { return false; } @Override 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); } @Override 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(SSAInstruction.NO_INDEX, value, params); if (DEBUG) System.err.println(("Placing " + phi + " at " + Y)); addPhi(Y, phi); } @Override protected SSAPhiInstruction getPhi(SSACFG.BasicBlock B, int index) { return B.getPhiForLocal(index); } @Override protected void setPhi(SSACFG.BasicBlock B, int index, SSAPhiInstruction inst) { B.addPhiForLocal(index, inst); } @Override protected SSAPhiInstruction repairPhiDefs(SSAPhiInstruction phi, int[] newDefs) { return (SSAPhiInstruction) phi.copyForSSA(CFG.getMethod().getDeclaringClass().getClassLoader().getInstructionFactory(), newDefs, null); } @Override 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); } @Override protected void pushAssignment(SSAInstruction inst, int index, int newRhs) { int lhs = getDef(inst, 0); int rhs = getUse(inst, 0); assignments.put(rhs, lhs); CopyPropagationRecord rec = new CopyPropagationRecord(index, lhs, newRhs); R[lhs].push(rec); if (topR(rhs) != null) { topR(rhs).addChild(rec); } } @Override 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.length <= lexicalUse || S[lexicalUse].isEmpty()) { lexicalUses[j] = -1; } else { int newUse = top(lexicalUse); lexicalUses[j] = newUse; if (topR(lexicalUse) != null) { topR(lexicalUse).addUse(index, -j - 1); } } } } } } @Override protected void repairInstructionDefs(SSAInstruction inst, int index, int[] newDefs, int[] newUses) { instructions[index] = inst.copyForSSA(CFG.getMethod().getDeclaringClass().getClassLoader().getInstructionFactory(), newDefs, newUses); } @Override protected void popAssignment(SSAInstruction inst, int index) { instructions[index] = null; } @Override protected boolean isConstant(int valueNumber) { return symtab.isConstant(valueNumber); } @Override 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 */ @SuppressWarnings("unchecked") private SSAConversion(AstMethod M, AstIR ir, SSAOptions options) { super(ir, options); HashMap<Object, CopyPropagationRecord> m = HashMapFactory.make(); this.copyPropagationMap = (ir.getLocalMap() instanceof SSAInformation) ? ((SSAInformation) ir.getLocalMap()).getCopyHistory() : m; this.ir = ir; this.debugInfo = M.debugInfo(); this.lexicalInfo = ir.lexicalInfo(); this.symtab = ir.getSymbolTable(); this.R = new Stack[ir.getSymbolTable().getMaxValueNumber() + 1]; for (int i = 0; i < CFG.getNumberOfNodes(); i++) { SSACFG.BasicBlock bb = 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++) { if (exitLive[i] > -1) { v.set(exitLive[i]); } } } this.liveness = LiveAnalysis.perform(CFG, symtab, v); if (DEBUG) { System.err.println(liveness); } } @Override protected int getNextNewValueNumber() { while (symtab.isConstant(nextSSAValue) || skip(nextSSAValue)) ++nextSSAValue; symtab.ensureSymbol(nextSSAValue); int v = nextSSAValue++; return v; } @Override protected void initializeVariables() { for (int V = 1; V <= getMaxValueNumber(); V++) { if (!skip(V)) { R[V] = new Stack<CopyPropagationRecord>(); } } 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]; } } } @Override protected void repairExit() { int[] exitLives = lexicalInfo.getExitExposedUses(); if (exitLives != null) { for (int i = 0; i < exitLives.length; i++) { if (exitLives[i] != -1 && !skip(exitLives[i])) { assert !S[exitLives[i]].isEmpty(); exitLives[i] = top(exitLives[i]); } } } } // // Global control. // @Override 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; } @Override public void perform() { super.perform(); if (DUMP) { System.err.println(ir); if (lexicalInfo != null) { for (int i = 0; i < instructions.length; i++) { int[] lexicalUses = lexicalInfo.getExposedUses(i); if (lexicalUses != null) { System.err.print(("extra uses for " + instructions[i] + ": ")); for (int j = 0; j < lexicalUses.length; j++) { System.err.print((new Integer(lexicalUses[j]).toString() + " ")); } System.err.println(""); } } } } computedLocalMap = new SSAInformation(); } private static IntSet valuesToConvert(AstIR ir) { SSAInstruction[] insts = ir.getInstructions(); MutableIntSet foundOne = new BitVectorIntSet(); MutableIntSet foundTwo = new BitVectorIntSet(); for (int i = 0; i < insts.length; i++) { SSAInstruction inst = 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) { System.err.println(("starting conversion for " + values)); System.err.println(ir); } if (DEBUG_UNDO) System.err.println((">>> starting " + ir.getMethod())); SSAConversion ssa = new SSAConversion(M, ir, options) { final int limit = ir.getSymbolTable().getMaxValueNumber(); @Override protected boolean skip(int i) { return (i >= 0) && (i <= limit) && (!values.contains(i)); } }; ssa.perform(); if (DEBUG_UNDO) System.err.println(("<<< done " + ir.getMethod())); return ssa.getComputedLocalMap(); } catch (RuntimeException e) { // System.err.println(("exception " + e + " while converting:")); // System.err.println(ir); throw e; } catch (Error e) { // System.err.println(("error " + e + " while converting:")); // System.err.println(ir); throw e; } } }