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.RopMethod; 20 import com.android.dx.rop.code.TranslationAdvice; 21 import com.android.dx.ssa.back.LivenessAnalyzer; 22 import com.android.dx.ssa.back.SsaToRop; 23 import java.util.EnumSet; 24 25 /** 26 * Runs a method through the SSA form conversion, any optimization algorithms, 27 * and returns it to rop form. 28 */ 29 public class Optimizer { 30 private static boolean preserveLocals = true; 31 32 private static TranslationAdvice advice; 33 34 /** optional optimizer steps */ 35 public enum OptionalStep { 36 MOVE_PARAM_COMBINER, SCCP, LITERAL_UPGRADE, CONST_COLLECTOR, 37 ESCAPE_ANALYSIS 38 } 39 40 /** 41 * @return true if local variable information should be preserved, even 42 * at code size/register size cost 43 */ getPreserveLocals()44 public static boolean getPreserveLocals() { 45 return preserveLocals; 46 } 47 48 /** 49 * @return {@code non-null;} translation advice 50 */ getAdvice()51 public static TranslationAdvice getAdvice() { 52 return advice; 53 } 54 55 /** 56 * Runs optimization algorthims over this method, and returns a new 57 * instance of RopMethod with the changes. 58 * 59 * @param rmeth method to process 60 * @param paramWidth the total width, in register-units, of this method's 61 * parameters 62 * @param isStatic true if this method has no 'this' pointer argument. 63 * @param inPreserveLocals true if local variable info should be preserved, 64 * at the cost of some registers and insns 65 * @param inAdvice {@code non-null;} translation advice 66 * @return optimized method 67 */ optimize(RopMethod rmeth, int paramWidth, boolean isStatic, boolean inPreserveLocals, TranslationAdvice inAdvice)68 public static RopMethod optimize(RopMethod rmeth, int paramWidth, 69 boolean isStatic, boolean inPreserveLocals, 70 TranslationAdvice inAdvice) { 71 72 return optimize(rmeth, paramWidth, isStatic, inPreserveLocals, inAdvice, 73 EnumSet.allOf(OptionalStep.class)); 74 } 75 76 /** 77 * Runs optimization algorthims over this method, and returns a new 78 * instance of RopMethod with the changes. 79 * 80 * @param rmeth method to process 81 * @param paramWidth the total width, in register-units, of this method's 82 * parameters 83 * @param isStatic true if this method has no 'this' pointer argument. 84 * @param inPreserveLocals true if local variable info should be preserved, 85 * at the cost of some registers and insns 86 * @param inAdvice {@code non-null;} translation advice 87 * @param steps set of optional optimization steps to run 88 * @return optimized method 89 */ optimize(RopMethod rmeth, int paramWidth, boolean isStatic, boolean inPreserveLocals, TranslationAdvice inAdvice, EnumSet<OptionalStep> steps)90 public static RopMethod optimize(RopMethod rmeth, int paramWidth, 91 boolean isStatic, boolean inPreserveLocals, 92 TranslationAdvice inAdvice, EnumSet<OptionalStep> steps) { 93 SsaMethod ssaMeth = null; 94 95 preserveLocals = inPreserveLocals; 96 advice = inAdvice; 97 98 ssaMeth = SsaConverter.convertToSsaMethod(rmeth, paramWidth, isStatic); 99 runSsaFormSteps(ssaMeth, steps); 100 101 RopMethod resultMeth = SsaToRop.convertToRopMethod(ssaMeth, false); 102 103 if (resultMeth.getBlocks().getRegCount() 104 > advice.getMaxOptimalRegisterCount()) { 105 // Try to see if we can squeeze it under the register count bar 106 resultMeth = optimizeMinimizeRegisters(rmeth, paramWidth, isStatic, 107 steps); 108 } 109 return resultMeth; 110 } 111 112 /** 113 * Runs the optimizer with a strategy to minimize the number of rop-form 114 * registers used by the end result. Dex bytecode does not have instruction 115 * forms that take register numbers larger than 15 for all instructions. 116 * If we've produced a method that uses more than 16 registers, try again 117 * with a different strategy to see if we can get under the bar. The end 118 * result will be much more efficient. 119 * 120 * @param rmeth method to process 121 * @param paramWidth the total width, in register-units, of this method's 122 * parameters 123 * @param isStatic true if this method has no 'this' pointer argument. 124 * @param steps set of optional optimization steps to run 125 * @return optimized method 126 */ optimizeMinimizeRegisters(RopMethod rmeth, int paramWidth, boolean isStatic, EnumSet<OptionalStep> steps)127 private static RopMethod optimizeMinimizeRegisters(RopMethod rmeth, 128 int paramWidth, boolean isStatic, 129 EnumSet<OptionalStep> steps) { 130 SsaMethod ssaMeth; 131 RopMethod resultMeth; 132 133 ssaMeth = SsaConverter.convertToSsaMethod( 134 rmeth, paramWidth, isStatic); 135 136 EnumSet<OptionalStep> newSteps = steps.clone(); 137 138 /* 139 * CONST_COLLECTOR trades insns for registers, which is not an 140 * appropriate strategy here. 141 */ 142 newSteps.remove(OptionalStep.CONST_COLLECTOR); 143 144 runSsaFormSteps(ssaMeth, newSteps); 145 146 resultMeth = SsaToRop.convertToRopMethod(ssaMeth, true); 147 return resultMeth; 148 } 149 runSsaFormSteps(SsaMethod ssaMeth, EnumSet<OptionalStep> steps)150 private static void runSsaFormSteps(SsaMethod ssaMeth, 151 EnumSet<OptionalStep> steps) { 152 boolean needsDeadCodeRemover = true; 153 154 if (steps.contains(OptionalStep.MOVE_PARAM_COMBINER)) { 155 MoveParamCombiner.process(ssaMeth); 156 } 157 158 if (steps.contains(OptionalStep.SCCP)) { 159 SCCP.process(ssaMeth); 160 DeadCodeRemover.process(ssaMeth); 161 needsDeadCodeRemover = false; 162 } 163 164 if (steps.contains(OptionalStep.LITERAL_UPGRADE)) { 165 LiteralOpUpgrader.process(ssaMeth); 166 DeadCodeRemover.process(ssaMeth); 167 needsDeadCodeRemover = false; 168 } 169 170 /* 171 * ESCAPE_ANALYSIS impacts debuggability, so left off by default 172 */ 173 steps.remove(OptionalStep.ESCAPE_ANALYSIS); 174 if (steps.contains(OptionalStep.ESCAPE_ANALYSIS)) { 175 EscapeAnalysis.process(ssaMeth); 176 DeadCodeRemover.process(ssaMeth); 177 needsDeadCodeRemover = false; 178 } 179 180 if (steps.contains(OptionalStep.CONST_COLLECTOR)) { 181 ConstCollector.process(ssaMeth); 182 DeadCodeRemover.process(ssaMeth); 183 needsDeadCodeRemover = false; 184 } 185 186 // dead code remover must be run before phi type resolver 187 if (needsDeadCodeRemover) { 188 DeadCodeRemover.process(ssaMeth); 189 } 190 191 PhiTypeResolver.process(ssaMeth); 192 } 193 debugEdgeSplit(RopMethod rmeth, int paramWidth, boolean isStatic, boolean inPreserveLocals, TranslationAdvice inAdvice)194 public static SsaMethod debugEdgeSplit(RopMethod rmeth, int paramWidth, 195 boolean isStatic, boolean inPreserveLocals, 196 TranslationAdvice inAdvice) { 197 198 preserveLocals = inPreserveLocals; 199 advice = inAdvice; 200 201 return SsaConverter.testEdgeSplit(rmeth, paramWidth, isStatic); 202 } 203 debugPhiPlacement(RopMethod rmeth, int paramWidth, boolean isStatic, boolean inPreserveLocals, TranslationAdvice inAdvice)204 public static SsaMethod debugPhiPlacement(RopMethod rmeth, int paramWidth, 205 boolean isStatic, boolean inPreserveLocals, 206 TranslationAdvice inAdvice) { 207 208 preserveLocals = inPreserveLocals; 209 advice = inAdvice; 210 211 return SsaConverter.testPhiPlacement(rmeth, paramWidth, isStatic); 212 } 213 debugRenaming(RopMethod rmeth, int paramWidth, boolean isStatic, boolean inPreserveLocals, TranslationAdvice inAdvice)214 public static SsaMethod debugRenaming(RopMethod rmeth, int paramWidth, 215 boolean isStatic, boolean inPreserveLocals, 216 TranslationAdvice inAdvice) { 217 218 preserveLocals = inPreserveLocals; 219 advice = inAdvice; 220 221 return SsaConverter.convertToSsaMethod(rmeth, paramWidth, isStatic); 222 } 223 debugDeadCodeRemover(RopMethod rmeth, int paramWidth, boolean isStatic, boolean inPreserveLocals, TranslationAdvice inAdvice)224 public static SsaMethod debugDeadCodeRemover(RopMethod rmeth, 225 int paramWidth, boolean isStatic, boolean inPreserveLocals, 226 TranslationAdvice inAdvice) { 227 228 SsaMethod ssaMeth; 229 230 preserveLocals = inPreserveLocals; 231 advice = inAdvice; 232 233 ssaMeth = SsaConverter.convertToSsaMethod(rmeth, paramWidth, isStatic); 234 DeadCodeRemover.process(ssaMeth); 235 236 return ssaMeth; 237 } 238 debugNoRegisterAllocation(RopMethod rmeth, int paramWidth, boolean isStatic, boolean inPreserveLocals, TranslationAdvice inAdvice, EnumSet<OptionalStep> steps)239 public static SsaMethod debugNoRegisterAllocation(RopMethod rmeth, 240 int paramWidth, boolean isStatic, boolean inPreserveLocals, 241 TranslationAdvice inAdvice, EnumSet<OptionalStep> steps) { 242 243 SsaMethod ssaMeth; 244 245 preserveLocals = inPreserveLocals; 246 advice = inAdvice; 247 248 ssaMeth = SsaConverter.convertToSsaMethod(rmeth, paramWidth, isStatic); 249 250 runSsaFormSteps(ssaMeth, steps); 251 252 LivenessAnalyzer.constructInterferenceGraph(ssaMeth); 253 254 return ssaMeth; 255 } 256 } 257