Extend profile rewriting to utility methods for code optimizations

Bug: b/265729283
Bug: b/267592755
Change-Id: I7084f16b898d461ee3c17c1d45c142f42729f379
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java
index 879beb8..7b3641f 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java
@@ -34,7 +34,6 @@
 public abstract class ClassConverter {
 
   protected final AppView<?> appView;
-  private final ArtProfileCollectionAdditions artProfileCollectionAdditions;
   private final PrimaryD8L8IRConverter converter;
   private final D8MethodProcessor methodProcessor;
   private final InterfaceProcessor interfaceProcessor;
@@ -45,7 +44,6 @@
       D8MethodProcessor methodProcessor,
       InterfaceProcessor interfaceProcessor) {
     this.appView = appView;
-    this.artProfileCollectionAdditions = ArtProfileCollectionAdditions.create(appView);
     this.converter = converter;
     this.methodProcessor = methodProcessor;
     this.interfaceProcessor = interfaceProcessor;
@@ -66,7 +64,6 @@
       throws ExecutionException {
     ClassConverterResult.Builder resultBuilder = ClassConverterResult.builder();
     internalConvertClasses(resultBuilder, executorService);
-    artProfileCollectionAdditions.commit(appView);
     notifyAllClassesConverted();
     return resultBuilder.build();
   }
@@ -117,6 +114,8 @@
       ClassConverterResult.Builder resultBuilder, ExecutorService executorService)
       throws ExecutionException {
     Collection<DexProgramClass> classes = appView.appInfo().classes();
+    ArtProfileCollectionAdditions artProfileCollectionAdditions =
+        methodProcessor.getArtProfileCollectionAdditions();
     CfClassSynthesizerDesugaringEventConsumer classSynthesizerEventConsumer =
         CfClassSynthesizerDesugaringEventConsumer.create(artProfileCollectionAdditions);
     converter.classSynthesisDesugaring(executorService, classSynthesizerEventConsumer);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/D8MethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/D8MethodProcessor.java
index 9332c11..7a04167 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/D8MethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/D8MethodProcessor.java
@@ -13,6 +13,7 @@
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer.D8CfInstructionDesugaringEventConsumer;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore;
+import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.google.common.collect.Sets;
 import java.util.ArrayList;
@@ -25,6 +26,7 @@
 
 public class D8MethodProcessor extends MethodProcessor {
 
+  private final ArtProfileCollectionAdditions artProfileCollectionAdditions;
   private final PrimaryD8L8IRConverter converter;
   private final MethodProcessorEventConsumer eventConsumer;
   private final ExecutorService executorService;
@@ -43,11 +45,12 @@
   private ProcessorContext processorContext;
 
   public D8MethodProcessor(
+      ArtProfileCollectionAdditions artProfileCollectionAdditions,
       PrimaryD8L8IRConverter converter,
-      MethodProcessorEventConsumer eventConsumer,
       ExecutorService executorService) {
+    this.artProfileCollectionAdditions = artProfileCollectionAdditions;
     this.converter = converter;
-    this.eventConsumer = eventConsumer;
+    this.eventConsumer = MethodProcessorEventConsumer.create(artProfileCollectionAdditions);
     this.executorService = executorService;
     this.processorContext = converter.appView.createProcessorContext();
   }
@@ -66,6 +69,10 @@
     return processorContext.createMethodProcessingContext(method);
   }
 
+  public ArtProfileCollectionAdditions getArtProfileCollectionAdditions() {
+    return artProfileCollectionAdditions;
+  }
+
   @Override
   public MethodProcessorEventConsumer getEventConsumer() {
     return eventConsumer;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessorEventConsumer.java b/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessorEventConsumer.java
index 1f45015..24b85b4 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessorEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessorEventConsumer.java
@@ -6,10 +6,18 @@
 
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.optimize.UtilityMethodsForCodeOptimizationsEventConsumer;
+import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions;
+import com.android.tools.r8.profile.art.rewriting.ArtProfileRewritingMethodProcessorEventConsumer;
 
 public abstract class MethodProcessorEventConsumer
     implements UtilityMethodsForCodeOptimizationsEventConsumer {
 
+  public static MethodProcessorEventConsumer create(
+      ArtProfileCollectionAdditions artProfileCollectionAdditions) {
+    return ArtProfileRewritingMethodProcessorEventConsumer.attach(
+        artProfileCollectionAdditions, empty());
+  }
+
   public static MethodProcessorEventConsumer empty() {
     return EmptyMethodProcessorEventConsumer.getInstance();
   }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryD8L8IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryD8L8IRConverter.java
index fb8e0b9..1d06a25 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryD8L8IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryD8L8IRConverter.java
@@ -55,8 +55,10 @@
     LambdaDeserializationMethodRemover.run(appView);
     workaroundAbstractMethodOnNonAbstractClassVerificationBug(executorService);
     DexApplication application = appView.appInfo().app();
-    MethodProcessorEventConsumer eventConsumer = MethodProcessorEventConsumer.empty();
-    D8MethodProcessor methodProcessor = new D8MethodProcessor(this, eventConsumer, executorService);
+    ArtProfileCollectionAdditions artProfileCollectionAdditions =
+        ArtProfileCollectionAdditions.create(appView);
+    D8MethodProcessor methodProcessor =
+        new D8MethodProcessor(artProfileCollectionAdditions, this, executorService);
     InterfaceProcessor interfaceProcessor = InterfaceProcessor.create(appView);
 
     timing.begin("IR conversion");
@@ -99,6 +101,8 @@
             appView.appInfo().getSyntheticItems().commit(application),
             appView.appInfo().getMainDexInfo()));
 
+    artProfileCollectionAdditions.commit(appView);
+
     printCfg();
   }
 
@@ -302,11 +306,11 @@
       InterfaceProcessor interfaceProcessor,
       ExecutorService executorService)
       throws ExecutionException {
-    ArtProfileCollectionAdditions artProfileCollectionAdditions =
-        ArtProfileCollectionAdditions.create(appView);
     CfPostProcessingDesugaringEventConsumer eventConsumer =
         CfPostProcessingDesugaringEventConsumer.createForD8(
-            artProfileCollectionAdditions, methodProcessor, instructionDesugaring);
+            methodProcessor.getArtProfileCollectionAdditions(),
+            methodProcessor,
+            instructionDesugaring);
     methodProcessor.newWave();
     InterfaceMethodProcessorFacade interfaceDesugaring =
         instructionDesugaring.getInterfaceMethodPostProcessingDesugaringD8(
@@ -315,7 +319,6 @@
         .postProcessingDesugaring(appView.appInfo().classes(), eventConsumer, executorService);
     methodProcessor.awaitMethodProcessing();
     eventConsumer.finalizeDesugaring();
-    artProfileCollectionAdditions.commit(appView);
   }
 
   void prepareDesugaring(
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java
index 41b4243..aae02ab 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java
@@ -16,6 +16,7 @@
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackDelayed;
 import com.android.tools.r8.logging.Log;
 import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagator;
+import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.CfgPrinter;
 import com.android.tools.r8.utils.Timing;
@@ -81,7 +82,10 @@
         new PostMethodProcessor.Builder(graphLensForPrimaryOptimizationPass);
     {
       timing.begin("Build primary method processor");
-      MethodProcessorEventConsumer eventConsumer = MethodProcessorEventConsumer.empty();
+      ArtProfileCollectionAdditions artProfileCollectionAdditions =
+          ArtProfileCollectionAdditions.create(appView);
+      MethodProcessorEventConsumer eventConsumer =
+          MethodProcessorEventConsumer.create(artProfileCollectionAdditions);
       PrimaryMethodProcessor primaryMethodProcessor =
           PrimaryMethodProcessor.create(
               appView.withLiveness(), eventConsumer, executorService, timing);
@@ -97,6 +101,7 @@
           timing,
           executorService);
       lastWaveDone(postMethodProcessorBuilder, executorService);
+      timing.time("Commit profile additions", () -> artProfileCollectionAdditions.commit(appView));
       assert appView.graphLens() == graphLensForPrimaryOptimizationPass;
       timing.end();
     }
@@ -150,11 +155,14 @@
 
     {
       timing.begin("IR conversion phase 2");
+      ArtProfileCollectionAdditions artProfileCollectionAdditions =
+          ArtProfileCollectionAdditions.create(appView);
       PostMethodProcessor postMethodProcessor =
           timing.time(
               "Build post method processor",
               () -> {
-                MethodProcessorEventConsumer eventConsumer = MethodProcessorEventConsumer.empty();
+                MethodProcessorEventConsumer eventConsumer =
+                    MethodProcessorEventConsumer.create(artProfileCollectionAdditions);
                 return postMethodProcessorBuilder.build(
                     appView, eventConsumer, executorService, timing);
               });
@@ -171,6 +179,8 @@
             timing);
         timing.end();
         timing.time("Update visible optimization info", feedback::updateVisibleOptimizationInfo);
+        timing.time(
+            "Commit profile additions", () -> artProfileCollectionAdditions.commit(appView));
         assert appView.graphLens() == graphLensForSecondaryOptimizationPass;
       }
       timing.end();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index b8d7c12..509d6c1 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -1484,8 +1484,7 @@
     // type.
     if (castType.isClassType()
         && castType.isAlwaysNull(appViewWithLiveness)
-        && !outValue.hasDebugUsers()
-        && options.testing.enableUtilityMethodsForCodeOptimizations) {
+        && !outValue.hasDebugUsers()) {
       // Replace all usages of the out-value by null.
       it.previous();
       Value nullValue = it.insertConstNullInstruction(code, options);
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingMethodProcessorEventConsumer.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingMethodProcessorEventConsumer.java
new file mode 100644
index 0000000..84143bf
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingMethodProcessorEventConsumer.java
@@ -0,0 +1,78 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.profile.art.rewriting;
+
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.conversion.MethodProcessorEventConsumer;
+
+public class ArtProfileRewritingMethodProcessorEventConsumer extends MethodProcessorEventConsumer {
+
+  private final ConcreteArtProfileCollectionAdditions additionsCollection;
+  private final MethodProcessorEventConsumer parent;
+
+  private ArtProfileRewritingMethodProcessorEventConsumer(
+      ConcreteArtProfileCollectionAdditions additionsCollection,
+      MethodProcessorEventConsumer parent) {
+    this.additionsCollection = additionsCollection;
+    this.parent = parent;
+  }
+
+  public static MethodProcessorEventConsumer attach(
+      ArtProfileCollectionAdditions artProfileCollectionAdditions,
+      MethodProcessorEventConsumer eventConsumer) {
+    if (artProfileCollectionAdditions.isNop()) {
+      return eventConsumer;
+    }
+    return new ArtProfileRewritingMethodProcessorEventConsumer(
+        artProfileCollectionAdditions.asConcrete(), eventConsumer);
+  }
+
+  @Override
+  public void acceptUtilityToStringIfNotNullMethod(ProgramMethod method, ProgramMethod context) {
+    additionsCollection.applyIfContextIsInProfile(
+        context, additionsBuilder -> additionsBuilder.addRule(method).addRule(method.getHolder()));
+    parent.acceptUtilityToStringIfNotNullMethod(method, context);
+  }
+
+  @Override
+  public void acceptUtilityThrowClassCastExceptionIfNotNullMethod(
+      ProgramMethod method, ProgramMethod context) {
+    additionsCollection.applyIfContextIsInProfile(
+        context, additionsBuilder -> additionsBuilder.addRule(method).addRule(method.getHolder()));
+    parent.acceptUtilityThrowClassCastExceptionIfNotNullMethod(method, context);
+  }
+
+  @Override
+  public void acceptUtilityThrowIllegalAccessErrorMethod(
+      ProgramMethod method, ProgramMethod context) {
+    additionsCollection.applyIfContextIsInProfile(
+        context, additionsBuilder -> additionsBuilder.addRule(method).addRule(method.getHolder()));
+    parent.acceptUtilityThrowIllegalAccessErrorMethod(method, context);
+  }
+
+  @Override
+  public void acceptUtilityThrowIncompatibleClassChangeErrorMethod(
+      ProgramMethod method, ProgramMethod context) {
+    additionsCollection.applyIfContextIsInProfile(
+        context, additionsBuilder -> additionsBuilder.addRule(method).addRule(method.getHolder()));
+    parent.acceptUtilityThrowIncompatibleClassChangeErrorMethod(method, context);
+  }
+
+  @Override
+  public void acceptUtilityThrowNoSuchMethodErrorMethod(
+      ProgramMethod method, ProgramMethod context) {
+    additionsCollection.applyIfContextIsInProfile(
+        context, additionsBuilder -> additionsBuilder.addRule(method).addRule(method.getHolder()));
+    parent.acceptUtilityThrowNoSuchMethodErrorMethod(method, context);
+  }
+
+  @Override
+  public void acceptUtilityThrowRuntimeExceptionWithMessageMethod(
+      ProgramMethod method, ProgramMethod context) {
+    additionsCollection.applyIfContextIsInProfile(
+        context, additionsBuilder -> additionsBuilder.addRule(method).addRule(method.getHolder()));
+    parent.acceptUtilityThrowRuntimeExceptionWithMessageMethod(method, context);
+  }
+}
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 f91b466..2c06cad 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -2095,7 +2095,6 @@
     public boolean enableSwitchToIfRewriting = true;
     public boolean enableEnumUnboxingDebugLogs =
         System.getProperty("com.android.tools.r8.enableEnumUnboxingDebugLogs") != null;
-    public boolean enableUtilityMethodsForCodeOptimizations = true;
     public boolean forceRedundantConstNumberRemoval = false;
     public boolean enableExperimentalDesugaredLibraryKeepRuleGenerator = false;
     public boolean invertConditionals = false;
diff --git a/src/test/java/com/android/tools/r8/internal/opensourceapps/TiviTest.java b/src/test/java/com/android/tools/r8/internal/opensourceapps/TiviTest.java
index 26aa1b1..38074be 100644
--- a/src/test/java/com/android/tools/r8/internal/opensourceapps/TiviTest.java
+++ b/src/test/java/com/android/tools/r8/internal/opensourceapps/TiviTest.java
@@ -56,8 +56,6 @@
             options -> options.apiModelingOptions().enableStubbingOfClasses = false)
         .addOptionsModification(options -> options.callSiteOptimizationOptions().setEnabled(false))
         .addOptionsModification(
-            options -> options.testing.enableUtilityMethodsForCodeOptimizations = false)
-        .addOptionsModification(
             options -> options.getArtProfileOptions().setEnableCompletenessCheckForTesting(true))
         .apply(this::configure)
         .compile();