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    public Type getType(int n) {
107        return get(n).getType().getType();
108    }
109
110    /** {@inheritDoc} */
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} */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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 compatible.
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     * compatible
370     * @return {@code non-null;} an appropriately-constructed instance
371     */
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        RegisterSpecList result = new RegisterSpecList(sz);
383
384        for (int i = 0; i < sz; i++) {
385            RegisterSpec one = (RegisterSpec) get0(i);
386            boolean replace = (compatRegs == null) ? true : !compatRegs.get(i);
387
388            if (replace) {
389                result.set0(i, one.withReg(base));
390                if (!duplicateFirst) {
391                    base += one.getCategory();
392                }
393            } else {
394                result.set0(i, one);
395            }
396
397            if (duplicateFirst) {
398                duplicateFirst = false;
399            }
400        }
401
402        if (isImmutable()) {
403            result.setImmutable();
404        }
405
406        return result;
407    }
408}
409