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