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