Include horizontal class merging virtual method bridges in profiles

Bug: b/265729283
Change-Id: I415c65ebd24b53c3b261c199cd07b02d945fe1eb
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
index ff9cb9e..9a52be0 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
@@ -29,6 +29,7 @@
 import com.android.tools.r8.ir.analysis.value.NumberFromIntervalValue;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
+import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions;
 import com.android.tools.r8.utils.SetUtils;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Sets;
@@ -196,19 +197,22 @@
   }
 
   void mergeMethods(
+      ArtProfileCollectionAdditions artProfileCollectionAdditions,
       SyntheticArgumentClass syntheticArgumentClass,
       SyntheticInitializerConverter.Builder syntheticInitializerConverterBuilder,
       Consumer<VirtuallyMergedMethodsKeepInfo> virtuallyMergedMethodsKeepInfoConsumer) {
-    mergeVirtualMethods(virtuallyMergedMethodsKeepInfoConsumer);
+    mergeVirtualMethods(artProfileCollectionAdditions, virtuallyMergedMethodsKeepInfoConsumer);
     mergeDirectMethods(syntheticArgumentClass, syntheticInitializerConverterBuilder);
     classMethodsBuilder.setClassMethods(group.getTarget());
   }
 
   void mergeVirtualMethods(
+      ArtProfileCollectionAdditions artProfileCollectionAdditions,
       Consumer<VirtuallyMergedMethodsKeepInfo> virtuallyMergedMethodsKeepInfoConsumer) {
     virtualMethodMergers.forEach(
         merger ->
             merger.merge(
+                artProfileCollectionAdditions,
                 classMethodsBuilder,
                 lensBuilder,
                 classIdentifiers,
@@ -327,6 +331,7 @@
   }
 
   public void mergeGroup(
+      ArtProfileCollectionAdditions artProfileCollectionAdditions,
       PrunedItems.Builder prunedItemsBuilder,
       SyntheticArgumentClass syntheticArgumentClass,
       SyntheticInitializerConverter.Builder syntheticInitializerConverterBuilder,
@@ -337,6 +342,7 @@
     mergeInterfaces();
     mergeFields(prunedItemsBuilder);
     mergeMethods(
+        artProfileCollectionAdditions,
         syntheticArgumentClass,
         syntheticInitializerConverterBuilder,
         virtuallyMergedMethodsKeepInfoConsumer);
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
index 3608975..8050e50 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -17,6 +17,7 @@
 import com.android.tools.r8.graph.PrunedItems;
 import com.android.tools.r8.horizontalclassmerging.code.SyntheticInitializerConverter;
 import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.FieldAccessInfoCollectionModifier;
 import com.android.tools.r8.shaking.KeepInfoCollection;
@@ -122,6 +123,8 @@
 
     // Merge the classes.
     List<ClassMerger> classMergers = initializeClassMergers(codeProvider, lensBuilder, groups);
+    ArtProfileCollectionAdditions artProfileCollectionAdditions =
+        ArtProfileCollectionAdditions.create(appView);
     SyntheticArgumentClass syntheticArgumentClass =
         mode.isInitial()
             ? new SyntheticArgumentClass.Builder(appView.withLiveness()).build(groups)
@@ -132,6 +135,7 @@
     PrunedItems prunedItems =
         applyClassMergers(
             classMergers,
+            artProfileCollectionAdditions,
             syntheticArgumentClass,
             syntheticInitializerConverterBuilder,
             virtuallyMergedMethodsKeepInfos::add);
@@ -148,6 +152,9 @@
 
     HorizontalClassMergerGraphLens horizontalClassMergerGraphLens =
         createLens(mergedClasses, lensBuilder, mode, syntheticArgumentClass);
+    artProfileCollectionAdditions =
+        artProfileCollectionAdditions.rewriteMethodReferences(
+            horizontalClassMergerGraphLens::getNextMethodToInvoke);
 
     assert verifyNoCyclesInInterfaceHierarchies(appView, groups);
 
@@ -179,6 +186,11 @@
     }
     codeProvider.setGraphLens(horizontalClassMergerGraphLens);
 
+    // Amend art profile collection.
+    artProfileCollectionAdditions
+        .setArtProfileCollection(appView.getArtProfileCollection())
+        .commit(appView);
+
     // Record where the synthesized $r8$classId fields are read and written.
     if (mode.isInitial()) {
       createFieldAccessInfoCollectionModifier(groups).modify(appView.withLiveness());
@@ -352,12 +364,14 @@
   /** Merges all class groups using {@link ClassMerger}. */
   private PrunedItems applyClassMergers(
       Collection<ClassMerger> classMergers,
+      ArtProfileCollectionAdditions artProfileCollectionAdditions,
       SyntheticArgumentClass syntheticArgumentClass,
       SyntheticInitializerConverter.Builder syntheticInitializerConverterBuilder,
       Consumer<VirtuallyMergedMethodsKeepInfo> virtuallyMergedMethodsKeepInfoConsumer) {
     PrunedItems.Builder prunedItemsBuilder = PrunedItems.builder().setPrunedApp(appView.app());
     for (ClassMerger merger : classMergers) {
       merger.mergeGroup(
+          artProfileCollectionAdditions,
           prunedItemsBuilder,
           syntheticArgumentClass,
           syntheticInitializerConverterBuilder,
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
index 14465fd..51b4939 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
@@ -44,6 +44,11 @@
     this.mergedClasses = mergedClasses;
   }
 
+  DexMethod getNextMethodToInvoke(DexMethod method) {
+    DexMethod nextMethod = methodMap.apply(method);
+    return nextMethod != null ? nextMethod : method;
+  }
+
   @Override
   public boolean isHorizontalClassMergerGraphLens() {
     return true;
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
index 4e3227a..7d26bd1 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
@@ -14,6 +14,7 @@
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
 import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions;
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.OptionalBool;
 import com.android.tools.r8.utils.structural.Ordered;
@@ -244,6 +245,7 @@
   }
 
   public void merge(
+      ArtProfileCollectionAdditions artProfileCollectionAdditions,
       ClassMethodsBuilder classMethodsBuilder,
       HorizontalClassMergerGraphLens.Builder lensBuilder,
       Reference2IntMap<DexType> classIdentifiers,
@@ -324,6 +326,15 @@
     // Add a mapping from a synthetic name to the synthetic merged method.
     lensBuilder.recordNewMethodSignature(bridgeMethodReference, newMethodReference);
 
+    // Amend the art profile collection.
+    if (!artProfileCollectionAdditions.isNop()) {
+      for (ProgramMethod oldMethod : methods) {
+        artProfileCollectionAdditions.applyIfContextIsInProfile(
+            oldMethod.getReference(),
+            additionsBuilder -> additionsBuilder.addRule(newMethodReference));
+      }
+    }
+
     classMethodsBuilder.addVirtualMethod(newMethod);
 
     if (!virtuallyMergedMethodsKeepInfo.getKeepInfo().isBottom()) {
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileAdditions.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileAdditions.java
index b4e5881..988215e 100644
--- a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileAdditions.java
+++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileAdditions.java
@@ -20,6 +20,7 @@
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Consumer;
+import java.util.function.Function;
 
 /** Mutable extension of an existing ArtProfile. */
 public class ArtProfileAdditions {
@@ -34,7 +35,7 @@
         ProgramMethod oldMethod, ProgramMethod newMethod);
   }
 
-  private final ArtProfile artProfile;
+  private ArtProfile artProfile;
 
   private final Map<DexType, ArtProfileClassRule.Builder> classRuleAdditions =
       new ConcurrentHashMap<>();
@@ -156,4 +157,23 @@
   private boolean hasRemovals() {
     return !methodRuleRemovals.isEmpty();
   }
+
+  ArtProfileAdditions rewriteMethodReferences(Function<DexMethod, DexMethod> methodFn) {
+    ArtProfileAdditions rewrittenAdditions = new ArtProfileAdditions(artProfile);
+    assert classRuleAdditions.isEmpty();
+    assert methodRuleRemovals.isEmpty();
+    methodRuleAdditions.forEach(
+        (method, methodRuleBuilder) -> {
+          DexMethod newMethod = methodFn.apply(method);
+          ArtProfileMethodRule.Builder existingMethodRuleBuilder =
+              rewrittenAdditions.methodRuleAdditions.put(
+                  newMethod, methodRuleBuilder.setMethod(newMethod));
+          assert existingMethodRuleBuilder == null;
+        });
+    return rewrittenAdditions;
+  }
+
+  void setArtProfile(ArtProfile artProfile) {
+    this.artProfile = artProfile;
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileCollectionAdditions.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileCollectionAdditions.java
index ebc3584..858de9d 100644
--- a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileCollectionAdditions.java
+++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileCollectionAdditions.java
@@ -9,6 +9,7 @@
 import com.android.tools.r8.profile.art.ArtProfileCollection;
 import com.android.tools.r8.profile.art.rewriting.ArtProfileAdditions.ArtProfileAdditionsBuilder;
 import java.util.function.Consumer;
+import java.util.function.Function;
 
 /**
  * Interface for adding (synthetic) items to an existing ArtProfileCollection.
@@ -45,4 +46,10 @@
   ConcreteArtProfileCollectionAdditions asConcrete() {
     return null;
   }
+
+  public abstract ArtProfileCollectionAdditions rewriteMethodReferences(
+      Function<DexMethod, DexMethod> methodFn);
+
+  public abstract ArtProfileCollectionAdditions setArtProfileCollection(
+      ArtProfileCollection artProfileCollection);
 }
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/ConcreteArtProfileCollectionAdditions.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/ConcreteArtProfileCollectionAdditions.java
index 4868638..9c75c21 100644
--- a/src/main/java/com/android/tools/r8/profile/art/rewriting/ConcreteArtProfileCollectionAdditions.java
+++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/ConcreteArtProfileCollectionAdditions.java
@@ -13,14 +13,21 @@
 import com.android.tools.r8.profile.art.rewriting.ArtProfileAdditions.ArtProfileAdditionsBuilder;
 import com.google.common.collect.Iterables;
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.List;
 import java.util.function.Consumer;
+import java.util.function.Function;
 
 public class ConcreteArtProfileCollectionAdditions extends ArtProfileCollectionAdditions {
 
-  private final List<ArtProfileAdditions> additionsCollection = new ArrayList<>();
+  private final List<ArtProfileAdditions> additionsCollection;
+
+  private ConcreteArtProfileCollectionAdditions(List<ArtProfileAdditions> additionsCollection) {
+    this.additionsCollection = additionsCollection;
+  }
 
   ConcreteArtProfileCollectionAdditions(NonEmptyArtProfileCollection artProfileCollection) {
+    additionsCollection = new ArrayList<>();
     for (ArtProfile artProfile : artProfileCollection) {
       additionsCollection.add(new ArtProfileAdditions(artProfile));
     }
@@ -64,4 +71,26 @@
   private boolean hasAdditions() {
     return Iterables.any(additionsCollection, ArtProfileAdditions::hasAdditions);
   }
+
+  @Override
+  public ConcreteArtProfileCollectionAdditions rewriteMethodReferences(
+      Function<DexMethod, DexMethod> methodFn) {
+    List<ArtProfileAdditions> rewrittenAdditionsCollection =
+        new ArrayList<>(additionsCollection.size());
+    for (ArtProfileAdditions additions : additionsCollection) {
+      rewrittenAdditionsCollection.add(additions.rewriteMethodReferences(methodFn));
+    }
+    return new ConcreteArtProfileCollectionAdditions(rewrittenAdditionsCollection);
+  }
+
+  @Override
+  public ConcreteArtProfileCollectionAdditions setArtProfileCollection(
+      ArtProfileCollection artProfileCollection) {
+    assert artProfileCollection.isNonEmpty();
+    Iterator<ArtProfile> artProfileIterator = artProfileCollection.asNonEmpty().iterator();
+    for (ArtProfileAdditions additions : additionsCollection) {
+      additions.setArtProfile(artProfileIterator.next());
+    }
+    return this;
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/NopArtProfileCollectionAdditions.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/NopArtProfileCollectionAdditions.java
index 8d73389..a0c6925 100644
--- a/src/main/java/com/android/tools/r8/profile/art/rewriting/NopArtProfileCollectionAdditions.java
+++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/NopArtProfileCollectionAdditions.java
@@ -6,8 +6,10 @@
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.profile.art.ArtProfileCollection;
 import com.android.tools.r8.profile.art.rewriting.ArtProfileAdditions.ArtProfileAdditionsBuilder;
 import java.util.function.Consumer;
+import java.util.function.Function;
 
 public class NopArtProfileCollectionAdditions extends ArtProfileCollectionAdditions {
 
@@ -35,4 +37,18 @@
   public boolean isNop() {
     return true;
   }
+
+  @Override
+  public NopArtProfileCollectionAdditions rewriteMethodReferences(
+      Function<DexMethod, DexMethod> methodFn) {
+    // Intentionally empty.
+    return this;
+  }
+
+  @Override
+  public NopArtProfileCollectionAdditions setArtProfileCollection(
+      ArtProfileCollection artProfileCollection) {
+    // Intentionally empty.
+    return this;
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/HorizontallyMergedVirtualMethodProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/HorizontallyMergedVirtualMethodProfileRewritingTest.java
index 29e39f9..28707d1 100644
--- a/src/test/java/com/android/tools/r8/profile/art/completeness/HorizontallyMergedVirtualMethodProfileRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/HorizontallyMergedVirtualMethodProfileRewritingTest.java
@@ -6,6 +6,7 @@
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
 
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
@@ -35,11 +36,11 @@
       switch (this) {
         case A_METHOD:
           return ExternalArtProfile.builder()
-              .addMethodRule(Reference.methodFromMethod(A.class.getDeclaredMethod("m")))
+              .addMethodRule(Reference.methodFromMethod(A.class.getDeclaredMethod("m", B.class)))
               .build();
         case B_METHOD:
           return ExternalArtProfile.builder()
-              .addMethodRule(Reference.methodFromMethod(B.class.getDeclaredMethod("m")))
+              .addMethodRule(Reference.methodFromMethod(B.class.getDeclaredMethod("m", B.class)))
               .build();
         default:
           throw new RuntimeException();
@@ -64,9 +65,11 @@
       MethodSubject syntheticBridgeMethodSubject =
           aClassSubject.uniqueMethodThatMatches(FoundMethodSubject::isVirtual);
       assertThat(syntheticBridgeMethodSubject, isPresent());
+      assertEquals(aClassSubject.asTypeSubject(), syntheticBridgeMethodSubject.getParameter(0));
 
-      // TODO(b/265729283): Should contain the synthetic bridge method from above.
-      profileInspector.assertContainsMethodRule(movedMethodSubject).assertContainsNoOtherRules();
+      profileInspector
+          .assertContainsMethodRules(movedMethodSubject, syntheticBridgeMethodSubject)
+          .assertContainsNoOtherRules();
     }
   }
 
@@ -92,7 +95,7 @@
             inspector -> inspector.assertMergedInto(B.class, A.class).assertNoOtherClassesMerged())
         .addOptionsModification(InlinerOptions::setOnlyForceInlining)
         .addOptionsModification(
-            options -> options.callSiteOptimizationOptions().setEnableMethodStaticizing(false))
+            options -> options.callSiteOptimizationOptions().disableOptimization())
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspectResidualArtProfile(artProfileInputOutput::inspect)
@@ -103,21 +106,21 @@
   static class Main {
 
     public static void main(String[] args) {
-      new A().m();
-      new B().m();
+      new A().m(null);
+      new B().m(null);
     }
   }
 
   static class A {
 
-    public void m() {
+    public void m(B b) {
       System.out.print("Hello");
     }
   }
 
   static class B {
 
-    public void m() {
+    public void m(B b) {
       System.out.println(", world!");
     }
   }