summaryrefslogtreecommitdiff
path: root/dx/src/com/android/dx/ssa/LocalVariableInfo.java
blob: 8845270c31bb141fc5fc12535dddbf5c97bfb301 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.dx.ssa;

import com.android.dx.util.MutabilityControl;
import com.android.dx.rop.code.RegisterSpecSet;
import com.android.dx.rop.code.RegisterSpec;

import java.util.HashMap;
import java.util.List;

/**
 * Container for local variable information for a particular {@link
 * com.android.dx.ssa.SsaMethod}.
 * Stolen from {@link com.android.dx.rop.code.LocalVariableInfo}.
 */
public class LocalVariableInfo         extends MutabilityControl {
    /** {@code >= 0;} the register count for the method */
    private final int regCount;

    /**
     * {@code non-null;} {@link com.android.dx.rop.code.RegisterSpecSet} to use when indicating a block
     * that has no locals; it is empty and immutable but has an appropriate
     * max size for the method
     */
    private final RegisterSpecSet emptySet;

    /**
     * {@code non-null;} array consisting of register sets representing the
     * sets of variables already assigned upon entry to each block,
     * where array indices correspond to block indices
     */
    private final RegisterSpecSet[] blockStarts;

    /** {@code non-null;} map from instructions to the variable each assigns */
    private final HashMap<SsaInsn, RegisterSpec> insnAssignments;

    /**
     * Constructs an instance.
     *
     * @param method {@code non-null;} the method being represented by this instance
     */
    public LocalVariableInfo(SsaMethod method) {
        if (method == null) {
            throw new NullPointerException("method == null");
        }

        List<SsaBasicBlock> blocks = method.getBlocks();

        this.regCount = method.getRegCount();
        this.emptySet = new RegisterSpecSet(regCount);
        this.blockStarts = new RegisterSpecSet[blocks.size()];
        this.insnAssignments =
            new HashMap<SsaInsn, RegisterSpec>(/*hint here*/);

        emptySet.setImmutable();
    }

    /**
     * Sets the register set associated with the start of the block with
     * the given index.
     *
     * @param index {@code >= 0;} the block index
     * @param specs {@code non-null;} the register set to associate with the block
     */
    public void setStarts(int index, RegisterSpecSet specs) {
        throwIfImmutable();

        if (specs == null) {
            throw new NullPointerException("specs == null");
        }

        try {
            blockStarts[index] = specs;
        } catch (ArrayIndexOutOfBoundsException ex) {
            // Translate the exception.
            throw new IllegalArgumentException("bogus index");
        }
    }

    /**
     * Merges the given register set into the set for the block with the
     * given index. If there was not already an associated set, then this
     * is the same as calling {@link #setStarts}. Otherwise, this will
     * merge the two sets and call {@link #setStarts} on the result of the
     * merge.
     *
     * @param index {@code >= 0;} the block index
     * @param specs {@code non-null;} the register set to merge into the start set
     * for the block
     * @return {@code true} if the merge resulted in an actual change
     * to the associated set (including storing one for the first time) or
     * {@code false} if there was no change
     */
    public boolean mergeStarts(int index, RegisterSpecSet specs) {
        RegisterSpecSet start = getStarts0(index);
        boolean changed = false;

        if (start == null) {
            setStarts(index, specs);
            return true;
        }

        RegisterSpecSet newStart = start.mutableCopy();
        newStart.intersect(specs, true);

        if (start.equals(newStart)) {
            return false;
        }

        newStart.setImmutable();
        setStarts(index, newStart);

        return true;
    }

    /**
     * Gets the register set associated with the start of the block
     * with the given index. This returns an empty set with the appropriate
     * max size if no set was associated with the block in question.
     *
     * @param index {@code >= 0;} the block index
     * @return {@code non-null;} the associated register set
     */
    public RegisterSpecSet getStarts(int index) {
        RegisterSpecSet result = getStarts0(index);

        return (result != null) ? result : emptySet;
    }

    /**
     * Gets the register set associated with the start of the given
     * block. This is just convenient shorthand for
     * {@code getStarts(block.getLabel())}.
     *
     * @param block {@code non-null;} the block in question
     * @return {@code non-null;} the associated register set
     */
    public RegisterSpecSet getStarts(SsaBasicBlock block) {
        return getStarts(block.getIndex());
    }

    /**
     * Gets a mutable copy of the register set associated with the
     * start of the block with the given index. This returns a
     * newly-allocated empty {@link RegisterSpecSet} of appropriate
     * max size if there is not yet any set associated with the block.
     *
     * @param index {@code >= 0;} the block index
     * @return {@code non-null;} the associated register set
     */
    public RegisterSpecSet mutableCopyOfStarts(int index) {
        RegisterSpecSet result = getStarts0(index);

        return (result != null) ?
            result.mutableCopy() : new RegisterSpecSet(regCount);
    }

    /**
     * Adds an assignment association for the given instruction and
     * register spec. This throws an exception if the instruction
     * doesn't actually perform a named variable assignment.
     *
     * <b>Note:</b> Although the instruction contains its own spec for
     * the result, it still needs to be passed in explicitly to this
     * method, since the spec that is stored here should always have a
     * simple type and the one in the instruction can be an arbitrary
     * {@link com.android.dx.rop.type.TypeBearer} (such as a constant value).
     *
     * @param insn {@code non-null;} the instruction in question
     * @param spec {@code non-null;} the associated register spec
     */
    public void addAssignment(SsaInsn insn, RegisterSpec spec) {
        throwIfImmutable();

        if (insn == null) {
            throw new NullPointerException("insn == null");
        }

        if (spec == null) {
            throw new NullPointerException("spec == null");
        }

        insnAssignments.put(insn, spec);
    }

    /**
     * Gets the named register being assigned by the given instruction, if
     * previously stored in this instance.
     *
     * @param insn {@code non-null;} instruction in question
     * @return {@code null-ok;} the named register being assigned, if any
     */
    public RegisterSpec getAssignment(SsaInsn insn) {
        return insnAssignments.get(insn);
    }

    /**
     * Gets the number of assignments recorded by this instance.
     *
     * @return {@code >= 0;} the number of assignments
     */
    public int getAssignmentCount() {
        return insnAssignments.size();
    }

    public void debugDump() {
        for (int index = 0 ; index < blockStarts.length; index++) {
            if (blockStarts[index] == null) {
                continue;
            }

            if (blockStarts[index] == emptySet) {
                System.out.printf("%04x: empty set\n", index);
            } else {
                System.out.printf("%04x: %s\n", index, blockStarts[index]);
            }
        }
    }

    /**
     * Helper method, to get the starts for a index, throwing the
     * right exception for range problems.
     *
     * @param index {@code >= 0;} the block index
     * @return {@code null-ok;} associated register set or {@code null} if there
     * is none
     */
    private RegisterSpecSet getStarts0(int index) {
        try {
            return blockStarts[index];
        } catch (ArrayIndexOutOfBoundsException ex) {
            // Translate the exception.
            throw new IllegalArgumentException("bogus index");
        }
    }
}