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 ssaMeth.computeReachability(); 129 130 for (SsaBasicBlock block : ssaMeth.getBlocks()) { 131 if (block.isReachable()) continue; 132 133 // Prune instructions from unreachable blocks 134 for (int i = 0; i < block.getInsns().size(); i++) { 135 SsaInsn insn = block.getInsns().get(i); 136 RegisterSpecList sources = insn.getSources(); 137 int sourcesSize = sources.size(); 138 139 // Delete this instruction completely if it has sources 140 if (sourcesSize != 0) { 141 deletedInsns.add(insn); 142 } 143 144 // Delete this instruction from all usage lists. 145 for (int j = 0; j < sourcesSize; j++) { 146 RegisterSpec source = sources.get(j); 147 useList[source.getReg()].remove(insn); 148 } 149 150 // Remove this instruction result from the sources of any phis 151 RegisterSpec result = insn.getResult(); 152 if (result == null) continue; 153 for (SsaInsn use : useList[result.getReg()]) { 154 if (use instanceof PhiInsn) { 155 PhiInsn phiUse = (PhiInsn) use; 156 phiUse.removePhiRegister(result); 157 } 158 } 159 } 160 } 161 162 ssaMeth.deleteInsns(deletedInsns); 163 } 164 165 /** 166 * Returns true if the only uses of this register form a circle of 167 * operations with no side effects. 168 * 169 * @param regV register to examine 170 * @param set a set of registers that we've already determined 171 * are only used as sources in operations with no side effect or null 172 * if this is the first recursion 173 * @return true if usage is circular without side effect 174 */ isCircularNoSideEffect(int regV, BitSet set)175 private boolean isCircularNoSideEffect(int regV, BitSet set) { 176 if ((set != null) && set.get(regV)) { 177 return true; 178 } 179 180 for (SsaInsn use : useList[regV]) { 181 if (hasSideEffect(use)) { 182 return false; 183 } 184 } 185 186 if (set == null) { 187 set = new BitSet(regCount); 188 } 189 190 // This register is only used in operations that have no side effect. 191 set.set(regV); 192 193 for (SsaInsn use : useList[regV]) { 194 RegisterSpec result = use.getResult(); 195 196 if (result == null 197 || !isCircularNoSideEffect(result.getReg(), set)) { 198 return false; 199 } 200 } 201 202 return true; 203 } 204 205 /** 206 * Returns true if this insn has a side-effect. Returns true 207 * if the insn is null for reasons stated in the code block. 208 * 209 * @param insn {@code null-ok;} instruction in question 210 * @return true if it has a side-effect 211 */ hasSideEffect(SsaInsn insn)212 private static boolean hasSideEffect(SsaInsn insn) { 213 if (insn == null) { 214 /* While false would seem to make more sense here, true 215 * prevents us from adding this back to a worklist unnecessarally. 216 */ 217 return true; 218 } 219 220 return insn.hasSideEffect(); 221 } 222 223 /** 224 * A callback class used to build up the initial worklist of 225 * registers defined by an instruction with no side effect. 226 */ 227 static private class NoSideEffectVisitor implements SsaInsn.Visitor { 228 BitSet noSideEffectRegs; 229 230 /** 231 * Passes in data structures that will be filled out after 232 * ssaMeth.forEachInsn() is called with this instance. 233 * 234 * @param noSideEffectRegs to-build bitset of regs that are 235 * results of regs with no side effects 236 */ NoSideEffectVisitor(BitSet noSideEffectRegs)237 public NoSideEffectVisitor(BitSet noSideEffectRegs) { 238 this.noSideEffectRegs = noSideEffectRegs; 239 } 240 241 /** {@inheritDoc} */ visitMoveInsn(NormalSsaInsn insn)242 public void visitMoveInsn (NormalSsaInsn insn) { 243 // If we're tracking local vars, some moves have side effects. 244 if (!hasSideEffect(insn)) { 245 noSideEffectRegs.set(insn.getResult().getReg()); 246 } 247 } 248 249 /** {@inheritDoc} */ visitPhiInsn(PhiInsn phi)250 public void visitPhiInsn (PhiInsn phi) { 251 // If we're tracking local vars, then some phis have side effects. 252 if (!hasSideEffect(phi)) { 253 noSideEffectRegs.set(phi.getResult().getReg()); 254 } 255 } 256 257 /** {@inheritDoc} */ visitNonMoveInsn(NormalSsaInsn insn)258 public void visitNonMoveInsn (NormalSsaInsn insn) { 259 RegisterSpec result = insn.getResult(); 260 if (!hasSideEffect(insn) && result != null) { 261 noSideEffectRegs.set(result.getReg()); 262 } 263 } 264 } 265 } 266