Extend profile rewriting to record desugaring

Bug: b/265729283
Change-Id: I2b53f06b9689884fc6a3e34435f9b4796416f009
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index d9e374d..7fac411 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -71,6 +71,7 @@
 import com.android.tools.r8.optimize.interfaces.analysis.CfOpenClosedInterfacesAnalysis;
 import com.android.tools.r8.optimize.proto.ProtoNormalizer;
 import com.android.tools.r8.origin.CommandLineOrigin;
+import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions;
 import com.android.tools.r8.repackaging.Repackaging;
 import com.android.tools.r8.repackaging.RepackagingLens;
 import com.android.tools.r8.shaking.AbstractMethodRemover;
@@ -302,10 +303,13 @@
           appView.dexItemFactory());
 
       // Upfront desugaring generation: Generates new program classes to be added in the app.
+      ArtProfileCollectionAdditions artProfileCollectionAdditions =
+          ArtProfileCollectionAdditions.create(appView);
       CfClassSynthesizerDesugaringEventConsumer classSynthesizerEventConsumer =
-          new CfClassSynthesizerDesugaringEventConsumer();
+          CfClassSynthesizerDesugaringEventConsumer.create(artProfileCollectionAdditions);
       CfClassSynthesizerDesugaringCollection.create(appView)
           .synthesizeClasses(executorService, classSynthesizerEventConsumer);
+      artProfileCollectionAdditions.commit(appView);
       if (appView.getSyntheticItems().hasPendingSyntheticClasses()) {
         appView.setAppInfo(
             appView
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 5dc2584..b108b8d 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
@@ -117,9 +117,8 @@
       ClassConverterResult.Builder resultBuilder, ExecutorService executorService)
       throws ExecutionException {
     Collection<DexProgramClass> classes = appView.appInfo().classes();
-
     CfClassSynthesizerDesugaringEventConsumer classSynthesizerEventConsumer =
-        new CfClassSynthesizerDesugaringEventConsumer();
+        CfClassSynthesizerDesugaringEventConsumer.create(artProfileCollectionAdditions);
     converter.classSynthesisDesugaring(executorService, classSynthesizerEventConsumer);
     if (!classSynthesizerEventConsumer.getSynthesizedClasses().isEmpty()) {
       classes =
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringEventConsumer.java
index dfaa50d..f2cdd81 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringEventConsumer.java
@@ -9,56 +9,81 @@
 import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryWrapperSynthesizerEventConsumer.DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.DesugaredLibraryRetargeterSynthesizerEventConsumer.DesugaredLibraryRetargeterL8SynthesizerEventConsumer;
 import com.android.tools.r8.ir.desugar.itf.EmulatedInterfaceSynthesizerEventConsumer.L8ProgramEmulatedInterfaceSynthesizerEventConsumer;
-import com.android.tools.r8.ir.desugar.records.RecordDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.records.RecordDesugaringEventConsumer.RecordClassSynthesizerDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.varhandle.VarHandleDesugaringEventConsumer;
+import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions;
+import com.android.tools.r8.profile.art.rewriting.ArtProfileRewritingCfClassSynthesizerDesugaringEventConsumer;
 import com.google.common.collect.Sets;
 import java.util.Set;
 
-public class CfClassSynthesizerDesugaringEventConsumer
+public abstract class CfClassSynthesizerDesugaringEventConsumer
     implements L8ProgramEmulatedInterfaceSynthesizerEventConsumer,
         DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer,
         DesugaredLibraryRetargeterL8SynthesizerEventConsumer,
-        RecordDesugaringEventConsumer,
+        RecordClassSynthesizerDesugaringEventConsumer,
         VarHandleDesugaringEventConsumer {
 
-  private Set<DexProgramClass> synthesizedClasses = Sets.newConcurrentHashSet();
+  protected CfClassSynthesizerDesugaringEventConsumer() {}
 
-  @Override
-  public void acceptProgramEmulatedInterface(DexProgramClass clazz) {
-    synthesizedClasses.add(clazz);
+  public static CfClassSynthesizerDesugaringEventConsumer create(
+      ArtProfileCollectionAdditions artProfileCollectionAdditions) {
+    CfClassSynthesizerDesugaringEventConsumer eventConsumer =
+        new D8R8CfClassSynthesizerDesugaringEventConsumer();
+    return ArtProfileRewritingCfClassSynthesizerDesugaringEventConsumer.attach(
+        artProfileCollectionAdditions, eventConsumer);
   }
 
-  @Override
-  public void acceptWrapperProgramClass(DexProgramClass clazz) {
-    synthesizedClasses.add(clazz);
-  }
+  public abstract Set<DexProgramClass> getSynthesizedClasses();
 
-  @Override
-  public void acceptEnumConversionProgramClass(DexProgramClass clazz) {
-    synthesizedClasses.add(clazz);
-  }
+  private static class D8R8CfClassSynthesizerDesugaringEventConsumer
+      extends CfClassSynthesizerDesugaringEventConsumer {
 
-  @Override
-  public void acceptDesugaredLibraryRetargeterDispatchProgramClass(DexProgramClass clazz) {
-    synthesizedClasses.add(clazz);
-  }
+    private final Set<DexProgramClass> synthesizedClasses = Sets.newConcurrentHashSet();
 
-  @Override
-  public void acceptRecordClass(DexProgramClass clazz) {
-    synthesizedClasses.add(clazz);
-  }
+    @Override
+    public void acceptProgramEmulatedInterface(DexProgramClass clazz) {
+      synthesizedClasses.add(clazz);
+    }
 
-  @Override
-  public void acceptVarHandleDesugaringClass(DexProgramClass clazz) {
-    synthesizedClasses.add(clazz);
-  }
+    @Override
+    public void acceptWrapperProgramClass(DexProgramClass clazz) {
+      synthesizedClasses.add(clazz);
+    }
 
-  public Set<DexProgramClass> getSynthesizedClasses() {
-    return synthesizedClasses;
-  }
+    @Override
+    public void acceptEnumConversionProgramClass(DexProgramClass clazz) {
+      synthesizedClasses.add(clazz);
+    }
 
-  @Override
-  public void acceptCollectionConversion(ProgramMethod arrayConversion) {
-    synthesizedClasses.add(arrayConversion.getHolder());
+    @Override
+    public void acceptDesugaredLibraryRetargeterDispatchProgramClass(DexProgramClass clazz) {
+      synthesizedClasses.add(clazz);
+    }
+
+    @Override
+    public void acceptRecordClass(DexProgramClass recordTagClass) {
+      synthesizedClasses.add(recordTagClass);
+    }
+
+    @Override
+    public void acceptRecordClassContext(
+        DexProgramClass recordTagClass, DexProgramClass recordClass) {
+      // Intentionally empty.
+    }
+
+    @Override
+    public void acceptVarHandleDesugaringClass(DexProgramClass clazz) {
+      synthesizedClasses.add(clazz);
+    }
+
+    @Override
+    public Set<DexProgramClass> getSynthesizedClasses() {
+      return synthesizedClasses;
+    }
+
+    @Override
+    public void acceptCollectionConversion(ProgramMethod arrayConversion) {
+      synthesizedClasses.add(arrayConversion.getHolder());
+    }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
index d852270..45a508e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
@@ -173,7 +173,23 @@
     }
 
     @Override
-    public void acceptRecordMethod(ProgramMethod method) {
+    public void acceptRecordEqualsHelperMethod(ProgramMethod method, ProgramMethod context) {
+      // Intentionally empty. Added to the program using ProgramAdditions.
+    }
+
+    @Override
+    public void acceptRecordGetFieldsAsObjectsHelperMethod(
+        ProgramMethod method, ProgramMethod context) {
+      // Intentionally empty. Added to the program using ProgramAdditions.
+    }
+
+    @Override
+    public void acceptRecordHashCodeHelperMethod(ProgramMethod method, ProgramMethod context) {
+      methodProcessor.scheduleDesugaredMethodForProcessing(method);
+    }
+
+    @Override
+    public void acceptRecordToStringHelperMethod(ProgramMethod method, ProgramMethod context) {
       methodProcessor.scheduleDesugaredMethodForProcessing(method);
     }
 
@@ -191,6 +207,11 @@
     }
 
     @Override
+    public void acceptRecordClassContext(DexProgramClass recordTagClass, ProgramMethod context) {
+      // Intentionally empty.
+    }
+
+    @Override
     public void acceptVarHandleDesugaringClass(DexProgramClass clazz) {
       clazz
           .programMethods()
@@ -423,6 +444,11 @@
     }
 
     @Override
+    public void acceptRecordClassContext(DexProgramClass recordTagClass, ProgramMethod context) {
+      // Intentionally empty.
+    }
+
+    @Override
     public void acceptVarHandleDesugaringClass(DexProgramClass clazz) {
       // Intentionally empty. The class will be hit by tracing if required.
     }
@@ -433,7 +459,23 @@
     }
 
     @Override
-    public void acceptRecordMethod(ProgramMethod method) {
+    public void acceptRecordEqualsHelperMethod(ProgramMethod method, ProgramMethod context) {
+      // Intentionally empty. The method will be hit by tracing if required.
+    }
+
+    @Override
+    public void acceptRecordGetFieldsAsObjectsHelperMethod(
+        ProgramMethod method, ProgramMethod context) {
+      // Intentionally empty. The method will be hit by tracing if required.
+    }
+
+    @Override
+    public void acceptRecordHashCodeHelperMethod(ProgramMethod method, ProgramMethod context) {
+      // Intentionally empty. The method will be hit by tracing if required.
+    }
+
+    @Override
+    public void acceptRecordToStringHelperMethod(ProgramMethod method, ProgramMethod context) {
       // Intentionally empty. The method will be hit by tracing if required.
     }
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaring.java
index afae01d..2140c13 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaring.java
@@ -48,6 +48,7 @@
 import com.android.tools.r8.ir.desugar.FreshLocalProvider;
 import com.android.tools.r8.ir.desugar.LocalStackAllocator;
 import com.android.tools.r8.ir.desugar.ProgramAdditions;
+import com.android.tools.r8.ir.desugar.records.RecordDesugaringEventConsumer.RecordClassSynthesizerDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.records.RecordDesugaringEventConsumer.RecordInstructionDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.records.RecordRewriterHelper.RecordInvokeDynamic;
 import com.android.tools.r8.ir.synthetic.CallObjectInitCfCodeProvider;
@@ -59,7 +60,6 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
-import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.function.BiFunction;
 import org.objectweb.asm.Opcodes;
@@ -103,22 +103,26 @@
     CfCode cfCode = method.getDefinition().getCode().asCfCode();
     for (CfInstruction instruction : cfCode.getInstructions()) {
       if (instruction.isInvokeDynamic() && needsDesugaring(instruction, method)) {
-        prepareInvokeDynamicOnRecord(instruction.asInvokeDynamic(), programAdditions, method);
+        prepareInvokeDynamicOnRecord(
+            instruction.asInvokeDynamic(), programAdditions, method, eventConsumer);
       }
     }
   }
 
   private void prepareInvokeDynamicOnRecord(
-      CfInvokeDynamic invokeDynamic, ProgramAdditions programAdditions, ProgramMethod context) {
+      CfInvokeDynamic invokeDynamic,
+      ProgramAdditions programAdditions,
+      ProgramMethod context,
+      RecordInstructionDesugaringEventConsumer eventConsumer) {
     RecordInvokeDynamic recordInvokeDynamic =
         parseInvokeDynamicOnRecord(invokeDynamic, appView, context);
     if (recordInvokeDynamic.getMethodName() == factory.toStringMethodName
         || recordInvokeDynamic.getMethodName() == factory.hashCodeMethodName) {
-      ensureGetFieldsAsObjects(recordInvokeDynamic, programAdditions);
+      ensureGetFieldsAsObjects(recordInvokeDynamic, programAdditions, context, eventConsumer);
       return;
     }
     if (recordInvokeDynamic.getMethodName() == factory.equalsMethodName) {
-      ensureEqualsRecord(recordInvokeDynamic, programAdditions);
+      ensureEqualsRecord(recordInvokeDynamic, programAdditions, context, eventConsumer);
       return;
     }
     throw new Unreachable("Invoke dynamic needs record desugaring but could not be desugared.");
@@ -207,11 +211,19 @@
         parseInvokeDynamicOnRecord(invokeDynamic, appView, context);
     if (recordInvokeDynamic.getMethodName() == factory.toStringMethodName) {
       return desugarInvokeRecordToString(
-          recordInvokeDynamic, localStackAllocator, eventConsumer, methodProcessingContext);
+          recordInvokeDynamic,
+          localStackAllocator,
+          eventConsumer,
+          context,
+          methodProcessingContext);
     }
     if (recordInvokeDynamic.getMethodName() == factory.hashCodeMethodName) {
       return desugarInvokeRecordHashCode(
-          recordInvokeDynamic, localStackAllocator, eventConsumer, methodProcessingContext);
+          recordInvokeDynamic,
+          localStackAllocator,
+          eventConsumer,
+          context,
+          methodProcessingContext);
     }
     if (recordInvokeDynamic.getMethodName() == factory.equalsMethodName) {
       return desugarInvokeRecordEquals(recordInvokeDynamic);
@@ -251,24 +263,37 @@
   }
 
   private DexMethod ensureEqualsRecord(
-      RecordInvokeDynamic recordInvokeDynamic, ProgramAdditions programAdditions) {
-    DexMethod getFieldsAsObjects = ensureGetFieldsAsObjects(recordInvokeDynamic, programAdditions);
+      RecordInvokeDynamic recordInvokeDynamic,
+      ProgramAdditions programAdditions,
+      ProgramMethod context,
+      RecordInstructionDesugaringEventConsumer eventConsumer) {
+    DexMethod getFieldsAsObjects =
+        ensureGetFieldsAsObjects(recordInvokeDynamic, programAdditions, context, eventConsumer);
     DexProgramClass clazz = recordInvokeDynamic.getRecordClass();
     DexMethod method = equalsRecordMethod(clazz.type);
     assert clazz.lookupProgramMethod(method) == null;
-    programAdditions.ensureMethod(
-        method, () -> synthesizeEqualsRecordMethod(clazz, getFieldsAsObjects, method));
+    ProgramMethod equalsHelperMethod =
+        programAdditions.ensureMethod(
+            method, () -> synthesizeEqualsRecordMethod(clazz, getFieldsAsObjects, method));
+    eventConsumer.acceptRecordEqualsHelperMethod(equalsHelperMethod, context);
     return method;
   }
 
   private DexMethod ensureGetFieldsAsObjects(
-      RecordInvokeDynamic recordInvokeDynamic, ProgramAdditions programAdditions) {
+      RecordInvokeDynamic recordInvokeDynamic,
+      ProgramAdditions programAdditions,
+      ProgramMethod context,
+      RecordInstructionDesugaringEventConsumer eventConsumer) {
     DexProgramClass clazz = recordInvokeDynamic.getRecordClass();
     DexMethod method = getFieldsAsObjectsMethod(clazz.type);
     assert clazz.lookupProgramMethod(method) == null;
-    programAdditions.ensureMethod(
-        method,
-        () -> synthesizeGetFieldsAsObjectsMethod(clazz, recordInvokeDynamic.getFields(), method));
+    ProgramMethod getFieldsAsObjectsHelperMethod =
+        programAdditions.ensureMethod(
+            method,
+            () ->
+                synthesizeGetFieldsAsObjectsMethod(clazz, recordInvokeDynamic.getFields(), method));
+    eventConsumer.acceptRecordGetFieldsAsObjectsHelperMethod(
+        getFieldsAsObjectsHelperMethod, context);
     return method;
   }
 
@@ -306,6 +331,7 @@
       RecordInvokeDynamic recordInvokeDynamic,
       LocalStackAllocator localStackAllocator,
       RecordInstructionDesugaringEventConsumer eventConsumer,
+      ProgramMethod context,
       MethodProcessingContext methodProcessingContext) {
     localStackAllocator.allocateLocalStack(1);
     DexMethod getFieldsAsObjects = getFieldsAsObjectsMethod(recordInvokeDynamic.getRecordType());
@@ -320,7 +346,7 @@
             recordHashCodeHelperProto,
             RecordCfMethods::RecordMethods_hashCode,
             methodProcessingContext);
-    eventConsumer.acceptRecordMethod(programMethod);
+    eventConsumer.acceptRecordHashCodeHelperMethod(programMethod, context);
     instructions.add(new CfInvoke(Opcodes.INVOKESTATIC, programMethod.getReference(), false));
     return instructions;
   }
@@ -335,6 +361,7 @@
       RecordInvokeDynamic recordInvokeDynamic,
       LocalStackAllocator localStackAllocator,
       RecordInstructionDesugaringEventConsumer eventConsumer,
+      ProgramMethod context,
       MethodProcessingContext methodProcessingContext) {
     localStackAllocator.allocateLocalStack(2);
     DexMethod getFieldsAsObjects = getFieldsAsObjectsMethod(recordInvokeDynamic.getRecordType());
@@ -357,7 +384,7 @@
             recordToStringHelperProto,
             RecordCfMethods::RecordMethods_toString,
             methodProcessingContext);
-    eventConsumer.acceptRecordMethod(programMethod);
+    eventConsumer.acceptRecordToStringHelperMethod(programMethod, context);
     instructions.add(new CfInvoke(Opcodes.INVOKESTATIC, programMethod.getReference(), false));
     return instructions;
   }
@@ -374,6 +401,21 @@
     return false;
   }
 
+  private void ensureRecordClass(
+      RecordInstructionDesugaringEventConsumer eventConsumer, ProgramMethod context) {
+    DexProgramClass recordTagClass =
+        internalEnsureRecordClass(eventConsumer, ImmutableList.of(context));
+    eventConsumer.acceptRecordClassContext(recordTagClass, context);
+  }
+
+  private void ensureRecordClass(
+      RecordClassSynthesizerDesugaringEventConsumer eventConsumer,
+      Collection<DexProgramClass> recordClasses) {
+    DexProgramClass recordTagClass = internalEnsureRecordClass(eventConsumer, recordClasses);
+    recordClasses.forEach(
+        recordClass -> eventConsumer.acceptRecordClassContext(recordTagClass, recordClass));
+  }
+
   /**
    * If java.lang.Record is referenced from a class' supertype or a program method/field signature,
    * then the global synthetic is generated upfront of the compilation to avoid confusing D8/R8.
@@ -382,11 +424,12 @@
    * contains "x instance of java.lang.Record" but no record type is present, then the global
    * synthetic is generated during instruction desugaring scanning.
    */
-  private void ensureRecordClass(
-      RecordDesugaringEventConsumer eventConsumer, Collection<ProgramDefinition> contexts) {
+  private DexProgramClass internalEnsureRecordClass(
+      RecordDesugaringEventConsumer eventConsumer,
+      Collection<? extends ProgramDefinition> contexts) {
     DexItemFactory factory = appView.dexItemFactory();
     checkRecordTagNotPresent(factory);
-    appView
+    return appView
         .getSyntheticItems()
         .ensureGlobalClass(
             () -> new MissingGlobalSyntheticsConsumerDiagnostic("Record desugaring"),
@@ -401,11 +444,6 @@
             eventConsumer::acceptRecordClass);
   }
 
-  private void ensureRecordClass(
-      RecordDesugaringEventConsumer eventConsumer, ProgramDefinition context) {
-    ensureRecordClass(eventConsumer, ImmutableList.of(context));
-  }
-
   private void checkRecordTagNotPresent(DexItemFactory factory) {
     DexClass r8RecordClass =
         appView.appInfo().definitionForWithoutExistenceAssert(factory.recordTagType);
@@ -505,7 +543,7 @@
       CfClassSynthesizerDesugaringEventConsumer eventConsumer) {
     DexApplicationReadFlags flags = appView.appInfo().app().getFlags();
     if (flags.hasReadRecordReferenceFromProgramClass()) {
-      List<ProgramDefinition> classes = new ArrayList<>();
+      List<DexProgramClass> classes = new ArrayList<>(flags.getRecordWitnesses().size());
       for (DexType recordWitness : flags.getRecordWitnesses()) {
         DexClass dexClass = appView.contextIndependentDefinitionFor(recordWitness);
         assert dexClass != null;
@@ -520,8 +558,7 @@
   public void postProcessingDesugaring(
       Collection<DexProgramClass> programClasses,
       CfPostProcessingDesugaringEventConsumer eventConsumer,
-      ExecutorService executorService)
-      throws ExecutionException {
+      ExecutorService executorService) {
     for (DexProgramClass clazz : programClasses) {
       if (clazz.isRecord()) {
         assert clazz.superType == factory.recordType;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaringEventConsumer.java
index f81b5d6..469e0de 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaringEventConsumer.java
@@ -8,10 +8,23 @@
 
 public interface RecordDesugaringEventConsumer {
 
-  void acceptRecordClass(DexProgramClass recordClass);
+  void acceptRecordClass(DexProgramClass recordTagClass);
+
+  interface RecordClassSynthesizerDesugaringEventConsumer extends RecordDesugaringEventConsumer {
+
+    void acceptRecordClassContext(DexProgramClass recordTagClass, DexProgramClass recordClass);
+  }
 
   interface RecordInstructionDesugaringEventConsumer extends RecordDesugaringEventConsumer {
 
-    void acceptRecordMethod(ProgramMethod method);
+    void acceptRecordClassContext(DexProgramClass recordTagClass, ProgramMethod context);
+
+    void acceptRecordEqualsHelperMethod(ProgramMethod method, ProgramMethod context);
+
+    void acceptRecordGetFieldsAsObjectsHelperMethod(ProgramMethod method, ProgramMethod context);
+
+    void acceptRecordHashCodeHelperMethod(ProgramMethod method, ProgramMethod context);
+
+    void acceptRecordToStringHelperMethod(ProgramMethod method, ProgramMethod context);
   }
 }
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 988215e..239df06 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
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.profile.art.rewriting;
 
+import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexType;
@@ -47,6 +48,12 @@
     this.artProfile = artProfile;
   }
 
+  void applyIfContextIsInProfile(DexType context, Consumer<ArtProfileAdditions> fn) {
+    if (artProfile.containsClassRule(context)) {
+      fn.accept(this);
+    }
+  }
+
   void applyIfContextIsInProfile(
       DexMethod context, Consumer<ArtProfileAdditionsBuilder> builderConsumer) {
     ArtProfileMethodRule contextMethodRule = artProfile.getMethodRule(context);
@@ -88,7 +95,11 @@
     }
   }
 
-  private void addClassRule(DexType type) {
+  public void addClassRule(DexClass clazz) {
+    addClassRule(clazz.getType());
+  }
+
+  public void addClassRule(DexType type) {
     if (artProfile.containsClassRule(type)) {
       return;
     }
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCfClassSynthesizerDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCfClassSynthesizerDesugaringEventConsumer.java
new file mode 100644
index 0000000..3cc4631
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCfClassSynthesizerDesugaringEventConsumer.java
@@ -0,0 +1,90 @@
+// 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.DexProgramClass;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaringEventConsumer;
+import java.util.Set;
+
+public class ArtProfileRewritingCfClassSynthesizerDesugaringEventConsumer
+    extends CfClassSynthesizerDesugaringEventConsumer {
+
+  private final ConcreteArtProfileCollectionAdditions additionsCollection;
+  private final CfClassSynthesizerDesugaringEventConsumer parent;
+
+  private ArtProfileRewritingCfClassSynthesizerDesugaringEventConsumer(
+      ConcreteArtProfileCollectionAdditions additionsCollection,
+      CfClassSynthesizerDesugaringEventConsumer parent) {
+    this.additionsCollection = additionsCollection;
+    this.parent = parent;
+  }
+
+  public static CfClassSynthesizerDesugaringEventConsumer attach(
+      ArtProfileCollectionAdditions artProfileCollectionAdditions,
+      CfClassSynthesizerDesugaringEventConsumer eventConsumer) {
+    if (artProfileCollectionAdditions.isNop()) {
+      return eventConsumer;
+    }
+    return new ArtProfileRewritingCfClassSynthesizerDesugaringEventConsumer(
+        artProfileCollectionAdditions.asConcrete(), eventConsumer);
+  }
+
+  @Override
+  public void acceptCollectionConversion(ProgramMethod arrayConversion) {
+    parent.acceptCollectionConversion(arrayConversion);
+  }
+
+  @Override
+  public void acceptWrapperProgramClass(DexProgramClass clazz) {
+    parent.acceptWrapperProgramClass(clazz);
+  }
+
+  @Override
+  public void acceptEnumConversionProgramClass(DexProgramClass clazz) {
+    parent.acceptEnumConversionProgramClass(clazz);
+  }
+
+  @Override
+  public void acceptDesugaredLibraryRetargeterDispatchProgramClass(DexProgramClass clazz) {
+    parent.acceptDesugaredLibraryRetargeterDispatchProgramClass(clazz);
+  }
+
+  @Override
+  public void acceptProgramEmulatedInterface(DexProgramClass clazz) {
+    parent.acceptProgramEmulatedInterface(clazz);
+  }
+
+  @Override
+  public void acceptRecordClass(DexProgramClass recordClass) {
+    parent.acceptRecordClass(recordClass);
+  }
+
+  @Override
+  public void acceptRecordClassContext(
+      DexProgramClass recordTagClass, DexProgramClass recordClass) {
+    additionsCollection.applyIfContextIsInProfile(
+        recordClass, additions -> additions.addClassRule(recordTagClass));
+    ProgramMethod recordTagInstanceInitializer = recordTagClass.getProgramDefaultInitializer();
+    if (recordTagInstanceInitializer != null) {
+      recordClass.forEachProgramInstanceInitializer(
+          recordInstanceInitializer ->
+              additionsCollection.applyIfContextIsInProfile(
+                  recordInstanceInitializer,
+                  additionsBuilder -> additionsBuilder.addRule(recordTagInstanceInitializer)));
+    }
+    parent.acceptRecordClassContext(recordTagClass, recordClass);
+  }
+
+  @Override
+  public void acceptVarHandleDesugaringClass(DexProgramClass varHandleClass) {
+    parent.acceptVarHandleDesugaringClass(varHandleClass);
+  }
+
+  @Override
+  public Set<DexProgramClass> getSynthesizedClasses() {
+    return parent.getSynthesizedClasses();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCfInstructionDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCfInstructionDesugaringEventConsumer.java
index 5b54cd3..221f967 100644
--- a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCfInstructionDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCfInstructionDesugaringEventConsumer.java
@@ -217,8 +217,37 @@
   }
 
   @Override
-  public void acceptRecordMethod(ProgramMethod method) {
-    parent.acceptRecordMethod(method);
+  public void acceptRecordClassContext(DexProgramClass recordTagClass, ProgramMethod context) {
+    parent.acceptRecordClassContext(recordTagClass, context);
+  }
+
+  @Override
+  public void acceptRecordEqualsHelperMethod(ProgramMethod method, ProgramMethod context) {
+    additionsCollection.applyIfContextIsInProfile(
+        context, additionsBuilder -> additionsBuilder.addRule(method));
+    parent.acceptRecordEqualsHelperMethod(method, context);
+  }
+
+  @Override
+  public void acceptRecordGetFieldsAsObjectsHelperMethod(
+      ProgramMethod method, ProgramMethod context) {
+    additionsCollection.applyIfContextIsInProfile(
+        context, additionsBuilder -> additionsBuilder.addRule(method));
+    parent.acceptRecordGetFieldsAsObjectsHelperMethod(method, context);
+  }
+
+  @Override
+  public void acceptRecordHashCodeHelperMethod(ProgramMethod method, ProgramMethod context) {
+    additionsCollection.applyIfContextIsInProfile(
+        context, additionsBuilder -> additionsBuilder.addRule(method).addRule(method.getHolder()));
+    parent.acceptRecordHashCodeHelperMethod(method, context);
+  }
+
+  @Override
+  public void acceptRecordToStringHelperMethod(ProgramMethod method, ProgramMethod context) {
+    additionsCollection.applyIfContextIsInProfile(
+        context, additionsBuilder -> additionsBuilder.addRule(method).addRule(method.getHolder()));
+    parent.acceptRecordToStringHelperMethod(method, context);
   }
 
   @Override
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 9c75c21..24d6911 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
@@ -5,8 +5,10 @@
 package com.android.tools.r8.profile.art.rewriting;
 
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.profile.art.ArtProfile;
 import com.android.tools.r8.profile.art.ArtProfileCollection;
 import com.android.tools.r8.profile.art.NonEmptyArtProfileCollection;
@@ -35,6 +37,17 @@
   }
 
   void applyIfContextIsInProfile(
+      DexClass context, Consumer<ArtProfileAdditions> additionsConsumer) {
+    applyIfContextIsInProfile(context.getType(), additionsConsumer);
+  }
+
+  void applyIfContextIsInProfile(DexType type, Consumer<ArtProfileAdditions> additionsConsumer) {
+    for (ArtProfileAdditions artProfileAdditions : additionsCollection) {
+      artProfileAdditions.applyIfContextIsInProfile(type, additionsConsumer);
+    }
+  }
+
+  void applyIfContextIsInProfile(
       DexClassAndMethod context, Consumer<ArtProfileAdditionsBuilder> builderConsumer) {
     applyIfContextIsInProfile(context.getReference(), builderConsumer);
   }
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
index bf4029e..d8ae369 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -91,7 +91,7 @@
           new ContextsForGlobalSyntheticsInSingleOutputMode() {
             @Override
             public void addGlobalContexts(
-                DexType globalType, Collection<ProgramDefinition> contexts) {
+                DexType globalType, Collection<? extends ProgramDefinition> contexts) {
               throw new Unreachable("Unexpected attempt to add globals to non-desugaring build.");
             }
           };
@@ -114,7 +114,7 @@
 
     void forEach(BiConsumer<DexType, Set<DexType>> fn);
 
-    void addGlobalContexts(DexType globalType, Collection<ProgramDefinition> contexts);
+    void addGlobalContexts(DexType globalType, Collection<? extends ProgramDefinition> contexts);
   }
 
   private static class ContextsForGlobalSyntheticsInSingleOutputMode
@@ -131,7 +131,8 @@
     }
 
     @Override
-    public void addGlobalContexts(DexType globalType, Collection<ProgramDefinition> contexts) {
+    public void addGlobalContexts(
+        DexType globalType, Collection<? extends ProgramDefinition> contexts) {
       // contexts are ignored in single output modes.
     }
   }
@@ -152,7 +153,8 @@
     }
 
     @Override
-    public void addGlobalContexts(DexType globalType, Collection<ProgramDefinition> contexts) {
+    public void addGlobalContexts(
+        DexType globalType, Collection<? extends ProgramDefinition> contexts) {
       Set<DexType> contextReferences =
           globalContexts.computeIfAbsent(globalType, k -> ConcurrentHashMap.newKeySet());
       contexts.forEach(definition -> contextReferences.add(definition.getContextType()));
@@ -976,7 +978,7 @@
       Supplier<MissingGlobalSyntheticsConsumerDiagnostic> diagnosticSupplier,
       SyntheticKindSelector kindSelector,
       DexType globalType,
-      Collection<ProgramDefinition> contexts,
+      Collection<? extends ProgramDefinition> contexts,
       AppView<?> appView,
       Consumer<SyntheticProgramClassBuilder> fn,
       Consumer<DexProgramClass> onCreationConsumer) {
@@ -1033,7 +1035,8 @@
     pending.definitions.put(definition.getHolder().getType(), definition);
   }
 
-  private void addGlobalContexts(DexType globalType, Collection<ProgramDefinition> contexts) {
+  private void addGlobalContexts(
+      DexType globalType, Collection<? extends ProgramDefinition> contexts) {
     globalContexts.addGlobalContexts(globalType, contexts);
   }
 
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java
index dcf365b..0969070 100644
--- a/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java
@@ -267,8 +267,6 @@
     assertThat(
         toStringMethodSubject, ifThen(!canUseRecords, invokesMethod(toStringHelperMethodSubject)));
 
-    // TODO(b/265729283): Should include all the synthetics from above when there is no native
-    //  support.
     profileInspector
         .assertContainsClassRule(personRecordClassSubject)
         .assertContainsMethodRules(
@@ -284,6 +282,19 @@
             i ->
                 i.assertContainsMethodRules(
                     nameNestAccessorMethodSubject, ageNestAccessorMethodSubject))
+        .applyIf(
+            !canUseRecords,
+            i ->
+                i.assertContainsClassRules(
+                        recordTagClassSubject,
+                        hashCodeHelperClassSubject,
+                        toStringHelperClassSubject)
+                    .assertContainsMethodRules(
+                        recordTagInstanceInitializerSubject,
+                        equalsHelperMethodSubject,
+                        getFieldsAsObjectsMethodSubject,
+                        hashCodeHelperMethodSubject,
+                        toStringHelperMethodSubject))
         .assertContainsNoOtherRules();
   }
 }