• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4 package com.android.tools.r8.ir.conversion;
5 
6 import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.Flavor.ExcludeDexResources;
7 import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.Flavor.IncludeAllResources;
8 
9 import com.android.tools.r8.errors.Unreachable;
10 import com.android.tools.r8.graph.AppInfo;
11 import com.android.tools.r8.graph.AppInfoWithSubtyping;
12 import com.android.tools.r8.graph.Code;
13 import com.android.tools.r8.graph.DexApplication;
14 import com.android.tools.r8.graph.DexApplication.Builder;
15 import com.android.tools.r8.graph.DexEncodedMethod;
16 import com.android.tools.r8.graph.DexItemFactory;
17 import com.android.tools.r8.graph.DexMethod;
18 import com.android.tools.r8.graph.DexProgramClass;
19 import com.android.tools.r8.graph.DexString;
20 import com.android.tools.r8.graph.DexType;
21 import com.android.tools.r8.graph.GraphLense;
22 import com.android.tools.r8.ir.code.IRCode;
23 import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter;
24 import com.android.tools.r8.ir.desugar.LambdaRewriter;
25 import com.android.tools.r8.ir.optimize.CodeRewriter;
26 import com.android.tools.r8.ir.optimize.DeadCodeRemover;
27 import com.android.tools.r8.ir.optimize.Inliner;
28 import com.android.tools.r8.ir.optimize.Inliner.Constraint;
29 import com.android.tools.r8.ir.optimize.MemberValuePropagation;
30 import com.android.tools.r8.ir.optimize.Outliner;
31 import com.android.tools.r8.ir.optimize.PeepholeOptimizer;
32 import com.android.tools.r8.ir.regalloc.LinearScanRegisterAllocator;
33 import com.android.tools.r8.ir.regalloc.RegisterAllocator;
34 import com.android.tools.r8.logging.Log;
35 import com.android.tools.r8.utils.CfgPrinter;
36 import com.android.tools.r8.utils.DescriptorUtils;
37 import com.android.tools.r8.utils.InternalOptions;
38 import com.android.tools.r8.utils.ThreadUtils;
39 import com.android.tools.r8.utils.Timing;
40 
41 import com.google.common.collect.ImmutableSet;
42 import java.util.ArrayList;
43 import java.util.List;
44 import java.util.Set;
45 import java.util.concurrent.ExecutionException;
46 import java.util.concurrent.ExecutorService;
47 import java.util.concurrent.Executors;
48 import java.util.concurrent.Future;
49 import java.util.function.BiConsumer;
50 
51 public class IRConverter {
52 
53   public static final int PEEPHOLE_OPTIMIZATION_PASSES = 2;
54 
55   private final Timing timing;
56   public final DexApplication application;
57   public final AppInfo appInfo;
58   private final Outliner outliner;
59   private final LambdaRewriter lambdaRewriter;
60   private final InterfaceMethodRewriter interfaceMethodRewriter;
61   private final InternalOptions options;
62   private final CfgPrinter printer;
63   private final GraphLense graphLense;
64   private final CodeRewriter codeRewriter;
65   private final MemberValuePropagation memberValuePropagation;
66   private final LensCodeRewriter lensCodeRewriter;
67   private final Inliner inliner;
68   private CallGraph callGraph;
69   private OptimizationFeedback ignoreOptimizationFeedback = new OptimizationFeedbackIgnore();
70 
71   private DexString highestSortingString;
72 
IRConverter( Timing timing, DexApplication application, AppInfo appInfo, GraphLense graphLense, InternalOptions options, CfgPrinter printer, boolean enableDesugaring, boolean enableWholeProgramOptimizations)73   private IRConverter(
74       Timing timing,
75       DexApplication application,
76       AppInfo appInfo,
77       GraphLense graphLense,
78       InternalOptions options,
79       CfgPrinter printer,
80       boolean enableDesugaring,
81       boolean enableWholeProgramOptimizations) {
82     assert application != null;
83     assert appInfo != null;
84     assert options != null;
85     this.timing = timing != null ? timing : new Timing("internal");
86     this.application = application;
87     this.appInfo = appInfo;
88     this.graphLense = graphLense != null ? graphLense : GraphLense.getIdentityLense();
89     this.options = options;
90     this.printer = printer;
91     Set<DexType> libraryClassesWithOptimizationInfo = markLibraryMethodsReturningReceiver();
92     this.codeRewriter = new CodeRewriter(appInfo, libraryClassesWithOptimizationInfo);
93     this.lambdaRewriter = enableDesugaring ? new LambdaRewriter(this) : null;
94     this.interfaceMethodRewriter =
95         (enableDesugaring && enableInterfaceMethodDesugaring())
96             ? new InterfaceMethodRewriter(this) : null;
97     if (enableWholeProgramOptimizations) {
98       assert appInfo.withSubtyping() != null;
99       this.inliner = new Inliner(appInfo.withSubtyping(), graphLense, options);
100       this.outliner = new Outliner(appInfo, options);
101       this.memberValuePropagation = new MemberValuePropagation(appInfo);
102       this.lensCodeRewriter = new LensCodeRewriter(graphLense, appInfo.withSubtyping());
103     } else {
104       this.inliner = null;
105       this.outliner = null;
106       this.memberValuePropagation = null;
107       this.lensCodeRewriter = null;
108     }
109   }
110 
111   /**
112    * Create an IR converter for processing methods with full program optimization disabled.
113    */
IRConverter( DexApplication application, AppInfo appInfo, InternalOptions options)114   public IRConverter(
115       DexApplication application,
116       AppInfo appInfo,
117       InternalOptions options) {
118     this(null, application, appInfo, null, options, null, true, false);
119   }
120 
121   /**
122    * Create an IR converter for processing methods without full program optimization enabled.
123    *
124    * The argument <code>enableDesugaring</code> if desugaing is enabled.
125    */
IRConverter( DexApplication application, AppInfo appInfo, InternalOptions options, boolean enableDesugaring)126   public IRConverter(
127       DexApplication application,
128       AppInfo appInfo,
129       InternalOptions options,
130       boolean enableDesugaring) {
131     this(null, application, appInfo, null, options, null, enableDesugaring, false);
132   }
133 
134   /**
135    * Create an IR converter for processing methods with full program optimization disabled.
136    */
IRConverter( Timing timing, DexApplication application, AppInfo appInfo, InternalOptions options, CfgPrinter printer)137   public IRConverter(
138       Timing timing,
139       DexApplication application,
140       AppInfo appInfo,
141       InternalOptions options,
142       CfgPrinter printer) {
143     this(timing, application, appInfo, null, options, printer, true, false);
144   }
145 
146   /**
147    * Create an IR converter for processing methods with full program optimization enabled.
148    */
IRConverter( Timing timing, DexApplication application, AppInfoWithSubtyping appInfo, InternalOptions options, CfgPrinter printer, GraphLense graphLense)149   public IRConverter(
150       Timing timing,
151       DexApplication application,
152       AppInfoWithSubtyping appInfo,
153       InternalOptions options,
154       CfgPrinter printer,
155       GraphLense graphLense) {
156     this(timing, application, appInfo, graphLense, options, printer, true, true);
157   }
158 
enableInterfaceMethodDesugaring()159   private boolean enableInterfaceMethodDesugaring() {
160     switch (options.interfaceMethodDesugaring) {
161       case Off:
162         return false;
163       case Auto:
164         return !options.canUseDefaultAndStaticInterfaceMethods();
165     }
166     throw new Unreachable();
167   }
168 
enableTryWithResourcesDesugaring()169   private boolean enableTryWithResourcesDesugaring() {
170     switch (options.tryWithResourcesDesugaring) {
171       case Off:
172         return false;
173       case Auto:
174         return !options.canUseSuppressedExceptions();
175     }
176     throw new Unreachable();
177   }
178 
markLibraryMethodsReturningReceiver()179   private Set<DexType> markLibraryMethodsReturningReceiver() {
180     DexItemFactory dexItemFactory = appInfo.dexItemFactory;
181     dexItemFactory.stringBuilderMethods.forEachAppendMethod(this::markReturnsReceiver);
182     dexItemFactory.stringBufferMethods.forEachAppendMethod(this::markReturnsReceiver);
183     return ImmutableSet.of(dexItemFactory.stringBuilderType, dexItemFactory.stringBufferType);
184   }
185 
markReturnsReceiver(DexMethod method)186   private void markReturnsReceiver(DexMethod method) {
187     DexEncodedMethod definition = appInfo.definitionFor(method);
188     if (definition != null) {
189       definition.markReturnsArgument(0);
190     }
191   }
192 
removeLambdaDeserializationMethods()193   private void removeLambdaDeserializationMethods() {
194     if (lambdaRewriter != null) {
195       lambdaRewriter.removeLambdaDeserializationMethods(application.classes());
196     }
197   }
198 
synthesizeLambdaClasses(Builder builder)199   private void synthesizeLambdaClasses(Builder builder) {
200     if (lambdaRewriter != null) {
201       lambdaRewriter.adjustAccessibility();
202       lambdaRewriter.synthesizeLambdaClasses(builder);
203     }
204   }
205 
desugarInterfaceMethods( Builder builder, InterfaceMethodRewriter.Flavor includeAllResources)206   private void desugarInterfaceMethods(
207       Builder builder, InterfaceMethodRewriter.Flavor includeAllResources) {
208     if (interfaceMethodRewriter != null) {
209       interfaceMethodRewriter.desugarInterfaceMethods(builder, includeAllResources);
210     }
211   }
212 
convertToDex(ExecutorService executor)213   public DexApplication convertToDex(ExecutorService executor) throws ExecutionException {
214     removeLambdaDeserializationMethods();
215 
216     convertClassesToDex(application.classes(), executor);
217 
218     // Build a new application with jumbo string info,
219     Builder builder = new Builder(application);
220     builder.setHighestSortingString(highestSortingString);
221 
222     synthesizeLambdaClasses(builder);
223     desugarInterfaceMethods(builder, ExcludeDexResources);
224 
225     return builder.build();
226   }
227 
convertClassesToDex(Iterable<DexProgramClass> classes, ExecutorService executor)228   private void convertClassesToDex(Iterable<DexProgramClass> classes,
229       ExecutorService executor) throws ExecutionException {
230     List<Future<?>> futures = new ArrayList<>();
231     for (DexProgramClass clazz : classes) {
232       futures.add(executor.submit(() -> {
233         convertMethodsToDex(clazz.directMethods());
234         convertMethodsToDex(clazz.virtualMethods());
235       }));
236     }
237     ThreadUtils.awaitFutures(futures);
238   }
239 
convertMethodsToDex(DexEncodedMethod[] methods)240   private void convertMethodsToDex(DexEncodedMethod[] methods) {
241     for (int i = 0; i < methods.length; i++) {
242       DexEncodedMethod method = methods[i];
243       if (method.getCode() != null) {
244         boolean matchesMethodFilter = options.methodMatchesFilter(method);
245         if (matchesMethodFilter) {
246           if (method.getCode().isJarCode()) {
247             rewriteCode(method, ignoreOptimizationFeedback, Outliner::noProcessing);
248           }
249           updateHighestSortingStrings(method);
250         }
251       }
252     }
253   }
254 
optimize()255   public DexApplication optimize() throws ExecutionException {
256     ExecutorService executor = Executors.newSingleThreadExecutor();
257     try {
258       return optimize(executor);
259     } finally {
260       executor.shutdown();
261     }
262   }
263 
optimize(ExecutorService executorService)264   public DexApplication optimize(ExecutorService executorService) throws ExecutionException {
265     removeLambdaDeserializationMethods();
266 
267     timing.begin("Build call graph");
268     callGraph = CallGraph.build(application, appInfo.withSubtyping(), graphLense);
269     timing.end();
270 
271     // The process is in two phases.
272     // 1) Subject all DexEncodedMethods to optimization (except outlining).
273     //    - a side effect is candidates for outlining are identified.
274     // 2) Perform outlining for the collected candidates.
275     // Ideally, we should outline eagerly when threshold for a template has been reached.
276 
277     // Process the application identifying outlining candidates.
278     timing.begin("IR conversion phase 1");
279     OptimizationFeedback directFeedback = new OptimizationFeedbackDirect();
280     while (!callGraph.isEmpty()) {
281       List<DexEncodedMethod> methods = callGraph.extractLeaves();
282       assert methods.size() > 0;
283       // For testing we have the option to determine the processing order of the methods.
284       if (options.testing.irOrdering != null) {
285         methods = options.testing.irOrdering.apply(methods);
286       }
287       List<Future<?>> futures = new ArrayList<>();
288       for (DexEncodedMethod method : methods) {
289         futures.add(executorService.submit(() -> {
290           processMethod(method, directFeedback,
291               outliner == null ? Outliner::noProcessing : outliner::identifyCandidates);
292         }));
293       }
294       ThreadUtils.awaitFutures(futures);
295     }
296     timing.end();
297 
298     // Build a new application with jumbo string info.
299     Builder builder = new Builder(application);
300     builder.setHighestSortingString(highestSortingString);
301 
302     // Second inlining pass for dealing with double inline callers.
303     if (inliner != null) {
304       inliner.processDoubleInlineCallers(this, ignoreOptimizationFeedback);
305     }
306 
307     synthesizeLambdaClasses(builder);
308     desugarInterfaceMethods(builder, IncludeAllResources);
309 
310     if (outliner != null) {
311       timing.begin("IR conversion phase 2");
312       // Compile all classes flagged for outlining and
313       // add the outline support class IF needed.
314       DexProgramClass outlineClass = prepareOutlining();
315       if (outlineClass != null) {
316         // Process the selected methods for outlining.
317         for (DexEncodedMethod method : outliner.getMethodsSelectedForOutlining()) {
318           // This is the second time we compile this method first mark it not processed.
319           assert !method.getCode().isOutlineCode();
320           processMethod(method, ignoreOptimizationFeedback, outliner::applyOutliningCandidate);
321           assert method.isProcessed();
322         }
323         builder.addSynthesizedClass(outlineClass, true);
324         clearDexMethodCompilationState(outlineClass);
325       }
326       timing.end();
327     }
328     clearDexMethodCompilationState();
329     return builder.build();
330   }
331 
processJumboStrings(DexEncodedMethod method, DexString firstJumboString)332   public void processJumboStrings(DexEncodedMethod method, DexString firstJumboString) {
333     convertMethodJumboStringsOnly(method, firstJumboString);
334   }
335 
clearDexMethodCompilationState()336   private void clearDexMethodCompilationState() {
337     application.classes().forEach(this::clearDexMethodCompilationState);
338   }
339 
clearDexMethodCompilationState(DexProgramClass clazz)340   private void clearDexMethodCompilationState(DexProgramClass clazz) {
341     clazz.forEachMethod(DexEncodedMethod::markNotProcessed);
342   }
343 
344   /**
345    * This will replace the Dex code in the method with the Dex code generated from the provided IR.
346    *
347    * This method is *only* intended for testing, where tests manipulate the IR and need runnable Dex
348    * code.
349    *
350    * @param method the method to replace code for
351    * @param code the IR code for the method
352    */
replaceCodeForTesting(DexEncodedMethod method, IRCode code)353   public void replaceCodeForTesting(DexEncodedMethod method, IRCode code) {
354     if (Log.ENABLED) {
355       Log.debug(getClass(), "Initial (SSA) flow graph for %s:\n%s", method.toSourceString(), code);
356     }
357     assert code.isConsistentSSA();
358     RegisterAllocator registerAllocator = performRegisterAllocation(code, method);
359     method.setCode(code, registerAllocator, appInfo.dexItemFactory);
360     if (Log.ENABLED) {
361       Log.debug(getClass(), "Resulting dex code for %s:\n%s",
362           method.toSourceString(), logCode(options, method));
363     }
364   }
365 
366   // Find an unused name for the outlining class. When multiple runs produces additional
367   // outlining the default outlining class might already be present.
computeOutlineClassType()368   private DexType computeOutlineClassType() {
369     DexType result;
370     int count = 0;
371     do {
372       String name = options.outline.className + (count == 0 ? "" : Integer.toString(count));
373       count++;
374       result = application.dexItemFactory.createType(DescriptorUtils.javaTypeToDescriptor(name));
375     } while (application.definitionFor(result) != null);
376     return result;
377   }
378 
prepareOutlining()379   private DexProgramClass prepareOutlining() {
380     if (!outliner.selectMethodsForOutlining()) {
381       return null;
382     }
383     DexProgramClass outlineClass = outliner.buildOutlinerClass(computeOutlineClassType());
384     optimizeSynthesizedClass(outlineClass);
385     return outlineClass;
386   }
387 
optimizeSynthesizedClass(DexProgramClass clazz)388   public void optimizeSynthesizedClass(DexProgramClass clazz) {
389     // Process the generated class, but don't apply any outlining.
390     clazz.forEachMethod(this::optimizeSynthesizedMethod);
391   }
392 
optimizeSynthesizedMethod(DexEncodedMethod method)393   public void optimizeSynthesizedMethod(DexEncodedMethod method) {
394     // Process the generated method, but don't apply any outlining.
395     processMethod(method, ignoreOptimizationFeedback, Outliner::noProcessing);
396   }
397 
logCode(InternalOptions options, DexEncodedMethod method)398   private String logCode(InternalOptions options, DexEncodedMethod method) {
399     return options.useSmaliSyntax ? method.toSmaliString(null) : method.codeToString();
400   }
401 
processMethod(DexEncodedMethod method, OptimizationFeedback feedback, BiConsumer<IRCode, DexEncodedMethod> outlineHandler)402   public void processMethod(DexEncodedMethod method,
403       OptimizationFeedback feedback,
404       BiConsumer<IRCode, DexEncodedMethod> outlineHandler) {
405     Code code = method.getCode();
406     boolean matchesMethodFilter = options.methodMatchesFilter(method);
407     if (code != null && matchesMethodFilter) {
408       rewriteCode(method, feedback, outlineHandler);
409     } else {
410       // Mark abstract methods as processed as well.
411       method.markProcessed(Constraint.NEVER);
412     }
413   }
414 
rewriteCode(DexEncodedMethod method, OptimizationFeedback feedback, BiConsumer<IRCode, DexEncodedMethod> outlineHandler)415   private void rewriteCode(DexEncodedMethod method,
416       OptimizationFeedback feedback,
417       BiConsumer<IRCode, DexEncodedMethod> outlineHandler) {
418     if (options.verbose) {
419       System.out.println("Processing: " + method.toSourceString());
420     }
421     if (Log.ENABLED) {
422       Log.debug(getClass(), "Original code for %s:\n%s",
423           method.toSourceString(), logCode(options, method));
424     }
425     IRCode code = method.buildIR(options);
426     if (code == null) {
427       feedback.markProcessed(method, Constraint.NEVER);
428       return;
429     }
430     if (Log.ENABLED) {
431       Log.debug(getClass(), "Initial (SSA) flow graph for %s:\n%s", method.toSourceString(), code);
432     }
433     // Compilation header if printing CFGs for this method.
434     printC1VisualizerHeader(method);
435     printMethod(code, "Initial IR (SSA)");
436 
437     if (lensCodeRewriter != null) {
438       lensCodeRewriter.rewrite(code, method);
439     } else {
440       assert graphLense.isIdentityLense();
441     }
442     if (memberValuePropagation != null) {
443       memberValuePropagation.rewriteWithConstantValues(code);
444     }
445     if (options.removeSwitchMaps) {
446       // TODO(zerny): Should we support removeSwitchMaps in debug mode? b/62936642
447       assert !options.debug;
448       codeRewriter.removeSwitchMaps(code);
449     }
450     if (options.disableAssertions) {
451       codeRewriter.disableAssertions(code);
452     }
453     if (options.inlineAccessors && inliner != null) {
454       // TODO(zerny): Should we support inlining in debug mode? b/62937285
455       assert !options.debug;
456       inliner.performInlining(method, code, callGraph);
457     }
458     codeRewriter.rewriteLongCompareAndRequireNonNull(code, options);
459     codeRewriter.commonSubexpressionElimination(code);
460     codeRewriter.simplifyArrayConstruction(code);
461     codeRewriter.rewriteMoveResult(code);
462     codeRewriter.splitConstants(code);
463     codeRewriter.foldConstants(code);
464     codeRewriter.rewriteSwitch(code);
465     codeRewriter.simplifyIf(code);
466     if (Log.ENABLED) {
467       Log.debug(getClass(), "Intermediate (SSA) flow graph for %s:\n%s",
468           method.toSourceString(), code);
469     }
470     // Dead code removal. Performed after simplifications to remove code that becomes dead
471     // as a result of those simplifications. The following optimizations could reveal more
472     // dead code which is removed right before register allocation in performRegisterAllocation.
473     DeadCodeRemover.removeDeadCode(code, codeRewriter, options);
474     assert code.isConsistentSSA();
475 
476     if (enableTryWithResourcesDesugaring()) {
477       codeRewriter.rewriteThrowableAddAndGetSuppressed(code);
478     }
479 
480     if (lambdaRewriter != null) {
481       lambdaRewriter.desugarLambdas(method, code);
482       assert code.isConsistentSSA();
483     }
484 
485     if (interfaceMethodRewriter != null) {
486       interfaceMethodRewriter.rewriteMethodReferences(method, code);
487       assert code.isConsistentSSA();
488     }
489 
490     if (options.outline.enabled) {
491       outlineHandler.accept(code, method);
492       assert code.isConsistentSSA();
493     }
494 
495     codeRewriter.shortenLiveRanges(code);
496     codeRewriter.identifyReturnsArgument(method, code, feedback);
497 
498     // Insert code to log arguments if requested.
499     if (options.methodMatchesLogArgumentsFilter(method)) {
500       codeRewriter.logArgumentTypes(method, code);
501     }
502 
503     printMethod(code, "Optimized IR (SSA)");
504     // Perform register allocation.
505     RegisterAllocator registerAllocator = performRegisterAllocation(code, method);
506     method.setCode(code, registerAllocator, appInfo.dexItemFactory);
507     updateHighestSortingStrings(method);
508     if (Log.ENABLED) {
509       Log.debug(getClass(), "Resulting dex code for %s:\n%s",
510           method.toSourceString(), logCode(options, method));
511     }
512     printMethod(code, "Final IR (non-SSA)");
513 
514     // After all the optimizations have take place, we compute whether method should be inlined.
515     Constraint state;
516     if (!options.inlineAccessors || inliner == null) {
517       state = Constraint.NEVER;
518     } else {
519       state = inliner.identifySimpleMethods(code, method);
520     }
521     feedback.markProcessed(method, state);
522   }
523 
updateHighestSortingStrings(DexEncodedMethod method)524   private synchronized void updateHighestSortingStrings(DexEncodedMethod method) {
525     DexString highestSortingReferencedString = method.getCode().asDexCode().highestSortingString;
526     if (highestSortingReferencedString != null) {
527       if (highestSortingString == null
528           || highestSortingReferencedString.slowCompareTo(highestSortingString) > 0) {
529         highestSortingString = highestSortingReferencedString;
530       }
531     }
532   }
533 
534   // Convert a method ensuring that strings sorting equal or higher than the argument
535   // firstJumboString are encoded as jumbo strings.
536   // TODO(sgjesse): Consider replacing this with a direct dex2dex converter instead of going
537   // through IR.
convertMethodJumboStringsOnly( DexEncodedMethod method, DexString firstJumboString)538   private void convertMethodJumboStringsOnly(
539       DexEncodedMethod method, DexString firstJumboString) {
540     // This is only used for methods already converted to Dex, but missing jumbo strings.
541     assert method.getCode() != null && method.getCode().isDexCode();
542     if (options.verbose) {
543       System.out.println("Processing jumbo strings: " + method.toSourceString());
544     }
545     if (Log.ENABLED) {
546       Log.debug(getClass(), "Original code for %s:\n%s",
547           method.toSourceString(), logCode(options, method));
548     }
549     IRCode code = method.buildIR(options);
550     if (Log.ENABLED) {
551       Log.debug(getClass(), "Initial (SSA) flow graph for %s:\n%s",
552           method.toSourceString(), code);
553     }
554     // Compilation header if printing CFGs for this method.
555     printC1VisualizerHeader(method);
556     printMethod(code, "Initial IR (SSA)");
557 
558     // Methods passed through here should have been through IR processing already and
559     // therefore, we skip most of the IR processing.
560 
561     // Perform register allocation.
562     RegisterAllocator registerAllocator = performRegisterAllocation(code, method);
563     method.setCode(code, registerAllocator, appInfo.dexItemFactory, firstJumboString);
564 
565     if (Log.ENABLED) {
566       Log.debug(getClass(), "Resulting dex code for %s:\n%s",
567           method.toSourceString(), logCode(options, method));
568     }
569     printMethod(code, "Final IR (non-SSA)");
570   }
571 
performRegisterAllocation(IRCode code, DexEncodedMethod method)572   private RegisterAllocator performRegisterAllocation(IRCode code, DexEncodedMethod method) {
573     // Always perform dead code elimination before register allocation. The register allocator
574     // does not allow dead code (to make sure that we do not waste registers for unneeded values).
575     DeadCodeRemover.removeDeadCode(code, codeRewriter, options);
576     LinearScanRegisterAllocator registerAllocator = new LinearScanRegisterAllocator(code, options);
577     registerAllocator.allocateRegisters(options.debug);
578     printMethod(code, "After register allocation (non-SSA)");
579     printLiveRanges(registerAllocator, "Final live ranges.");
580     if (!options.debug) {
581       CodeRewriter.removedUnneededDebugPositions(code);
582     }
583     for (int i = 0; i < PEEPHOLE_OPTIMIZATION_PASSES; i++) {
584       CodeRewriter.collapsTrivialGotos(method, code);
585       PeepholeOptimizer.optimize(code, registerAllocator);
586     }
587     CodeRewriter.collapsTrivialGotos(method, code);
588     if (Log.ENABLED) {
589       Log.debug(getClass(), "Final (non-SSA) flow graph for %s:\n%s",
590           method.toSourceString(), code);
591     }
592     return registerAllocator;
593   }
594 
printC1VisualizerHeader(DexEncodedMethod method)595   private void printC1VisualizerHeader(DexEncodedMethod method) {
596     if (printer != null) {
597       printer.begin("compilation");
598       printer.print("name \"").append(method.toSourceString()).append("\"").ln();
599       printer.print("method \"").append(method.toSourceString()).append("\"").ln();
600       printer.print("date 0").ln();
601       printer.end("compilation");
602     }
603   }
604 
printMethod(IRCode code, String title)605   private void printMethod(IRCode code, String title) {
606     if (printer != null) {
607       printer.resetUnusedValue();
608       printer.begin("cfg");
609       printer.print("name \"").append(title).append("\"\n");
610       code.print(printer);
611       printer.end("cfg");
612     }
613   }
614 
printLiveRanges(LinearScanRegisterAllocator allocator, String title)615   private void printLiveRanges(LinearScanRegisterAllocator allocator, String title) {
616     if (printer != null) {
617       allocator.print(printer, title);
618     }
619   }
620 }
621