blob: 99a10eeefc2b80408cd90d685610b7df8099a65b [file] [log] [blame]
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.dx.rop.code;
import com.android.dx.rop.type.TypeBearer;
import com.android.dx.util.MutabilityControl;
import java.util.HashMap;
/**
* Container for local variable information for a particular {@link
* RopMethod}.
*/
public final class LocalVariableInfo
extends MutabilityControl {
/** {@code >= 0;} the register count for the method */
private final int regCount;
/**
* {@code non-null;} {@link RegisterSpecSet} to use when indicating a block
* that has no locals; it is empty and immutable but has an appropriate
* max size for the method
*/
private final RegisterSpecSet emptySet;
/**
* {@code non-null;} array consisting of register sets representing the
* sets of variables already assigned upon entry to each block,
* where array indices correspond to block labels
*/
private final RegisterSpecSet[] blockStarts;
/** {@code non-null;} map from instructions to the variable each assigns */
private final HashMap<Insn, RegisterSpec> insnAssignments;
/**
* Constructs an instance.
*
* @param method {@code non-null;} the method being represented by this instance
*/
public LocalVariableInfo(RopMethod method) {
if (method == null) {
throw new NullPointerException("method == null");
}
BasicBlockList blocks = method.getBlocks();
int maxLabel = blocks.getMaxLabel();
this.regCount = blocks.getRegCount();
this.emptySet = new RegisterSpecSet(regCount);
this.blockStarts = new RegisterSpecSet[maxLabel];
this.insnAssignments =
new HashMap<Insn, RegisterSpec>(blocks.getInstructionCount());
emptySet.setImmutable();
}
/**
* Sets the register set associated with the start of the block with
* the given label.
*
* @param label {@code >= 0;} the block label
* @param specs {@code non-null;} the register set to associate with the block
*/
public void setStarts(int label, RegisterSpecSet specs) {
throwIfImmutable();
if (specs == null) {
throw new NullPointerException("specs == null");
}
try {
blockStarts[label] = specs;
} catch (ArrayIndexOutOfBoundsException ex) {
// Translate the exception.
throw new IllegalArgumentException("bogus label");
}
}
/**
* Merges the given register set into the set for the block with the
* given label. If there was not already an associated set, then this
* is the same as calling {@link #setStarts}. Otherwise, this will
* merge the two sets and call {@link #setStarts} on the result of the
* merge.
*
* @param label {@code >= 0;} the block label
* @param specs {@code non-null;} the register set to merge into the start set
* for the block
* @return {@code true} if the merge resulted in an actual change
* to the associated set (including storing one for the first time) or
* {@code false} if there was no change
*/
public boolean mergeStarts(int label, RegisterSpecSet specs) {
RegisterSpecSet start = getStarts0(label);
boolean changed = false;
if (start == null) {
setStarts(label, specs);
return true;
}
RegisterSpecSet newStart = start.mutableCopy();
newStart.intersect(specs, true);
if (start.equals(newStart)) {
return false;
}
newStart.setImmutable();
setStarts(label, newStart);
return true;
}
/**
* Gets the register set associated with the start of the block
* with the given label. This returns an empty set with the appropriate
* max size if no set was associated with the block in question.
*
* @param label {@code >= 0;} the block label
* @return {@code non-null;} the associated register set
*/
public RegisterSpecSet getStarts(int label) {
RegisterSpecSet result = getStarts0(label);
return (result != null) ? result : emptySet;
}
/**
* Gets the register set associated with the start of the given
* block. This is just convenient shorthand for
* {@code getStarts(block.getLabel())}.
*
* @param block {@code non-null;} the block in question
* @return {@code non-null;} the associated register set
*/
public RegisterSpecSet getStarts(BasicBlock block) {
return getStarts(block.getLabel());
}
/**
* Gets a mutable copy of the register set associated with the
* start of the block with the given label. This returns a
* newly-allocated empty {@link RegisterSpecSet} of appropriate
* max size if there is not yet any set associated with the block.
*
* @param label {@code >= 0;} the block label
* @return {@code non-null;} the associated register set
*/
public RegisterSpecSet mutableCopyOfStarts(int label) {
RegisterSpecSet result = getStarts0(label);
return (result != null) ?
result.mutableCopy() : new RegisterSpecSet(regCount);
}
/**
* Adds an assignment association for the given instruction and
* register spec. This throws an exception if the instruction
* doesn't actually perform a named variable assignment.
*
* <b>Note:</b> Although the instruction contains its own spec for
* the result, it still needs to be passed in explicitly to this
* method, since the spec that is stored here should always have a
* simple type and the one in the instruction can be an arbitrary
* {@link TypeBearer} (such as a constant value).
*
* @param insn {@code non-null;} the instruction in question
* @param spec {@code non-null;} the associated register spec
*/
public void addAssignment(Insn insn, RegisterSpec spec) {
throwIfImmutable();
if (insn == null) {
throw new NullPointerException("insn == null");
}
if (spec == null) {
throw new NullPointerException("spec == null");
}
insnAssignments.put(insn, spec);
}
/**
* Gets the named register being assigned by the given instruction, if
* previously stored in this instance.
*
* @param insn {@code non-null;} instruction in question
* @return {@code null-ok;} the named register being assigned, if any
*/
public RegisterSpec getAssignment(Insn insn) {
return insnAssignments.get(insn);
}
/**
* Gets the number of assignments recorded by this instance.
*
* @return {@code >= 0;} the number of assignments
*/
public int getAssignmentCount() {
return insnAssignments.size();
}
public void debugDump() {
for (int label = 0 ; label < blockStarts.length; label++) {
if (blockStarts[label] == null) {
continue;
}
if (blockStarts[label] == emptySet) {
System.out.printf("%04x: empty set\n", label);
} else {
System.out.printf("%04x: %s\n", label, blockStarts[label]);
}
}
}
/**
* Helper method, to get the starts for a label, throwing the
* right exception for range problems.
*
* @param label {@code >= 0;} the block label
* @return {@code null-ok;} associated register set or {@code null} if there
* is none
*/
private RegisterSpecSet getStarts0(int label) {
try {
return blockStarts[label];
} catch (ArrayIndexOutOfBoundsException ex) {
// Translate the exception.
throw new IllegalArgumentException("bogus label");
}
}
}