| /* |
| * Copyright (C) 2007 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.dx.ssa; |
| |
| import com.android.dx.rop.code.RopMethod; |
| import com.android.dx.rop.code.TranslationAdvice; |
| import com.android.dx.ssa.back.SsaToRop; |
| import com.android.dx.ssa.back.LivenessAnalyzer; |
| |
| import java.util.EnumSet; |
| import java.util.BitSet; |
| import java.util.ArrayList; |
| |
| /** |
| * Runs a method through the SSA form conversion, any optimization algorithms, |
| * and returns it to rop form. |
| */ |
| public class Optimizer { |
| private static boolean preserveLocals = true; |
| |
| private static TranslationAdvice advice; |
| |
| /** optional optimizer steps */ |
| public enum OptionalStep { |
| MOVE_PARAM_COMBINER,SCCP,LITERAL_UPGRADE,CONST_COLLECTOR |
| } |
| |
| /** |
| * @return true if local variable information should be preserved, even |
| * at code size/register size cost |
| */ |
| public static boolean getPreserveLocals() { |
| return preserveLocals; |
| } |
| |
| /** |
| * @return {@code non-null;} translation advice |
| */ |
| public static TranslationAdvice getAdvice() { |
| return advice; |
| } |
| |
| /** |
| * Runs optimization algorthims over this method, and returns a new |
| * instance of RopMethod with the changes. |
| * |
| * @param rmeth method to process |
| * @param paramWidth the total width, in register-units, of this method's |
| * parameters |
| * @param isStatic true if this method has no 'this' pointer argument. |
| * @param inPreserveLocals true if local variable info should be preserved, |
| * at the cost of some registers and insns |
| * @param inAdvice {@code non-null;} translation advice |
| * @return optimized method |
| */ |
| public static RopMethod optimize(RopMethod rmeth, int paramWidth, |
| boolean isStatic, boolean inPreserveLocals, |
| TranslationAdvice inAdvice) { |
| |
| return optimize(rmeth, paramWidth, isStatic, inPreserveLocals, inAdvice, |
| EnumSet.allOf(OptionalStep.class)); |
| } |
| |
| /** |
| * Runs optimization algorthims over this method, and returns a new |
| * instance of RopMethod with the changes. |
| * |
| * @param rmeth method to process |
| * @param paramWidth the total width, in register-units, of this method's |
| * parameters |
| * @param isStatic true if this method has no 'this' pointer argument. |
| * @param inPreserveLocals true if local variable info should be preserved, |
| * at the cost of some registers and insns |
| * @param inAdvice {@code non-null;} translation advice |
| * @param steps set of optional optimization steps to run |
| * @return optimized method |
| */ |
| public static RopMethod optimize(RopMethod rmeth, int paramWidth, |
| boolean isStatic, boolean inPreserveLocals, |
| TranslationAdvice inAdvice, EnumSet<OptionalStep> steps) { |
| SsaMethod ssaMeth = null; |
| |
| preserveLocals = inPreserveLocals; |
| advice = inAdvice; |
| |
| ssaMeth = SsaConverter.convertToSsaMethod(rmeth, paramWidth, isStatic); |
| runSsaFormSteps(ssaMeth, steps); |
| |
| RopMethod resultMeth = SsaToRop.convertToRopMethod(ssaMeth, false); |
| |
| if (resultMeth.getBlocks().getRegCount() |
| > advice.getMaxOptimalRegisterCount()) { |
| // Try to see if we can squeeze it under the register count bar |
| resultMeth = optimizeMinimizeRegisters(rmeth, paramWidth, isStatic, |
| steps); |
| } |
| return resultMeth; |
| } |
| |
| /** |
| * Runs the optimizer with a strategy to minimize the number of rop-form |
| * registers used by the end result. Dex bytecode does not have instruction |
| * forms that take register numbers larger than 15 for all instructions. |
| * If we've produced a method that uses more than 16 registers, try again |
| * with a different strategy to see if we can get under the bar. The end |
| * result will be much more efficient. |
| * |
| * @param rmeth method to process |
| * @param paramWidth the total width, in register-units, of this method's |
| * parameters |
| * @param isStatic true if this method has no 'this' pointer argument. |
| * @param steps set of optional optimization steps to run |
| * @return optimized method |
| */ |
| private static RopMethod optimizeMinimizeRegisters(RopMethod rmeth, |
| int paramWidth, boolean isStatic, |
| EnumSet<OptionalStep> steps) { |
| SsaMethod ssaMeth; |
| RopMethod resultMeth; |
| |
| ssaMeth = SsaConverter.convertToSsaMethod( |
| rmeth, paramWidth, isStatic); |
| |
| EnumSet<OptionalStep> newSteps = steps.clone(); |
| |
| /* |
| * CONST_COLLECTOR trades insns for registers, which is not an |
| * appropriate strategy here. |
| */ |
| newSteps.remove(OptionalStep.CONST_COLLECTOR); |
| |
| runSsaFormSteps(ssaMeth, newSteps); |
| |
| resultMeth = SsaToRop.convertToRopMethod(ssaMeth, true); |
| return resultMeth; |
| } |
| |
| private static void runSsaFormSteps(SsaMethod ssaMeth, |
| EnumSet<OptionalStep> steps) { |
| boolean needsDeadCodeRemover = true; |
| |
| if (steps.contains(OptionalStep.MOVE_PARAM_COMBINER)) { |
| MoveParamCombiner.process(ssaMeth); |
| } |
| |
| if (steps.contains(OptionalStep.SCCP)) { |
| SCCP.process(ssaMeth); |
| } |
| |
| if (steps.contains(OptionalStep.LITERAL_UPGRADE)) { |
| LiteralOpUpgrader.process(ssaMeth); |
| DeadCodeRemover.process(ssaMeth); |
| needsDeadCodeRemover = false; |
| } |
| |
| if (steps.contains(OptionalStep.CONST_COLLECTOR)) { |
| ConstCollector.process(ssaMeth); |
| DeadCodeRemover.process(ssaMeth); |
| needsDeadCodeRemover = false; |
| } |
| |
| // dead code remover must be run before phi type resolver |
| if (needsDeadCodeRemover) { |
| DeadCodeRemover.process(ssaMeth); |
| } |
| |
| PhiTypeResolver.process(ssaMeth); |
| } |
| |
| public static SsaMethod debugEdgeSplit(RopMethod rmeth, int paramWidth, |
| boolean isStatic, boolean inPreserveLocals, |
| TranslationAdvice inAdvice) { |
| |
| preserveLocals = inPreserveLocals; |
| advice = inAdvice; |
| |
| return SsaConverter.testEdgeSplit(rmeth, paramWidth, isStatic); |
| } |
| |
| public static SsaMethod debugPhiPlacement(RopMethod rmeth, int paramWidth, |
| boolean isStatic, boolean inPreserveLocals, |
| TranslationAdvice inAdvice) { |
| |
| preserveLocals = inPreserveLocals; |
| advice = inAdvice; |
| |
| return SsaConverter.testPhiPlacement(rmeth, paramWidth, isStatic); |
| } |
| |
| public static SsaMethod debugRenaming(RopMethod rmeth, int paramWidth, |
| boolean isStatic, boolean inPreserveLocals, |
| TranslationAdvice inAdvice) { |
| |
| preserveLocals = inPreserveLocals; |
| advice = inAdvice; |
| |
| return SsaConverter.convertToSsaMethod(rmeth, paramWidth, isStatic); |
| } |
| |
| public static SsaMethod debugDeadCodeRemover(RopMethod rmeth, |
| int paramWidth, boolean isStatic, boolean inPreserveLocals, |
| TranslationAdvice inAdvice) { |
| |
| SsaMethod ssaMeth; |
| |
| preserveLocals = inPreserveLocals; |
| advice = inAdvice; |
| |
| ssaMeth = SsaConverter.convertToSsaMethod(rmeth, paramWidth, isStatic); |
| DeadCodeRemover.process(ssaMeth); |
| |
| return ssaMeth; |
| } |
| |
| public static SsaMethod debugNoRegisterAllocation(RopMethod rmeth, |
| int paramWidth, boolean isStatic, boolean inPreserveLocals, |
| TranslationAdvice inAdvice, EnumSet<OptionalStep> steps) { |
| |
| SsaMethod ssaMeth; |
| |
| preserveLocals = inPreserveLocals; |
| advice = inAdvice; |
| |
| ssaMeth = SsaConverter.convertToSsaMethod(rmeth, paramWidth, isStatic); |
| |
| runSsaFormSteps(ssaMeth, steps); |
| |
| LivenessAnalyzer.constructInterferenceGraph(ssaMeth); |
| |
| return ssaMeth; |
| } |
| } |