All Downloads are FREE. Search and download functionalities are using the official Maven repository.

edu.umd.cs.findbugs.ba.vna.ValueNumberAnalysis Maven / Gradle / Ivy

The newest version!
/*
 * Bytecode Analysis Framework
 * Copyright (C) 2003,2004 University of Maryland
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package edu.umd.cs.findbugs.ba.vna;

import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Iterator;

import javax.annotation.CheckForNull;

import org.apache.bcel.generic.INVOKEDYNAMIC;
import org.apache.bcel.generic.Instruction;
import org.apache.bcel.generic.InstructionHandle;
import org.apache.bcel.generic.InvokeInstruction;
import org.apache.bcel.generic.MethodGen;

import edu.umd.cs.findbugs.SystemProperties;
import edu.umd.cs.findbugs.ba.BasicBlock;
import edu.umd.cs.findbugs.ba.Dataflow;
import edu.umd.cs.findbugs.ba.DataflowAnalysisException;
import edu.umd.cs.findbugs.ba.DepthFirstSearch;
import edu.umd.cs.findbugs.ba.Edge;
import edu.umd.cs.findbugs.ba.FrameDataflowAnalysis;
import edu.umd.cs.findbugs.ba.Location;
import edu.umd.cs.findbugs.ba.RepositoryLookupFailureCallback;
import edu.umd.cs.findbugs.ba.SignatureParser;
import edu.umd.cs.findbugs.internalAnnotations.DottedClassName;

/**
 * A dataflow analysis to track the production and flow of values in the Java
 * stack frame. See the {@link ValueNumber ValueNumber} class for an explanation
 * of what the value numbers mean, and when they can be compared.
 *
 * 

* This class is still experimental. * * @author David Hovemeyer * @see ValueNumber * @see edu.umd.cs.findbugs.ba.DominatorsAnalysis */ public class ValueNumberAnalysis extends FrameDataflowAnalysis { private final static boolean TRACE = SystemProperties.getBoolean("vna.trace"); public static final boolean DEBUG = TRACE || SystemProperties.getBoolean("vna.debug"); private final MethodGen methodGen; private final ValueNumberFactory factory; private final ValueNumberFrameModelingVisitor visitor; private final ValueNumber[] entryLocalValueList; private final IdentityHashMap exceptionHandlerValueNumberMap; private ValueNumber thisValue; private final HashMap factAtLocationMap; private final HashMap factAfterLocationMap; private MergeTree mergeTree; public ValueNumberAnalysis(MethodGen methodGen, DepthFirstSearch dfs, LoadedFieldSet loadedFieldSet, RepositoryLookupFailureCallback lookupFailureCallback) { super(dfs); this.methodGen = methodGen; this.factory = new ValueNumberFactory(); ValueNumberCache cache = new ValueNumberCache(); this.visitor = new ValueNumberFrameModelingVisitor(methodGen, factory, cache, loadedFieldSet, lookupFailureCallback); int numLocals = methodGen.getMaxLocals(); this.entryLocalValueList = new ValueNumber[numLocals]; for (int i = 0; i < numLocals; ++i) { this.entryLocalValueList[i] = factory.createFreshValue(); } this.exceptionHandlerValueNumberMap = new IdentityHashMap(); // For non-static methods, keep track of which value represents the // "this" reference if (!methodGen.isStatic()) { this.thisValue = entryLocalValueList[0]; } this.factAtLocationMap = new HashMap(); this.factAfterLocationMap = new HashMap(); if (DEBUG) { System.out.println("VNA Analysis " + methodGen.getClassName() + "." + methodGen.getName() + " : " + methodGen.getSignature()); } } public ValueNumber getClassObjectValue(String className) { return visitor.factory.getClassObjectValue(className); } public void setMergeTree(MergeTree mergeTree) { this.mergeTree = mergeTree; } public MergeTree getMergeTree() { return mergeTree; } public ValueNumberFactory getFactory() { return factory; } public int getNumValuesAllocated() { return factory.getNumValuesAllocated(); } public boolean isThisValue(ValueNumber value) { return thisValue != null && thisValue.getNumber() == value.getNumber(); } public ValueNumber getThisValue() { return thisValue; } /** * Get the value number assigned to the given local variable upon entry to * the method. * * @param local * local variable number * @return ValueNumber assigned to the local variable */ public ValueNumber getEntryValue(int local) { return entryLocalValueList[local]; } /** * Get the value number assigned to the given parameter upon entry to the * method. * * @param param * a parameter (0 == first parameter) * @return the ValueNumber assigned to that parameter */ public ValueNumber getEntryValueForParameter(int param) { SignatureParser sigParser = new SignatureParser(methodGen.getSignature()); int p = 0; int slotOffset = methodGen.isStatic() ? 0 : 1; for ( String paramSig : sigParser.parameterSignatures()) { if (p == param) { return getEntryValue(slotOffset); } param++; slotOffset += SignatureParser.getNumSlotsForType(paramSig); } throw new IllegalStateException(); } @Override public ValueNumberFrame createFact() { return new ValueNumberFrame(methodGen.getMaxLocals()); } @Override public void initEntryFact(ValueNumberFrame result) { // Change the frame from TOP to something valid. result.setValid(); // At entry to the method, each local has (as far as we know) a unique // value. int numSlots = result.getNumSlots(); for (int i = 0; i < numSlots; ++i) { result.setValue(i, entryLocalValueList[i]); } } @Override public void transfer(BasicBlock basicBlock, InstructionHandle end, ValueNumberFrame start, ValueNumberFrame result) throws DataflowAnalysisException { if(basicBlock.isExceptionThrower() && isFactValid(start)) { /* If exceptionThrower is invoke instruction then it's possible that * it was partially executed before an exception occurred * So we have to kill available loads when control is transferred to the catch block */ InstructionHandle handle = basicBlock.getExceptionThrower(); Instruction inst = handle.getInstruction(); if(inst instanceof InvokeInstruction || inst instanceof INVOKEDYNAMIC) { copy(start, result); visitor.setFrameAndLocation(result, new Location(handle, basicBlock)); visitor.setHandle(handle); visitor.visitInvokeOnException(inst); return; } } super.transfer(basicBlock, end, start, result); } @Override public void transferInstruction(InstructionHandle handle, BasicBlock basicBlock, ValueNumberFrame fact) throws DataflowAnalysisException { Location location = new Location(handle, basicBlock); ValueNumberFrame atLocation = getFactAtLocation(location); copy(fact, atLocation); visitor.setFrameAndLocation(fact, location); visitor.setHandle(handle); visitor.analyzeInstruction(handle.getInstruction()); ValueNumberFrame afterLocation = getFactAfterLocation(location); copy(fact, afterLocation); } @Override public void meetInto(ValueNumberFrame fact, Edge edge, ValueNumberFrame result) throws DataflowAnalysisException { if (edge.getTarget().isExceptionHandler() && fact.isValid()) { // Special case: when merging predecessor facts for entry to // an exception handler, we clear the stack and push a // single entry for the exception object. That way, the locals // can still be merged. // Get the value number for the exception BasicBlock handlerBlock = edge.getTarget(); ValueNumber exceptionValueNumber = getExceptionValueNumber(handlerBlock); // Set up the stack frame ValueNumberFrame tmpFact = createFact(); tmpFact.copyFrom(fact); tmpFact.clearStack(); tmpFact.pushValue(exceptionValueNumber); fact = tmpFact; } mergeInto(fact, result); } @Override protected void mergeInto(ValueNumberFrame frame, ValueNumberFrame result) throws DataflowAnalysisException { result.mergeAvailableLoadSets(frame, factory, mergeTree); super.mergeInto(frame, result); } @Override protected void mergeValues(ValueNumberFrame otherFrame, ValueNumberFrame resultFrame, int slot) throws DataflowAnalysisException { ValueNumber value = mergeValues(resultFrame, slot, resultFrame.getValue(slot), otherFrame.getValue(slot)); resultFrame.setValue(slot, value); } private ValueNumber mergeValues(ValueNumberFrame frame, int slot, ValueNumber mine, ValueNumber other) { // Merging slot values: // - Merging identical values results in no change // - If the values are different, and the value in the result // frame is not the result of a previous result, a fresh value // is allocated. // - If the value in the result frame is the result of a // previous merge, IT STAYS THE SAME. // // The "one merge" rule means that merged values are essentially like // phi nodes. They combine some number of other values. // I believe (but haven't proved) that this technique is a dumb way // of computing SSA. if (mine != frame.getValue(slot)) { throw new IllegalStateException(); } if (mine.equals(other)) { return mine; } ValueNumber mergedValue = frame.getMergedValue(slot); if (mergedValue == null) { mergedValue = factory.createFreshValue(ValueNumber.mergeFlags(mine.getFlags(), other.getFlags()) | ValueNumber.PHI_NODE); frame.setMergedValue(slot, mergedValue); } if (mergeTree != null) { mergeTree.mapInputToOutput(mine, mergedValue); mergeTree.mapInputToOutput(other, mergedValue); } return mergedValue; } @Override public ValueNumberFrame getFactAtLocation(Location location) { ValueNumberFrame fact = factAtLocationMap.get(location); if (fact == null) { fact = createFact(); makeFactTop(fact); factAtLocationMap.put(location, fact); } return fact; } @Override public ValueNumberFrame getFactAfterLocation(Location location) { if (TRACE) { System.out.println("getting fact after " + location); } ValueNumberFrame fact = factAfterLocationMap.get(location); if (fact == null) { if (TRACE) { System.out .println("Initialized fact after " + location + " @ " + Integer.toHexString(System.identityHashCode(location)) + " in " + Integer.toHexString(System.identityHashCode(this)) + " : " + factAfterLocationMap.containsKey(location)); } fact = createFact(); makeFactTop(fact); factAfterLocationMap.put(location, fact); } return fact; } /** * Get an Iterator over all dataflow facts that we've recorded for the * Locations in the CFG. Note that this does not include result facts (since * there are no Locations corresponding to the end of basic blocks). */ public Iterator factIterator() { return factAtLocationMap.values().iterator(); } // These fields are used by the compactValueNumbers() method. /* private static class ValueCompacter { public final BitSet valuesUsed; public int numValuesUsed; public final int[] discovered; public ValueCompacter(int origNumValuesAllocated) { valuesUsed = new BitSet(); numValuesUsed = 0; // The "discovered" array tells us the mapping of old value numbers // to new (which are based on order of discovery). Negative values // specify value numbers which are not actually used (and thus can // be purged.) discovered = new int[origNumValuesAllocated]; for (int i = 0; i < discovered.length; ++i) { discovered[i] = -1; } } public boolean isUsed(int number) { return valuesUsed.get(number); } public void setUsed(int number) { valuesUsed.set(number, true); } public int allocateValue() { return numValuesUsed++; } } */ /** * Compact the value numbers assigned. This should be done only after the * dataflow algorithm has executed. This works by modifying the actual * ValueNumber objects assigned. After this method is called, the * getNumValuesAllocated() method of this object will return a value less * than or equal to the value it would have returned before the call to this * method. *

*

* This method should be called at most once. * * @param dataflow * the Dataflow object which executed this analysis (and has all * of the block result values) */ @Deprecated public void compactValueNumbers(Dataflow dataflow) { if (true) { throw new UnsupportedOperationException(); } /* ValueCompacter compacter = new ValueCompacter(factory.getNumValuesAllocated()); // We can get all extant Frames by looking at the values in // the location to value map, and also the block result values. for (Iterator i = factIterator(); i.hasNext();) { ValueNumberFrame frame = i.next(); markFrameValues(frame, compacter); } for (Iterator i = resultFactIterator(); i.hasNext();) { ValueNumberFrame frame = i.next(); markFrameValues(frame, compacter); } int before = factory.getNumValuesAllocated(); // Now the factory can modify the ValueNumbers. factory.compact(compacter.discovered, compacter.numValuesUsed); int after = factory.getNumValuesAllocated(); if (DEBUG && after < before && before > 0) { System.out.println("Value compaction: " + after + "/" + before + " (" + ((after * 100) / before) + "%)"); } */ } /** * Mark value numbers in a value number frame for compaction. * private static void markFrameValues(ValueNumberFrame frame, ValueCompacter compacter) { // We don't need to do anything for top and bottom frames. if (!frame.isValid()) { return; } for (int j = 0; j < frame.getNumSlots(); ++j) { ValueNumber value = frame.getValue(j); int number = value.getNumber(); if (!compacter.isUsed(number)) { compacter.discovered[number] = compacter.allocateValue(); compacter.setUsed(number); } } } */ // /** // * Test driver. // */ // public static void main(String[] argv) throws Exception { // // if (argv.length != 1) { // System.out.println("Usage: edu.umd.cs.findbugs.ba.ValueNumberAnalysis "); // System.exit(1); // } // // DataflowTestDriver driver = // new DataflowTestDriver() { // @Override // public Dataflow // createDataflow(ClassContext classContext, Method method) // throws CFGBuilderException, DataflowAnalysisException { // return classContext.getValueNumberDataflow(method); // } // }; // // driver.execute(argv[0]); // } private ValueNumber getExceptionValueNumber(BasicBlock handlerBlock) { ValueNumber valueNumber = exceptionHandlerValueNumberMap.get(handlerBlock); if (valueNumber == null) { valueNumber = factory.createFreshValue(); exceptionHandlerValueNumberMap.put(handlerBlock, valueNumber); } return valueNumber; } public @CheckForNull @DottedClassName String getClassName(ValueNumber v) { return factory.getClassName(v); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy