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