summaryrefslogtreecommitdiffstats
path: root/dx/src/com/android/dx/ssa/LocalVariableExtractor.java
blob: 11d53cf2ad154875e4e9dcd557ba8ab1b03a764e (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
/*
 * 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.rop.code.RegisterSpecSet;
import com.android.dx.rop.code.RegisterSpec;
import com.android.dx.util.IntList;

import java.util.ArrayList;
import java.util.BitSet;
import java.util.List;

/**
 * Code to figure out which local variables are active at which points in
 * a method. Stolen and retrofitted from
 * com.android.dx.rop.code.LocalVariableExtractor
 *
 * TODO remove this. Allow Rop-form LocalVariableInfo to be passed in,
 * converted, and adapted through edge-splitting.
 */
public class LocalVariableExtractor {
    /** {@code non-null;} method being extracted from */
    private final SsaMethod method;

    /** {@code non-null;} block list for the method */
    private final ArrayList<SsaBasicBlock> blocks;

    /** {@code non-null;} result in-progress */
    private final LocalVariableInfo resultInfo;

    /** {@code non-null;} work set indicating blocks needing to be processed */
    private final BitSet workSet;

    /**
     * Extracts out all the local variable information from the given method.
     *
     * @param method {@code non-null;} the method to extract from
     * @return {@code non-null;} the extracted information
     */
    public static LocalVariableInfo extract(SsaMethod method) {
        LocalVariableExtractor lve = new LocalVariableExtractor(method);
        return lve.doit();
    }

    /**
     * Constructs an instance. This method is private. Use {@link #extract}.
     *
     * @param method {@code non-null;} the method to extract from
     */
    private LocalVariableExtractor(SsaMethod method) {
        if (method == null) {
            throw new NullPointerException("method == null");
        }

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

        this.method = method;
        this.blocks = blocks;
        this.resultInfo = new LocalVariableInfo(method);
        this.workSet = new BitSet(blocks.size());
    }

    /**
     * Does the extraction.
     *
     * @return {@code non-null;} the extracted information
     */
    private LocalVariableInfo doit() {

        //FIXME why is this needed here?
        if (method.getRegCount() > 0 ) {
            for (int bi = method.getEntryBlockIndex();
                 bi >= 0;
                 bi = workSet.nextSetBit(0)) {
                workSet.clear(bi);
                processBlock(bi);
            }
        }

        resultInfo.setImmutable();
        return resultInfo;
    }

    /**
     * Processes a single block.
     *
     * @param blockIndex {@code >= 0;} block index of the block to process
     */
    private void processBlock(int blockIndex) {
        RegisterSpecSet primaryState
                = resultInfo.mutableCopyOfStarts(blockIndex);
        SsaBasicBlock block = blocks.get(blockIndex);
        List<SsaInsn> insns = block.getInsns();
        int insnSz = insns.size();

        // The exit block has no insns and no successors
        if (blockIndex == method.getExitBlockIndex()) {
            return;
        }

        /*
         * We may have to treat the last instruction specially: If it
         * can (but doesn't always) throw, and the exception can be
         * caught within the same method, then we need to use the
         * state *before* executing it to be what is merged into
         * exception targets.
         */
        SsaInsn lastInsn = insns.get(insnSz - 1);
        boolean hasExceptionHandlers
                = lastInsn.getOriginalRopInsn().getCatches().size() !=0 ;
        boolean canThrowDuringLastInsn = hasExceptionHandlers
                && (lastInsn.getResult() != null);
        int freezeSecondaryStateAt = insnSz - 1;
        RegisterSpecSet secondaryState = primaryState;

        /*
         * Iterate over the instructions, adding information for each place
         * that the active variable set changes.
         */

        for (int i = 0; i < insnSz; i++) {
            if (canThrowDuringLastInsn && (i == freezeSecondaryStateAt)) {
                // Until this point, primaryState == secondaryState.
                primaryState.setImmutable();
                primaryState = primaryState.mutableCopy();
            }

            SsaInsn insn = insns.get(i);
            RegisterSpec result;

            result = insn.getLocalAssignment();

            if (result == null) {
                // We may be nuking an existing local

                result = insn.getResult();

                if (result != null && primaryState.get(result.getReg()) != null) {
                    primaryState.remove(primaryState.get(result.getReg()));
                }
                continue;
            }

            result = result.withSimpleType();

            RegisterSpec already = primaryState.get(result);
            /*
             * The equals() check ensures we only add new info if
             * the instruction causes a change to the set of
             * active variables.
             */
            if (!result.equals(already)) {
                /*
                 * If this insn represents a local moving from one register
                 * to another, remove the association between the old register
                 * and the local.
                 */
                RegisterSpec previous
                        = primaryState.localItemToSpec(result.getLocalItem());

                if (previous != null
                        && (previous.getReg() != result.getReg())) {

                    primaryState.remove(previous);
                }

                resultInfo.addAssignment(insn, result);
                primaryState.put(result);
            }
        }

        primaryState.setImmutable();

        /*
         * Merge this state into the start state for each successor,
         * and update the work set where required (that is, in cases
         * where the start state for a block changes).
         */

        IntList successors = block.getSuccessorList();
        int succSz = successors.size();
        int primarySuccessor = block.getPrimarySuccessorIndex();

        for (int i = 0; i < succSz; i++) {
            int succ = successors.get(i);
            RegisterSpecSet state = (succ == primarySuccessor) ?
                primaryState : secondaryState;

            if (resultInfo.mergeStarts(succ, state)) {
                workSet.set(succ);
            }
        }
    }
}