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.rop.code;
18
19import com.android.dx.rop.type.Type;
20import com.android.dx.rop.type.TypeList;
21import com.android.dx.util.FixedSizeList;
22import java.util.BitSet;
23
24/**
25 * List of {@link RegisterSpec} instances.
26 */
27public 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     */
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     */
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     */
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     */
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     */
101    public RegisterSpecList(int size) {
102        super(size);
103    }
104
105    /** {@inheritDoc} */
106    @Override
107    public Type getType(int n) {
108        return get(n).getType().getType();
109    }
110
111    /** {@inheritDoc} */
112    @Override
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
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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
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
410      private void expandRegister(int regIdx) {
411        expandRegister(regIdx, (RegisterSpec) regSpecList.get0(regIdx));
412      }
413
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
433      private RegisterSpecList getResult() {
434        if (regSpecList.isImmutable()) {
435          result.setImmutable();
436        }
437
438        return result;
439      }
440    }
441}
442