/****************************************************************************** * Copyright (c) 2009 - 2015 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.memsat.frontEnd.fieldssa; import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import com.ibm.wala.cast.ir.ssa.AbstractSSAConversion; import com.ibm.wala.cast.ir.ssa.AstIsDefinedInstruction; import com.ibm.wala.cast.ir.ssa.AstLexicalAccess.Access; import com.ibm.wala.cast.ir.ssa.AstLexicalRead; import com.ibm.wala.cast.ir.ssa.AstLexicalWrite; import com.ibm.wala.cast.java.ipa.callgraph.AstJavaSSAPropagationCallGraphBuilder.EnclosingObjectReferenceKey; import com.ibm.wala.cast.java.loader.JavaSourceLoaderImpl.JavaClass; import com.ibm.wala.cast.java.ssa.EnclosingObjectReference; import com.ibm.wala.classLoader.IClass; import com.ibm.wala.classLoader.IField; import com.ibm.wala.ipa.callgraph.CGNode; import com.ibm.wala.ipa.callgraph.propagation.ArrayContentsKey; import com.ibm.wala.ipa.callgraph.propagation.HeapModel; import com.ibm.wala.ipa.callgraph.propagation.InstanceKey; import com.ibm.wala.ipa.callgraph.propagation.PointerAnalysis; import com.ibm.wala.ipa.callgraph.propagation.PointerKey; import com.ibm.wala.ipa.cha.IClassHierarchy; import com.ibm.wala.ipa.modref.ArrayLengthKey; import com.ibm.wala.memsat.frontEnd.FieldSSATable; import com.ibm.wala.ssa.DefUse; import com.ibm.wala.ssa.IR; import com.ibm.wala.ssa.SSAAbstractInvokeInstruction; import com.ibm.wala.ssa.SSAArrayLengthInstruction; import com.ibm.wala.ssa.SSAArrayLoadInstruction; import com.ibm.wala.ssa.SSAArrayStoreInstruction; import com.ibm.wala.ssa.SSACFG; import com.ibm.wala.ssa.SSAGetInstruction; import com.ibm.wala.ssa.SSAInstruction; import com.ibm.wala.ssa.SSANewInstruction; import com.ibm.wala.ssa.SSAOptions; import com.ibm.wala.ssa.SSAPhiInstruction; import com.ibm.wala.ssa.SSAPutInstruction; import com.ibm.wala.types.FieldReference; import com.ibm.wala.util.collections.ArrayIterator; import com.ibm.wala.util.collections.EmptyIterator; import com.ibm.wala.util.collections.ObjectArrayMapping; import com.ibm.wala.util.collections.Pair; import com.ibm.wala.util.debug.Assertions; import com.ibm.wala.util.intset.OrdinalSet; public class FieldNameSSAConversion extends AbstractSSAConversion { private static final Object USES = new Object(); private static final Object DEFS = new Object(); public static class LexicalPointerKey implements PointerKey { private final String varName; private final String varDefiner; private LexicalPointerKey(String varName, String varDefiner) { this.varName = varName; this.varDefiner = varDefiner; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((varDefiner == null) ? 0 : varDefiner.hashCode()); result = prime * result + ((varName == null) ? 0 : varName.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; LexicalPointerKey other = (LexicalPointerKey) obj; if (varDefiner == null) { if (other.varDefiner != null) return false; } else if (!varDefiner.equals(other.varDefiner)) return false; if (varName == null) { if (other.varName != null) return false; } else if (!varName.equals(other.varName)) return false; return true; } } public static class LocalFieldAccesses implements FieldAccesses { private final boolean heapWritesAsUpdates; private final IClassHierarchy cha; private final HeapModel heapModel; private final PointerAnalysis pointerAnalysis; private final CGNode node; public LocalFieldAccesses(boolean heapWritesAsUpdates, CGNode node, PointerAnalysis pointerAnalysis, IClassHierarchy cha) { this.cha = cha; this.node = node; this.pointerAnalysis = pointerAnalysis; this.heapWritesAsUpdates = heapWritesAsUpdates; this.heapModel = pointerAnalysis.getHeapModel(); } private boolean isNewArray(SSAInstruction inst) { if (inst instanceof SSANewInstruction) { SSANewInstruction ni = (SSANewInstruction) inst; if (ni.getConcreteType().isArrayType()) { return true; } } return false; } private PointerKey[] resolve(int valueNumber, FieldReference ref) { IField f = cha.resolveField(ref); if (f.isStatic()) { return new PointerKey[]{ heapModel.getPointerKeyForStaticField(f) }; } else { OrdinalSet objs = pointerAnalysis.getPointsToSet( heapModel.getPointerKeyForLocal(node, valueNumber)); PointerKey[] result = new PointerKey[ objs.size() ]; int i = 0; for(Iterator iks = objs.iterator(); iks.hasNext(); ) { result[i++] = heapModel .getPointerKeyForInstanceField((InstanceKey)iks.next(), f); } return result; } } private PointerKey[] resolveLength(int valueNumber) { OrdinalSet objs = pointerAnalysis.getPointsToSet( heapModel.getPointerKeyForLocal(node, valueNumber)); PointerKey[] result = new PointerKey[ objs.size() ]; int i = 0; for(Iterator iks = objs.iterator(); iks.hasNext(); ) { result[i++] = new ArrayLengthKey((InstanceKey)iks.next()); } return result; } private PointerKey[] resolveState(int valueNumber) { OrdinalSet objs = pointerAnalysis.getPointsToSet( heapModel.getPointerKeyForLocal(node, valueNumber)); PointerKey[] result = new PointerKey[ objs.size() ]; int i = 0; for(Iterator iks = objs.iterator(); iks.hasNext(); ) { result[i++] = (PointerKey) heapModel.getPointerKeyForArrayContents((InstanceKey)iks.next()); } return result; } private PointerKey[] resolveEnclosing(int valueNumber, IClass cls) { OrdinalSet objs = pointerAnalysis.getPointsToSet( heapModel.getPointerKeyForLocal(node, valueNumber)); PointerKey[] result = new PointerKey[ objs.size() ]; int i = 0; for(Iterator iks = objs.iterator(); iks.hasNext(); ) { result[i++] = new EnclosingObjectReferenceKey((InstanceKey)iks.next(), cls); } return result; } private PointerKey[] resolveLexical(Access[] accesses) { int i = 0; PointerKey[] keys = new PointerKey[ accesses.length ]; for(Access a : accesses) { keys[i++] = new LexicalPointerKey(a.variableName, a.variableDefiner); } return keys; } private PointerKey[] combine(PointerKey[] a, PointerKey[] b) { PointerKey[] result = new PointerKey[ a.length+b.length ]; System.arraycopy(a, 0, result, 0, a.length); System.arraycopy(b, 0, result, a.length, b.length); return result; } public PointerKey[] getUses(SSAInstruction inst) { if (inst instanceof SSAGetInstruction) { SSAGetInstruction i = (SSAGetInstruction) inst; return resolve(i.getRef(), i.getDeclaredField()); } else if (inst instanceof SSAArrayLoadInstruction) { return combine(resolveState(inst.getUse(0)), resolveLength( inst.getUse(0) )); } else if (inst instanceof SSAArrayStoreInstruction) { if (heapWritesAsUpdates) { return combine(resolveState(inst.getUse(0)), resolveLength( inst.getUse(0) )); } else { return resolveLength( inst.getUse(0) ); } } else if (inst instanceof SSAArrayLengthInstruction) { return resolveLength( inst.getUse(0) ); } else if (inst instanceof AstIsDefinedInstruction) { AstIsDefinedInstruction i = (AstIsDefinedInstruction)inst; if (i.getFieldRef() != null) { return resolve(i.getUse(0), i.getFieldRef()); } else { throw new AssertionError("unreachable"); } } else if (inst instanceof EnclosingObjectReference) { EnclosingObjectReference i = (EnclosingObjectReference)inst; return resolveEnclosing(/* i.getUse(0) */ 1, cha.lookupClass(i.getEnclosingType())); } else if (inst instanceof AstLexicalRead) { return resolveLexical(((AstLexicalRead)inst).getAccesses()); } else if (heapWritesAsUpdates) { return getDefs(inst); } else { return new PointerKey[0]; } } public PointerKey[] getDefs(SSAInstruction inst) { if (inst instanceof SSAPutInstruction) { SSAPutInstruction i = (SSAPutInstruction) inst; return resolve(i.getRef(), i.getDeclaredField()); } else if (inst instanceof SSAArrayStoreInstruction) { return resolveState(inst.getUse(0)); } else if (inst instanceof SSANewInstruction) { SSANewInstruction i = (SSANewInstruction)inst; IClass klass = cha.lookupClass(i.getConcreteType()); if (isNewArray(inst)) { return resolveLength(inst.getDef(0)); } else if (klass instanceof JavaClass) { List x = new ArrayList(); IClass enclosingClass = ((JavaClass) klass).getEnclosingClass(); while (enclosingClass != null) { PointerKey[] y = resolveEnclosing(inst.getDef(0), enclosingClass); for(int j = 0; j < y.length; j++) { x.add(y[j]); } enclosingClass = ((JavaClass) enclosingClass).getEnclosingClass(); } return (PointerKey[])x.toArray(new PointerKey[x.size()]); } else { return new PointerKey[0]; } } else if (inst instanceof AstLexicalWrite) { return resolveLexical(((AstLexicalWrite)inst).getAccesses()); } else { return new PointerKey[0]; } } } public static class IPFieldAccesses extends LocalFieldAccesses { private final FieldAccesses invokeAccesses; public IPFieldAccesses(boolean heapWritesAsUpdates, CGNode node, PointerAnalysis pointerAnalysis, IClassHierarchy cha, FieldAccesses invokeAccesses) { super(heapWritesAsUpdates, node, pointerAnalysis, cha); this.invokeAccesses = invokeAccesses; } public PointerKey[] getUses(SSAInstruction inst) { if (inst instanceof SSAAbstractInvokeInstruction) { return invokeAccesses.getUses(inst); } else { return super.getUses(inst); } } public PointerKey[] getDefs(SSAInstruction inst) { if (inst instanceof SSAAbstractInvokeInstruction) { return invokeAccesses.getDefs(inst); } else { return super.getDefs(inst); } } } public FieldNameSSAConversion(IR ir, FieldAccesses fieldAccesses) { super(ir, new SSAOptions()); this.ir = ir; this.fieldAccesses = fieldAccesses; fieldPhiSet = new LinkedHashSet(); fieldPhiNodes = new LinkedHashMap(); initialFieldNumbers = makeInitialFieldNumbers(ir); valueNumbers = makeValueNumbers(ir); nextFreeNumber = getMaxValueNumber() + 1; } private final IR ir; private final FieldAccesses fieldAccesses; private final ObjectArrayMapping initialFieldNumbers; private final Set fieldPhiSet; private final Map fieldPhiNodes; private final Map valueNumbers; private int[] exitLives; private int nextFreeNumber; private int getInitialFieldNumber(PointerKey fr) { assert initialFieldNumbers.getMappedIndex(fr) >= 0 : "no mapping for " + fr + " in " + ir.getMethod(); return initialFieldNumbers.getMappedIndex(fr) + 1; } private ObjectArrayMapping makeInitialFieldNumbers(IR ir) { Set fields = new LinkedHashSet(); for(Iterator is = iterateInstructions(ir); is.hasNext(); ) { SSAInstruction i = (SSAInstruction)is.next(); if (i == null) continue; PointerKey[] defs = fieldAccesses.getDefs(i); for(int f = 0; f < defs.length; f++) { fields.add( defs[f] ); } PointerKey[] uses = fieldAccesses.getUses(i); for(int f = 0; f < uses.length; f++) { fields.add( uses[f] ); } } return new ObjectArrayMapping( fields.toArray(new PointerKey[ fields.size() ]) ); } private Map makeValueNumbers(IR ir) { Map vns = new LinkedHashMap(); for(Iterator is = iterateInstructions(ir); is.hasNext(); ) { SSAInstruction inst = (SSAInstruction)is.next(); if (inst == null) continue; PointerKey[] uses = fieldAccesses.getUses(inst); int[] useValueNumbers = new int[ uses.length ]; for(int j = 0; j < uses.length; j++) { useValueNumbers[j] = getInitialFieldNumber( uses[j] ); } vns.put(Pair.make(inst, USES), useValueNumbers); PointerKey[] defs = fieldAccesses.getDefs(inst); int[] defValueNumbers = new int[ defs.length ]; for(int j = 0; j < defs.length; j++) { defValueNumbers[j] = getInitialFieldNumber( defs[j] ); } vns.put(Pair.make(inst, DEFS), defValueNumbers); } return vns; } protected int getNumberOfDefs(SSAInstruction inst) { if (fieldPhiSet.contains(inst)) { return inst.getNumberOfDefs(); } else { int[] defs = (int[])valueNumbers.get(Pair.make(inst, DEFS)); if ( defs == null) { return 0; } else { return defs.length; } } } protected int getDef(SSAInstruction inst, int index) { if (fieldPhiSet.contains(inst)) { return inst.getDef(index); } else { return ((int[])valueNumbers.get(Pair.make(inst, DEFS)))[index]; } } protected int getNumberOfUses(SSAInstruction inst) { if (fieldPhiSet.contains(inst)) { return inst.getNumberOfUses(); } else { return ((int[])valueNumbers.get(Pair.make(inst, USES))).length; } } protected int getUse(SSAInstruction inst, int index) { if (fieldPhiSet.contains(inst)) { return inst.getUse(index); } else { return ((int[])valueNumbers.get(Pair.make(inst, USES)))[index]; } } protected int getMaxValueNumber() { return initialFieldNumbers.getSize(); } protected int getNextNewValueNumber() { return nextFreeNumber++; } protected boolean isLive(SSACFG.BasicBlock Y, int V) { return true; } protected boolean skip(int vn) { return false; } protected boolean isConstant(int valueNumber) { return false; } protected boolean isAssignInstruction(SSAInstruction inst) { return false; } protected void initializeVariables() { for (int i = 1; i <= getMaxValueNumber(); i++) { S[i].push( i ); valueMap[ i ] = i; } } protected void repairExit() { exitLives = new int[ getMaxValueNumber() ]; for(int i = 1; i <= getMaxValueNumber(); i++) { assert ! S[i].isEmpty(); exitLives[i-1] = top(i); } } 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(Y.getGraphNodeId(), value, params); fieldPhiSet.add(phi); SSAPhiInstruction[] phis = (SSAPhiInstruction[])fieldPhiNodes.get(Y); if (phis == null) { phis = new SSAPhiInstruction[1]; phis[0] = phi; fieldPhiNodes.put(Y, phis); } else { SSAPhiInstruction[] newPhis = new SSAPhiInstruction[ phis.length+1 ]; System.arraycopy(phis, 0, newPhis, 1, phis.length); newPhis[0] = phi; fieldPhiNodes.put(Y, newPhis); } } protected SSAPhiInstruction getPhi(SSACFG.BasicBlock B, int index) { return ((SSAPhiInstruction[])fieldPhiNodes.get(B))[index]; } protected void setPhi(SSACFG.BasicBlock B, int index, SSAPhiInstruction inst) { ((SSAPhiInstruction[])fieldPhiNodes.get(B))[index] = inst; } protected SSAPhiInstruction repairPhiDefs(SSAPhiInstruction phi, int[] newDefs) { SSAPhiInstruction np = (SSAPhiInstruction)phi.copyForSSA(ir.getMethod().getDeclaringClass().getClassLoader().getInstructionFactory(), newDefs, null); fieldPhiSet.remove(phi); fieldPhiSet.add(np); return np; } 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; } phi.setValues(newUses); } protected void repairInstructionUses(SSAInstruction inst, int index, int[] newUses) { } protected void repairInstructionDefs(SSAInstruction inst, int index, int[] newDefs, int[] newUses) { valueNumbers.put(Pair.make(inst, DEFS), newDefs); valueNumbers.put(Pair.make(inst, USES), newUses); for(int i = 0; i < newDefs.length; i++){ for(int j = 0; j < newUses.length; j++){ assert newDefs[i] != newUses[j]; } } } protected void pushAssignment(SSAInstruction inst, int index, int newRhs) { Assertions.UNREACHABLE(); } protected void popAssignment(SSAInstruction inst, int index) { Assertions.UNREACHABLE(); } public FieldSSATable convert() { perform(); return new FieldSSATable() { public Iterator<PointerKey> getFields() { return initialFieldNumbers.iterator(); } public boolean isHeapPhi(SSAPhiInstruction inst) { return fieldPhiSet.contains(inst); } public int getMaxHeapNumber() { return nextFreeNumber-1; } public int getMaxInitialHeapNumber() { return initialFieldNumbers.getSize(); } public boolean isArrayNumber(int valueNumber) { return getField(valueNumber) instanceof ArrayContentsKey; } public PointerKey getField(int vn) { return (PointerKey)initialFieldNumbers.getMappedObject(valueMap[vn]-1); } public Iterator getPhiNodes(SSACFG.BasicBlock BB) { if (! fieldPhiNodes.containsKey( BB )) { return EmptyIterator.instance(); } else { return new ArrayIterator(((SSAPhiInstruction[])fieldPhiNodes.get(BB))); } } public Iterator getPhiNodes() { return fieldPhiSet.iterator(); } public int getEntryValue(PointerKey field) { return getInitialFieldNumber(field); } public int getExitValue(PointerKey field) { if (initialFieldNumbers.getMappedIndex(field) == -1) return -1; return exitLives[ initialFieldNumbers.getMappedIndex(field) ]; } public int[] getUses(SSAInstruction inst) { return (int[]) valueNumbers.get(Pair.make(inst, USES)); } public int[] getDefs(SSAInstruction inst) { return (int[]) valueNumbers.get(Pair.make(inst, DEFS)); } public int getUse(SSAInstruction inst, int i) { return getUses(inst)[i]; } public int getDef(SSAInstruction inst, int i) { return getDefs(inst)[i]; } public int getUse(SSAInstruction inst, PointerKey field) { for(int i = 0; i < getNumberOfUses(inst); i++) { if (field.equals(getField(getUse(inst, i)))) return i; } Assertions.UNREACHABLE(); return -1; } public int getDef(SSAInstruction inst, PointerKey field) { for(int i = 0; i < getNumberOfDefs(inst); i++) { if (field.equals(getField(getDef(inst, i)))) return i; } Assertions.UNREACHABLE(); return -1; } public DefUse getDefUse() { return new DefUse(ir) { protected int getMaxValueNumber() { return getMaxHeapNumber(); } protected void initAllInstructions() { for (SSAInstruction inst : FieldNameSSAConversion.this.getInstructions(ir)) { allInstructions.add(inst); } allInstructions.addAll(fieldPhiSet); } protected int getDef(SSAInstruction s, int i) { return FieldNameSSAConversion.this.getDef(s, i); } protected int getUse(SSAInstruction s, int i) { return FieldNameSSAConversion.this.getUse(s, i); } protected int getNumberOfDefs(SSAInstruction s) { return FieldNameSSAConversion.this.getNumberOfDefs(s); } protected int getNumberOfUses(SSAInstruction s) { return FieldNameSSAConversion.this.getNumberOfUses(s); } }; } public String toString() { StringBuffer B = new StringBuffer(); B.append("Field SSA for ").append(CFG.getMethod()).append("\n"); for(int i = 1; i < nextFreeNumber; i++) { B.append("number ") .append(i) .append(" is ") .append(getField(i)) .append(":") .append(i) .append(i <= getMaxInitialHeapNumber()? "(initial)": "(derived)") .append("\n"); } SSAInstruction[] insts = getInstructions(ir); for(Iterator BBs = CFG.iterator(); BBs.hasNext(); ) { SSACFG.BasicBlock BB = (SSACFG.BasicBlock)BBs.next(); B.append("block ").append(BB).append("\n"); if (fieldPhiNodes.containsKey(BB)) { SSAPhiInstruction[] phis = (SSAPhiInstruction[])fieldPhiNodes.get(BB); for(int i = 0; i < phis.length; i++) { B.append(" phi ").append(phis[i]).append("\n"); } } for(int i = BB.getFirstInstructionIndex(); i <= BB.getLastInstructionIndex(); i++) { if (insts[i] != null) { B.append(" ").append( insts[i] ); int[] uses = (int[]) valueNumbers.get(Pair.make(insts[i], USES)); if (uses.length > 0) { B.append(" (uses: "); for(int j = 0; j < uses.length; j++) { B.append(uses[j]).append(" "); } B.append(")"); } int[] defs = (int[]) valueNumbers.get(Pair.make(insts[i], DEFS)); if (defs.length > 0) { B.append(" (defs: "); for(int j = 0; j < defs.length; j++) { B.append(defs[j]).append(" "); } B.append(")"); } } B.append("\n"); } } return B.toString(); } }; } }