summaryrefslogtreecommitdiff
path: root/dx/src/com/android/dx/ssa/SsaConverter.java
blob: a7d044c55db799cbba786253fbb2edff3f2514b0 (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
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
/*
 * 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.RegisterSpec;
import com.android.dx.rop.code.RopMethod;
import com.android.dx.util.IntIterator;
import java.util.ArrayList;
import java.util.BitSet;

/**
 * Converts ROP methods to SSA Methods
 */
public class SsaConverter {
    public static final boolean DEBUG = false;

    /**
     * Returns an SSA representation, edge-split and with phi
     * functions placed.
     *
     * @param rmeth input
     * @param paramWidth the total width, in register-units, of the method's
     * parameters
     * @param isStatic {@code true} if this method has no {@code this}
     * pointer argument
     * @return output in SSA form
     */
    public static SsaMethod convertToSsaMethod(RopMethod rmeth,
            int paramWidth, boolean isStatic) {
        SsaMethod result
            = SsaMethod.newFromRopMethod(rmeth, paramWidth, isStatic);

        edgeSplit(result);

        LocalVariableInfo localInfo = LocalVariableExtractor.extract(result);

        placePhiFunctions(result, localInfo, 0);
        new SsaRenamer(result).run();

        /*
         * The exit block, added here, is not considered for edge splitting
         * or phi placement since no actual control flows to it.
         */
        result.makeExitBlock();

        return result;
    }

    /**
     * Updates an SSA representation, placing phi functions and renaming all
     * registers above a certain threshold number.
     *
     * @param ssaMeth input
     * @param threshold registers below this number are unchanged
     */
    public static void updateSsaMethod(SsaMethod ssaMeth, int threshold) {
        LocalVariableInfo localInfo = LocalVariableExtractor.extract(ssaMeth);
        placePhiFunctions(ssaMeth, localInfo, threshold);
        new SsaRenamer(ssaMeth, threshold).run();
    }

    /**
     * Returns an SSA represention with only the edge-splitter run.
     *
     * @param rmeth method to process
     * @param paramWidth width of all arguments in the method
     * @param isStatic {@code true} if this method has no {@code this}
     * pointer argument
     * @return an SSA represention with only the edge-splitter run
     */
    public static SsaMethod testEdgeSplit (RopMethod rmeth, int paramWidth,
            boolean isStatic) {
        SsaMethod result;

        result = SsaMethod.newFromRopMethod(rmeth, paramWidth, isStatic);

        edgeSplit(result);
        return result;
    }

    /**
     * Returns an SSA represention with only the steps through the
     * phi placement run.
     *
     * @param rmeth method to process
     * @param paramWidth width of all arguments in the method
     * @param isStatic {@code true} if this method has no {@code this}
     * pointer argument
     * @return an SSA represention with only the edge-splitter run
     */
    public static SsaMethod testPhiPlacement (RopMethod rmeth, int paramWidth,
            boolean isStatic) {
        SsaMethod result;

        result = SsaMethod.newFromRopMethod(rmeth, paramWidth, isStatic);

        edgeSplit(result);

        LocalVariableInfo localInfo = LocalVariableExtractor.extract(result);

        placePhiFunctions(result, localInfo, 0);
        return result;
    }

    /**
     * See Appel section 19.1:
     *
     * Converts CFG into "edge-split" form, such that each node either a
     * unique successor or unique predecessor.<p>
     *
     * In addition, the SSA form we use enforces a further constraint,
     * requiring each block with a final instruction that returns a
     * value to have a primary successor that has no other
     * predecessor. This ensures move statements can always be
     * inserted correctly when phi statements are removed.
     *
     * @param result method to process
     */
    private static void edgeSplit(SsaMethod result) {
        edgeSplitPredecessors(result);
        edgeSplitMoveExceptionsAndResults(result);
        edgeSplitSuccessors(result);
    }

    /**
     * Inserts Z nodes as new predecessors for every node that has multiple
     * successors and multiple predecessors.
     *
     * @param result {@code non-null;} method to process
     */
    private static void edgeSplitPredecessors(SsaMethod result) {
        ArrayList<SsaBasicBlock> blocks = result.getBlocks();

        /*
         * New blocks are added to the end of the block list during
         * this iteration.
         */
        for (int i = blocks.size() - 1; i >= 0; i-- ) {
            SsaBasicBlock block = blocks.get(i);
            if (nodeNeedsUniquePredecessor(block)) {
                block.insertNewPredecessor();
            }
        }
    }

    /**
     * @param block {@code non-null;} block in question
     * @return {@code true} if this node needs to have a unique
     * predecessor created for it
     */
    private static boolean nodeNeedsUniquePredecessor(SsaBasicBlock block) {
        /*
         * Any block with that has both multiple successors and multiple
         * predecessors needs a new predecessor node.
         */

        int countPredecessors = block.getPredecessors().cardinality();
        int countSuccessors = block.getSuccessors().cardinality();

        return  (countPredecessors > 1 && countSuccessors > 1);
    }

    /**
     * In ROP form, move-exception must occur as the first insn in a block
     * immediately succeeding the insn that could thrown an exception.
     * We may need room to insert move insns later, so make sure to split
     * any block that starts with a move-exception such that there is a
     * unique move-exception block for each predecessor.
     *
     * @param ssaMeth method to process
     */
    private static void edgeSplitMoveExceptionsAndResults(SsaMethod ssaMeth) {
        ArrayList<SsaBasicBlock> blocks = ssaMeth.getBlocks();

        /*
         * New blocks are added to the end of the block list during
         * this iteration.
         */
        for (int i = blocks.size() - 1; i >= 0; i-- ) {
            SsaBasicBlock block = blocks.get(i);

            /*
             * Any block that starts with a move-exception and has more than
             * one predecessor...
             */
            if (!block.isExitBlock()
                    && block.getPredecessors().cardinality() > 1
                    && block.getInsns().get(0).isMoveException()) {

                // block.getPredecessors() is changed in the loop below.
                BitSet preds = (BitSet)block.getPredecessors().clone();
                for (int j = preds.nextSetBit(0); j >= 0;
                     j = preds.nextSetBit(j + 1)) {
                    SsaBasicBlock predecessor = blocks.get(j);
                    SsaBasicBlock zNode
                        = predecessor.insertNewSuccessor(block);

                    /*
                     * Make sure to place the move-exception as the
                     * first insn.
                     */
                    zNode.getInsns().add(0, block.getInsns().get(0).clone());
                }

                // Remove the move-exception from the original block.
                block.getInsns().remove(0);
            }
        }
    }

    /**
     * Inserts Z nodes for every node that needs a new
     * successor.
     *
     * @param result {@code non-null;} method to process
     */
    private static void edgeSplitSuccessors(SsaMethod result) {
        ArrayList<SsaBasicBlock> blocks = result.getBlocks();

        /*
         * New blocks are added to the end of the block list during
         * this iteration.
         */
        for (int i = blocks.size() - 1; i >= 0; i-- ) {
            SsaBasicBlock block = blocks.get(i);

            // Successors list is modified in loop below.
            BitSet successors = (BitSet)block.getSuccessors().clone();
            for (int j = successors.nextSetBit(0);
                 j >= 0; j = successors.nextSetBit(j+1)) {

                SsaBasicBlock succ = blocks.get(j);

                if (needsNewSuccessor(block, succ)) {
                    block.insertNewSuccessor(succ);
                }
            }
        }
    }

    /**
     * Returns {@code true} if block and successor need a Z-node
     * between them. Presently, this is {@code true} if either:
     * <p><ul>
     * <li> there is a critical edge between block and successor.
     * <li> the final instruction has any sources or results and the current
     * successor block has more than one predecessor.
     * </ul></p>
     *
     * @param block predecessor node
     * @param succ successor node
     * @return {@code true} if a Z node is needed
     */
    private static boolean needsNewSuccessor(SsaBasicBlock block,
            SsaBasicBlock succ) {
        ArrayList<SsaInsn> insns = block.getInsns();
        SsaInsn lastInsn = insns.get(insns.size() - 1);

        // This is to address b/69128828 where the moves associated
        // with a phi were being propagated along a critical
        // edge. Consequently, the move instruction inserted was
        // positioned before the first instruction in the predecessor
        // block. The generated bytecode was rejected by the ART
        // verifier.
        if (block.getSuccessors().cardinality() > 1 && succ.getPredecessors().cardinality() > 1) {
            return true;
        }

        return ((lastInsn.getResult() != null)
                    || (lastInsn.getSources().size() > 0))
                && succ.getPredecessors().cardinality() > 1;
    }

    /**
     * See Appel algorithm 19.6:
     *
     * Place Phi functions in appropriate locations.
     *
     * @param ssaMeth {@code non-null;} method to process.
     * Modifications are made in-place.
     * @param localInfo {@code non-null;} local variable info, used
     * when placing phis
     * @param threshold registers below this number are ignored
     */
    private static void placePhiFunctions (SsaMethod ssaMeth,
            LocalVariableInfo localInfo, int threshold) {
        ArrayList<SsaBasicBlock> ssaBlocks;
        int regCount;
        int blockCount;

        ssaBlocks = ssaMeth.getBlocks();
        blockCount = ssaBlocks.size();
        regCount = ssaMeth.getRegCount() - threshold;

        DomFront df = new DomFront(ssaMeth);
        DomFront.DomInfo[] domInfos = df.run();

        // Bit set of registers vs block index "definition sites"
        BitSet[] defsites = new BitSet[regCount];

        // Bit set of registers vs block index "phi placement sites"
        BitSet[] phisites = new BitSet[regCount];

        for (int i = 0; i < regCount; i++) {
            defsites[i] = new BitSet(blockCount);
            phisites[i] = new BitSet(blockCount);
        }

        /*
         * For each register, build a set of all basic blocks where
         * containing an assignment to that register.
         */
        for (int bi = 0, s = ssaBlocks.size(); bi < s; bi++) {
            SsaBasicBlock b = ssaBlocks.get(bi);

            for (SsaInsn insn : b.getInsns()) {
                RegisterSpec rs = insn.getResult();

                if (rs != null && rs.getReg() - threshold >= 0) {
                    defsites[rs.getReg() - threshold].set(bi);
                }
            }
        }

        if (DEBUG) {
            System.out.println("defsites");

            for (int i = 0; i < regCount; i++) {
                StringBuilder sb = new StringBuilder();
                sb.append('v').append(i).append(": ");
                sb.append(defsites[i].toString());
                System.out.println(sb);
            }
        }

        BitSet worklist;

        /*
         * For each register, compute all locations for phi placement
         * based on dominance-frontier algorithm.
         */
        for (int reg = 0, s = regCount; reg < s; reg++) {
            int workBlockIndex;

            /* Worklist set starts out with each node where reg is assigned. */

            worklist = (BitSet) (defsites[reg].clone());

            while (0 <= (workBlockIndex = worklist.nextSetBit(0))) {
                worklist.clear(workBlockIndex);
                IntIterator dfIterator
                    = domInfos[workBlockIndex].dominanceFrontiers.iterator();

                while (dfIterator.hasNext()) {
                    int dfBlockIndex = dfIterator.next();

                    if (!phisites[reg].get(dfBlockIndex)) {
                        phisites[reg].set(dfBlockIndex);

                        int tReg = reg + threshold;
                        RegisterSpec rs
                            = localInfo.getStarts(dfBlockIndex).get(tReg);

                        if (rs == null) {
                            ssaBlocks.get(dfBlockIndex).addPhiInsnForReg(tReg);
                        } else {
                            ssaBlocks.get(dfBlockIndex).addPhiInsnForReg(rs);
                        }

                        if (!defsites[reg].get(dfBlockIndex)) {
                            worklist.set(dfBlockIndex);
                        }
                    }
                }
            }
        }

        if (DEBUG) {
            System.out.println("phisites");

            for (int i = 0; i < regCount; i++) {
                StringBuilder sb = new StringBuilder();
                sb.append('v').append(i).append(": ");
                sb.append(phisites[i].toString());
                System.out.println(sb);
            }
        }
    }
}