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.RopMethod;
21import com.android.dx.util.IntIterator;
22import java.util.ArrayList;
23import java.util.BitSet;
24
25/**
26 * Converts ROP methods to SSA Methods
27 */
28public class SsaConverter {
29    public static final boolean DEBUG = false;
30
31    /**
32     * Returns an SSA representation, edge-split and with phi
33     * functions placed.
34     *
35     * @param rmeth input
36     * @param paramWidth the total width, in register-units, of the method's
37     * parameters
38     * @param isStatic {@code true} if this method has no {@code this}
39     * pointer argument
40     * @return output in SSA form
41     */
42    public static SsaMethod convertToSsaMethod(RopMethod rmeth,
43            int paramWidth, boolean isStatic) {
44        SsaMethod result
45            = SsaMethod.newFromRopMethod(rmeth, paramWidth, isStatic);
46
47        edgeSplit(result);
48
49        LocalVariableInfo localInfo = LocalVariableExtractor.extract(result);
50
51        placePhiFunctions(result, localInfo, 0);
52        new SsaRenamer(result).run();
53
54        /*
55         * The exit block, added here, is not considered for edge splitting
56         * or phi placement since no actual control flows to it.
57         */
58        result.makeExitBlock();
59
60        return result;
61    }
62
63    /**
64     * Updates an SSA representation, placing phi functions and renaming all
65     * registers above a certain threshold number.
66     *
67     * @param ssaMeth input
68     * @param threshold registers below this number are unchanged
69     */
70    public static void updateSsaMethod(SsaMethod ssaMeth, int threshold) {
71        LocalVariableInfo localInfo = LocalVariableExtractor.extract(ssaMeth);
72        placePhiFunctions(ssaMeth, localInfo, threshold);
73        new SsaRenamer(ssaMeth, threshold).run();
74    }
75
76    /**
77     * Returns an SSA represention with only the edge-splitter run.
78     *
79     * @param rmeth method to process
80     * @param paramWidth width of all arguments in the method
81     * @param isStatic {@code true} if this method has no {@code this}
82     * pointer argument
83     * @return an SSA represention with only the edge-splitter run
84     */
85    public static SsaMethod testEdgeSplit (RopMethod rmeth, int paramWidth,
86            boolean isStatic) {
87        SsaMethod result;
88
89        result = SsaMethod.newFromRopMethod(rmeth, paramWidth, isStatic);
90
91        edgeSplit(result);
92        return result;
93    }
94
95    /**
96     * Returns an SSA represention with only the steps through the
97     * phi placement run.
98     *
99     * @param rmeth method to process
100     * @param paramWidth width of all arguments in the method
101     * @param isStatic {@code true} if this method has no {@code this}
102     * pointer argument
103     * @return an SSA represention with only the edge-splitter run
104     */
105    public static SsaMethod testPhiPlacement (RopMethod rmeth, int paramWidth,
106            boolean isStatic) {
107        SsaMethod result;
108
109        result = SsaMethod.newFromRopMethod(rmeth, paramWidth, isStatic);
110
111        edgeSplit(result);
112
113        LocalVariableInfo localInfo = LocalVariableExtractor.extract(result);
114
115        placePhiFunctions(result, localInfo, 0);
116        return result;
117    }
118
119    /**
120     * See Appel section 19.1:
121     *
122     * Converts CFG into "edge-split" form, such that each node either a
123     * unique successor or unique predecessor.<p>
124     *
125     * In addition, the SSA form we use enforces a further constraint,
126     * requiring each block with a final instruction that returns a
127     * value to have a primary successor that has no other
128     * predecessor. This ensures move statements can always be
129     * inserted correctly when phi statements are removed.
130     *
131     * @param result method to process
132     */
133    private static void edgeSplit(SsaMethod result) {
134        edgeSplitPredecessors(result);
135        edgeSplitMoveExceptionsAndResults(result);
136        edgeSplitSuccessors(result);
137    }
138
139    /**
140     * Inserts Z nodes as new predecessors for every node that has multiple
141     * successors and multiple predecessors.
142     *
143     * @param result {@code non-null;} method to process
144     */
145    private static void edgeSplitPredecessors(SsaMethod result) {
146        ArrayList<SsaBasicBlock> blocks = result.getBlocks();
147
148        /*
149         * New blocks are added to the end of the block list during
150         * this iteration.
151         */
152        for (int i = blocks.size() - 1; i >= 0; i-- ) {
153            SsaBasicBlock block = blocks.get(i);
154            if (nodeNeedsUniquePredecessor(block)) {
155                block.insertNewPredecessor();
156            }
157        }
158    }
159
160    /**
161     * @param block {@code non-null;} block in question
162     * @return {@code true} if this node needs to have a unique
163     * predecessor created for it
164     */
165    private static boolean nodeNeedsUniquePredecessor(SsaBasicBlock block) {
166        /*
167         * Any block with that has both multiple successors and multiple
168         * predecessors needs a new predecessor node.
169         */
170
171        int countPredecessors = block.getPredecessors().cardinality();
172        int countSuccessors = block.getSuccessors().cardinality();
173
174        return  (countPredecessors > 1 && countSuccessors > 1);
175    }
176
177    /**
178     * In ROP form, move-exception must occur as the first insn in a block
179     * immediately succeeding the insn that could thrown an exception.
180     * We may need room to insert move insns later, so make sure to split
181     * any block that starts with a move-exception such that there is a
182     * unique move-exception block for each predecessor.
183     *
184     * @param ssaMeth method to process
185     */
186    private static void edgeSplitMoveExceptionsAndResults(SsaMethod ssaMeth) {
187        ArrayList<SsaBasicBlock> blocks = ssaMeth.getBlocks();
188
189        /*
190         * New blocks are added to the end of the block list during
191         * this iteration.
192         */
193        for (int i = blocks.size() - 1; i >= 0; i-- ) {
194            SsaBasicBlock block = blocks.get(i);
195
196            /*
197             * Any block that starts with a move-exception and has more than
198             * one predecessor...
199             */
200            if (!block.isExitBlock()
201                    && block.getPredecessors().cardinality() > 1
202                    && block.getInsns().get(0).isMoveException()) {
203
204                // block.getPredecessors() is changed in the loop below.
205                BitSet preds = (BitSet)block.getPredecessors().clone();
206                for (int j = preds.nextSetBit(0); j >= 0;
207                     j = preds.nextSetBit(j + 1)) {
208                    SsaBasicBlock predecessor = blocks.get(j);
209                    SsaBasicBlock zNode
210                        = predecessor.insertNewSuccessor(block);
211
212                    /*
213                     * Make sure to place the move-exception as the
214                     * first insn.
215                     */
216                    zNode.getInsns().add(0, block.getInsns().get(0).clone());
217                }
218
219                // Remove the move-exception from the original block.
220                block.getInsns().remove(0);
221            }
222        }
223    }
224
225    /**
226     * Inserts Z nodes for every node that needs a new
227     * successor.
228     *
229     * @param result {@code non-null;} method to process
230     */
231    private static void edgeSplitSuccessors(SsaMethod result) {
232        ArrayList<SsaBasicBlock> blocks = result.getBlocks();
233
234        /*
235         * New blocks are added to the end of the block list during
236         * this iteration.
237         */
238        for (int i = blocks.size() - 1; i >= 0; i-- ) {
239            SsaBasicBlock block = blocks.get(i);
240
241            // Successors list is modified in loop below.
242            BitSet successors = (BitSet)block.getSuccessors().clone();
243            for (int j = successors.nextSetBit(0);
244                 j >= 0; j = successors.nextSetBit(j+1)) {
245
246                SsaBasicBlock succ = blocks.get(j);
247
248                if (needsNewSuccessor(block, succ)) {
249                    block.insertNewSuccessor(succ);
250                }
251            }
252        }
253    }
254
255    /**
256     * Returns {@code true} if block and successor need a Z-node
257     * between them. Presently, this is {@code true} if the final
258     * instruction has any sources or results and the current
259     * successor block has more than one predecessor.
260     *
261     * @param block predecessor node
262     * @param succ successor node
263     * @return {@code true} if a Z node is needed
264     */
265    private static boolean needsNewSuccessor(SsaBasicBlock block,
266            SsaBasicBlock succ) {
267        ArrayList<SsaInsn> insns = block.getInsns();
268        SsaInsn lastInsn = insns.get(insns.size() - 1);
269
270        return ((lastInsn.getResult() != null)
271                    || (lastInsn.getSources().size() > 0))
272                && succ.getPredecessors().cardinality() > 1;
273    }
274
275    /**
276     * See Appel algorithm 19.6:
277     *
278     * Place Phi functions in appropriate locations.
279     *
280     * @param ssaMeth {@code non-null;} method to process.
281     * Modifications are made in-place.
282     * @param localInfo {@code non-null;} local variable info, used
283     * when placing phis
284     * @param threshold registers below this number are ignored
285     */
286    private static void placePhiFunctions (SsaMethod ssaMeth,
287            LocalVariableInfo localInfo, int threshold) {
288        ArrayList<SsaBasicBlock> ssaBlocks;
289        int regCount;
290        int blockCount;
291
292        ssaBlocks = ssaMeth.getBlocks();
293        blockCount = ssaBlocks.size();
294        regCount = ssaMeth.getRegCount() - threshold;
295
296        DomFront df = new DomFront(ssaMeth);
297        DomFront.DomInfo[] domInfos = df.run();
298
299        // Bit set of registers vs block index "definition sites"
300        BitSet[] defsites = new BitSet[regCount];
301
302        // Bit set of registers vs block index "phi placement sites"
303        BitSet[] phisites = new BitSet[regCount];
304
305        for (int i = 0; i < regCount; i++) {
306            defsites[i] = new BitSet(blockCount);
307            phisites[i] = new BitSet(blockCount);
308        }
309
310        /*
311         * For each register, build a set of all basic blocks where
312         * containing an assignment to that register.
313         */
314        for (int bi = 0, s = ssaBlocks.size(); bi < s; bi++) {
315            SsaBasicBlock b = ssaBlocks.get(bi);
316
317            for (SsaInsn insn : b.getInsns()) {
318                RegisterSpec rs = insn.getResult();
319
320                if (rs != null && rs.getReg() - threshold >= 0) {
321                    defsites[rs.getReg() - threshold].set(bi);
322                }
323            }
324        }
325
326        if (DEBUG) {
327            System.out.println("defsites");
328
329            for (int i = 0; i < regCount; i++) {
330                StringBuilder sb = new StringBuilder();
331                sb.append('v').append(i).append(": ");
332                sb.append(defsites[i].toString());
333                System.out.println(sb);
334            }
335        }
336
337        BitSet worklist;
338
339        /*
340         * For each register, compute all locations for phi placement
341         * based on dominance-frontier algorithm.
342         */
343        for (int reg = 0, s = regCount; reg < s; reg++) {
344            int workBlockIndex;
345
346            /* Worklist set starts out with each node where reg is assigned. */
347
348            worklist = (BitSet) (defsites[reg].clone());
349
350            while (0 <= (workBlockIndex = worklist.nextSetBit(0))) {
351                worklist.clear(workBlockIndex);
352                IntIterator dfIterator
353                    = domInfos[workBlockIndex].dominanceFrontiers.iterator();
354
355                while (dfIterator.hasNext()) {
356                    int dfBlockIndex = dfIterator.next();
357
358                    if (!phisites[reg].get(dfBlockIndex)) {
359                        phisites[reg].set(dfBlockIndex);
360
361                        int tReg = reg + threshold;
362                        RegisterSpec rs
363                            = localInfo.getStarts(dfBlockIndex).get(tReg);
364
365                        if (rs == null) {
366                            ssaBlocks.get(dfBlockIndex).addPhiInsnForReg(tReg);
367                        } else {
368                            ssaBlocks.get(dfBlockIndex).addPhiInsnForReg(rs);
369                        }
370
371                        if (!defsites[reg].get(dfBlockIndex)) {
372                            worklist.set(dfBlockIndex);
373                        }
374                    }
375                }
376            }
377        }
378
379        if (DEBUG) {
380            System.out.println("phisites");
381
382            for (int i = 0; i < regCount; i++) {
383                StringBuilder sb = new StringBuilder();
384                sb.append('v').append(i).append(": ");
385                sb.append(phisites[i].toString());
386                System.out.println(sb);
387            }
388        }
389    }
390}
391