Generate Record class based on AppReader flag

Bug: 197081367
Change-Id: Ic29b332eaa5949e32a6922df886fb95049c4436d
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
index 4e999be..4050961 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
@@ -331,6 +331,7 @@
     // as there was a dex resource.
     private boolean hasReadProgramResourceFromCf = false;
     private boolean hasReadProgramResourceFromDex = false;
+    private boolean hasReadProgramRecord = false;
 
     ClassReader(ExecutorService executorService, List<Future<?>> futures) {
       this.executorService = executorService;
@@ -339,7 +340,7 @@
 
     public DexApplicationReadFlags getDexApplicationReadFlags() {
       return new DexApplicationReadFlags(
-          hasReadProgramResourceFromDex, hasReadProgramResourceFromCf);
+          hasReadProgramResourceFromDex, hasReadProgramResourceFromCf, hasReadProgramRecord);
     }
 
     private void readDexSources(List<ProgramResource> dexSources, Queue<DexProgramClass> classes)
@@ -381,7 +382,15 @@
       }
       hasReadProgramResourceFromCf = true;
       JarClassFileReader<DexProgramClass> reader =
-          new JarClassFileReader<>(application, classes::add, PROGRAM);
+          new JarClassFileReader<>(
+              application,
+              clazz -> {
+                classes.add(clazz);
+                if (clazz.isRecord()) {
+                  hasReadProgramRecord = true;
+                }
+              },
+              PROGRAM);
       // Read classes in parallel.
       for (ProgramResource input : classSources) {
         futures.add(
diff --git a/src/main/java/com/android/tools/r8/graph/DexApplicationReadFlags.java b/src/main/java/com/android/tools/r8/graph/DexApplicationReadFlags.java
index 525bd9a..d5089a7 100644
--- a/src/main/java/com/android/tools/r8/graph/DexApplicationReadFlags.java
+++ b/src/main/java/com/android/tools/r8/graph/DexApplicationReadFlags.java
@@ -9,11 +9,15 @@
 
   private final boolean hasReadProgramClassFromDex;
   private final boolean hasReadProgramClassFromCf;
+  private final boolean hasReadProgramRecord;
 
   public DexApplicationReadFlags(
-      boolean hasReadProgramClassFromDex, boolean hasReadProgramClassFromCf) {
+      boolean hasReadProgramClassFromDex,
+      boolean hasReadProgramClassFromCf,
+      boolean hasReadProgramRecord) {
     this.hasReadProgramClassFromDex = hasReadProgramClassFromDex;
     this.hasReadProgramClassFromCf = hasReadProgramClassFromCf;
+    this.hasReadProgramRecord = hasReadProgramRecord;
   }
 
   public boolean hasReadProgramClassFromCf() {
@@ -23,4 +27,8 @@
   public boolean hasReadProgramClassFromDex() {
     return hasReadProgramClassFromDex;
   }
+
+  public boolean hasReadProgramRecord() {
+    return hasReadProgramRecord;
+  }
 }
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 38d9d56..5d49406 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
@@ -9,8 +9,6 @@
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.desugar.CfClassDesugaringEventConsumer;
-import com.android.tools.r8.ir.desugar.CfClassDesugaringEventConsumer.D8CfClassDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer.D8CfInstructionDesugaringEventConsumer;
@@ -55,10 +53,10 @@
       throws ExecutionException {
     List<DexProgramClass> classes = appView.appInfo().classes();
 
-    if (appView.options().isDesugaredLibraryCompilation()) {
       CfClassSynthesizerDesugaringEventConsumer classSynthesizerEventConsumer =
           new CfClassSynthesizerDesugaringEventConsumer();
       converter.classSynthesisDesugaring(executorService, classSynthesizerEventConsumer);
+    if (!classSynthesizerEventConsumer.getSynthesizedClasses().isEmpty()) {
       classes =
           ImmutableList.<DexProgramClass>builder()
               .addAll(classes)
@@ -66,9 +64,6 @@
               .build();
     }
 
-    D8CfClassDesugaringEventConsumer classDesugaringEventConsumer =
-        CfClassDesugaringEventConsumer.createForD8(methodProcessor);
-    converter.desugarClassesForD8(classes, classDesugaringEventConsumer, executorService);
     converter.prepareDesugaringForD8(executorService);
 
     while (!classes.isEmpty()) {
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 c6f7d4a..a1d1e2a 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
@@ -44,8 +44,6 @@
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.conversion.MethodConversionOptions.DefaultMethodConversionOptions;
 import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
-import com.android.tools.r8.ir.desugar.CfClassDesugaringCollection;
-import com.android.tools.r8.ir.desugar.CfClassDesugaringEventConsumer.D8CfClassDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaringCollection;
 import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaringCollection;
@@ -134,7 +132,6 @@
   private final Timing timing;
   private final Outliner outliner;
   private final ClassInitializerDefaultsOptimization classInitializerDefaultsOptimization;
-  private final CfClassDesugaringCollection classDesugaring;
   private final CfInstructionDesugaringCollection instructionDesugaring;
   private final FieldAccessAnalysis fieldAccessAnalysis;
   private final LibraryMethodOverrideAnalysis libraryMethodOverrideAnalysis;
@@ -225,7 +222,6 @@
       // - invoke-special desugaring.
       assert options.desugarState.isOn();
       this.instructionDesugaring = CfInstructionDesugaringCollection.create(appView);
-      this.classDesugaring = CfClassDesugaringCollection.create(appView);
       this.interfaceMethodRewriter = null;
       this.covariantReturnTypeAnnotationTransformer = null;
       this.dynamicTypeOptimization = null;
@@ -252,10 +248,6 @@
         appView.enableWholeProgramOptimizations()
             ? CfInstructionDesugaringCollection.empty()
             : CfInstructionDesugaringCollection.create(appView);
-    this.classDesugaring =
-        appView.enableWholeProgramOptimizations()
-            ? CfClassDesugaringCollection.empty()
-            : CfClassDesugaringCollection.create(appView);
     this.interfaceMethodRewriter =
         options.isInterfaceMethodDesugaringEnabled() && appView.enableWholeProgramOptimizations()
             ? new InterfaceMethodRewriter(appView, this)
@@ -456,7 +448,8 @@
       ExecutorService executorService,
       CfClassSynthesizerDesugaringEventConsumer classSynthesizerEventConsumer)
       throws ExecutionException {
-    new CfClassSynthesizerDesugaringCollection(appView, instructionDesugaring.getRetargetingInfo())
+    CfClassSynthesizerDesugaringCollection.create(
+            appView, instructionDesugaring.getRetargetingInfo())
         .synthesizeClasses(executorService, classSynthesizerEventConsumer);
   }
 
@@ -494,26 +487,6 @@
         DesugaredLibraryAPIConverter::generateTrackingWarnings);
   }
 
-  public void desugarClassesForD8(
-      List<DexProgramClass> classes,
-      D8CfClassDesugaringEventConsumer desugaringEventConsumer,
-      ExecutorService executorService)
-      throws ExecutionException {
-    if (classDesugaring.isEmpty()) {
-      return;
-    }
-    // Currently the classes can be processed in any order and do not require to be sorted.
-    ThreadUtils.processItems(
-        classes, clazz -> desugarClassForD8(clazz, desugaringEventConsumer), executorService);
-  }
-
-  private void desugarClassForD8(
-      DexProgramClass clazz, D8CfClassDesugaringEventConsumer desugaringEventConsumer) {
-    if (classDesugaring.needsDesugaring(clazz)) {
-      classDesugaring.desugar(clazz, desugaringEventConsumer);
-    }
-  }
-
   public void prepareDesugaringForD8(ExecutorService executorService) throws ExecutionException {
     // Prepare desugaring by collecting all the synthetic methods required on program classes.
     ProgramAdditions programAdditions = new ProgramAdditions();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfClassDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/CfClassDesugaring.java
deleted file mode 100644
index a885344..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfClassDesugaring.java
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright (c) 2021, 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.ir.desugar;
-
-import com.android.tools.r8.graph.DexProgramClass;
-
-/** Interface for desugaring a class. */
-public interface CfClassDesugaring {
-
-  void desugar(DexProgramClass clazz, CfClassDesugaringEventConsumer eventConsumer);
-
-  /** Returns true if the given class needs desugaring. */
-  boolean needsDesugaring(DexProgramClass clazz);
-}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfClassDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/CfClassDesugaringCollection.java
deleted file mode 100644
index 1ddeb09..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfClassDesugaringCollection.java
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright (c) 2021, 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.ir.desugar;
-
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.ir.desugar.records.RecordRewriter;
-import com.google.common.collect.Iterables;
-import java.util.ArrayList;
-import java.util.List;
-
-/** Interface for desugaring a class. */
-public abstract class CfClassDesugaringCollection {
-
-  public abstract void desugar(DexProgramClass clazz, CfClassDesugaringEventConsumer eventConsumer);
-
-  /** Returns true if the given class needs desugaring. */
-  public abstract boolean needsDesugaring(DexProgramClass clazz);
-
-  public abstract boolean isEmpty();
-
-  public static CfClassDesugaringCollection empty() {
-    return EmptyCfClassDesugaringCollection.getInstance();
-  }
-
-  public static CfClassDesugaringCollection create(AppView<?> appView) {
-    List<CfClassDesugaring> desugarings = new ArrayList<>();
-    RecordRewriter recordRewriter = RecordRewriter.create(appView);
-    if (recordRewriter != null) {
-      desugarings.add(recordRewriter);
-    }
-    if (desugarings.isEmpty()) {
-      return empty();
-    }
-    return new NonEmptyCfClassDesugaringCollection(desugarings);
-  }
-
-  public static class NonEmptyCfClassDesugaringCollection extends CfClassDesugaringCollection {
-    private final List<CfClassDesugaring> desugarings;
-
-    NonEmptyCfClassDesugaringCollection(List<CfClassDesugaring> desugarings) {
-      this.desugarings = desugarings;
-    }
-
-    @Override
-    public void desugar(DexProgramClass clazz, CfClassDesugaringEventConsumer eventConsumer) {
-      for (CfClassDesugaring desugaring : desugarings) {
-        desugaring.desugar(clazz, eventConsumer);
-      }
-    }
-
-    @Override
-    public boolean needsDesugaring(DexProgramClass clazz) {
-      return Iterables.any(desugarings, desugaring -> desugaring.needsDesugaring(clazz));
-    }
-
-    @Override
-    public boolean isEmpty() {
-      return false;
-    }
-  }
-
-  public static class EmptyCfClassDesugaringCollection extends CfClassDesugaringCollection {
-
-    private static final EmptyCfClassDesugaringCollection INSTANCE =
-        new EmptyCfClassDesugaringCollection();
-
-    public static EmptyCfClassDesugaringCollection getInstance() {
-      return INSTANCE;
-    }
-
-    @Override
-    public void desugar(DexProgramClass clazz, CfClassDesugaringEventConsumer eventConsumer) {
-      // Intentionally empty.
-    }
-
-    @Override
-    public boolean needsDesugaring(DexProgramClass clazz) {
-      return false;
-    }
-
-    @Override
-    public boolean isEmpty() {
-      return true;
-    }
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfClassDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/CfClassDesugaringEventConsumer.java
deleted file mode 100644
index ef09aba..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfClassDesugaringEventConsumer.java
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright (c) 2021, 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.ir.desugar;
-
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.conversion.D8MethodProcessor;
-import com.android.tools.r8.ir.desugar.records.RecordDesugaringEventConsumer;
-
-public abstract class CfClassDesugaringEventConsumer implements RecordDesugaringEventConsumer {
-
-  public static D8CfClassDesugaringEventConsumer createForD8(D8MethodProcessor methodProcessor) {
-    return new D8CfClassDesugaringEventConsumer(methodProcessor);
-  }
-
-  public static class D8CfClassDesugaringEventConsumer extends CfClassDesugaringEventConsumer {
-
-    private final D8MethodProcessor methodProcessor;
-
-    public D8CfClassDesugaringEventConsumer(D8MethodProcessor methodProcessor) {
-      this.methodProcessor = methodProcessor;
-    }
-
-    @Override
-    public void acceptRecordClass(DexProgramClass recordClass) {
-      methodProcessor.scheduleDesugaredMethodsForProcessing(recordClass.programMethods());
-    }
-
-    @Override
-    public void acceptRecordMethod(ProgramMethod method) {
-      assert false;
-    }
-  }
-
-  // TODO(b/): Implement R8CfClassDesugaringEventConsumer
-}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringCollection.java
index 4bca24a..b0ea9ef 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringCollection.java
@@ -1,7 +1,6 @@
 // Copyright (c) 2021, 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.ir.desugar;
 
 import com.android.tools.r8.graph.AppView;
@@ -9,36 +8,73 @@
 import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryWrapperSynthesizer;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.RetargetingInfo;
 import com.android.tools.r8.ir.desugar.itf.ProgramEmulatedInterfaceSynthesizer;
+import com.android.tools.r8.ir.desugar.records.RecordRewriter;
 import com.android.tools.r8.utils.ThreadUtils;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 
-public class CfClassSynthesizerDesugaringCollection {
+public abstract class CfClassSynthesizerDesugaringCollection {
 
-  private Collection<CfClassSynthesizerDesugaring> synthesizers = new ArrayList<>();
-
-  public CfClassSynthesizerDesugaringCollection(
+  public static CfClassSynthesizerDesugaringCollection create(
       AppView<?> appView, RetargetingInfo retargetingInfo) {
-    assert appView.options().isDesugaredLibraryCompilation();
-    ProgramEmulatedInterfaceSynthesizer emulatedInterfaceSynthesizer =
-        ProgramEmulatedInterfaceSynthesizer.create(appView);
-    if (emulatedInterfaceSynthesizer != null) {
-      synthesizers.add(emulatedInterfaceSynthesizer);
+    Collection<CfClassSynthesizerDesugaring> synthesizers = new ArrayList<>();
+    if (appView.options().isDesugaredLibraryCompilation()) {
+      ProgramEmulatedInterfaceSynthesizer emulatedInterfaceSynthesizer =
+          ProgramEmulatedInterfaceSynthesizer.create(appView);
+      if (emulatedInterfaceSynthesizer != null) {
+        synthesizers.add(emulatedInterfaceSynthesizer);
+      }
+      DesugaredLibraryRetargeterL8Synthesizer retargeterL8Synthesizer =
+          DesugaredLibraryRetargeterL8Synthesizer.create(appView, retargetingInfo);
+      if (retargeterL8Synthesizer != null) {
+        synthesizers.add(retargeterL8Synthesizer);
+      }
+      synthesizers.add(new DesugaredLibraryWrapperSynthesizer(appView));
     }
-    DesugaredLibraryRetargeterL8Synthesizer retargeterL8Synthesizer =
-        DesugaredLibraryRetargeterL8Synthesizer.create(appView, retargetingInfo);
-    if (retargeterL8Synthesizer != null) {
-      synthesizers.add(retargeterL8Synthesizer);
+    RecordRewriter recordRewriter = RecordRewriter.create(appView);
+    if (recordRewriter != null) {
+      synthesizers.add(recordRewriter);
     }
-    synthesizers.add(new DesugaredLibraryWrapperSynthesizer(appView));
+    if (synthesizers.isEmpty()) {
+      return new EmptyCfClassSynthesizerCollection();
+    }
+    return new NonEmptyCfClassSynthesizerCollection(synthesizers);
   }
 
-  public void synthesizeClasses(
+  public abstract void synthesizeClasses(
       ExecutorService executorService, CfClassSynthesizerDesugaringEventConsumer eventConsumer)
-      throws ExecutionException {
-    ThreadUtils.processItems(
-        synthesizers, synthesizer -> synthesizer.synthesizeClasses(eventConsumer), executorService);
+      throws ExecutionException;
+
+  static class NonEmptyCfClassSynthesizerCollection extends CfClassSynthesizerDesugaringCollection {
+
+    private final Collection<CfClassSynthesizerDesugaring> synthesizers;
+
+    public NonEmptyCfClassSynthesizerCollection(
+        Collection<CfClassSynthesizerDesugaring> synthesizers) {
+      assert !synthesizers.isEmpty();
+      this.synthesizers = synthesizers;
+    }
+
+    @Override
+    public void synthesizeClasses(
+        ExecutorService executorService, CfClassSynthesizerDesugaringEventConsumer eventConsumer)
+        throws ExecutionException {
+      ThreadUtils.processItems(
+          synthesizers,
+          synthesizer -> synthesizer.synthesizeClasses(eventConsumer),
+          executorService);
+    }
+  }
+
+  static class EmptyCfClassSynthesizerCollection extends CfClassSynthesizerDesugaringCollection {
+
+    @Override
+    public void synthesizeClasses(
+        ExecutorService executorService, CfClassSynthesizerDesugaringEventConsumer eventConsumer)
+        throws ExecutionException {
+      // Intentionally empty.
+    }
   }
 }
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 6ff6ec9..3f60ffe 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
@@ -8,13 +8,15 @@
 import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryRetargeterSynthesizerEventConsumer.DesugaredLibraryRetargeterL8SynthesizerEventConsumer;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryWrapperSynthesizerEventConsumer.DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer;
 import com.android.tools.r8.ir.desugar.itf.EmulatedInterfaceSynthesizerEventConsumer.L8ProgramEmulatedInterfaceSynthesizerEventConsumer;
+import com.android.tools.r8.ir.desugar.records.RecordDesugaringEventConsumer;
 import com.google.common.collect.Sets;
 import java.util.Set;
 
 public class CfClassSynthesizerDesugaringEventConsumer
     implements L8ProgramEmulatedInterfaceSynthesizerEventConsumer,
         DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer,
-        DesugaredLibraryRetargeterL8SynthesizerEventConsumer {
+        DesugaredLibraryRetargeterL8SynthesizerEventConsumer,
+        RecordDesugaringEventConsumer {
 
   private Set<DexProgramClass> synthesizedClasses = Sets.newConcurrentHashSet();
 
@@ -33,6 +35,11 @@
     synthesizedClasses.add(clazz);
   }
 
+  @Override
+  public void acceptRecordClass(DexProgramClass clazz) {
+    synthesizedClasses.add(clazz);
+  }
+
   public Set<DexProgramClass> getSynthesizedClasses() {
     return synthesizedClasses;
   }
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 9c90fd7..afd0856 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
@@ -23,7 +23,7 @@
 import com.android.tools.r8.ir.desugar.lambda.LambdaDeserializationMethodRemover;
 import com.android.tools.r8.ir.desugar.lambda.LambdaDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaringEventConsumer;
-import com.android.tools.r8.ir.desugar.records.RecordDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.records.RecordDesugaringEventConsumer.RecordInstructionDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.twr.TwrCloseResourceDesugaringEventConsumer;
 import com.android.tools.r8.shaking.Enqueuer.SyntheticAdditions;
 import com.google.common.collect.Sets;
@@ -46,7 +46,7 @@
         InvokeSpecialToSelfDesugaringEventConsumer,
         LambdaDesugaringEventConsumer,
         NestBasedAccessDesugaringEventConsumer,
-        RecordDesugaringEventConsumer,
+        RecordInstructionDesugaringEventConsumer,
         TwrCloseResourceDesugaringEventConsumer,
         InterfaceMethodDesugaringEventConsumer,
         DesugaredLibraryRetargeterInstructionEventConsumer,
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java
index 2d99720..dbba296 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java
@@ -9,6 +9,7 @@
 import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryRetargeterPostProcessor;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.RetargetingInfo;
 import com.android.tools.r8.ir.desugar.itf.InterfaceMethodProcessorFacade;
+import com.android.tools.r8.ir.desugar.records.RecordRewriter;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
@@ -69,6 +70,10 @@
       if (apiCallbackSynthesizor != null) {
         desugarings.add(apiCallbackSynthesizor);
       }
+      RecordRewriter recordRewriter = RecordRewriter.create(appView);
+      if (recordRewriter != null) {
+        desugarings.add(recordRewriter);
+      }
       if (desugarings.isEmpty()) {
         return empty();
       }
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 5affc2b..f81b5d6 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
@@ -1,7 +1,6 @@
 // Copyright (c) 2021, 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.ir.desugar.records;
 
 import com.android.tools.r8.graph.DexProgramClass;
@@ -11,5 +10,8 @@
 
   void acceptRecordClass(DexProgramClass recordClass);
 
-  void acceptRecordMethod(ProgramMethod method);
+  interface RecordInstructionDesugaringEventConsumer extends RecordDesugaringEventConsumer {
+
+    void acceptRecordMethod(ProgramMethod method);
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordRewriter.java
index 3f5eccc..3bce77c 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordRewriter.java
@@ -35,12 +35,15 @@
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.ParameterAnnotationsList;
 import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.desugar.CfClassDesugaring;
-import com.android.tools.r8.ir.desugar.CfClassDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaring;
+import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaring;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaring;
+import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.FreshLocalProvider;
 import com.android.tools.r8.ir.desugar.LocalStackAllocator;
+import com.android.tools.r8.ir.desugar.records.RecordDesugaringEventConsumer.RecordInstructionDesugaringEventConsumer;
 import com.android.tools.r8.ir.synthetic.CallObjectInitCfCodeProvider;
 import com.android.tools.r8.ir.synthetic.RecordGetFieldsAsObjectsCfCodeProvider;
 import com.android.tools.r8.naming.dexitembasedstring.ClassNameComputationInfo;
@@ -51,10 +54,13 @@
 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;
 
-public class RecordRewriter implements CfInstructionDesugaring, CfClassDesugaring {
+public class RecordRewriter
+    implements CfInstructionDesugaring, CfClassSynthesizerDesugaring, CfPostProcessingDesugaring {
 
   private final AppView<?> appView;
   private final DexItemFactory factory;
@@ -134,7 +140,10 @@
       MethodProcessingContext methodProcessingContext,
       DexItemFactory dexItemFactory) {
     assert !instruction.isInitClass();
-    if (instruction.isInvokeDynamic() && needsDesugaring(instruction.asInvokeDynamic(), context)) {
+    if (!needsDesugaring(instruction, context)) {
+      return null;
+    }
+    if (instruction.isInvokeDynamic()) {
       return desugarInvokeDynamicOnRecord(
           instruction.asInvokeDynamic(),
           localStackAllocator,
@@ -142,16 +151,13 @@
           eventConsumer,
           methodProcessingContext);
     }
-    if (instruction.isInvoke()) {
-      CfInvoke cfInvoke = instruction.asInvoke();
-      DexMethod newMethod =
-          rewriteMethod(cfInvoke.getMethod(), cfInvoke.isInvokeSuper(context.getHolderType()));
-      if (newMethod != cfInvoke.getMethod()) {
-        return Collections.singletonList(
-            new CfInvoke(cfInvoke.getOpcode(), newMethod, cfInvoke.isInterface()));
-      }
-    }
-    return null;
+    assert instruction.isInvoke();
+    CfInvoke cfInvoke = instruction.asInvoke();
+    DexMethod newMethod =
+        rewriteMethod(cfInvoke.getMethod(), cfInvoke.isInvokeSuper(context.getHolderType()));
+    assert newMethod != cfInvoke.getMethod();
+    return Collections.singletonList(
+        new CfInvoke(cfInvoke.getOpcode(), newMethod, cfInvoke.isInterface()));
   }
 
   public List<CfInstruction> desugarInvokeDynamicOnRecord(
@@ -217,7 +223,9 @@
   }
 
   private void ensureGetFieldsAsObjects(
-      DexProgramClass clazz, DexField[] fields, RecordDesugaringEventConsumer eventConsumer) {
+      DexProgramClass clazz,
+      DexField[] fields,
+      RecordInstructionDesugaringEventConsumer eventConsumer) {
     DexMethod method = getFieldsAsObjectsMethod(clazz.type);
     synchronized (clazz.getMethodCollection()) {
       ProgramMethod getFieldsAsObjects = clazz.lookupProgramMethod(method);
@@ -256,7 +264,7 @@
   private List<CfInstruction> desugarInvokeRecordHashCode(
       DexProgramClass recordClass,
       DexField[] fields,
-      CfInstructionDesugaringEventConsumer eventConsumer,
+      RecordInstructionDesugaringEventConsumer eventConsumer,
       MethodProcessingContext methodProcessingContext) {
     ensureGetFieldsAsObjects(recordClass, fields, eventConsumer);
     ProgramMethod programMethod =
@@ -272,7 +280,7 @@
   private List<CfInstruction> desugarInvokeRecordEquals(
       DexProgramClass recordClass,
       DexField[] fields,
-      CfInstructionDesugaringEventConsumer eventConsumer,
+      RecordInstructionDesugaringEventConsumer eventConsumer,
       MethodProcessingContext methodProcessingContext) {
     ensureGetFieldsAsObjects(recordClass, fields, eventConsumer);
     ProgramMethod programMethod =
@@ -291,7 +299,7 @@
       DexField[] fields,
       DexString simpleName,
       LocalStackAllocator localStackAllocator,
-      CfInstructionDesugaringEventConsumer eventConsumer,
+      RecordInstructionDesugaringEventConsumer eventConsumer,
       MethodProcessingContext methodProcessingContext) {
     ensureGetFieldsAsObjects(recordClass, fields, eventConsumer);
     ArrayList<CfInstruction> instructions = new ArrayList<>();
@@ -356,20 +364,6 @@
     }
   }
 
-  @Override
-  public boolean needsDesugaring(DexProgramClass clazz) {
-    return clazz.isRecord();
-  }
-
-  @Override
-  public void desugar(DexProgramClass clazz, CfClassDesugaringEventConsumer eventConsumer) {
-    if (clazz.isRecord()) {
-      assert clazz.superType == factory.recordType;
-      ensureRecordClass(eventConsumer);
-      clazz.accessFlags.unsetRecord();
-    }
-  }
-
   private boolean refersToRecord(DexField field) {
     assert !refersToRecord(field.holder) : "The java.lang.Record class has no fields.";
     return refersToRecord(field.type);
@@ -531,4 +525,25 @@
         new CallObjectInitCfCodeProvider(appView, factory.recordTagType).generateCfCode(), appView);
     return init;
   }
+
+  @Override
+  public void synthesizeClasses(CfClassSynthesizerDesugaringEventConsumer eventConsumer) {
+    if (appView.appInfo().app().getFlags().hasReadProgramRecord()) {
+      ensureRecordClass(eventConsumer);
+    }
+  }
+
+  @Override
+  public void postProcessingDesugaring(
+      Collection<DexProgramClass> programClasses,
+      CfPostProcessingDesugaringEventConsumer eventConsumer,
+      ExecutorService executorService)
+      throws ExecutionException {
+    for (DexProgramClass clazz : programClasses) {
+      if (clazz.isRecord()) {
+        assert clazz.superType == factory.recordType;
+        clazz.accessFlags.unsetRecord();
+      }
+    }
+  }
 }