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.ssa;
18
19import com.android.dx.rop.code.RegisterSpec;
20import com.android.dx.rop.code.RegisterSpecSet;
21import com.android.dx.util.MutabilityControl;
22import java.util.HashMap;
23import java.util.List;
24
25/**
26 * Container for local variable information for a particular {@link
27 * com.android.dx.ssa.SsaMethod}.
28 * Stolen from {@link com.android.dx.rop.code.LocalVariableInfo}.
29 */
30public class LocalVariableInfo         extends MutabilityControl {
31    /** {@code >= 0;} the register count for the method */
32    private final int regCount;
33
34    /**
35     * {@code non-null;} {@link com.android.dx.rop.code.RegisterSpecSet} to use when indicating a block
36     * that has no locals; it is empty and immutable but has an appropriate
37     * max size for the method
38     */
39    private final RegisterSpecSet emptySet;
40
41    /**
42     * {@code non-null;} array consisting of register sets representing the
43     * sets of variables already assigned upon entry to each block,
44     * where array indices correspond to block indices
45     */
46    private final RegisterSpecSet[] blockStarts;
47
48    /** {@code non-null;} map from instructions to the variable each assigns */
49    private final HashMap<SsaInsn, RegisterSpec> insnAssignments;
50
51    /**
52     * Constructs an instance.
53     *
54     * @param method {@code non-null;} the method being represented by this instance
55     */
56    public LocalVariableInfo(SsaMethod method) {
57        if (method == null) {
58            throw new NullPointerException("method == null");
59        }
60
61        List<SsaBasicBlock> blocks = method.getBlocks();
62
63        this.regCount = method.getRegCount();
64        this.emptySet = new RegisterSpecSet(regCount);
65        this.blockStarts = new RegisterSpecSet[blocks.size()];
66        this.insnAssignments =
67            new HashMap<SsaInsn, RegisterSpec>(/*hint here*/);
68
69        emptySet.setImmutable();
70    }
71
72    /**
73     * Sets the register set associated with the start of the block with
74     * the given index.
75     *
76     * @param index {@code >= 0;} the block index
77     * @param specs {@code non-null;} the register set to associate with the block
78     */
79    public void setStarts(int index, RegisterSpecSet specs) {
80        throwIfImmutable();
81
82        if (specs == null) {
83            throw new NullPointerException("specs == null");
84        }
85
86        try {
87            blockStarts[index] = specs;
88        } catch (ArrayIndexOutOfBoundsException ex) {
89            // Translate the exception.
90            throw new IllegalArgumentException("bogus index");
91        }
92    }
93
94    /**
95     * Merges the given register set into the set for the block with the
96     * given index. If there was not already an associated set, then this
97     * is the same as calling {@link #setStarts}. Otherwise, this will
98     * merge the two sets and call {@link #setStarts} on the result of the
99     * merge.
100     *
101     * @param index {@code >= 0;} the block index
102     * @param specs {@code non-null;} the register set to merge into the start set
103     * for the block
104     * @return {@code true} if the merge resulted in an actual change
105     * to the associated set (including storing one for the first time) or
106     * {@code false} if there was no change
107     */
108    public boolean mergeStarts(int index, RegisterSpecSet specs) {
109        RegisterSpecSet start = getStarts0(index);
110        boolean changed = false;
111
112        if (start == null) {
113            setStarts(index, specs);
114            return true;
115        }
116
117        RegisterSpecSet newStart = start.mutableCopy();
118        newStart.intersect(specs, true);
119
120        if (start.equals(newStart)) {
121            return false;
122        }
123
124        newStart.setImmutable();
125        setStarts(index, newStart);
126
127        return true;
128    }
129
130    /**
131     * Gets the register set associated with the start of the block
132     * with the given index. This returns an empty set with the appropriate
133     * max size if no set was associated with the block in question.
134     *
135     * @param index {@code >= 0;} the block index
136     * @return {@code non-null;} the associated register set
137     */
138    public RegisterSpecSet getStarts(int index) {
139        RegisterSpecSet result = getStarts0(index);
140
141        return (result != null) ? result : emptySet;
142    }
143
144    /**
145     * Gets the register set associated with the start of the given
146     * block. This is just convenient shorthand for
147     * {@code getStarts(block.getLabel())}.
148     *
149     * @param block {@code non-null;} the block in question
150     * @return {@code non-null;} the associated register set
151     */
152    public RegisterSpecSet getStarts(SsaBasicBlock block) {
153        return getStarts(block.getIndex());
154    }
155
156    /**
157     * Gets a mutable copy of the register set associated with the
158     * start of the block with the given index. This returns a
159     * newly-allocated empty {@link RegisterSpecSet} of appropriate
160     * max size if there is not yet any set associated with the block.
161     *
162     * @param index {@code >= 0;} the block index
163     * @return {@code non-null;} the associated register set
164     */
165    public RegisterSpecSet mutableCopyOfStarts(int index) {
166        RegisterSpecSet result = getStarts0(index);
167
168        return (result != null) ?
169            result.mutableCopy() : new RegisterSpecSet(regCount);
170    }
171
172    /**
173     * Adds an assignment association for the given instruction and
174     * register spec. This throws an exception if the instruction
175     * doesn't actually perform a named variable assignment.
176     *
177     * <b>Note:</b> Although the instruction contains its own spec for
178     * the result, it still needs to be passed in explicitly to this
179     * method, since the spec that is stored here should always have a
180     * simple type and the one in the instruction can be an arbitrary
181     * {@link com.android.dx.rop.type.TypeBearer} (such as a constant value).
182     *
183     * @param insn {@code non-null;} the instruction in question
184     * @param spec {@code non-null;} the associated register spec
185     */
186    public void addAssignment(SsaInsn insn, RegisterSpec spec) {
187        throwIfImmutable();
188
189        if (insn == null) {
190            throw new NullPointerException("insn == null");
191        }
192
193        if (spec == null) {
194            throw new NullPointerException("spec == null");
195        }
196
197        insnAssignments.put(insn, spec);
198    }
199
200    /**
201     * Gets the named register being assigned by the given instruction, if
202     * previously stored in this instance.
203     *
204     * @param insn {@code non-null;} instruction in question
205     * @return {@code null-ok;} the named register being assigned, if any
206     */
207    public RegisterSpec getAssignment(SsaInsn insn) {
208        return insnAssignments.get(insn);
209    }
210
211    /**
212     * Gets the number of assignments recorded by this instance.
213     *
214     * @return {@code >= 0;} the number of assignments
215     */
216    public int getAssignmentCount() {
217        return insnAssignments.size();
218    }
219
220    public void debugDump() {
221        for (int index = 0 ; index < blockStarts.length; index++) {
222            if (blockStarts[index] == null) {
223                continue;
224            }
225
226            if (blockStarts[index] == emptySet) {
227                System.out.printf("%04x: empty set\n", index);
228            } else {
229                System.out.printf("%04x: %s\n", index, blockStarts[index]);
230            }
231        }
232    }
233
234    /**
235     * Helper method, to get the starts for a index, throwing the
236     * right exception for range problems.
237     *
238     * @param index {@code >= 0;} the block index
239     * @return {@code null-ok;} associated register set or {@code null} if there
240     * is none
241     */
242    private RegisterSpecSet getStarts0(int index) {
243        try {
244            return blockStarts[index];
245        } catch (ArrayIndexOutOfBoundsException ex) {
246            // Translate the exception.
247            throw new IllegalArgumentException("bogus index");
248        }
249    }
250}
251