1/* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.dx.ssa; 18 19import com.android.dx.rop.code.RegisterSpec; 20import com.android.dx.rop.code.RegisterSpecSet; 21import com.android.dx.util.MutabilityControl; 22import java.util.HashMap; 23import java.util.List; 24 25/** 26 * Container for local variable information for a particular {@link 27 * com.android.dx.ssa.SsaMethod}. 28 * Stolen from {@link com.android.dx.rop.code.LocalVariableInfo}. 29 */ 30public class LocalVariableInfo extends MutabilityControl { 31 /** {@code >= 0;} the register count for the method */ 32 private final int regCount; 33 34 /** 35 * {@code non-null;} {@link com.android.dx.rop.code.RegisterSpecSet} to use when indicating a block 36 * that has no locals; it is empty and immutable but has an appropriate 37 * max size for the method 38 */ 39 private final RegisterSpecSet emptySet; 40 41 /** 42 * {@code non-null;} array consisting of register sets representing the 43 * sets of variables already assigned upon entry to each block, 44 * where array indices correspond to block indices 45 */ 46 private final RegisterSpecSet[] blockStarts; 47 48 /** {@code non-null;} map from instructions to the variable each assigns */ 49 private final HashMap<SsaInsn, RegisterSpec> insnAssignments; 50 51 /** 52 * Constructs an instance. 53 * 54 * @param method {@code non-null;} the method being represented by this instance 55 */ 56 public LocalVariableInfo(SsaMethod method) { 57 if (method == null) { 58 throw new NullPointerException("method == null"); 59 } 60 61 List<SsaBasicBlock> blocks = method.getBlocks(); 62 63 this.regCount = method.getRegCount(); 64 this.emptySet = new RegisterSpecSet(regCount); 65 this.blockStarts = new RegisterSpecSet[blocks.size()]; 66 this.insnAssignments = 67 new HashMap<SsaInsn, RegisterSpec>(/*hint here*/); 68 69 emptySet.setImmutable(); 70 } 71 72 /** 73 * Sets the register set associated with the start of the block with 74 * the given index. 75 * 76 * @param index {@code >= 0;} the block index 77 * @param specs {@code non-null;} the register set to associate with the block 78 */ 79 public void setStarts(int index, RegisterSpecSet specs) { 80 throwIfImmutable(); 81 82 if (specs == null) { 83 throw new NullPointerException("specs == null"); 84 } 85 86 try { 87 blockStarts[index] = specs; 88 } catch (ArrayIndexOutOfBoundsException ex) { 89 // Translate the exception. 90 throw new IllegalArgumentException("bogus index"); 91 } 92 } 93 94 /** 95 * Merges the given register set into the set for the block with the 96 * given index. If there was not already an associated set, then this 97 * is the same as calling {@link #setStarts}. Otherwise, this will 98 * merge the two sets and call {@link #setStarts} on the result of the 99 * merge. 100 * 101 * @param index {@code >= 0;} the block index 102 * @param specs {@code non-null;} the register set to merge into the start set 103 * for the block 104 * @return {@code true} if the merge resulted in an actual change 105 * to the associated set (including storing one for the first time) or 106 * {@code false} if there was no change 107 */ 108 public boolean mergeStarts(int index, RegisterSpecSet specs) { 109 RegisterSpecSet start = getStarts0(index); 110 boolean changed = false; 111 112 if (start == null) { 113 setStarts(index, specs); 114 return true; 115 } 116 117 RegisterSpecSet newStart = start.mutableCopy(); 118 newStart.intersect(specs, true); 119 120 if (start.equals(newStart)) { 121 return false; 122 } 123 124 newStart.setImmutable(); 125 setStarts(index, newStart); 126 127 return true; 128 } 129 130 /** 131 * Gets the register set associated with the start of the block 132 * with the given index. This returns an empty set with the appropriate 133 * max size if no set was associated with the block in question. 134 * 135 * @param index {@code >= 0;} the block index 136 * @return {@code non-null;} the associated register set 137 */ 138 public RegisterSpecSet getStarts(int index) { 139 RegisterSpecSet result = getStarts0(index); 140 141 return (result != null) ? result : emptySet; 142 } 143 144 /** 145 * Gets the register set associated with the start of the given 146 * block. This is just convenient shorthand for 147 * {@code getStarts(block.getLabel())}. 148 * 149 * @param block {@code non-null;} the block in question 150 * @return {@code non-null;} the associated register set 151 */ 152 public RegisterSpecSet getStarts(SsaBasicBlock block) { 153 return getStarts(block.getIndex()); 154 } 155 156 /** 157 * Gets a mutable copy of the register set associated with the 158 * start of the block with the given index. This returns a 159 * newly-allocated empty {@link RegisterSpecSet} of appropriate 160 * max size if there is not yet any set associated with the block. 161 * 162 * @param index {@code >= 0;} the block index 163 * @return {@code non-null;} the associated register set 164 */ 165 public RegisterSpecSet mutableCopyOfStarts(int index) { 166 RegisterSpecSet result = getStarts0(index); 167 168 return (result != null) ? 169 result.mutableCopy() : new RegisterSpecSet(regCount); 170 } 171 172 /** 173 * Adds an assignment association for the given instruction and 174 * register spec. This throws an exception if the instruction 175 * doesn't actually perform a named variable assignment. 176 * 177 * <b>Note:</b> Although the instruction contains its own spec for 178 * the result, it still needs to be passed in explicitly to this 179 * method, since the spec that is stored here should always have a 180 * simple type and the one in the instruction can be an arbitrary 181 * {@link com.android.dx.rop.type.TypeBearer} (such as a constant value). 182 * 183 * @param insn {@code non-null;} the instruction in question 184 * @param spec {@code non-null;} the associated register spec 185 */ 186 public void addAssignment(SsaInsn insn, RegisterSpec spec) { 187 throwIfImmutable(); 188 189 if (insn == null) { 190 throw new NullPointerException("insn == null"); 191 } 192 193 if (spec == null) { 194 throw new NullPointerException("spec == null"); 195 } 196 197 insnAssignments.put(insn, spec); 198 } 199 200 /** 201 * Gets the named register being assigned by the given instruction, if 202 * previously stored in this instance. 203 * 204 * @param insn {@code non-null;} instruction in question 205 * @return {@code null-ok;} the named register being assigned, if any 206 */ 207 public RegisterSpec getAssignment(SsaInsn insn) { 208 return insnAssignments.get(insn); 209 } 210 211 /** 212 * Gets the number of assignments recorded by this instance. 213 * 214 * @return {@code >= 0;} the number of assignments 215 */ 216 public int getAssignmentCount() { 217 return insnAssignments.size(); 218 } 219 220 public void debugDump() { 221 for (int index = 0 ; index < blockStarts.length; index++) { 222 if (blockStarts[index] == null) { 223 continue; 224 } 225 226 if (blockStarts[index] == emptySet) { 227 System.out.printf("%04x: empty set\n", index); 228 } else { 229 System.out.printf("%04x: %s\n", index, blockStarts[index]); 230 } 231 } 232 } 233 234 /** 235 * Helper method, to get the starts for a index, throwing the 236 * right exception for range problems. 237 * 238 * @param index {@code >= 0;} the block index 239 * @return {@code null-ok;} associated register set or {@code null} if there 240 * is none 241 */ 242 private RegisterSpecSet getStarts0(int index) { 243 try { 244 return blockStarts[index]; 245 } catch (ArrayIndexOutOfBoundsException ex) { 246 // Translate the exception. 247 throw new IllegalArgumentException("bogus index"); 248 } 249 } 250} 251