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