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 ssaMethod {@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             @Override
143             public void visitBlock(SsaBasicBlock b, SsaBasicBlock parent) {
144                 ArrayList<SsaInsn> insns = b.getInsns();
145 
146                 if ((insns.size() == 1)
147                         && (insns.get(0).getOpcode() == Rops.GOTO)) {
148                     BitSet preds = (BitSet) b.getPredecessors().clone();
149 
150                     for (int i = preds.nextSetBit(0); i >= 0;
151                             i = preds.nextSetBit(i + 1)) {
152                         SsaBasicBlock pb = blocks.get(i);
153                         pb.replaceSuccessor(b.getIndex(),
154                                 b.getPrimarySuccessorIndex());
155                     }
156                 }
157             }
158         });
159     }
160 
161     /**
162      * See Appel 19.6. To remove the phi instructions in an edge-split
163      * SSA representation we know we can always insert a move in a
164      * predecessor block.
165      */
removePhiFunctions()166     private void removePhiFunctions() {
167         ArrayList<SsaBasicBlock> blocks = ssaMeth.getBlocks();
168 
169         for (SsaBasicBlock block : blocks) {
170             // Add moves in all the pred blocks for each phi insn.
171             block.forEachPhiInsn(new PhiVisitor(blocks));
172 
173             // Delete the phi insns.
174             block.removeAllPhiInsns();
175         }
176 
177         /*
178          * After all move insns have been added, sort them so they don't
179          * destructively interfere.
180          */
181         for (SsaBasicBlock block : blocks) {
182             block.scheduleMovesFromPhis();
183         }
184     }
185 
186     /**
187      * Helper for {@link #removePhiFunctions}: PhiSuccessorUpdater for
188      * adding move instructions to predecessors based on phi insns.
189      */
190     private static class PhiVisitor implements PhiInsn.Visitor {
191         private final ArrayList<SsaBasicBlock> blocks;
192 
PhiVisitor(ArrayList<SsaBasicBlock> blocks)193         public PhiVisitor(ArrayList<SsaBasicBlock> blocks) {
194             this.blocks = blocks;
195         }
196 
197         @Override
visitPhiInsn(PhiInsn insn)198         public void visitPhiInsn(PhiInsn insn) {
199             RegisterSpecList sources = insn.getSources();
200             RegisterSpec result = insn.getResult();
201             int sz = sources.size();
202 
203             for (int i = 0; i < sz; i++) {
204                 RegisterSpec source = sources.get(i);
205                 SsaBasicBlock predBlock = blocks.get(
206                         insn.predBlockIndexForSourcesIndex(i));
207 
208                 predBlock.addMoveToEnd(result, source);
209             }
210         }
211     }
212 
213     /**
214      * Moves the parameter registers, which allocateRegisters() places
215      * at the bottom of the frame, up to the top of the frame to match
216      * Dalvik calling convention.
217      */
moveParametersToHighRegisters()218     private void moveParametersToHighRegisters() {
219         int paramWidth = ssaMeth.getParamWidth();
220         BasicRegisterMapper mapper
221                 = new BasicRegisterMapper(ssaMeth.getRegCount());
222         int regCount = ssaMeth.getRegCount();
223 
224         for (int i = 0; i < regCount; i++) {
225             if (i < paramWidth) {
226                 mapper.addMapping(i, regCount - paramWidth + i, 1);
227             } else {
228                 mapper.addMapping(i, i - paramWidth, 1);
229             }
230         }
231 
232         if (DEBUG) {
233             System.out.printf("Moving %d registers from 0 to %d\n",
234                     paramWidth, regCount - paramWidth);
235         }
236 
237         ssaMeth.mapRegisters(mapper);
238     }
239 
240     /**
241      * @return rop-form basic block list
242      */
convertBasicBlocks()243     private BasicBlockList convertBasicBlocks() {
244         ArrayList<SsaBasicBlock> blocks = ssaMeth.getBlocks();
245 
246         // Exit block may be null.
247         SsaBasicBlock exitBlock = ssaMeth.getExitBlock();
248 
249         BitSet reachable = ssaMeth.computeReachability();
250         int ropBlockCount = reachable.cardinality();
251 
252         // Don't count the exit block, if it exists and is reachable.
253         if (exitBlock != null && reachable.get(exitBlock.getIndex())) {
254             ropBlockCount -= 1;
255         }
256 
257         BasicBlockList result = new BasicBlockList(ropBlockCount);
258 
259         // Convert all the reachable blocks except the exit block.
260         int ropBlockIndex = 0;
261         for (SsaBasicBlock b : blocks) {
262             if (reachable.get(b.getIndex()) && b != exitBlock) {
263                 result.set(ropBlockIndex++, convertBasicBlock(b));
264             }
265         }
266 
267         // The exit block, which is discarded, must do nothing.
268         if (exitBlock != null && !exitBlock.getInsns().isEmpty()) {
269             throw new RuntimeException(
270                     "Exit block must have no insns when leaving SSA form");
271         }
272 
273         return result;
274     }
275 
276     /**
277      * Validates that a basic block is a valid end predecessor. It must
278      * end in a RETURN or a THROW. Throws a runtime exception on error.
279      *
280      * @param b {@code non-null;} block to validate
281      * @throws RuntimeException on error
282      */
verifyValidExitPredecessor(SsaBasicBlock b)283     private void verifyValidExitPredecessor(SsaBasicBlock b) {
284         ArrayList<SsaInsn> insns = b.getInsns();
285         SsaInsn lastInsn = insns.get(insns.size() - 1);
286         Rop opcode = lastInsn.getOpcode();
287 
288         if (opcode.getBranchingness() != Rop.BRANCH_RETURN
289                 && opcode != Rops.THROW) {
290             throw new RuntimeException("Exit predecessor must end"
291                     + " in valid exit statement.");
292         }
293     }
294 
295     /**
296      * Converts a single basic block to rop form.
297      *
298      * @param block SSA block to process
299      * @return {@code non-null;} ROP block
300      */
convertBasicBlock(SsaBasicBlock block)301     private BasicBlock convertBasicBlock(SsaBasicBlock block) {
302         IntList successorList = block.getRopLabelSuccessorList();
303         int primarySuccessorLabel = block.getPrimarySuccessorRopLabel();
304 
305         // Filter out any reference to the SSA form's exit block.
306 
307         // Exit block may be null.
308         SsaBasicBlock exitBlock = ssaMeth.getExitBlock();
309         int exitRopLabel = (exitBlock == null) ? -1 : exitBlock.getRopLabel();
310 
311         if (successorList.contains(exitRopLabel)) {
312             if (successorList.size() > 1) {
313                 throw new RuntimeException(
314                         "Exit predecessor must have no other successors"
315                                 + Hex.u2(block.getRopLabel()));
316             } else {
317                 successorList = IntList.EMPTY;
318                 primarySuccessorLabel = -1;
319 
320                 verifyValidExitPredecessor(block);
321             }
322         }
323 
324         successorList.setImmutable();
325 
326         BasicBlock result = new BasicBlock(
327                 block.getRopLabel(), convertInsns(block.getInsns()),
328                 successorList,
329                 primarySuccessorLabel);
330 
331         return result;
332     }
333 
334     /**
335      * Converts an insn list to rop form.
336      *
337      * @param ssaInsns {@code non-null;} old instructions
338      * @return {@code non-null;} immutable instruction list
339      */
convertInsns(ArrayList<SsaInsn> ssaInsns)340     private InsnList convertInsns(ArrayList<SsaInsn> ssaInsns) {
341         int insnCount = ssaInsns.size();
342         InsnList result = new InsnList(insnCount);
343 
344         for (int i = 0; i < insnCount; i++) {
345             result.set(i, ssaInsns.get(i).toRopInsn());
346         }
347 
348         result.setImmutable();
349 
350         return result;
351     }
352 
353     /**
354      * <b>Note:</b> This method is not presently used.
355      *
356      * @return a list of registers ordered by most-frequently-used to
357      * least-frequently-used. Each register is listed once and only
358      * once.
359      */
getRegistersByFrequency()360     public int[] getRegistersByFrequency() {
361         int regCount = ssaMeth.getRegCount();
362         Integer[] ret = new Integer[regCount];
363 
364         for (int i = 0; i < regCount; i++) {
365             ret[i] = i;
366         }
367 
368         Arrays.sort(ret, new Comparator<Integer>() {
369             @Override
370             public int compare(Integer o1, Integer o2) {
371                 return ssaMeth.getUseListForRegister(o2).size()
372                         - ssaMeth.getUseListForRegister(o1).size();
373             }
374         });
375 
376         int result[] = new int[regCount];
377 
378         for (int i = 0; i < regCount; i++) {
379             result[i] = ret[i];
380         }
381 
382         return result;
383     }
384 }
385