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