Merge "Add extensive logging for debugging"
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 20c04ab..3d1f080 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -509,6 +509,8 @@
     // 2) Perform outlining for the collected candidates.
     // Ideally, we should outline eagerly when threshold for a template has been reached.
 
+    printPhase("Primary optimization pass");
+
     // Process the application identifying outlining candidates.
     OptimizationFeedbackDelayed feedback = new OptimizationFeedbackDelayed();
     {
@@ -533,30 +535,39 @@
 
     // TODO(b/112831361): Implement support for staticizeClasses in CF backend.
     if (!options.isGeneratingClassFiles()) {
+      printPhase("Class staticizer post processing");
       staticizeClasses(feedback, executorService);
     }
 
     // Second inlining pass for dealing with double inline callers.
     if (inliner != null) {
+      printPhase("Double caller inlining");
       // Use direct feedback still, since methods after inlining may
       // change their status or other properties.
       inliner.processDoubleInlineCallers(this, feedback);
     }
 
+    printPhase("Lambda class synthesis");
     synthesizeLambdaClasses(builder, executorService);
-    desugarInterfaceMethods(builder, IncludeAllResources, executorService);
-    synthesizeTwrCloseResourceUtilityClass(builder);
 
+    printPhase("Interface method desugaring");
+    desugarInterfaceMethods(builder, IncludeAllResources, executorService);
+
+    printPhase("Twr close resource utility class synthesis");
+    synthesizeTwrCloseResourceUtilityClass(builder);
     handleSynthesizedClassMapping(builder);
+
+    printPhase("Lambda merging finalization");
     finalizeLambdaMerging(application, feedback, builder, executorService);
 
     if (outliner != null) {
+      printPhase("Outlining");
       timing.begin("IR conversion phase 2");
       if (outliner.selectMethodsForOutlining()) {
         forEachSelectedOutliningMethod(
             executorService,
             (code, method) -> {
-              printMethod(code, "IR before outlining (SSA)");
+              printMethod(code, "IR before outlining (SSA)", null);
               outliner.identifyOutlineSites(code, method);
             });
         DexProgramClass outlineClass = outliner.buildOutlinerClass(computeOutlineClassType());
@@ -565,7 +576,7 @@
             executorService,
             (code, method) -> {
               outliner.applyOutliningCandidate(code, method);
-              printMethod(code, "IR after outlining (SSA)");
+              printMethod(code, "IR after outlining (SSA)", null);
               finalizeIR(method, code, ignoreOptimizationFeedback);
             });
         assert outliner.checkAllOutlineSitesFoundAgain();
@@ -831,7 +842,7 @@
     }
     // Compilation header if printing CFGs for this method.
     printC1VisualizerHeader(method);
-    printMethod(code, "Initial IR (SSA)");
+    String previous = printMethod(code, "Initial IR (SSA)", null);
 
     if (method.getCode() != null && method.getCode().isJarCode()) {
       computeKotlinNonNullParamHints(feedback, method, code);
@@ -879,6 +890,8 @@
       assert code.isConsistentSSA();
     }
 
+    previous = printMethod(code, "IR after class staticizer (SSA)", previous);
+
     if (identifierNameStringMarker != null) {
       identifierNameStringMarker.decoupleIdentifierNameStringsInMethod(method, code);
       assert code.isConsistentSSA();
@@ -903,6 +916,8 @@
       inliner.performInlining(method, code, isProcessedConcurrently, callSiteInformation);
     }
 
+    previous = printMethod(code, "IR after inlining (SSA)", previous);
+
     if (appInfo.hasLiveness()) {
       // Reflection optimization 1. getClass() -> const-class
       ReflectionOptimizer.rewriteGetClass(appInfo.withLiveness(), code);
@@ -982,8 +997,12 @@
       assert code.isConsistentSSA();
     }
 
+    previous = printMethod(code, "IR after lambda desugaring (SSA)", previous);
+
     assert code.verifyTypes(appInfo, appView, graphLense());
 
+    previous = printMethod(code, "IR before class inlining (SSA)", previous);
+
     if (classInliner != null) {
       // Class inliner should work before lambda merger, so if it inlines the
       // lambda, it does not get collected by merger.
@@ -1000,25 +1019,35 @@
       assert code.isConsistentSSA();
     }
 
+    previous = printMethod(code, "IR after class inlining (SSA)", previous);
+
     if (interfaceMethodRewriter != null) {
       interfaceMethodRewriter.rewriteMethodReferences(method, code);
       assert code.isConsistentSSA();
     }
 
+    previous = printMethod(code, "IR after interface method rewriting (SSA)", previous);
+
     if (twrCloseResourceRewriter != null) {
       twrCloseResourceRewriter.rewriteMethodCode(code);
     }
 
+    previous = printMethod(code, "IR after twr close resource rewriter (SSA)", previous);
+
     if (lambdaMerger != null) {
       lambdaMerger.processMethodCode(method, code);
       assert code.isConsistentSSA();
     }
 
+    previous = printMethod(code, "IR after lambda merger (SSA)", previous);
+
     if (options.outline.enabled) {
       outlineHandler.accept(code, method);
       assert code.isConsistentSSA();
     }
 
+    previous = printMethod(code, "IR after outline handler (SSA)", previous);
+
     // TODO(mkroghj) Test if shorten live ranges is worth it.
     if (!options.isGeneratingClassFiles()) {
       ConstantCanonicalizer.canonicalize(code);
@@ -1027,10 +1056,14 @@
     }
     idempotentFunctionCallCanonicalizer.canonicalize(code);
 
+    previous =
+        printMethod(code, "IR after idempotent function call canonicalization (SSA)", previous);
+
     codeRewriter.identifyReturnsArgument(method, code, feedback);
     if (options.enableInlining && inliner != null) {
       codeRewriter.identifyInvokeSemanticsForInlining(method, code, feedback);
     }
+
     // If hints from Kotlin metadata or use of Kotlin Intrinsics were not available, track usage of
     // parameters and compute their nullability.
     if (method.getOptimizationInfo().getNonNullParamHints() == null) {
@@ -1043,8 +1076,13 @@
       assert code.isConsistentSSA();
     }
 
+    previous = printMethod(code, "IR after argument type logging (SSA)", previous);
+
     // Analysis must be done after method is rewritten by logArgumentTypes()
     codeRewriter.identifyClassInlinerEligibility(method, code, feedback);
+
+    previous = printMethod(code, "IR after class inliner eligibility (SSA)", previous);
+
     if (method.isInstanceInitializer() || method.isClassInitializer()) {
       codeRewriter.identifyTrivialInitializer(method, code, feedback);
     }
@@ -1057,7 +1095,7 @@
       codeRewriter.workaroundNumberConversionRegisterAllocationBug(code);
     }
 
-    printMethod(code, "Optimized IR (SSA)");
+    printMethod(code, "Optimized IR (SSA)", previous);
     finalizeIR(method, code, feedback);
   }
 
@@ -1174,7 +1212,7 @@
       Log.debug(getClass(), "Resulting dex code for %s:\n%s",
           method.toSourceString(), logCode(options, method));
     }
-    printMethod(code, "Final IR (non-SSA)");
+    printMethod(code, "Final IR (non-SSA)", null);
     markProcessed(method, code, feedback);
   }
 
@@ -1213,7 +1251,7 @@
     if (options.canHaveExceptionTargetingLoopHeaderBug()) {
       codeRewriter.workaroundExceptionTargetingLoopHeaderBug(code);
     }
-    printMethod(code, "After register allocation (non-SSA)");
+    printMethod(code, "After register allocation (non-SSA)", null);
     for (int i = 0; i < PEEPHOLE_OPTIMIZATION_PASSES; i++) {
       CodeRewriter.collapseTrivialGotos(method, code);
       PeepholeOptimizer.optimize(code, registerAllocator);
@@ -1399,7 +1437,13 @@
     }
   }
 
-  private void printMethod(IRCode code, String title) {
+  private void printPhase(String phase) {
+    if (!options.extensiveLoggingFilter.isEmpty()) {
+      System.out.println("Entering phase: " + phase);
+    }
+  }
+
+  private String printMethod(IRCode code, String title, String previous) {
     if (printer != null) {
       printer.resetUnusedValue();
       printer.begin("cfg");
@@ -1407,5 +1451,20 @@
       code.print(printer);
       printer.end("cfg");
     }
+    if (options.extensiveLoggingFilter.contains(code.method.method.toSourceString())) {
+      String current = code.toString();
+      System.out.println();
+      System.out.println("-----------------------------------------------------------------------");
+      System.out.println(title);
+      System.out.println("-----------------------------------------------------------------------");
+      if (previous != null && previous.equals(current)) {
+        System.out.println("Unchanged");
+      } else {
+        System.out.println(current);
+      }
+      System.out.println("-----------------------------------------------------------------------");
+      return current;
+    }
+    return previous;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index d8f4dba..fe187d7 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -25,6 +25,7 @@
 import com.android.tools.r8.utils.IROrdering.IdentityIROrdering;
 import com.android.tools.r8.utils.IROrdering.NondeterministicIROrdering;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -207,6 +208,7 @@
     }
   }
 
+  public Set<String> extensiveLoggingFilter = getExtensiveLoggingFilter();
   public List<String> methodsFilter = ImmutableList.of();
   public int minApiLevel = AndroidApiLevel.getDefault().getLevel();
   // Skipping min_api check and compiling an intermediate result intended for later merging.
@@ -265,6 +267,18 @@
 
   public LineNumberOptimization lineNumberOptimization = LineNumberOptimization.ON;
 
+  private static Set<String> getExtensiveLoggingFilter() {
+    String property = System.getProperty("com.android.tools.r8.extensiveLoggingFilter");
+    if (property != null) {
+      ImmutableSet.Builder<String> builder = ImmutableSet.builder();
+      for (String method : property.split(";")) {
+        builder.add(method);
+      }
+      return builder.build();
+    }
+    return ImmutableSet.of();
+  }
+
   public static class InvalidParameterAnnotationInfo {
 
     final DexMethod method;