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 17 package com.android.dx.rop.code; 18 19 import com.android.dx.rop.type.Type; 20 import com.android.dx.rop.type.TypeList; 21 import com.android.dx.util.FixedSizeList; 22 import java.util.BitSet; 23 24 /** 25 * List of {@link RegisterSpec} instances. 26 */ 27 public final class RegisterSpecList 28 extends FixedSizeList implements TypeList { 29 /** {@code non-null;} no-element instance */ 30 public static final RegisterSpecList EMPTY = new RegisterSpecList(0); 31 32 /** 33 * Makes a single-element instance. 34 * 35 * @param spec {@code non-null;} the element 36 * @return {@code non-null;} an appropriately-constructed instance 37 */ make(RegisterSpec spec)38 public static RegisterSpecList make(RegisterSpec spec) { 39 RegisterSpecList result = new RegisterSpecList(1); 40 result.set(0, spec); 41 return result; 42 } 43 44 /** 45 * Makes a two-element instance. 46 * 47 * @param spec0 {@code non-null;} the first element 48 * @param spec1 {@code non-null;} the second element 49 * @return {@code non-null;} an appropriately-constructed instance 50 */ make(RegisterSpec spec0, RegisterSpec spec1)51 public static RegisterSpecList make(RegisterSpec spec0, 52 RegisterSpec spec1) { 53 RegisterSpecList result = new RegisterSpecList(2); 54 result.set(0, spec0); 55 result.set(1, spec1); 56 return result; 57 } 58 59 /** 60 * Makes a three-element instance. 61 * 62 * @param spec0 {@code non-null;} the first element 63 * @param spec1 {@code non-null;} the second element 64 * @param spec2 {@code non-null;} the third element 65 * @return {@code non-null;} an appropriately-constructed instance 66 */ make(RegisterSpec spec0, RegisterSpec spec1, RegisterSpec spec2)67 public static RegisterSpecList make(RegisterSpec spec0, RegisterSpec spec1, 68 RegisterSpec spec2) { 69 RegisterSpecList result = new RegisterSpecList(3); 70 result.set(0, spec0); 71 result.set(1, spec1); 72 result.set(2, spec2); 73 return result; 74 } 75 76 /** 77 * Makes a four-element instance. 78 * 79 * @param spec0 {@code non-null;} the first element 80 * @param spec1 {@code non-null;} the second element 81 * @param spec2 {@code non-null;} the third element 82 * @param spec3 {@code non-null;} the fourth element 83 * @return {@code non-null;} an appropriately-constructed instance 84 */ make(RegisterSpec spec0, RegisterSpec spec1, RegisterSpec spec2, RegisterSpec spec3)85 public static RegisterSpecList make(RegisterSpec spec0, RegisterSpec spec1, 86 RegisterSpec spec2, 87 RegisterSpec spec3) { 88 RegisterSpecList result = new RegisterSpecList(4); 89 result.set(0, spec0); 90 result.set(1, spec1); 91 result.set(2, spec2); 92 result.set(3, spec3); 93 return result; 94 } 95 96 /** 97 * Constructs an instance. All indices initially contain {@code null}. 98 * 99 * @param size the size of the list 100 */ RegisterSpecList(int size)101 public RegisterSpecList(int size) { 102 super(size); 103 } 104 105 /** {@inheritDoc} */ getType(int n)106 public Type getType(int n) { 107 return get(n).getType().getType(); 108 } 109 110 /** {@inheritDoc} */ getWordCount()111 public int getWordCount() { 112 int sz = size(); 113 int result = 0; 114 115 for (int i = 0; i < sz; i++) { 116 result += getType(i).getCategory(); 117 } 118 119 return result; 120 } 121 122 /** {@inheritDoc} */ withAddedType(Type type)123 public TypeList withAddedType(Type type) { 124 throw new UnsupportedOperationException("unsupported"); 125 } 126 127 /** 128 * Gets the indicated element. It is an error to call this with the 129 * index for an element which was never set; if you do that, this 130 * will throw {@code NullPointerException}. 131 * 132 * @param n {@code >= 0, < size();} which element 133 * @return {@code non-null;} the indicated element 134 */ get(int n)135 public RegisterSpec get(int n) { 136 return (RegisterSpec) get0(n); 137 } 138 139 /** 140 * Returns a RegisterSpec in this list that uses the specified register, 141 * or null if there is none in this list. 142 * @param reg Register to find 143 * @return RegisterSpec that uses argument or null. 144 */ specForRegister(int reg)145 public RegisterSpec specForRegister(int reg) { 146 int sz = size(); 147 for (int i = 0; i < sz; i++) { 148 RegisterSpec rs; 149 150 rs = get(i); 151 152 if (rs.getReg() == reg) { 153 return rs; 154 } 155 } 156 157 return null; 158 } 159 160 /** 161 * Returns the index of a RegisterSpec in this list that uses the specified 162 * register, or -1 if none in this list uses the register. 163 * @param reg Register to find 164 * @return index of RegisterSpec or -1 165 */ indexOfRegister(int reg)166 public int indexOfRegister(int reg) { 167 int sz = size(); 168 for (int i = 0; i < sz; i++) { 169 RegisterSpec rs; 170 171 rs = get(i); 172 173 if (rs.getReg() == reg) { 174 return i; 175 } 176 } 177 178 return -1; 179 } 180 181 /** 182 * Sets the element at the given index. 183 * 184 * @param n {@code >= 0, < size();} which element 185 * @param spec {@code non-null;} the value to store 186 */ set(int n, RegisterSpec spec)187 public void set(int n, RegisterSpec spec) { 188 set0(n, spec); 189 } 190 191 /** 192 * Gets the minimum required register count implied by this 193 * instance. This is equal to the highest register number referred 194 * to plus the widest width (largest category) of the type used in 195 * that register. 196 * 197 * @return {@code >= 0;} the required registers size 198 */ getRegistersSize()199 public int getRegistersSize() { 200 int sz = size(); 201 int result = 0; 202 203 for (int i = 0; i < sz; i++) { 204 RegisterSpec spec = (RegisterSpec) get0(i); 205 if (spec != null) { 206 int min = spec.getNextReg(); 207 if (min > result) { 208 result = min; 209 } 210 } 211 } 212 213 return result; 214 } 215 216 /** 217 * Returns a new instance, which is the same as this instance, 218 * except that it has an additional element prepended to the original. 219 * Mutability of the result is inherited from the original. 220 * 221 * @param spec {@code non-null;} the new first spec (to prepend) 222 * @return {@code non-null;} an appropriately-constructed instance 223 */ withFirst(RegisterSpec spec)224 public RegisterSpecList withFirst(RegisterSpec spec) { 225 int sz = size(); 226 RegisterSpecList result = new RegisterSpecList(sz + 1); 227 228 for (int i = 0; i < sz; i++) { 229 result.set0(i + 1, get0(i)); 230 } 231 232 result.set0(0, spec); 233 if (isImmutable()) { 234 result.setImmutable(); 235 } 236 237 return result; 238 } 239 240 /** 241 * Returns a new instance, which is the same as this instance, 242 * except that its first element is removed. Mutability of the 243 * result is inherited from the original. 244 * 245 * @return {@code non-null;} an appropriately-constructed instance 246 */ withoutFirst()247 public RegisterSpecList withoutFirst() { 248 int newSize = size() - 1; 249 250 if (newSize == 0) { 251 return EMPTY; 252 } 253 254 RegisterSpecList result = new RegisterSpecList(newSize); 255 256 for (int i = 0; i < newSize; i++) { 257 result.set0(i, get0(i + 1)); 258 } 259 260 if (isImmutable()) { 261 result.setImmutable(); 262 } 263 264 return result; 265 } 266 267 /** 268 * Returns a new instance, which is the same as this instance, 269 * except that its last element is removed. Mutability of the 270 * result is inherited from the original. 271 * 272 * @return {@code non-null;} an appropriately-constructed instance 273 */ withoutLast()274 public RegisterSpecList withoutLast() { 275 int newSize = size() - 1; 276 277 if (newSize == 0) { 278 return EMPTY; 279 } 280 281 RegisterSpecList result = new RegisterSpecList(newSize); 282 283 for (int i = 0; i < newSize; i++) { 284 result.set0(i, get0(i)); 285 } 286 287 if (isImmutable()) { 288 result.setImmutable(); 289 } 290 291 return result; 292 } 293 294 /** 295 * Returns a new instance, which contains a subset of the elements 296 * specified by the given BitSet. Indexes in the BitSet with a zero 297 * are included, while indexes with a one are excluded. Mutability 298 * of the result is inherited from the original. 299 * 300 * @param exclusionSet {@code non-null;} set of registers to exclude 301 * @return {@code non-null;} an appropriately-constructed instance 302 */ subset(BitSet exclusionSet)303 public RegisterSpecList subset(BitSet exclusionSet) { 304 int newSize = size() - exclusionSet.cardinality(); 305 306 if (newSize == 0) { 307 return EMPTY; 308 } 309 310 RegisterSpecList result = new RegisterSpecList(newSize); 311 312 int newIndex = 0; 313 for (int oldIndex = 0; oldIndex < size(); oldIndex++) { 314 if (!exclusionSet.get(oldIndex)) { 315 result.set0(newIndex, get0(oldIndex)); 316 newIndex++; 317 } 318 } 319 320 if (isImmutable()) { 321 result.setImmutable(); 322 } 323 324 return result; 325 } 326 327 /** 328 * Returns an instance that is identical to this one, except that 329 * all register numbers are offset by the given amount. Mutability 330 * of the result is inherited from the original. 331 * 332 * @param delta the amount to offset the register numbers by 333 * @return {@code non-null;} an appropriately-constructed instance 334 */ withOffset(int delta)335 public RegisterSpecList withOffset(int delta) { 336 int sz = size(); 337 338 if (sz == 0) { 339 // Don't bother making a new zero-element instance. 340 return this; 341 } 342 343 RegisterSpecList result = new RegisterSpecList(sz); 344 345 for (int i = 0; i < sz; i++) { 346 RegisterSpec one = (RegisterSpec) get0(i); 347 if (one != null) { 348 result.set0(i, one.withOffset(delta)); 349 } 350 } 351 352 if (isImmutable()) { 353 result.setImmutable(); 354 } 355 356 return result; 357 } 358 359 /** 360 * Returns an instance that is identical to this one, except that 361 * all incompatible register numbers are renumbered sequentially from 362 * the given base, with the first number duplicated if indicated. If 363 * a null BitSet is given, it indicates all registers are incompatible. 364 * 365 * @param base the base register number 366 * @param duplicateFirst whether to duplicate the first number 367 * @param compatRegs {@code null-ok;} either a {@code non-null} set of 368 * compatible registers, or {@code null} to indicate all registers are 369 * incompatible 370 * @return {@code non-null;} an appropriately-constructed instance 371 */ withExpandedRegisters(int base, boolean duplicateFirst, BitSet compatRegs)372 public RegisterSpecList withExpandedRegisters(int base, 373 boolean duplicateFirst, 374 BitSet compatRegs) { 375 int sz = size(); 376 377 if (sz == 0) { 378 // Don't bother making a new zero-element instance. 379 return this; 380 } 381 382 Expander expander = new Expander(this, compatRegs, base, duplicateFirst); 383 384 for (int regIdx = 0; regIdx < sz; regIdx++) { 385 expander.expandRegister(regIdx); 386 } 387 388 return expander.getResult(); 389 } 390 391 private static class Expander { 392 private BitSet compatRegs; 393 private RegisterSpecList regSpecList; 394 private int base; 395 private RegisterSpecList result; 396 private boolean duplicateFirst; 397 Expander(RegisterSpecList regSpecList, BitSet compatRegs, int base, boolean duplicateFirst)398 private Expander(RegisterSpecList regSpecList, BitSet compatRegs, int base, 399 boolean duplicateFirst) { 400 this.regSpecList = regSpecList; 401 this.compatRegs = compatRegs; 402 this.base = base; 403 this.result = new RegisterSpecList(regSpecList.size()); 404 this.duplicateFirst = duplicateFirst; 405 } 406 expandRegister(int regIdx)407 private void expandRegister(int regIdx) { 408 expandRegister(regIdx, (RegisterSpec) regSpecList.get0(regIdx)); 409 } 410 expandRegister(int regIdx, RegisterSpec registerToExpand)411 private void expandRegister(int regIdx, RegisterSpec registerToExpand) { 412 boolean replace = (compatRegs == null) ? true : !compatRegs.get(regIdx); 413 RegisterSpec expandedReg; 414 415 if (replace) { 416 expandedReg = registerToExpand.withReg(base); 417 if (!duplicateFirst) { 418 base += expandedReg.getCategory(); 419 } 420 } else { 421 expandedReg = registerToExpand; 422 } 423 424 // Reset duplicateFirst when the first register has been dealt with. 425 duplicateFirst = false; 426 427 result.set0(regIdx, expandedReg); 428 } 429 getResult()430 private RegisterSpecList getResult() { 431 if (regSpecList.isImmutable()) { 432 result.setImmutable(); 433 } 434 435 return result; 436 } 437 } 438 } 439