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