• 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;
18 
19 import com.android.dx.rop.code.RegisterSpec;
20 import com.android.dx.rop.code.RegisterSpecList;
21 import java.util.ArrayList;
22 import java.util.BitSet;
23 import java.util.HashSet;
24 
25 /**
26  * A variation on Appel Algorithm 19.12 "Dead code elimination in SSA form".
27  *
28  * TODO this algorithm is more efficient if run in reverse from exit
29  * block to entry block.
30  */
31 public class DeadCodeRemover {
32     /** method we're processing */
33     private final SsaMethod ssaMeth;
34 
35     /** ssaMeth.getRegCount() */
36     private final int regCount;
37 
38     /**
39      * indexed by register: whether reg should be examined
40      * (does it correspond to a no-side-effect insn?)
41      */
42     private final BitSet worklist;
43 
44     /** use list indexed by register; modified during operation */
45     private final ArrayList<SsaInsn>[] useList;
46 
47     /**
48      * Process a method with the dead-code remver
49      *
50      * @param ssaMethod method to process
51      */
process(SsaMethod ssaMethod)52     public static void process(SsaMethod ssaMethod) {
53         DeadCodeRemover dc = new DeadCodeRemover(ssaMethod);
54         dc.run();
55     }
56 
57     /**
58      * Constructs an instance.
59      *
60      * @param ssaMethod method to process
61      */
DeadCodeRemover(SsaMethod ssaMethod)62     private DeadCodeRemover(SsaMethod ssaMethod) {
63         this.ssaMeth = ssaMethod;
64 
65         regCount = ssaMethod.getRegCount();
66         worklist = new BitSet(regCount);
67         useList = ssaMeth.getUseListCopy();
68     }
69 
70     /**
71      * Runs the dead code remover.
72      */
run()73     private void run() {
74         pruneDeadInstructions();
75 
76         HashSet<SsaInsn> deletedInsns = new HashSet<SsaInsn>();
77 
78         ssaMeth.forEachInsn(new NoSideEffectVisitor(worklist));
79 
80         int regV;
81 
82         while ( 0 <= (regV = worklist.nextSetBit(0)) ) {
83             worklist.clear(regV);
84 
85             if (useList[regV].size() == 0
86                     || isCircularNoSideEffect(regV, null)) {
87 
88                 SsaInsn insnS = ssaMeth.getDefinitionForRegister(regV);
89 
90                 // This insn has already been deleted.
91                 if (deletedInsns.contains(insnS)) {
92                     continue;
93                 }
94 
95                 RegisterSpecList sources = insnS.getSources();
96 
97                 int sz = sources.size();
98                 for (int i = 0; i < sz; i++) {
99                     // Delete this insn from all usage lists.
100                     RegisterSpec source = sources.get(i);
101                     useList[source.getReg()].remove(insnS);
102 
103                     if (!hasSideEffect(
104                             ssaMeth.getDefinitionForRegister(
105                                     source.getReg()))) {
106                         /*
107                          * Only registers whose definition has no side effect
108                          * should be added back to the worklist.
109                          */
110                         worklist.set(source.getReg());
111                     }
112                 }
113 
114                 // Schedule this insn for later deletion.
115                 deletedInsns.add(insnS);
116             }
117         }
118 
119         ssaMeth.deleteInsns(deletedInsns);
120     }
121 
122     /**
123      * Removes all instructions from every unreachable block.
124      */
pruneDeadInstructions()125     private void pruneDeadInstructions() {
126         HashSet<SsaInsn> deletedInsns = new HashSet<SsaInsn>();
127 
128         BitSet reachable = ssaMeth.computeReachability();
129         ArrayList<SsaBasicBlock> blocks = ssaMeth.getBlocks();
130         int blockIndex = 0;
131 
132         while ((blockIndex = reachable.nextClearBit(blockIndex)) < blocks.size()) {
133             SsaBasicBlock block = blocks.get(blockIndex);
134             blockIndex++;
135 
136             // Prune instructions from unreachable blocks
137             for (int i = 0; i < block.getInsns().size(); i++) {
138                 SsaInsn insn = block.getInsns().get(i);
139                 RegisterSpecList sources = insn.getSources();
140                 int sourcesSize = sources.size();
141 
142                 // Delete this instruction completely if it has sources
143                 if (sourcesSize != 0) {
144                     deletedInsns.add(insn);
145                 }
146 
147                 // Delete this instruction from all usage lists.
148                 for (int j = 0; j < sourcesSize; j++) {
149                     RegisterSpec source = sources.get(j);
150                     useList[source.getReg()].remove(insn);
151                 }
152 
153                 // Remove this instruction result from the sources of any phis
154                 RegisterSpec result = insn.getResult();
155                 if (result == null) continue;
156                 for (SsaInsn use : useList[result.getReg()]) {
157                     if (use instanceof PhiInsn) {
158                         PhiInsn phiUse = (PhiInsn) use;
159                         phiUse.removePhiRegister(result);
160                     }
161                 }
162             }
163         }
164 
165         ssaMeth.deleteInsns(deletedInsns);
166     }
167 
168     /**
169      * Returns true if the only uses of this register form a circle of
170      * operations with no side effects.
171      *
172      * @param regV register to examine
173      * @param set a set of registers that we've already determined
174      * are only used as sources in operations with no side effect or null
175      * if this is the first recursion
176      * @return true if usage is circular without side effect
177      */
isCircularNoSideEffect(int regV, BitSet set)178     private boolean isCircularNoSideEffect(int regV, BitSet set) {
179         if ((set != null) && set.get(regV)) {
180             return true;
181         }
182 
183         for (SsaInsn use : useList[regV]) {
184             if (hasSideEffect(use)) {
185                 return false;
186             }
187         }
188 
189         if (set == null) {
190             set = new BitSet(regCount);
191         }
192 
193         // This register is only used in operations that have no side effect.
194         set.set(regV);
195 
196         for (SsaInsn use : useList[regV]) {
197             RegisterSpec result = use.getResult();
198 
199             if (result == null
200                     || !isCircularNoSideEffect(result.getReg(), set)) {
201                 return false;
202             }
203         }
204 
205         return true;
206     }
207 
208     /**
209      * Returns true if this insn has a side-effect. Returns true
210      * if the insn is null for reasons stated in the code block.
211      *
212      * @param insn {@code null-ok;} instruction in question
213      * @return true if it has a side-effect
214      */
hasSideEffect(SsaInsn insn)215     private static boolean hasSideEffect(SsaInsn insn) {
216         if (insn == null) {
217             /* While false would seem to make more sense here, true
218              * prevents us from adding this back to a worklist unnecessarally.
219              */
220             return true;
221         }
222 
223         return insn.hasSideEffect();
224     }
225 
226     /**
227      * A callback class used to build up the initial worklist of
228      * registers defined by an instruction with no side effect.
229      */
230     static private class NoSideEffectVisitor implements SsaInsn.Visitor {
231         BitSet noSideEffectRegs;
232 
233         /**
234          * Passes in data structures that will be filled out after
235          * ssaMeth.forEachInsn() is called with this instance.
236          *
237          * @param noSideEffectRegs to-build bitset of regs that are
238          * results of regs with no side effects
239          */
NoSideEffectVisitor(BitSet noSideEffectRegs)240         public NoSideEffectVisitor(BitSet noSideEffectRegs) {
241             this.noSideEffectRegs = noSideEffectRegs;
242         }
243 
244         /** {@inheritDoc} */
245         @Override
visitMoveInsn(NormalSsaInsn insn)246         public void visitMoveInsn (NormalSsaInsn insn) {
247             // If we're tracking local vars, some moves have side effects.
248             if (!hasSideEffect(insn)) {
249                 noSideEffectRegs.set(insn.getResult().getReg());
250             }
251         }
252 
253         /** {@inheritDoc} */
254         @Override
visitPhiInsn(PhiInsn phi)255         public void visitPhiInsn (PhiInsn phi) {
256             // If we're tracking local vars, then some phis have side effects.
257             if (!hasSideEffect(phi)) {
258                 noSideEffectRegs.set(phi.getResult().getReg());
259             }
260         }
261 
262         /** {@inheritDoc} */
263         @Override
visitNonMoveInsn(NormalSsaInsn insn)264         public void visitNonMoveInsn (NormalSsaInsn insn) {
265             RegisterSpec result = insn.getResult();
266             if (!hasSideEffect(insn) && result != null) {
267                 noSideEffectRegs.set(result.getReg());
268             }
269         }
270     }
271 }
272