• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.back;
18 
19 import com.android.dx.rop.code.BasicBlock;
20 import com.android.dx.rop.code.BasicBlockList;
21 import com.android.dx.rop.code.InsnList;
22 import com.android.dx.rop.code.RegisterSpec;
23 import com.android.dx.rop.code.RegisterSpecList;
24 import com.android.dx.rop.code.Rop;
25 import com.android.dx.rop.code.RopMethod;
26 import com.android.dx.rop.code.Rops;
27 import com.android.dx.ssa.BasicRegisterMapper;
28 import com.android.dx.ssa.PhiInsn;
29 import com.android.dx.ssa.RegisterMapper;
30 import com.android.dx.ssa.SsaBasicBlock;
31 import com.android.dx.ssa.SsaInsn;
32 import com.android.dx.ssa.SsaMethod;
33 import com.android.dx.util.Hex;
34 import com.android.dx.util.IntList;
35 import java.util.ArrayList;
36 import java.util.Arrays;
37 import java.util.BitSet;
38 import java.util.Comparator;
39 
40 /**
41  * Converts a method in SSA form to ROP form.
42  */
43 public class SsaToRop {
44     /** local debug flag */
45     private static final boolean DEBUG = false;
46 
47     /** {@code non-null;} method to process */
48     private final SsaMethod ssaMeth;
49 
50     /**
51      * {@code true} if the converter should attempt to minimize
52      * the rop-form register count
53      */
54     private final boolean minimizeRegisters;
55 
56     /** {@code non-null;} interference graph */
57     private final InterferenceGraph interference;
58 
59     /**
60      * Converts a method in SSA form to ROP form.
61      *
62      * @param ssaMeth {@code non-null;} method to process
63      * @param minimizeRegisters {@code true} if the converter should
64      * attempt to minimize the rop-form register count
65      * @return {@code non-null;} rop-form output
66      */
convertToRopMethod(SsaMethod ssaMeth, boolean minimizeRegisters)67     public static RopMethod convertToRopMethod(SsaMethod ssaMeth,
68             boolean minimizeRegisters) {
69         return new SsaToRop(ssaMeth, minimizeRegisters).convert();
70     }
71 
72     /**
73      * Constructs an instance.
74      *
75      * @param ssaMeth {@code non-null;} method to process
76      * @param minimizeRegisters {@code true} if the converter should
77      * attempt to minimize the rop-form register count
78      */
SsaToRop(SsaMethod ssaMethod, boolean minimizeRegisters)79     private SsaToRop(SsaMethod ssaMethod, boolean minimizeRegisters) {
80         this.minimizeRegisters = minimizeRegisters;
81         this.ssaMeth = ssaMethod;
82         this.interference =
83             LivenessAnalyzer.constructInterferenceGraph(ssaMethod);
84     }
85 
86     /**
87      * Performs the conversion.
88      *
89      * @return {@code non-null;} rop-form output
90      */
convert()91     private RopMethod convert() {
92         if (DEBUG) {
93             interference.dumpToStdout();
94         }
95 
96         // These are other allocators for debugging or historical comparison:
97         // allocator = new NullRegisterAllocator(ssaMeth, interference);
98         // allocator = new FirstFitAllocator(ssaMeth, interference);
99 
100         RegisterAllocator allocator =
101             new FirstFitLocalCombiningAllocator(ssaMeth, interference,
102                     minimizeRegisters);
103 
104         RegisterMapper mapper = allocator.allocateRegisters();
105 
106         if (DEBUG) {
107             System.out.println("Printing reg map");
108             System.out.println(((BasicRegisterMapper)mapper).toHuman());
109         }
110 
111         ssaMeth.setBackMode();
112 
113         ssaMeth.mapRegisters(mapper);
114 
115         removePhiFunctions();
116 
117         if (allocator.wantsParamsMovedHigh()) {
118             moveParametersToHighRegisters();
119         }
120 
121         removeEmptyGotos();
122 
123         RopMethod ropMethod = new RopMethod(convertBasicBlocks(),
124                 ssaMeth.blockIndexToRopLabel(ssaMeth.getEntryBlockIndex()));
125         ropMethod = new IdenticalBlockCombiner(ropMethod).process();
126 
127         return ropMethod;
128     }
129 
130     /**
131      * Removes all blocks containing only GOTOs from the control flow.
132      * Although much of this work will be done later when converting
133      * from rop to dex, not all simplification cases can be handled
134      * there. Furthermore, any no-op block between the exit block and
135      * blocks containing the real return or throw statements must be
136      * removed.
137      */
removeEmptyGotos()138     private void removeEmptyGotos() {
139         final ArrayList<SsaBasicBlock> blocks = ssaMeth.getBlocks();
140 
141         ssaMeth.forEachBlockDepthFirst(false, new SsaBasicBlock.Visitor() {
142             public void visitBlock(SsaBasicBlock b, SsaBasicBlock parent) {
143                 ArrayList<SsaInsn> insns = b.getInsns();
144 
145                 if ((insns.size() == 1)
146                         && (insns.get(0).getOpcode() == Rops.GOTO)) {
147                     BitSet preds = (BitSet) b.getPredecessors().clone();
148 
149                     for (int i = preds.nextSetBit(0); i >= 0;
150                             i = preds.nextSetBit(i + 1)) {
151                         SsaBasicBlock pb = blocks.get(i);
152                         pb.replaceSuccessor(b.getIndex(),
153                                 b.getPrimarySuccessorIndex());
154                     }
155                 }
156             }
157         });
158     }
159 
160     /**
161      * See Appel 19.6. To remove the phi instructions in an edge-split
162      * SSA representation we know we can always insert a move in a
163      * predecessor block.
164      */
removePhiFunctions()165     private void removePhiFunctions() {
166         ArrayList<SsaBasicBlock> blocks = ssaMeth.getBlocks();
167 
168         for (SsaBasicBlock block : blocks) {
169             // Add moves in all the pred blocks for each phi insn.
170             block.forEachPhiInsn(new PhiVisitor(blocks));
171 
172             // Delete the phi insns.
173             block.removeAllPhiInsns();
174         }
175 
176         /*
177          * After all move insns have been added, sort them so they don't
178          * destructively interfere.
179          */
180         for (SsaBasicBlock block : blocks) {
181             block.scheduleMovesFromPhis();
182         }
183     }
184 
185     /**
186      * Helper for {@link #removePhiFunctions}: PhiSuccessorUpdater for
187      * adding move instructions to predecessors based on phi insns.
188      */
189     private static class PhiVisitor implements PhiInsn.Visitor {
190         private final ArrayList<SsaBasicBlock> blocks;
191 
PhiVisitor(ArrayList<SsaBasicBlock> blocks)192         public PhiVisitor(ArrayList<SsaBasicBlock> blocks) {
193             this.blocks = blocks;
194         }
195 
visitPhiInsn(PhiInsn insn)196         public void visitPhiInsn(PhiInsn insn) {
197             RegisterSpecList sources = insn.getSources();
198             RegisterSpec result = insn.getResult();
199             int sz = sources.size();
200 
201             for (int i = 0; i < sz; i++) {
202                 RegisterSpec source = sources.get(i);
203                 SsaBasicBlock predBlock = blocks.get(
204                         insn.predBlockIndexForSourcesIndex(i));
205 
206                 predBlock.addMoveToEnd(result, source);
207             }
208         }
209     }
210 
211     /**
212      * Moves the parameter registers, which allocateRegisters() places
213      * at the bottom of the frame, up to the top of the frame to match
214      * Dalvik calling convention.
215      */
moveParametersToHighRegisters()216     private void moveParametersToHighRegisters() {
217         int paramWidth = ssaMeth.getParamWidth();
218         BasicRegisterMapper mapper
219                 = new BasicRegisterMapper(ssaMeth.getRegCount());
220         int regCount = ssaMeth.getRegCount();
221 
222         for (int i = 0; i < regCount; i++) {
223             if (i < paramWidth) {
224                 mapper.addMapping(i, regCount - paramWidth + i, 1);
225             } else {
226                 mapper.addMapping(i, i - paramWidth, 1);
227             }
228         }
229 
230         if (DEBUG) {
231             System.out.printf("Moving %d registers from 0 to %d\n",
232                     paramWidth, regCount - paramWidth);
233         }
234 
235         ssaMeth.mapRegisters(mapper);
236     }
237 
238     /**
239      * @return rop-form basic block list
240      */
convertBasicBlocks()241     private BasicBlockList convertBasicBlocks() {
242         ArrayList<SsaBasicBlock> blocks = ssaMeth.getBlocks();
243 
244         // Exit block may be null.
245         SsaBasicBlock exitBlock = ssaMeth.getExitBlock();
246 
247         ssaMeth.computeReachability();
248         int ropBlockCount = ssaMeth.getCountReachableBlocks();
249 
250         // Don't count the exit block, if it exists and is reachable.
251         ropBlockCount -= (exitBlock != null && exitBlock.isReachable()) ? 1 : 0;
252 
253         BasicBlockList result = new BasicBlockList(ropBlockCount);
254 
255         // Convert all the reachable blocks except the exit block.
256         int ropBlockIndex = 0;
257         for (SsaBasicBlock b : blocks) {
258             if (b.isReachable() && b != exitBlock) {
259                 result.set(ropBlockIndex++, convertBasicBlock(b));
260             }
261         }
262 
263         // The exit block, which is discarded, must do nothing.
264         if (exitBlock != null && exitBlock.getInsns().size() != 0) {
265             throw new RuntimeException(
266                     "Exit block must have no insns when leaving SSA form");
267         }
268 
269         return result;
270     }
271 
272     /**
273      * Validates that a basic block is a valid end predecessor. It must
274      * end in a RETURN or a THROW. Throws a runtime exception on error.
275      *
276      * @param b {@code non-null;} block to validate
277      * @throws RuntimeException on error
278      */
verifyValidExitPredecessor(SsaBasicBlock b)279     private void verifyValidExitPredecessor(SsaBasicBlock b) {
280         ArrayList<SsaInsn> insns = b.getInsns();
281         SsaInsn lastInsn = insns.get(insns.size() - 1);
282         Rop opcode = lastInsn.getOpcode();
283 
284         if (opcode.getBranchingness() != Rop.BRANCH_RETURN
285                 && opcode != Rops.THROW) {
286             throw new RuntimeException("Exit predecessor must end"
287                     + " in valid exit statement.");
288         }
289     }
290 
291     /**
292      * Converts a single basic block to rop form.
293      *
294      * @param block SSA block to process
295      * @return {@code non-null;} ROP block
296      */
convertBasicBlock(SsaBasicBlock block)297     private BasicBlock convertBasicBlock(SsaBasicBlock block) {
298         IntList successorList = block.getRopLabelSuccessorList();
299         int primarySuccessorLabel = block.getPrimarySuccessorRopLabel();
300 
301         // Filter out any reference to the SSA form's exit block.
302 
303         // Exit block may be null.
304         SsaBasicBlock exitBlock = ssaMeth.getExitBlock();
305         int exitRopLabel = (exitBlock == null) ? -1 : exitBlock.getRopLabel();
306 
307         if (successorList.contains(exitRopLabel)) {
308             if (successorList.size() > 1) {
309                 throw new RuntimeException(
310                         "Exit predecessor must have no other successors"
311                                 + Hex.u2(block.getRopLabel()));
312             } else {
313                 successorList = IntList.EMPTY;
314                 primarySuccessorLabel = -1;
315 
316                 verifyValidExitPredecessor(block);
317             }
318         }
319 
320         successorList.setImmutable();
321 
322         BasicBlock result = new BasicBlock(
323                 block.getRopLabel(), convertInsns(block.getInsns()),
324                 successorList,
325                 primarySuccessorLabel);
326 
327         return result;
328     }
329 
330     /**
331      * Converts an insn list to rop form.
332      *
333      * @param ssaInsns {@code non-null;} old instructions
334      * @return {@code non-null;} immutable instruction list
335      */
convertInsns(ArrayList<SsaInsn> ssaInsns)336     private InsnList convertInsns(ArrayList<SsaInsn> ssaInsns) {
337         int insnCount = ssaInsns.size();
338         InsnList result = new InsnList(insnCount);
339 
340         for (int i = 0; i < insnCount; i++) {
341             result.set(i, ssaInsns.get(i).toRopInsn());
342         }
343 
344         result.setImmutable();
345 
346         return result;
347     }
348 
349     /**
350      * <b>Note:</b> This method is not presently used.
351      *
352      * @return a list of registers ordered by most-frequently-used to
353      * least-frequently-used. Each register is listed once and only
354      * once.
355      */
getRegistersByFrequency()356     public int[] getRegistersByFrequency() {
357         int regCount = ssaMeth.getRegCount();
358         Integer[] ret = new Integer[regCount];
359 
360         for (int i = 0; i < regCount; i++) {
361             ret[i] = i;
362         }
363 
364         Arrays.sort(ret, new Comparator<Integer>() {
365             public int compare(Integer o1, Integer o2) {
366                 return ssaMeth.getUseListForRegister(o2).size()
367                         - ssaMeth.getUseListForRegister(o1).size();
368             }
369         });
370 
371         int result[] = new int[regCount];
372 
373         for (int i = 0; i < regCount; i++) {
374             result[i] = ret[i];
375         }
376 
377         return result;
378     }
379 }
380