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