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 
17 package com.android.dx.ssa;
18 
19 import com.android.dx.rop.code.RegisterSpec;
20 import com.android.dx.rop.code.RopMethod;
21 import com.android.dx.util.IntIterator;
22 import java.util.ArrayList;
23 import java.util.BitSet;
24 
25 /**
26  * Converts ROP methods to SSA Methods
27  */
28 public 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      */
convertToSsaMethod(RopMethod rmeth, int paramWidth, boolean isStatic)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      */
updateSsaMethod(SsaMethod ssaMeth, int threshold)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      */
testEdgeSplit(RopMethod rmeth, int paramWidth, boolean isStatic)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      */
testPhiPlacement(RopMethod rmeth, int paramWidth, boolean isStatic)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      */
edgeSplit(SsaMethod result)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      */
edgeSplitPredecessors(SsaMethod result)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      */
nodeNeedsUniquePredecessor(SsaBasicBlock block)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      */
edgeSplitMoveExceptionsAndResults(SsaMethod ssaMeth)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      */
edgeSplitSuccessors(SsaMethod result)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 either:
258      * <p><ul>
259      * <li> there is a critical edge between block and successor.
260      * <li> the final instruction has any sources or results and the current
261      * successor block has more than one predecessor.
262      * </ul></p>
263      *
264      * @param block predecessor node
265      * @param succ successor node
266      * @return {@code true} if a Z node is needed
267      */
needsNewSuccessor(SsaBasicBlock block, SsaBasicBlock succ)268     private static boolean needsNewSuccessor(SsaBasicBlock block,
269             SsaBasicBlock succ) {
270         ArrayList<SsaInsn> insns = block.getInsns();
271         SsaInsn lastInsn = insns.get(insns.size() - 1);
272 
273         // This is to address b/69128828 where the moves associated
274         // with a phi were being propagated along a critical
275         // edge. Consequently, the move instruction inserted was
276         // positioned before the first instruction in the predecessor
277         // block. The generated bytecode was rejected by the ART
278         // verifier.
279         if (block.getSuccessors().cardinality() > 1 && succ.getPredecessors().cardinality() > 1) {
280             return true;
281         }
282 
283         return ((lastInsn.getResult() != null)
284                     || (lastInsn.getSources().size() > 0))
285                 && succ.getPredecessors().cardinality() > 1;
286     }
287 
288     /**
289      * See Appel algorithm 19.6:
290      *
291      * Place Phi functions in appropriate locations.
292      *
293      * @param ssaMeth {@code non-null;} method to process.
294      * Modifications are made in-place.
295      * @param localInfo {@code non-null;} local variable info, used
296      * when placing phis
297      * @param threshold registers below this number are ignored
298      */
placePhiFunctions(SsaMethod ssaMeth, LocalVariableInfo localInfo, int threshold)299     private static void placePhiFunctions (SsaMethod ssaMeth,
300             LocalVariableInfo localInfo, int threshold) {
301         ArrayList<SsaBasicBlock> ssaBlocks;
302         int regCount;
303         int blockCount;
304 
305         ssaBlocks = ssaMeth.getBlocks();
306         blockCount = ssaBlocks.size();
307         regCount = ssaMeth.getRegCount() - threshold;
308 
309         DomFront df = new DomFront(ssaMeth);
310         DomFront.DomInfo[] domInfos = df.run();
311 
312         // Bit set of registers vs block index "definition sites"
313         BitSet[] defsites = new BitSet[regCount];
314 
315         // Bit set of registers vs block index "phi placement sites"
316         BitSet[] phisites = new BitSet[regCount];
317 
318         for (int i = 0; i < regCount; i++) {
319             defsites[i] = new BitSet(blockCount);
320             phisites[i] = new BitSet(blockCount);
321         }
322 
323         /*
324          * For each register, build a set of all basic blocks where
325          * containing an assignment to that register.
326          */
327         for (int bi = 0, s = ssaBlocks.size(); bi < s; bi++) {
328             SsaBasicBlock b = ssaBlocks.get(bi);
329 
330             for (SsaInsn insn : b.getInsns()) {
331                 RegisterSpec rs = insn.getResult();
332 
333                 if (rs != null && rs.getReg() - threshold >= 0) {
334                     defsites[rs.getReg() - threshold].set(bi);
335                 }
336             }
337         }
338 
339         if (DEBUG) {
340             System.out.println("defsites");
341 
342             for (int i = 0; i < regCount; i++) {
343                 StringBuilder sb = new StringBuilder();
344                 sb.append('v').append(i).append(": ");
345                 sb.append(defsites[i].toString());
346                 System.out.println(sb);
347             }
348         }
349 
350         BitSet worklist;
351 
352         /*
353          * For each register, compute all locations for phi placement
354          * based on dominance-frontier algorithm.
355          */
356         for (int reg = 0, s = regCount; reg < s; reg++) {
357             int workBlockIndex;
358 
359             /* Worklist set starts out with each node where reg is assigned. */
360 
361             worklist = (BitSet) (defsites[reg].clone());
362 
363             while (0 <= (workBlockIndex = worklist.nextSetBit(0))) {
364                 worklist.clear(workBlockIndex);
365                 IntIterator dfIterator
366                     = domInfos[workBlockIndex].dominanceFrontiers.iterator();
367 
368                 while (dfIterator.hasNext()) {
369                     int dfBlockIndex = dfIterator.next();
370 
371                     if (!phisites[reg].get(dfBlockIndex)) {
372                         phisites[reg].set(dfBlockIndex);
373 
374                         int tReg = reg + threshold;
375                         RegisterSpec rs
376                             = localInfo.getStarts(dfBlockIndex).get(tReg);
377 
378                         if (rs == null) {
379                             ssaBlocks.get(dfBlockIndex).addPhiInsnForReg(tReg);
380                         } else {
381                             ssaBlocks.get(dfBlockIndex).addPhiInsnForReg(rs);
382                         }
383 
384                         if (!defsites[reg].get(dfBlockIndex)) {
385                             worklist.set(dfBlockIndex);
386                         }
387                     }
388                 }
389             }
390         }
391 
392         if (DEBUG) {
393             System.out.println("phisites");
394 
395             for (int i = 0; i < regCount; i++) {
396                 StringBuilder sb = new StringBuilder();
397                 sb.append('v').append(i).append(": ");
398                 sb.append(phisites[i].toString());
399                 System.out.println(sb);
400             }
401         }
402     }
403 }
404