Home | History | Annotate | Download | only in conversion
      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 
     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    */
    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    */
    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    */
    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    */
    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 
    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 
    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 
    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 
    186   private void markReturnsReceiver(DexMethod method) {
    187     DexEncodedMethod definition = appInfo.definitionFor(method);
    188     if (definition != null) {
    189       definition.markReturnsArgument(0);
    190     }
    191   }
    192 
    193   private void removeLambdaDeserializationMethods() {
    194     if (lambdaRewriter != null) {
    195       lambdaRewriter.removeLambdaDeserializationMethods(application.classes());
    196     }
    197   }
    198 
    199   private void synthesizeLambdaClasses(Builder builder) {
    200     if (lambdaRewriter != null) {
    201       lambdaRewriter.adjustAccessibility();
    202       lambdaRewriter.synthesizeLambdaClasses(builder);
    203     }
    204   }
    205 
    206   private void desugarInterfaceMethods(
    207       Builder builder, InterfaceMethodRewriter.Flavor includeAllResources) {
    208     if (interfaceMethodRewriter != null) {
    209       interfaceMethodRewriter.desugarInterfaceMethods(builder, includeAllResources);
    210     }
    211   }
    212 
    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 
    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 
    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 
    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 
    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 
    332   public void processJumboStrings(DexEncodedMethod method, DexString firstJumboString) {
    333     convertMethodJumboStringsOnly(method, firstJumboString);
    334   }
    335 
    336   private void clearDexMethodCompilationState() {
    337     application.classes().forEach(this::clearDexMethodCompilationState);
    338   }
    339 
    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    */
    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.
    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 
    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 
    388   public void optimizeSynthesizedClass(DexProgramClass clazz) {
    389     // Process the generated class, but don't apply any outlining.
    390     clazz.forEachMethod(this::optimizeSynthesizedMethod);
    391   }
    392 
    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 
    398   private String logCode(InternalOptions options, DexEncodedMethod method) {
    399     return options.useSmaliSyntax ? method.toSmaliString(null) : method.codeToString();
    400   }
    401 
    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 
    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 
    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.
    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 
    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 
    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 
    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 
    615   private void printLiveRanges(LinearScanRegisterAllocator allocator, String title) {
    616     if (printer != null) {
    617       allocator.print(printer, title);
    618     }
    619   }
    620 }
    621