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