| /* |
| * 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.ssa; |
| |
| import com.android.dx.util.MutabilityControl; |
| import com.android.dx.rop.code.RegisterSpecSet; |
| import com.android.dx.rop.code.RegisterSpec; |
| |
| import java.util.HashMap; |
| import java.util.List; |
| |
| /** |
| * Container for local variable information for a particular {@link |
| * com.android.dx.ssa.SsaMethod}. |
| * Stolen from {@link com.android.dx.rop.code.LocalVariableInfo}. |
| */ |
| public class LocalVariableInfo extends MutabilityControl { |
| /** {@code >= 0;} the register count for the method */ |
| private final int regCount; |
| |
| /** |
| * {@code non-null;} {@link com.android.dx.rop.code.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 indices |
| */ |
| private final RegisterSpecSet[] blockStarts; |
| |
| /** {@code non-null;} map from instructions to the variable each assigns */ |
| private final HashMap<SsaInsn, RegisterSpec> insnAssignments; |
| |
| /** |
| * Constructs an instance. |
| * |
| * @param method {@code non-null;} the method being represented by this instance |
| */ |
| public LocalVariableInfo(SsaMethod method) { |
| if (method == null) { |
| throw new NullPointerException("method == null"); |
| } |
| |
| List<SsaBasicBlock> blocks = method.getBlocks(); |
| |
| this.regCount = method.getRegCount(); |
| this.emptySet = new RegisterSpecSet(regCount); |
| this.blockStarts = new RegisterSpecSet[blocks.size()]; |
| this.insnAssignments = |
| new HashMap<SsaInsn, RegisterSpec>(/*hint here*/); |
| |
| emptySet.setImmutable(); |
| } |
| |
| /** |
| * Sets the register set associated with the start of the block with |
| * the given index. |
| * |
| * @param index {@code >= 0;} the block index |
| * @param specs {@code non-null;} the register set to associate with the block |
| */ |
| public void setStarts(int index, RegisterSpecSet specs) { |
| throwIfImmutable(); |
| |
| if (specs == null) { |
| throw new NullPointerException("specs == null"); |
| } |
| |
| try { |
| blockStarts[index] = specs; |
| } catch (ArrayIndexOutOfBoundsException ex) { |
| // Translate the exception. |
| throw new IllegalArgumentException("bogus index"); |
| } |
| } |
| |
| /** |
| * Merges the given register set into the set for the block with the |
| * given index. 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 index {@code >= 0;} the block index |
| * @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 index, RegisterSpecSet specs) { |
| RegisterSpecSet start = getStarts0(index); |
| boolean changed = false; |
| |
| if (start == null) { |
| setStarts(index, specs); |
| return true; |
| } |
| |
| RegisterSpecSet newStart = start.mutableCopy(); |
| newStart.intersect(specs, true); |
| |
| if (start.equals(newStart)) { |
| return false; |
| } |
| |
| newStart.setImmutable(); |
| setStarts(index, newStart); |
| |
| return true; |
| } |
| |
| /** |
| * Gets the register set associated with the start of the block |
| * with the given index. This returns an empty set with the appropriate |
| * max size if no set was associated with the block in question. |
| * |
| * @param index {@code >= 0;} the block index |
| * @return {@code non-null;} the associated register set |
| */ |
| public RegisterSpecSet getStarts(int index) { |
| RegisterSpecSet result = getStarts0(index); |
| |
| 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(SsaBasicBlock block) { |
| return getStarts(block.getIndex()); |
| } |
| |
| /** |
| * Gets a mutable copy of the register set associated with the |
| * start of the block with the given index. This returns a |
| * newly-allocated empty {@link RegisterSpecSet} of appropriate |
| * max size if there is not yet any set associated with the block. |
| * |
| * @param index {@code >= 0;} the block index |
| * @return {@code non-null;} the associated register set |
| */ |
| public RegisterSpecSet mutableCopyOfStarts(int index) { |
| RegisterSpecSet result = getStarts0(index); |
| |
| 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 com.android.dx.rop.type.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(SsaInsn 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(SsaInsn 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 index = 0 ; index < blockStarts.length; index++) { |
| if (blockStarts[index] == null) { |
| continue; |
| } |
| |
| if (blockStarts[index] == emptySet) { |
| System.out.printf("%04x: empty set\n", index); |
| } else { |
| System.out.printf("%04x: %s\n", index, blockStarts[index]); |
| } |
| } |
| } |
| |
| /** |
| * Helper method, to get the starts for a index, throwing the |
| * right exception for range problems. |
| * |
| * @param index {@code >= 0;} the block index |
| * @return {@code null-ok;} associated register set or {@code null} if there |
| * is none |
| */ |
| private RegisterSpecSet getStarts0(int index) { |
| try { |
| return blockStarts[index]; |
| } catch (ArrayIndexOutOfBoundsException ex) { |
| // Translate the exception. |
| throw new IllegalArgumentException("bogus index"); |
| } |
| } |
| } |