Initial support for rewriting baseline profiles in R8

Bug: b/237043695
Change-Id: Ib7d355d674222ee24a5fca0664897f199f7c79bc
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index 3bd5968..093b08d 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -64,6 +64,7 @@
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.OriginalSourceFiles;
 import com.android.tools.r8.utils.PredicateUtils;
+import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.ThreadUtils;
@@ -594,16 +595,21 @@
 
   public static void supplyAdditionalConsumers(AppView<?> appView) {
     InternalOptions options = appView.options();
+    Reporter reporter = options.reporter;
+    if (appView.hasClassHierarchy()) {
+      appView.appInfoWithClassHierarchy().getArtProfiles().supplyConsumers(appView);
+    }
     if (options.configurationConsumer != null) {
       ExceptionUtils.withConsumeResourceHandler(
-          options.reporter, options.configurationConsumer,
+          reporter,
+          options.configurationConsumer,
           options.getProguardConfiguration().getParsedConfiguration());
-      ExceptionUtils.withFinishedResourceHandler(options.reporter, options.configurationConsumer);
+      ExceptionUtils.withFinishedResourceHandler(reporter, options.configurationConsumer);
     }
     if (options.mainDexListConsumer != null) {
       ExceptionUtils.withConsumeResourceHandler(
-          options.reporter, options.mainDexListConsumer, writeMainDexList(appView));
-      ExceptionUtils.withFinishedResourceHandler(options.reporter, options.mainDexListConsumer);
+          reporter, options.mainDexListConsumer, writeMainDexList(appView));
+      ExceptionUtils.withFinishedResourceHandler(reporter, options.mainDexListConsumer);
     }
 
     KotlinModuleSynthesizer kotlinModuleSynthesizer = new KotlinModuleSynthesizer(appView);
@@ -642,13 +648,13 @@
                               .getBytes(),
                           AppServices.SERVICE_DIRECTORY_NAME + serviceName,
                           Origin.unknown()),
-                      options.reporter);
+                      reporter);
                 });
       }
       // Rewrite/synthesize kotlin_module files
       kotlinModuleSynthesizer
           .synthesizeKotlinModuleFiles()
-          .forEach(file -> dataResourceConsumer.accept(file, options.reporter));
+          .forEach(file -> dataResourceConsumer.accept(file, reporter));
     }
 
     if (options.featureSplitConfiguration != null) {
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupOptions.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupOptions.java
index 01f91e0..531c50e 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupOptions.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupOptions.java
@@ -7,7 +7,6 @@
 import static com.android.tools.r8.utils.SystemPropertyUtils.parseSystemPropertyForDevelopmentOrDefault;
 
 import com.android.tools.r8.startup.StartupProfileProvider;
-import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.SystemPropertyUtils;
 import com.google.common.collect.ImmutableList;
 import java.nio.file.Paths;
@@ -54,7 +53,7 @@
 
   private Collection<StartupProfileProvider> startupProfileProviders;
 
-  public StartupOptions(InternalOptions options) {
+  public StartupOptions() {
     this.startupProfileProviders =
         SystemPropertyUtils.applySystemProperty(
             "com.android.tools.r8.startup.profile",
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
index b891c89..a1622f5 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.ir.analysis.type.InterfaceCollection;
 import com.android.tools.r8.ir.analysis.type.InterfaceCollection.Builder;
 import com.android.tools.r8.ir.desugar.LambdaDescriptor;
+import com.android.tools.r8.profile.art.ArtProfileCollection;
 import com.android.tools.r8.shaking.MainDexInfo;
 import com.android.tools.r8.shaking.MissingClasses;
 import com.android.tools.r8.synthesis.CommittedItems;
@@ -48,18 +49,21 @@
 
   public static AppInfoWithClassHierarchy createInitialAppInfoWithClassHierarchy(
       DexApplication application,
+      ArtProfileCollection artProfiles,
       ClassToFeatureSplitMap classToFeatureSplitMap,
       MainDexInfo mainDexInfo,
       GlobalSyntheticsStrategy globalSyntheticsStrategy,
       StartupOrder startupOrder) {
     return new AppInfoWithClassHierarchy(
         SyntheticItems.createInitialSyntheticItems(application, globalSyntheticsStrategy),
+        artProfiles,
         classToFeatureSplitMap,
         mainDexInfo,
         MissingClasses.empty(),
         startupOrder);
   }
 
+  private final ArtProfileCollection artProfiles;
   private final ClassToFeatureSplitMap classToFeatureSplitMap;
   private final StartupOrder startupOrder;
 
@@ -70,11 +74,13 @@
   // For AppInfoWithLiveness subclass.
   protected AppInfoWithClassHierarchy(
       CommittedItems committedItems,
+      ArtProfileCollection artProfiles,
       ClassToFeatureSplitMap classToFeatureSplitMap,
       MainDexInfo mainDexInfo,
       MissingClasses missingClasses,
       StartupOrder startupOrder) {
     super(committedItems, mainDexInfo);
+    this.artProfiles = artProfiles;
     this.classToFeatureSplitMap = classToFeatureSplitMap;
     this.missingClasses = missingClasses;
     this.startupOrder = startupOrder;
@@ -83,6 +89,7 @@
   // For desugaring.
   private AppInfoWithClassHierarchy(CreateDesugaringViewOnAppInfo witness, AppInfo appInfo) {
     super(witness, appInfo);
+    this.artProfiles = ArtProfileCollection.empty();
     this.classToFeatureSplitMap = ClassToFeatureSplitMap.createEmptyClassToFeatureSplitMap();
     // TODO(b/175659048): Migrate the reporting of missing classes in D8 desugar to MissingClasses,
     //  and use the missing classes from AppInfo instead of MissingClasses.empty().
@@ -98,6 +105,7 @@
   public final AppInfoWithClassHierarchy rebuildWithClassHierarchy(CommittedItems commit) {
     return new AppInfoWithClassHierarchy(
         commit,
+        getArtProfiles(),
         getClassToFeatureSplitMap(),
         getMainDexInfo(),
         getMissingClasses(),
@@ -109,6 +117,7 @@
     assert checkIfObsolete();
     return new AppInfoWithClassHierarchy(
         getSyntheticItems().commit(fn.apply(app())),
+        getArtProfiles(),
         getClassToFeatureSplitMap(),
         getMainDexInfo(),
         getMissingClasses(),
@@ -121,6 +130,7 @@
     assert checkIfObsolete();
     return new AppInfoWithClassHierarchy(
         getSyntheticItems().commit(app()),
+        getArtProfiles(),
         getClassToFeatureSplitMap(),
         mainDexInfo,
         getMissingClasses(),
@@ -138,12 +148,17 @@
     }
     return new AppInfoWithClassHierarchy(
         getSyntheticItems().commitPrunedItems(prunedItems),
+        getArtProfiles().withoutPrunedItems(prunedItems),
         getClassToFeatureSplitMap().withoutPrunedItems(prunedItems),
         getMainDexInfo().withoutPrunedItems(prunedItems),
         getMissingClasses(),
         getStartupOrder().withoutPrunedItems(prunedItems, getSyntheticItems()));
   }
 
+  public ArtProfileCollection getArtProfiles() {
+    return artProfiles;
+  }
+
   public ClassToFeatureSplitMap getClassToFeatureSplitMap() {
     return classToFeatureSplitMap;
   }
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index 3633506..95baf65 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -34,6 +34,7 @@
 import com.android.tools.r8.naming.SeedMapper;
 import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagator;
 import com.android.tools.r8.optimize.interfaces.collection.OpenClosedInterfacesCollection;
+import com.android.tools.r8.profile.art.ArtProfileCollection;
 import com.android.tools.r8.retrace.internal.RetraceUtils;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.AssumeInfoCollection;
@@ -206,12 +207,15 @@
 
   public static AppView<AppInfoWithClassHierarchy> createForR8(
       DexApplication application, MainDexInfo mainDexInfo) {
+    ArtProfileCollection artProfiles =
+        ArtProfileCollection.createInitialArtProfileCollection(application.options);
     ClassToFeatureSplitMap classToFeatureSplitMap =
         ClassToFeatureSplitMap.createInitialClassToFeatureSplitMap(application.options);
     StartupOrder startupOrder = StartupOrder.createInitialStartupOrderForR8(application);
     AppInfoWithClassHierarchy appInfo =
         AppInfoWithClassHierarchy.createInitialAppInfoWithClassHierarchy(
             application,
+            artProfiles,
             classToFeatureSplitMap,
             mainDexInfo,
             GlobalSyntheticsStrategy.forSingleOutputMode(),
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfile.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfile.java
new file mode 100644
index 0000000..8cf52c5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfile.java
@@ -0,0 +1,117 @@
+// Copyright (c) 2022, 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;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.PrunedItems;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.Reporter;
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.function.UnaryOperator;
+
+public class ArtProfile {
+
+  private final List<ArtProfileRule> rules;
+
+  ArtProfile(List<ArtProfileRule> rules) {
+    this.rules = rules;
+  }
+
+  public static Builder builder(DexItemFactory dexItemFactory) {
+    return new Builder(dexItemFactory);
+  }
+
+  public ArtProfile rewrittenWithLens(GraphLens lens) {
+    return transform(rule -> rule.rewrittenWithLens(lens));
+  }
+
+  public ArtProfile rewrittenWithLens(NamingLens lens, DexItemFactory dexItemFactory) {
+    assert !lens.isIdentityLens();
+    return transform(rule -> rule.rewrittenWithLens(dexItemFactory, lens));
+  }
+
+  public ArtProfile withoutPrunedItems(PrunedItems prunedItems) {
+    return transform(
+        rule -> {
+          if (rule.isClassRule()) {
+            if (prunedItems.isRemoved(rule.asClassRule().getType())) {
+              return null;
+            }
+          } else {
+            assert rule.isMethodRule();
+            if (prunedItems.isRemoved(rule.asMethodRule().getMethod())) {
+              return null;
+            }
+          }
+          return rule;
+        });
+  }
+
+  private ArtProfile transform(UnaryOperator<ArtProfileRule> transformation) {
+    ImmutableList.Builder<ArtProfileRule> newRules =
+        ImmutableList.builderWithExpectedSize(rules.size());
+    for (ArtProfileRule rule : rules) {
+      ArtProfileRule transformedRule = transformation.apply(rule);
+      if (transformedRule != null) {
+        newRules.add(transformedRule);
+      }
+    }
+    return new ArtProfile(newRules.build());
+  }
+
+  public void supplyConsumer(ResidualArtProfileConsumer consumer, Reporter reporter) {
+    if (consumer != null) {
+      ResidualArtProfileRuleConsumer ruleConsumer = consumer.getRuleConsumer();
+      if (ruleConsumer != null) {
+        for (ArtProfileRule rule : rules) {
+          rule.accept(
+              classRule ->
+                  ruleConsumer.acceptClassRule(
+                      classRule.getClassReference(), classRule.getClassRuleInfo()),
+              methodRule ->
+                  ruleConsumer.acceptMethodRule(
+                      methodRule.getMethodReference(), methodRule.getMethodRuleInfo()));
+        }
+      }
+      consumer.finished(reporter);
+    }
+  }
+
+  public static class Builder implements ArtProfileBuilder {
+
+    private final DexItemFactory dexItemFactory;
+    private final List<ArtProfileRule> rules = new ArrayList<>();
+
+    Builder(DexItemFactory dexItemFactory) {
+      this.dexItemFactory = dexItemFactory;
+    }
+
+    @Override
+    public ArtProfileBuilder addClassRule(
+        Consumer<ArtProfileClassRuleBuilder> classRuleBuilderConsumer) {
+      ArtProfileClassRule.Builder classRuleBuilder = ArtProfileClassRule.builder(dexItemFactory);
+      classRuleBuilderConsumer.accept(classRuleBuilder);
+      rules.add(classRuleBuilder.build());
+      return this;
+    }
+
+    @Override
+    public ArtProfileBuilder addMethodRule(
+        Consumer<ArtProfileMethodRuleBuilder> methodRuleBuilderConsumer) {
+      ArtProfileMethodRule.Builder methodRuleBuilder = ArtProfileMethodRule.builder(dexItemFactory);
+      methodRuleBuilderConsumer.accept(methodRuleBuilder);
+      rules.add(methodRuleBuilder.build());
+      return this;
+    }
+
+    public ArtProfile build() {
+      return new ArtProfile(rules);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileBuilderUtils.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileBuilderUtils.java
index 3426e7b..44de1a8 100644
--- a/src/main/java/com/android/tools/r8/profile/art/ArtProfileBuilderUtils.java
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileBuilderUtils.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.references.MethodReference;
 import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.startup.StartupProfileBuilder;
+import java.util.function.Consumer;
 
 public class ArtProfileBuilderUtils {
 
@@ -72,15 +73,19 @@
     return new ArtProfileBuilder() {
 
       @Override
-      public void addClassRule(
-          ClassReference classReference, ArtProfileClassRuleInfo classRuleInfo) {
-        if (rulePredicate.testClassRule(classReference, classRuleInfo)) {
+      public ArtProfileBuilder addClassRule(
+          Consumer<ArtProfileClassRuleBuilder> classRuleBuilderConsumer) {
+        MutableArtProfileClassRule classRule = new MutableArtProfileClassRule();
+        classRuleBuilderConsumer.accept(classRule);
+        if (rulePredicate.testClassRule(
+            classRule.getClassReference(), classRule.getClassRuleInfo())) {
           ClassReference syntheticContextReference =
               syntheticToSyntheticContextGeneralization.getSyntheticContextReference(
-                  classReference);
+                  classRule.getClassReference());
           if (syntheticContextReference == null) {
             startupProfileBuilder.addStartupClass(
-                startupClassBuilder -> startupClassBuilder.setClassReference(classReference));
+                startupClassBuilder ->
+                    startupClassBuilder.setClassReference(classRule.getClassReference()));
           } else {
             startupProfileBuilder.addSyntheticStartupMethod(
                 syntheticStartupMethodBuilder ->
@@ -88,18 +93,23 @@
                         syntheticContextReference));
           }
         }
+        return this;
       }
 
       @Override
-      public void addMethodRule(
-          MethodReference methodReference, ArtProfileMethodRuleInfo methodRuleInfo) {
-        if (rulePredicate.testMethodRule(methodReference, methodRuleInfo)) {
+      public ArtProfileBuilder addMethodRule(
+          Consumer<ArtProfileMethodRuleBuilder> methodRuleBuilderConsumer) {
+        MutableArtProfileMethodRule methodRule = new MutableArtProfileMethodRule();
+        methodRuleBuilderConsumer.accept(methodRule);
+        if (rulePredicate.testMethodRule(
+            methodRule.getMethodReference(), methodRule.getMethodRuleInfo())) {
           ClassReference syntheticContextReference =
               syntheticToSyntheticContextGeneralization.getSyntheticContextReference(
-                  methodReference.getHolderClass());
+                  methodRule.getMethodReference().getHolderClass());
           if (syntheticContextReference == null) {
             startupProfileBuilder.addStartupMethod(
-                startupMethodBuilder -> startupMethodBuilder.setMethodReference(methodReference));
+                startupMethodBuilder ->
+                    startupMethodBuilder.setMethodReference(methodRule.getMethodReference()));
           } else {
             startupProfileBuilder.addSyntheticStartupMethod(
                 syntheticStartupMethodBuilder ->
@@ -107,7 +117,61 @@
                         syntheticContextReference));
           }
         }
+        return this;
       }
     };
   }
+
+  private static class MutableArtProfileClassRule implements ArtProfileClassRuleBuilder {
+
+    private ClassReference classReference;
+
+    MutableArtProfileClassRule() {}
+
+    public ClassReference getClassReference() {
+      return classReference;
+    }
+
+    @Override
+    public ArtProfileClassRuleBuilder setClassReference(ClassReference classReference) {
+      this.classReference = classReference;
+      return this;
+    }
+
+    public ArtProfileClassRuleInfo getClassRuleInfo() {
+      return ArtProfileClassRuleInfoImpl.empty();
+    }
+  }
+
+  private static class MutableArtProfileMethodRule implements ArtProfileMethodRuleBuilder {
+
+    private MethodReference methodReference;
+    private ArtProfileMethodRuleInfo methodRuleInfo = ArtProfileMethodRuleInfoImpl.empty();
+
+    MutableArtProfileMethodRule() {}
+
+    public MethodReference getMethodReference() {
+      return methodReference;
+    }
+
+    public ArtProfileMethodRuleInfo getMethodRuleInfo() {
+      return methodRuleInfo;
+    }
+
+    @Override
+    public ArtProfileMethodRuleBuilder setMethodReference(MethodReference methodReference) {
+      this.methodReference = methodReference;
+      return this;
+    }
+
+    @Override
+    public ArtProfileMethodRuleBuilder setMethodRuleInfo(
+        Consumer<ArtProfileMethodRuleInfoBuilder> methodRuleInfoBuilderConsumer) {
+      ArtProfileMethodRuleInfoImpl.Builder methodRuleInfoBuilder =
+          ArtProfileMethodRuleInfoImpl.builder();
+      methodRuleInfoBuilderConsumer.accept(methodRuleInfoBuilder);
+      methodRuleInfo = methodRuleInfoBuilder.build();
+      return this;
+    }
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileClassRule.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileClassRule.java
new file mode 100644
index 0000000..be3a39e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileClassRule.java
@@ -0,0 +1,107 @@
+// Copyright (c) 2022, 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;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.Reference;
+import java.util.function.Consumer;
+
+public class ArtProfileClassRule extends ArtProfileRule {
+
+  private final DexType type;
+
+  ArtProfileClassRule(DexType type) {
+    this.type = type;
+  }
+
+  public static Builder builder(DexItemFactory dexItemFactory) {
+    return new Builder(dexItemFactory);
+  }
+
+  @Override
+  public void accept(
+      Consumer<ArtProfileClassRule> classRuleConsumer,
+      Consumer<ArtProfileMethodRule> methodRuleConsumer) {
+    classRuleConsumer.accept(this);
+  }
+
+  public ClassReference getClassReference() {
+    return Reference.classFromDescriptor(type.toDescriptorString());
+  }
+
+  public ArtProfileClassRuleInfo getClassRuleInfo() {
+    return ArtProfileClassRuleInfoImpl.empty();
+  }
+
+  public DexType getType() {
+    return type;
+  }
+
+  @Override
+  public boolean isClassRule() {
+    return true;
+  }
+
+  @Override
+  public ArtProfileClassRule asClassRule() {
+    return this;
+  }
+
+  @Override
+  public ArtProfileClassRule rewrittenWithLens(GraphLens lens) {
+    return new ArtProfileClassRule(lens.lookupType(type));
+  }
+
+  @Override
+  public ArtProfileRule rewrittenWithLens(DexItemFactory dexItemFactory, NamingLens lens) {
+    return new ArtProfileClassRule(lens.lookupType(type, dexItemFactory));
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+    ArtProfileClassRule that = (ArtProfileClassRule) o;
+    return type == that.type;
+  }
+
+  @Override
+  public int hashCode() {
+    return type.hashCode();
+  }
+
+  @Override
+  public String toString() {
+    return type.toSmaliString();
+  }
+
+  public static class Builder implements ArtProfileClassRuleBuilder {
+
+    private final DexItemFactory dexItemFactory;
+    private DexType type;
+
+    Builder(DexItemFactory dexItemFactory) {
+      this.dexItemFactory = dexItemFactory;
+    }
+
+    @Override
+    public ArtProfileClassRuleBuilder setClassReference(ClassReference classReference) {
+      this.type = dexItemFactory.createType(classReference.getDescriptor());
+      return this;
+    }
+
+    public ArtProfileClassRule build() {
+      return new ArtProfileClassRule(type);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileClassRuleInfoImpl.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileClassRuleInfoImpl.java
index 7be8869..9fa3001 100644
--- a/src/main/java/com/android/tools/r8/profile/art/ArtProfileClassRuleInfoImpl.java
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileClassRuleInfoImpl.java
@@ -13,4 +13,14 @@
   public static ArtProfileClassRuleInfoImpl empty() {
     return INSTANCE;
   }
+
+  @Override
+  public boolean equals(Object obj) {
+    return this == obj;
+  }
+
+  @Override
+  public int hashCode() {
+    return System.identityHashCode(this);
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileCollection.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileCollection.java
new file mode 100644
index 0000000..bba8705
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileCollection.java
@@ -0,0 +1,43 @@
+// Copyright (c) 2022, 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;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.PrunedItems;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.InternalOptions;
+import java.util.ArrayList;
+import java.util.List;
+
+public abstract class ArtProfileCollection {
+
+  public static ArtProfileCollection createInitialArtProfileCollection(InternalOptions options) {
+    List<ArtProfile> artProfiles = new ArrayList<>();
+    for (ArtProfileInput input : options.getArtProfileOptions().getArtProfileInputs()) {
+      ArtProfile.Builder artProfileBuilder = ArtProfile.builder(options.dexItemFactory());
+      input.getArtProfile(artProfileBuilder);
+      artProfiles.add(artProfileBuilder.build());
+    }
+    if (artProfiles.isEmpty()) {
+      return empty();
+    }
+    return new NonEmptyArtProfileCollection(artProfiles);
+  }
+
+  public static EmptyArtProfileCollection empty() {
+    return EmptyArtProfileCollection.getInstance();
+  }
+
+  public abstract ArtProfileCollection rewrittenWithLens(GraphLens lens);
+
+  public abstract ArtProfileCollection rewrittenWithLens(
+      NamingLens lens, DexItemFactory dexItemFactory);
+
+  public abstract void supplyConsumers(AppView<?> appView);
+
+  public abstract ArtProfileCollection withoutPrunedItems(PrunedItems prunedItems);
+}
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRule.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRule.java
new file mode 100644
index 0000000..521e359
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRule.java
@@ -0,0 +1,123 @@
+// Copyright (c) 2022, 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;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import java.util.function.Consumer;
+
+public class ArtProfileMethodRule extends ArtProfileRule {
+
+  private final DexMethod method;
+  private final ArtProfileMethodRuleInfo info;
+
+  ArtProfileMethodRule(DexMethod method, ArtProfileMethodRuleInfo info) {
+    this.method = method;
+    this.info = info;
+  }
+
+  public static Builder builder(DexItemFactory dexItemFactory) {
+    return new Builder(dexItemFactory);
+  }
+
+  @Override
+  public void accept(
+      Consumer<ArtProfileClassRule> classRuleConsumer,
+      Consumer<ArtProfileMethodRule> methodRuleConsumer) {
+    methodRuleConsumer.accept(this);
+  }
+
+  public DexMethod getMethod() {
+    return method;
+  }
+
+  public MethodReference getMethodReference() {
+    return method.asMethodReference();
+  }
+
+  public ArtProfileMethodRuleInfo getMethodRuleInfo() {
+    return info;
+  }
+
+  @Override
+  public boolean isMethodRule() {
+    return true;
+  }
+
+  @Override
+  public ArtProfileMethodRule asMethodRule() {
+    return this;
+  }
+
+  @Override
+  public ArtProfileMethodRule rewrittenWithLens(GraphLens lens) {
+    return new ArtProfileMethodRule(lens.getRenamedMethodSignature(method), info);
+  }
+
+  @Override
+  public ArtProfileRule rewrittenWithLens(DexItemFactory dexItemFactory, NamingLens lens) {
+    return new ArtProfileMethodRule(lens.lookupMethod(method, dexItemFactory), info);
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+    ArtProfileMethodRule that = (ArtProfileMethodRule) o;
+    return method.equals(that.method) && info.equals(that.info);
+  }
+
+  @Override
+  public int hashCode() {
+    // A profile does not have two rules with the same reference but different flags, so no need to
+    // include the flags in the hash.
+    return method.hashCode();
+  }
+
+  @Override
+  public String toString() {
+    return info.toString() + method.toSmaliString();
+  }
+
+  public static class Builder implements ArtProfileMethodRuleBuilder {
+
+    private final DexItemFactory dexItemFactory;
+
+    private DexMethod method;
+    private ArtProfileMethodRuleInfo methodRuleInfo = ArtProfileMethodRuleInfoImpl.empty();
+
+    Builder(DexItemFactory dexItemFactory) {
+      this.dexItemFactory = dexItemFactory;
+    }
+
+    @Override
+    public ArtProfileMethodRuleBuilder setMethodReference(MethodReference methodReference) {
+      this.method = MethodReferenceUtils.toDexMethod(methodReference, dexItemFactory);
+      return this;
+    }
+
+    @Override
+    public ArtProfileMethodRuleBuilder setMethodRuleInfo(
+        Consumer<ArtProfileMethodRuleInfoBuilder> methodRuleInfoBuilderConsumer) {
+      ArtProfileMethodRuleInfoImpl.Builder methodRuleInfoBuilder =
+          ArtProfileMethodRuleInfoImpl.builder();
+      methodRuleInfoBuilderConsumer.accept(methodRuleInfoBuilder);
+      methodRuleInfo = methodRuleInfoBuilder.build();
+      return this;
+    }
+
+    public ArtProfileMethodRule build() {
+      return new ArtProfileMethodRule(method, methodRuleInfo);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRuleFlagsUtils.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRuleFlagsUtils.java
new file mode 100644
index 0000000..31f9b10
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRuleFlagsUtils.java
@@ -0,0 +1,52 @@
+// Copyright (c) 2022, 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;
+
+public class ArtProfileMethodRuleFlagsUtils {
+
+  private static final int FLAG_HOT = 1;
+  private static final int FLAG_STARTUP = 2;
+  private static final int FLAG_POST_STARTUP = 4;
+
+  // Getters.
+
+  public static boolean isHot(int flags) {
+    return isFlagSet(flags, FLAG_HOT);
+  }
+
+  public static boolean isStartup(int flags) {
+    return isFlagSet(flags, FLAG_STARTUP);
+  }
+
+  public static boolean isPostStartup(int flags) {
+    return isFlagSet(flags, FLAG_POST_STARTUP);
+  }
+
+  private static boolean isFlagSet(int flags, int flag) {
+    return (flags & flag) != 0;
+  }
+
+  // Setters.
+
+  public static int setIsHot(int flags, boolean isHot) {
+    return isHot ? setFlag(flags, FLAG_HOT) : unsetFlag(flags, FLAG_HOT);
+  }
+
+  public static int setIsStartup(int flags, boolean isStartup) {
+    return isStartup ? setFlag(flags, FLAG_STARTUP) : unsetFlag(flags, FLAG_STARTUP);
+  }
+
+  public static int setIsPostStartup(int flags, boolean isPostStartup) {
+    return isPostStartup ? setFlag(flags, FLAG_POST_STARTUP) : unsetFlag(flags, FLAG_POST_STARTUP);
+  }
+
+  private static int setFlag(int flags, int flag) {
+    return flags | flag;
+  }
+
+  private static int unsetFlag(int flags, int flag) {
+    return flags & ~flag;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRuleInfoImpl.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRuleInfoImpl.java
index ca2d241..4ec56b6 100644
--- a/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRuleInfoImpl.java
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRuleInfoImpl.java
@@ -6,9 +6,7 @@
 
 public class ArtProfileMethodRuleInfoImpl implements ArtProfileMethodRuleInfo {
 
-  private static final int FLAG_HOT = 1;
-  private static final int FLAG_STARTUP = 2;
-  private static final int FLAG_POST_STARTUP = 4;
+  private static final ArtProfileMethodRuleInfoImpl EMPTY = new ArtProfileMethodRuleInfoImpl(0);
 
   private final int flags;
 
@@ -20,41 +18,92 @@
     return new Builder();
   }
 
+  public static ArtProfileMethodRuleInfoImpl empty() {
+    return EMPTY;
+  }
+
   public boolean isEmpty() {
     return flags == 0;
   }
 
   @Override
   public boolean isHot() {
-    return (flags & FLAG_HOT) != 0;
+    return ArtProfileMethodRuleFlagsUtils.isHot(flags);
   }
 
   @Override
   public boolean isStartup() {
-    return (flags & FLAG_STARTUP) != 0;
+    return ArtProfileMethodRuleFlagsUtils.isStartup(flags);
   }
 
   @Override
   public boolean isPostStartup() {
-    return (flags & FLAG_POST_STARTUP) != 0;
+    return ArtProfileMethodRuleFlagsUtils.isPostStartup(flags);
   }
 
-  public static class Builder {
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+    ArtProfileMethodRuleInfoImpl that = (ArtProfileMethodRuleInfoImpl) o;
+    return flags == that.flags;
+  }
+
+  @Override
+  public int hashCode() {
+    return flags;
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder builder = new StringBuilder();
+    if (isHot()) {
+      builder.append('H');
+    }
+    if (isStartup()) {
+      builder.append('S');
+    }
+    if (isPostStartup()) {
+      builder.append('P');
+    }
+    return builder.toString();
+  }
+
+  public static class Builder implements ArtProfileMethodRuleInfoBuilder {
 
     private int flags;
 
-    public Builder setHot() {
-      flags |= FLAG_HOT;
+    public Builder setIsHot() {
+      return setIsHot(true);
+    }
+
+    @Override
+    public Builder setIsHot(boolean isHot) {
+      flags = ArtProfileMethodRuleFlagsUtils.setIsHot(flags, isHot);
       return this;
     }
 
-    public Builder setStartup() {
-      flags |= FLAG_STARTUP;
+    public Builder setIsStartup() {
+      return setIsStartup(true);
+    }
+
+    @Override
+    public Builder setIsStartup(boolean isStartup) {
+      flags = ArtProfileMethodRuleFlagsUtils.setIsStartup(flags, isStartup);
       return this;
     }
 
-    public Builder setPostStartup() {
-      flags |= FLAG_POST_STARTUP;
+    public Builder setIsPostStartup() {
+      return setIsPostStartup(true);
+    }
+
+    @Override
+    public Builder setIsPostStartup(boolean isPostStartup) {
+      flags = ArtProfileMethodRuleFlagsUtils.setIsPostStartup(flags, isPostStartup);
       return this;
     }
 
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileOptions.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileOptions.java
new file mode 100644
index 0000000..5f5ae7f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileOptions.java
@@ -0,0 +1,24 @@
+// Copyright (c) 2022, 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;
+
+import java.util.Collection;
+import java.util.Collections;
+
+public class ArtProfileOptions {
+
+  private Collection<ArtProfileInput> inputs = Collections.emptyList();
+
+  public ArtProfileOptions() {}
+
+  public Collection<ArtProfileInput> getArtProfileInputs() {
+    return inputs;
+  }
+
+  public ArtProfileOptions setArtProfileInputs(Collection<ArtProfileInput> inputs) {
+    this.inputs = inputs;
+    return this;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileRule.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileRule.java
new file mode 100644
index 0000000..15260ef
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileRule.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2022, 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;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.naming.NamingLens;
+import java.util.function.Consumer;
+
+public abstract class ArtProfileRule {
+
+  public abstract void accept(
+      Consumer<ArtProfileClassRule> classRuleConsumer,
+      Consumer<ArtProfileMethodRule> methodRuleConsumer);
+
+  public boolean isClassRule() {
+    return false;
+  }
+
+  public ArtProfileClassRule asClassRule() {
+    return null;
+  }
+
+  public boolean isMethodRule() {
+    return false;
+  }
+
+  public ArtProfileMethodRule asMethodRule() {
+    return null;
+  }
+
+  public abstract ArtProfileRule rewrittenWithLens(GraphLens lens);
+
+  public abstract ArtProfileRule rewrittenWithLens(
+      DexItemFactory dexItemFactory, NamingLens namingLens);
+}
diff --git a/src/main/java/com/android/tools/r8/profile/art/EmptyArtProfileCollection.java b/src/main/java/com/android/tools/r8/profile/art/EmptyArtProfileCollection.java
new file mode 100644
index 0000000..c3b420c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/EmptyArtProfileCollection.java
@@ -0,0 +1,42 @@
+// Copyright (c) 2022, 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;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.PrunedItems;
+import com.android.tools.r8.naming.NamingLens;
+
+public class EmptyArtProfileCollection extends ArtProfileCollection {
+
+  private static final EmptyArtProfileCollection INSTANCE = new EmptyArtProfileCollection();
+
+  private EmptyArtProfileCollection() {}
+
+  static EmptyArtProfileCollection getInstance() {
+    return INSTANCE;
+  }
+
+  @Override
+  public ArtProfileCollection rewrittenWithLens(GraphLens lens) {
+    return this;
+  }
+
+  @Override
+  public ArtProfileCollection rewrittenWithLens(NamingLens lens, DexItemFactory dexItemFactory) {
+    return this;
+  }
+
+  @Override
+  public void supplyConsumers(AppView<?> appView) {
+    assert appView.options().getArtProfileOptions().getArtProfileInputs().isEmpty();
+  }
+
+  @Override
+  public ArtProfileCollection withoutPrunedItems(PrunedItems prunedItems) {
+    return this;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/profile/art/HumanReadableArtProfileParser.java b/src/main/java/com/android/tools/r8/profile/art/HumanReadableArtProfileParser.java
index 5f0c92b..7b7a09c 100644
--- a/src/main/java/com/android/tools/r8/profile/art/HumanReadableArtProfileParser.java
+++ b/src/main/java/com/android/tools/r8/profile/art/HumanReadableArtProfileParser.java
@@ -64,9 +64,9 @@
   public boolean parseRule(String rule) {
     ArtProfileMethodRuleInfoImpl.Builder methodRuleInfoBuilder =
         ArtProfileMethodRuleInfoImpl.builder();
-    rule = parseFlag(rule, 'H', methodRuleInfoBuilder::setHot);
-    rule = parseFlag(rule, 'S', methodRuleInfoBuilder::setStartup);
-    rule = parseFlag(rule, 'P', methodRuleInfoBuilder::setPostStartup);
+    rule = parseFlag(rule, 'H', methodRuleInfoBuilder::setIsHot);
+    rule = parseFlag(rule, 'S', methodRuleInfoBuilder::setIsStartup);
+    rule = parseFlag(rule, 'P', methodRuleInfoBuilder::setIsPostStartup);
     return parseClassOrMethodDescriptor(rule, methodRuleInfoBuilder.build());
   }
 
@@ -95,7 +95,8 @@
     if (classReference == null) {
       return false;
     }
-    profileBuilder.addClassRule(classReference, ArtProfileClassRuleInfoImpl.empty());
+    profileBuilder.addClassRule(
+        classRuleBuilder -> classRuleBuilder.setClassReference(classReference));
     return true;
   }
 
@@ -106,7 +107,16 @@
     if (methodReference == null) {
       return false;
     }
-    profileBuilder.addMethodRule(methodReference, methodRuleInfo);
+    profileBuilder.addMethodRule(
+        methodRuleBuilder ->
+            methodRuleBuilder
+                .setMethodReference(methodReference)
+                .setMethodRuleInfo(
+                    methodRuleInfoBuilder ->
+                        methodRuleInfoBuilder
+                            .setIsHot(methodRuleInfo.isHot())
+                            .setIsStartup(methodRuleInfo.isStartup())
+                            .setIsPostStartup(methodRuleInfo.isPostStartup())));
     return true;
   }
 
diff --git a/src/main/java/com/android/tools/r8/profile/art/NonEmptyArtProfileCollection.java b/src/main/java/com/android/tools/r8/profile/art/NonEmptyArtProfileCollection.java
new file mode 100644
index 0000000..56c9683
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/NonEmptyArtProfileCollection.java
@@ -0,0 +1,68 @@
+// Copyright (c) 2022, 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;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.PrunedItems;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.function.Function;
+
+public class NonEmptyArtProfileCollection extends ArtProfileCollection {
+
+  private final Collection<ArtProfile> artProfiles;
+
+  NonEmptyArtProfileCollection(Collection<ArtProfile> artProfiles) {
+    this.artProfiles = artProfiles;
+  }
+
+  @Override
+  public NonEmptyArtProfileCollection rewrittenWithLens(GraphLens lens) {
+    return map(artProfile -> artProfile.rewrittenWithLens(lens));
+  }
+
+  @Override
+  public NonEmptyArtProfileCollection rewrittenWithLens(
+      NamingLens lens, DexItemFactory dexItemFactory) {
+    assert !lens.isIdentityLens();
+    return map(artProfile -> artProfile.rewrittenWithLens(lens, dexItemFactory));
+  }
+
+  @Override
+  public void supplyConsumers(AppView<?> appView) {
+    NonEmptyArtProfileCollection collection =
+        appView.getNamingLens().isIdentityLens()
+            ? this
+            : rewrittenWithLens(appView.getNamingLens(), appView.dexItemFactory());
+    InternalOptions options = appView.options();
+    Collection<ArtProfileInput> inputs = options.getArtProfileOptions().getArtProfileInputs();
+    assert !inputs.isEmpty();
+    assert collection.artProfiles.size() == inputs.size();
+    Iterator<ArtProfileInput> inputIterator = inputs.iterator();
+    for (ArtProfile artProfile : collection.artProfiles) {
+      ArtProfileInput input = inputIterator.next();
+      artProfile.supplyConsumer(input.getArtProfileConsumer(), options.reporter);
+    }
+  }
+
+  @Override
+  public NonEmptyArtProfileCollection withoutPrunedItems(PrunedItems prunedItems) {
+    return map(artProfile -> artProfile.withoutPrunedItems(prunedItems));
+  }
+
+  private NonEmptyArtProfileCollection map(Function<ArtProfile, ArtProfile> fn) {
+    ImmutableList.Builder<ArtProfile> newArtProfiles =
+        ImmutableList.builderWithExpectedSize(artProfiles.size());
+    for (ArtProfile artProfile : artProfiles) {
+      newArtProfiles.add(fn.apply(artProfile));
+    }
+    return new NonEmptyArtProfileCollection(newArtProfiles.build());
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index f4e27a1..3112e48 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -57,6 +57,7 @@
 import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryAPIConverter;
 import com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper;
 import com.android.tools.r8.naming.SeedMapper;
+import com.android.tools.r8.profile.art.ArtProfileCollection;
 import com.android.tools.r8.shaking.KeepInfo.Joiner;
 import com.android.tools.r8.synthesis.CommittedItems;
 import com.android.tools.r8.utils.CollectionUtils;
@@ -199,6 +200,7 @@
   // TODO(zerny): Clean up the constructors so we have just one.
   AppInfoWithLiveness(
       CommittedItems committedItems,
+      ArtProfileCollection artProfiles,
       ClassToFeatureSplitMap classToFeatureSplitMap,
       MainDexInfo mainDexInfo,
       MissingClasses missingClasses,
@@ -234,7 +236,13 @@
       Set<DexType> lockCandidates,
       Map<DexType, Visibility> initClassReferences,
       Set<DexMethod> recordFieldValuesReferences) {
-    super(committedItems, classToFeatureSplitMap, mainDexInfo, missingClasses, startupOrder);
+    super(
+        committedItems,
+        artProfiles,
+        classToFeatureSplitMap,
+        mainDexInfo,
+        missingClasses,
+        startupOrder);
     this.deadProtoTypes = deadProtoTypes;
     this.liveTypes = liveTypes;
     this.targetedMethods = targetedMethods;
@@ -272,6 +280,7 @@
   private AppInfoWithLiveness(AppInfoWithLiveness previous, CommittedItems committedItems) {
     this(
         committedItems,
+        previous.getArtProfiles(),
         previous.getClassToFeatureSplitMap(),
         previous.getMainDexInfo(),
         previous.getMissingClasses(),
@@ -316,6 +325,7 @@
       List<Future<?>> futures) {
     this(
         previous.getSyntheticItems().commitPrunedItems(prunedItems),
+        previous.getArtProfiles().withoutPrunedItems(prunedItems),
         previous.getClassToFeatureSplitMap().withoutPrunedItems(prunedItems),
         previous.getMainDexInfo().withoutPrunedItems(prunedItems),
         previous.getMissingClasses(),
@@ -520,6 +530,7 @@
   public AppInfoWithLiveness rebuildWithMainDexInfo(MainDexInfo mainDexInfo) {
     return new AppInfoWithLiveness(
         getSyntheticItems().commit(app()),
+        getArtProfiles(),
         getClassToFeatureSplitMap(),
         mainDexInfo,
         getMissingClasses(),
@@ -598,6 +609,7 @@
       AppInfoWithLiveness previous, Map<DexField, Int2ReferenceMap<DexField>> switchMaps) {
     super(
         previous.getSyntheticItems().commit(previous.app()),
+        previous.getArtProfiles(),
         previous.getClassToFeatureSplitMap(),
         previous.getMainDexInfo(),
         previous.getMissingClasses(),
@@ -1183,6 +1195,7 @@
         committedItems.getApplication().getDefinitionsSupplier(committedItems);
     return new AppInfoWithLiveness(
         committedItems,
+        getArtProfiles().rewrittenWithLens(lens),
         getClassToFeatureSplitMap().rewrittenWithLens(lens),
         getMainDexInfo().rewrittenWithLens(getSyntheticItems(), lens),
         getMissingClasses(),
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 90b9dfb..9cbe49f 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -4191,6 +4191,7 @@
     AppInfoWithLiveness appInfoWithLiveness =
         new AppInfoWithLiveness(
             appInfo.getSyntheticItems().commit(app),
+            appInfo.getArtProfiles(),
             appInfo.getClassToFeatureSplitMap(),
             appInfo.getMainDexInfo(),
             mode.isInitialTreeShaking()
diff --git a/src/main/java/com/android/tools/r8/tracereferences/Tracer.java b/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
index 97c4499..11b610a 100644
--- a/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
+++ b/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
@@ -37,6 +37,7 @@
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.desugar.LambdaDescriptor;
+import com.android.tools.r8.profile.art.ArtProfileCollection;
 import com.android.tools.r8.references.ClassReference;
 import com.android.tools.r8.references.FieldReference;
 import com.android.tools.r8.references.MethodReference;
@@ -73,6 +74,7 @@
         AppView.createForTracer(
             AppInfoWithClassHierarchy.createInitialAppInfoWithClassHierarchy(
                 new ApplicationReader(inputApp, options, Timing.empty()).read().toDirect(),
+                ArtProfileCollection.empty(),
                 ClassToFeatureSplitMap.createEmptyClassToFeatureSplitMap(),
                 MainDexInfo.none(),
                 GlobalSyntheticsStrategy.forSingleOutputMode(),
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 a7841fc..9c2c3b1 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -72,6 +72,7 @@
 import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagatorEventConsumer;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.position.Position;
+import com.android.tools.r8.profile.art.ArtProfileOptions;
 import com.android.tools.r8.references.ClassReference;
 import com.android.tools.r8.references.FieldReference;
 import com.android.tools.r8.references.MethodReference;
@@ -819,7 +820,8 @@
       new KotlinOptimizationOptions();
   private final ApiModelTestingOptions apiModelTestingOptions = new ApiModelTestingOptions();
   private final DesugarSpecificOptions desugarSpecificOptions = new DesugarSpecificOptions();
-  private final StartupOptions startupOptions = new StartupOptions(this);
+  private final ArtProfileOptions artProfileOptions = new ArtProfileOptions();
+  private final StartupOptions startupOptions = new StartupOptions();
   private final StartupInstrumentationOptions startupInstrumentationOptions =
       new StartupInstrumentationOptions();
   public final TestingOptions testing = new TestingOptions();
@@ -881,6 +883,10 @@
     return openClosedInterfacesOptions;
   }
 
+  public ArtProfileOptions getArtProfileOptions() {
+    return artProfileOptions;
+  }
+
   public StartupOptions getStartupOptions() {
     return startupOptions;
   }
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 4f8181d..ab3a2a9 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -46,6 +46,7 @@
 import com.android.tools.r8.jasmin.JasminBuilder;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.origin.PathOrigin;
+import com.android.tools.r8.profile.art.ArtProfileCollection;
 import com.android.tools.r8.references.ClassReference;
 import com.android.tools.r8.references.FieldReference;
 import com.android.tools.r8.references.MethodReference;
@@ -775,6 +776,7 @@
       throws Exception {
     return AppInfoWithClassHierarchy.createInitialAppInfoWithClassHierarchy(
         readApplicationForDexOutput(app, new InternalOptions()),
+        ArtProfileCollection.empty(),
         ClassToFeatureSplitMap.createEmptyClassToFeatureSplitMap(),
         MainDexInfo.none(),
         GlobalSyntheticsStrategy.forSingleOutputMode(),
diff --git a/src/test/java/com/android/tools/r8/profile/art/ArtProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/ArtProfileRewritingTest.java
new file mode 100644
index 0000000..860d603
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/profile/art/ArtProfileRewritingTest.java
@@ -0,0 +1,169 @@
+// Copyright (c) 2022, 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;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.Lists;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ArtProfileRewritingTest extends TestBase {
+
+  private static final ClassReference mainClassReference = Reference.classFromClass(Main.class);
+  private static final MethodReference mainMethodReference =
+      MethodReferenceUtils.mainMethod(Main.class);
+
+  private static final ClassReference greeterClassReference =
+      Reference.classFromClass(Greeter.class);
+  private static final MethodReference greetMethodReference =
+      MethodReferenceUtils.methodFromMethod(Greeter.class, "greet");
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  @Test
+  public void test() throws Exception {
+    MyArtProfileInput artProfileInput = new MyArtProfileInput();
+    testForR8(Backend.DEX)
+        .addInnerClasses(getClass())
+        .addKeepMainRule(Main.class)
+        .addOptionsModification(
+            options ->
+                options
+                    .getArtProfileOptions()
+                    .setArtProfileInputs(Collections.singleton(artProfileInput)))
+        .enableInliningAnnotations()
+        .setMinApi(AndroidApiLevel.LATEST)
+        .compile()
+        .inspect(inspector -> inspect(inspector, artProfileInput));
+  }
+
+  private void inspect(CodeInspector inspector, MyArtProfileInput artProfileInput) {
+    ClassSubject greeterClassSubject = inspector.clazz(Greeter.class);
+    assertThat(greeterClassSubject, isPresentAndRenamed());
+
+    MethodSubject greetMethodSubject = greeterClassSubject.uniqueMethodWithName("greet");
+    assertThat(greetMethodSubject, isPresentAndRenamed());
+
+    assertTrue(artProfileInput.finished);
+    assertEquals(
+        Lists.newArrayList(
+            mainClassReference,
+            mainMethodReference,
+            greeterClassSubject.getFinalReference(),
+            greetMethodSubject.getFinalReference()),
+        artProfileInput.references);
+    assertEquals(
+        Lists.newArrayList(
+            ArtProfileClassRuleInfoImpl.empty(),
+            ArtProfileMethodRuleInfoImpl.builder().setIsStartup().build(),
+            ArtProfileClassRuleInfoImpl.empty(),
+            ArtProfileMethodRuleInfoImpl.builder().setIsHot().setIsPostStartup().build()),
+        artProfileInput.infos);
+  }
+
+  static class MyArtProfileInput implements ArtProfileInput {
+
+    boolean finished;
+    List<Object> references = new ArrayList<>();
+    List<Object> infos = new ArrayList<>();
+
+    @Override
+    public ResidualArtProfileConsumer getArtProfileConsumer() {
+      return new ResidualArtProfileConsumer() {
+
+        @Override
+        public ResidualArtProfileRuleConsumer getRuleConsumer() {
+          return new ResidualArtProfileRuleConsumer() {
+
+            @Override
+            public void acceptClassRule(
+                ClassReference classReference, ArtProfileClassRuleInfo classRuleInfo) {
+              references.add(classReference);
+              infos.add(classRuleInfo);
+            }
+
+            @Override
+            public void acceptMethodRule(
+                MethodReference methodReference, ArtProfileMethodRuleInfo methodRuleInfo) {
+              references.add(methodReference);
+              infos.add(methodRuleInfo);
+            }
+          };
+        }
+
+        @Override
+        public void finished(DiagnosticsHandler handler) {
+          finished = true;
+        }
+      };
+    }
+
+    @Override
+    public void getArtProfile(ArtProfileBuilder profileBuilder) {
+      profileBuilder
+          .addClassRule(classRuleBuilder -> classRuleBuilder.setClassReference(mainClassReference))
+          .addMethodRule(
+              methodRuleBuilder ->
+                  methodRuleBuilder
+                      .setMethodReference(mainMethodReference)
+                      .setMethodRuleInfo(
+                          methodRuleInfoBuilder -> methodRuleInfoBuilder.setIsStartup(true)))
+          .addClassRule(
+              classRuleBuilder -> classRuleBuilder.setClassReference(greeterClassReference))
+          .addMethodRule(
+              methodRuleBuilder ->
+                  methodRuleBuilder
+                      .setMethodReference(greetMethodReference)
+                      .setMethodRuleInfo(
+                          methodRuleInfoBuilder ->
+                              methodRuleInfoBuilder.setIsHot(true).setIsPostStartup(true)));
+    }
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      Greeter.greet();
+    }
+  }
+
+  static class Greeter {
+
+    @NeverInline
+    static void greet() {
+      System.out.println("Hello, world!");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java
index e3855b0..aeeb8f4 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
 import com.android.tools.r8.naming.MemberNaming.Signature;
+import com.android.tools.r8.references.MethodReference;
 import java.util.List;
 
 public class AbsentMethodSubject extends MethodSubject {
@@ -82,6 +83,11 @@
   }
 
   @Override
+  public MethodReference getFinalReference() {
+    throw new Unreachable("Cannot get the final reference for an absent method");
+  }
+
+  @Override
   public TypeSubject getParameter(int index) {
     throw new Unreachable("Cannot get the parameter for an absent method");
   }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
index 533fa55..3e28a56 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
@@ -134,6 +134,11 @@
   }
 
   @Override
+  public MethodReference getFinalReference() {
+    return dexMethod.getReference().asMethodReference();
+  }
+
+  @Override
   public TypeSubject getParameter(int index) {
     return new TypeSubject(codeInspector, getMethod().getParameter(index));
   }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
index a083636..ccc63b0 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.references.MethodReference;
 import com.google.common.collect.Streams;
 import java.util.Iterator;
 import java.util.List;
@@ -54,6 +55,8 @@
 
   public abstract DexEncodedMethod getMethod();
 
+  public abstract MethodReference getFinalReference();
+
   public abstract TypeSubject getParameter(int index);
 
   public abstract List<TypeSubject> getParameters();