Support for adding static synthetic methods to profile

Bug: b/265729283
Change-Id: I33ec266c5b57492abb1924249a2b4a9df6774913
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 42b36ff..edead3e 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
@@ -29,6 +29,7 @@
 import com.android.tools.r8.ir.desugar.records.RecordDesugaringEventConsumer.RecordInstructionDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.twr.TwrCloseResourceDesugaringEventConsumer;
 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.ArtProfileRewritingCfInstructionDesugaringEventConsumer;
 import com.android.tools.r8.shaking.Enqueuer.SyntheticAdditions;
 import com.android.tools.r8.shaking.KeepMethodInfo.Joiner;
@@ -71,11 +72,16 @@
     CfInstructionDesugaringEventConsumer eventConsumer =
         new D8CfInstructionDesugaringEventConsumer(
             appView, classConverterResultBuilder, methodProcessor);
-    return ArtProfileRewritingCfInstructionDesugaringEventConsumer.attachIfNeeded(eventConsumer);
+    // TODO(b/265729283): Also include synthetics in the baseline profile in D8.
+    ArtProfileCollectionAdditions artProfileCollectionAdditions =
+        ArtProfileCollectionAdditions.nop();
+    return ArtProfileRewritingCfInstructionDesugaringEventConsumer.attach(
+        artProfileCollectionAdditions, eventConsumer);
   }
 
   public static CfInstructionDesugaringEventConsumer createForR8(
       AppView<? extends AppInfoWithClassHierarchy> appView,
+      ArtProfileCollectionAdditions artProfileCollectionAdditions,
       BiConsumer<LambdaClass, ProgramMethod> lambdaClassConsumer,
       BiConsumer<ConstantDynamicClass, ProgramMethod> constantDynamicClassConsumer,
       BiConsumer<ProgramMethod, ProgramMethod> twrCloseResourceMethodConsumer,
@@ -89,7 +95,8 @@
             twrCloseResourceMethodConsumer,
             additions,
             companionMethodConsumer);
-    return ArtProfileRewritingCfInstructionDesugaringEventConsumer.attachIfNeeded(eventConsumer);
+    return ArtProfileRewritingCfInstructionDesugaringEventConsumer.attach(
+        artProfileCollectionAdditions, eventConsumer);
   }
 
   public abstract List<ProgramMethod> finalizeDesugaring();
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
index f3d4f1d..55d7ef6 100644
--- a/src/main/java/com/android/tools/r8/profile/art/ArtProfile.java
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfile.java
@@ -17,13 +17,12 @@
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.Reporter;
-import com.google.common.collect.ImmutableList;
+import com.android.tools.r8.utils.ThrowingConsumer;
 import java.io.IOException;
 import java.io.OutputStreamWriter;
 import java.io.UncheckedIOException;
-import java.util.ArrayList;
+import java.util.Collection;
 import java.util.LinkedHashMap;
-import java.util.List;
 import java.util.Map;
 import java.util.function.BiConsumer;
 import java.util.function.Consumer;
@@ -31,16 +30,53 @@
 
 public class ArtProfile {
 
-  private final List<ArtProfileRule> rules;
+  private final Map<DexReference, ArtProfileRule> rules;
 
-  ArtProfile(List<ArtProfileRule> rules) {
+  ArtProfile(Map<DexReference, ArtProfileRule> rules) {
     this.rules = rules;
   }
 
-  public static Builder builder(ArtProfileProvider artProfileProvider, InternalOptions options) {
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  public static Builder builderForInitialArtProfile(
+      ArtProfileProvider artProfileProvider, InternalOptions options) {
     return new Builder(artProfileProvider, options);
   }
 
+  public boolean containsClassRule(DexType type) {
+    return rules.containsKey(type);
+  }
+
+  public boolean containsMethodRule(DexMethod method) {
+    return rules.containsKey(method);
+  }
+
+  public <E extends Exception> void forEachRule(ThrowingConsumer<ArtProfileRule, E> ruleConsumer)
+      throws E {
+    for (ArtProfileRule rule : rules.values()) {
+      ruleConsumer.accept(rule);
+    }
+  }
+
+  public <E1 extends Exception, E2 extends Exception> void forEachRule(
+      ThrowingConsumer<ArtProfileClassRule, E1> classRuleConsumer,
+      ThrowingConsumer<ArtProfileMethodRule, E2> methodRuleConsumer)
+      throws E1, E2 {
+    for (ArtProfileRule rule : rules.values()) {
+      rule.accept(classRuleConsumer, methodRuleConsumer);
+    }
+  }
+
+  public ArtProfileClassRule getClassRule(DexType type) {
+    return (ArtProfileClassRule) rules.get(type);
+  }
+
+  public ArtProfileMethodRule getMethodRule(DexMethod method) {
+    return (ArtProfileMethodRule) rules.get(method);
+  }
+
   public ArtProfile rewrittenWithLens(GraphLens lens) {
     return transform(
         (classRule, builderFactory) -> builderFactory.accept(lens.lookupType(classRule.getType())),
@@ -88,37 +124,32 @@
       BiConsumer<ArtProfileMethodRule, Function<DexMethod, ArtProfileMethodRule.Builder>>
           methodTransformation) {
     Map<DexReference, ArtProfileRule.Builder> ruleBuilders = new LinkedHashMap<>();
-    for (ArtProfileRule rule : rules) {
-      if (rule.isClassRule()) {
+    forEachRule(
         // Supply a factory method for creating a builder. If the current rule should be included in
         // the rewritten profile, the caller should call the provided builder factory method to
         // create a class rule builder. If two rules are mapped to the same reference, the same rule
         // builder is reused so that the two rules are merged into a single rule (with their flags
         // merged).
-        classTransformation.accept(
-            rule.asClassRule(),
-            newType ->
-                ruleBuilders
-                    .computeIfAbsent(
-                        newType, ignoreKey(() -> ArtProfileClassRule.builder().setType(newType)))
-                    .asClassRuleBuilder());
-      } else {
+        classRule ->
+            classTransformation.accept(
+                classRule,
+                newType ->
+                    ruleBuilders
+                        .computeIfAbsent(
+                            newType,
+                            ignoreKey(() -> ArtProfileClassRule.builder().setType(newType)))
+                        .asClassRuleBuilder()),
         // As above.
-        assert rule.isMethodRule();
-        methodTransformation.accept(
-            rule.asMethodRule(),
-            newMethod ->
-                ruleBuilders
-                    .computeIfAbsent(
-                        newMethod,
-                        ignoreKey(() -> ArtProfileMethodRule.builder().setMethod(newMethod)))
-                    .asMethodRuleBuilder());
-      }
-    }
-    ImmutableList.Builder<ArtProfileRule> newRules =
-        ImmutableList.builderWithExpectedSize(ruleBuilders.size());
-    ruleBuilders.values().forEach(ruleBuilder -> newRules.add(ruleBuilder.build()));
-    return new ArtProfile(newRules.build());
+        methodRule ->
+            methodTransformation.accept(
+                methodRule,
+                newMethod ->
+                    ruleBuilders
+                        .computeIfAbsent(
+                            newMethod,
+                            ignoreKey(() -> ArtProfileMethodRule.builder().setMethod(newMethod)))
+                        .asMethodRuleBuilder()));
+    return builder().addRuleBuilders(ruleBuilders.values()).build();
   }
 
   public void supplyConsumer(ArtProfileConsumer consumer, Reporter reporter) {
@@ -140,10 +171,11 @@
       try (OutputStreamWriter outputStreamWriter =
           new OutputStreamWriter(
               textOutputStream.getOutputStream(), textOutputStream.getCharset())) {
-        for (ArtProfileRule rule : rules) {
-          rule.writeHumanReadableRuleString(outputStreamWriter);
-          outputStreamWriter.write('\n');
-        }
+        forEachRule(
+            rule -> {
+              rule.writeHumanReadableRuleString(outputStreamWriter);
+              outputStreamWriter.write('\n');
+            });
       }
     } catch (IOException e) {
       throw new UncheckedIOException(e);
@@ -151,15 +183,13 @@
   }
 
   private void supplyRuleConsumer(ArtProfileRuleConsumer ruleConsumer) {
-    for (ArtProfileRule rule : rules) {
-      rule.accept(
-          classRule ->
-              ruleConsumer.acceptClassRule(
-                  classRule.getClassReference(), classRule.getClassRuleInfo()),
-          methodRule ->
-              ruleConsumer.acceptMethodRule(
-                  methodRule.getMethodReference(), methodRule.getMethodRuleInfo()));
-    }
+    forEachRule(
+        classRule ->
+            ruleConsumer.acceptClassRule(
+                classRule.getClassReference(), classRule.getClassRuleInfo()),
+        methodRule ->
+            ruleConsumer.acceptMethodRule(
+                methodRule.getMethodReference(), methodRule.getMethodRuleInfo()));
   }
 
   public static class Builder implements ArtProfileBuilder {
@@ -167,34 +197,57 @@
     private final ArtProfileProvider artProfileProvider;
     private final DexItemFactory dexItemFactory;
     private Reporter reporter;
-    private final List<ArtProfileRule> rules = new ArrayList<>();
+    private final Map<DexReference, ArtProfileRule> rules = new LinkedHashMap<>();
 
+    Builder() {
+      this.artProfileProvider = null;
+      this.dexItemFactory = null;
+      this.reporter = null;
+    }
+
+    // Constructor for building the initial ART profile. The input is based on the Reference API, so
+    // access to the DexItemFactory is needed for conversion into the internal DexReference.
+    // Moreover, access to the Reporter is needed for diagnostics reporting.
     Builder(ArtProfileProvider artProfileProvider, InternalOptions options) {
       this.artProfileProvider = artProfileProvider;
       this.dexItemFactory = options.dexItemFactory();
       this.reporter = options.reporter;
     }
 
+    public Builder addRule(ArtProfileRule rule) {
+      assert !rules.containsKey(rule.getReference());
+      rule.accept(
+          classRule -> rules.put(classRule.getType(), classRule),
+          methodRule -> rules.put(methodRule.getMethod(), methodRule));
+      return this;
+    }
+
+    public Builder addRules(Collection<ArtProfileRule> rules) {
+      rules.forEach(this::addRule);
+      return this;
+    }
+
+    public Builder addRuleBuilders(Collection<ArtProfileRule.Builder> ruleBuilders) {
+      ruleBuilders.forEach(ruleBuilder -> addRule(ruleBuilder.build()));
+      return this;
+    }
+
     @Override
-    public ArtProfileBuilder addClassRule(
-        Consumer<ArtProfileClassRuleBuilder> classRuleBuilderConsumer) {
+    public Builder addClassRule(Consumer<ArtProfileClassRuleBuilder> classRuleBuilderConsumer) {
       ArtProfileClassRule.Builder classRuleBuilder = ArtProfileClassRule.builder(dexItemFactory);
       classRuleBuilderConsumer.accept(classRuleBuilder);
-      rules.add(classRuleBuilder.build());
-      return this;
+      return addRule(classRuleBuilder.build());
     }
 
     @Override
-    public ArtProfileBuilder addMethodRule(
-        Consumer<ArtProfileMethodRuleBuilder> methodRuleBuilderConsumer) {
+    public Builder addMethodRule(Consumer<ArtProfileMethodRuleBuilder> methodRuleBuilderConsumer) {
       ArtProfileMethodRule.Builder methodRuleBuilder = ArtProfileMethodRule.builder(dexItemFactory);
       methodRuleBuilderConsumer.accept(methodRuleBuilder);
-      rules.add(methodRuleBuilder.build());
-      return this;
+      return addRule(methodRuleBuilder.build());
     }
 
     @Override
-    public ArtProfileBuilder addHumanReadableArtProfile(
+    public Builder addHumanReadableArtProfile(
         TextInputStream textInputStream,
         Consumer<HumanReadableArtProfileParserBuilder> parserBuilderConsumer) {
       HumanReadableArtProfileParser.Builder parserBuilder =
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
index 932ecb8..283e22b 100644
--- a/src/main/java/com/android/tools/r8/profile/art/ArtProfileClassRule.java
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileClassRule.java
@@ -5,12 +5,13 @@
 package com.android.tools.r8.profile.art;
 
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.references.ClassReference;
 import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.ThrowingConsumer;
 import java.io.IOException;
 import java.io.OutputStreamWriter;
-import java.util.function.Consumer;
 
 public class ArtProfileClassRule extends ArtProfileRule {
 
@@ -29,9 +30,10 @@
   }
 
   @Override
-  public void accept(
-      Consumer<ArtProfileClassRule> classRuleConsumer,
-      Consumer<ArtProfileMethodRule> methodRuleConsumer) {
+  public <E1 extends Exception, E2 extends Exception> void accept(
+      ThrowingConsumer<ArtProfileClassRule, E1> classRuleConsumer,
+      ThrowingConsumer<ArtProfileMethodRule, E2> methodRuleConsumer)
+      throws E1 {
     classRuleConsumer.accept(this);
   }
 
@@ -43,6 +45,11 @@
     return ArtProfileClassRuleInfoImpl.empty();
   }
 
+  @Override
+  public DexReference getReference() {
+    return getType();
+  }
+
   public DexType getType() {
     return type;
   }
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
index 3a885d9..2cdb3e0 100644
--- a/src/main/java/com/android/tools/r8/profile/art/ArtProfileCollection.java
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileCollection.java
@@ -30,7 +30,8 @@
     for (ArtProfileForRewriting artProfileForRewriting :
         options.getArtProfileOptions().getArtProfilesForRewriting()) {
       ArtProfileProvider artProfileProvider = artProfileForRewriting.getArtProfileProvider();
-      ArtProfile.Builder artProfileBuilder = ArtProfile.builder(artProfileProvider, options);
+      ArtProfile.Builder artProfileBuilder =
+          ArtProfile.builderForInitialArtProfile(artProfileProvider, options);
       artProfileForRewriting.getArtProfileProvider().getArtProfile(artProfileBuilder);
       artProfiles.add(artProfileBuilder.build());
     }
@@ -45,6 +46,10 @@
     return PassthroughArtProfileCollection.getInstance();
   }
 
+  public abstract boolean isNonEmpty();
+
+  public abstract NonEmptyArtProfileCollection asNonEmpty();
+
   public abstract ArtProfileCollection rewrittenWithLens(GraphLens lens);
 
   public abstract ArtProfileCollection rewrittenWithLens(
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
index f9380b8..df469c9 100644
--- a/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRule.java
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRule.java
@@ -8,6 +8,7 @@
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.references.MethodReference;
 import com.android.tools.r8.utils.MethodReferenceUtils;
+import com.android.tools.r8.utils.ThrowingConsumer;
 import java.io.IOException;
 import java.io.OutputStreamWriter;
 import java.util.function.Consumer;
@@ -31,9 +32,10 @@
   }
 
   @Override
-  public void accept(
-      Consumer<ArtProfileClassRule> classRuleConsumer,
-      Consumer<ArtProfileMethodRule> methodRuleConsumer) {
+  public <E1 extends Exception, E2 extends Exception> void accept(
+      ThrowingConsumer<ArtProfileClassRule, E1> classRuleConsumer,
+      ThrowingConsumer<ArtProfileMethodRule, E2> methodRuleConsumer)
+      throws E2 {
     methodRuleConsumer.accept(this);
   }
 
@@ -45,11 +47,16 @@
     return method.asMethodReference();
   }
 
-  public ArtProfileMethodRuleInfo getMethodRuleInfo() {
+  public ArtProfileMethodRuleInfoImpl getMethodRuleInfo() {
     return info;
   }
 
   @Override
+  public DexMethod getReference() {
+    return getMethod();
+  }
+
+  @Override
   public boolean isMethodRule() {
     return true;
   }
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 86d4c84..252cdac 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
@@ -140,6 +140,11 @@
       return this;
     }
 
+    public Builder joinFlags(ArtProfileMethodRuleInfoImpl methodRuleInfo) {
+      flags |= methodRuleInfo.flags;
+      return this;
+    }
+
     public ArtProfileMethodRuleInfoImpl build() {
       return new ArtProfileMethodRuleInfoImpl(flags);
     }
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
index f05167c..8d6eefd 100644
--- a/src/main/java/com/android/tools/r8/profile/art/ArtProfileRule.java
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileRule.java
@@ -4,15 +4,24 @@
 
 package com.android.tools.r8.profile.art;
 
+import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.utils.ThrowingConsumer;
 import java.io.IOException;
 import java.io.OutputStreamWriter;
-import java.util.function.Consumer;
 
-public abstract class ArtProfileRule {
+public abstract class ArtProfileRule implements Comparable<ArtProfileRule> {
 
-  public abstract void accept(
-      Consumer<ArtProfileClassRule> classRuleConsumer,
-      Consumer<ArtProfileMethodRule> methodRuleConsumer);
+  public abstract <E1 extends Exception, E2 extends Exception> void accept(
+      ThrowingConsumer<ArtProfileClassRule, E1> classRuleConsumer,
+      ThrowingConsumer<ArtProfileMethodRule, E2> methodRuleConsumer)
+      throws E1, E2;
+
+  @Override
+  public final int compareTo(ArtProfileRule rule) {
+    return getReference().compareTo(rule.getReference());
+  }
+
+  public abstract DexReference getReference();
 
   public boolean isClassRule() {
     return false;
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
index f0313ef..0540aa4 100644
--- a/src/main/java/com/android/tools/r8/profile/art/EmptyArtProfileCollection.java
+++ b/src/main/java/com/android/tools/r8/profile/art/EmptyArtProfileCollection.java
@@ -21,6 +21,16 @@
   }
 
   @Override
+  public boolean isNonEmpty() {
+    return false;
+  }
+
+  @Override
+  public NonEmptyArtProfileCollection asNonEmpty() {
+    return null;
+  }
+
+  @Override
   public ArtProfileCollection rewrittenWithLens(GraphLens lens) {
     return this;
   }
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
index e3cbaf7..b196906 100644
--- a/src/main/java/com/android/tools/r8/profile/art/NonEmptyArtProfileCollection.java
+++ b/src/main/java/com/android/tools/r8/profile/art/NonEmptyArtProfileCollection.java
@@ -15,15 +15,31 @@
 import java.util.Iterator;
 import java.util.function.Function;
 
-public class NonEmptyArtProfileCollection extends ArtProfileCollection {
+public class NonEmptyArtProfileCollection extends ArtProfileCollection
+    implements Iterable<ArtProfile> {
 
   private final Collection<ArtProfile> artProfiles;
 
-  NonEmptyArtProfileCollection(Collection<ArtProfile> artProfiles) {
+  public NonEmptyArtProfileCollection(Collection<ArtProfile> artProfiles) {
     this.artProfiles = artProfiles;
   }
 
   @Override
+  public boolean isNonEmpty() {
+    return true;
+  }
+
+  @Override
+  public NonEmptyArtProfileCollection asNonEmpty() {
+    return this;
+  }
+
+  @Override
+  public Iterator<ArtProfile> iterator() {
+    return artProfiles.iterator();
+  }
+
+  @Override
   public NonEmptyArtProfileCollection rewrittenWithLens(GraphLens lens) {
     return map(artProfile -> artProfile.rewrittenWithLens(lens));
   }
diff --git a/src/main/java/com/android/tools/r8/profile/art/PassthroughArtProfileCollection.java b/src/main/java/com/android/tools/r8/profile/art/PassthroughArtProfileCollection.java
index 2198da6..ac92f64 100644
--- a/src/main/java/com/android/tools/r8/profile/art/PassthroughArtProfileCollection.java
+++ b/src/main/java/com/android/tools/r8/profile/art/PassthroughArtProfileCollection.java
@@ -30,6 +30,16 @@
   }
 
   @Override
+  public boolean isNonEmpty() {
+    return false;
+  }
+
+  @Override
+  public NonEmptyArtProfileCollection asNonEmpty() {
+    return null;
+  }
+
+  @Override
   public ArtProfileCollection rewrittenWithLens(GraphLens lens) {
     return this;
   }
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
new file mode 100644
index 0000000..9427a5a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileAdditions.java
@@ -0,0 +1,105 @@
+// 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.DexMethod;
+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.profile.art.ArtProfile;
+import com.android.tools.r8.profile.art.ArtProfileClassRule;
+import com.android.tools.r8.profile.art.ArtProfileMethodRule;
+import com.android.tools.r8.profile.art.ArtProfileRule;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/** Mutable extension of an existing ArtProfile. */
+class ArtProfileAdditions {
+
+  private final ArtProfile artProfile;
+
+  private final Map<DexType, ArtProfileClassRule.Builder> classRuleAdditions =
+      new ConcurrentHashMap<>();
+  private final Map<DexMethod, ArtProfileMethodRule.Builder> methodRuleAdditions =
+      new ConcurrentHashMap<>();
+
+  ArtProfileAdditions(ArtProfile artProfile) {
+    this.artProfile = artProfile;
+  }
+
+  void addClassRule(DexProgramClass clazz) {
+    if (artProfile.containsClassRule(clazz.getType())) {
+      return;
+    }
+
+    // Create profile rule for class.
+    classRuleAdditions.computeIfAbsent(
+        clazz.getType(), type -> ArtProfileClassRule.builder().setType(type));
+  }
+
+  boolean addMethodRuleIfContextIsInProfile(ProgramMethod method, ProgramMethod context) {
+    return addMethodRuleIfContextIsInProfile(
+        method, context, MethodRuleAdditionConfig.getDefault());
+  }
+
+  /**
+   * Adds the given {@param method} to the ART profile if {@param context} is live (i.e., present in
+   * the baseline profile).
+   *
+   * @return true if {@param context} is live.
+   */
+  boolean addMethodRuleIfContextIsInProfile(
+      ProgramMethod method, ProgramMethod context, MethodRuleAdditionConfig config) {
+    assert !artProfile.containsMethodRule(method.getReference());
+
+    ArtProfileMethodRule contextMethodRule = artProfile.getMethodRule(context.getReference());
+    if (contextMethodRule == null) {
+      return false;
+    }
+
+    // Create profile rule for method.
+    ArtProfileMethodRule.Builder methodRuleBuilder =
+        methodRuleAdditions.computeIfAbsent(
+            method.getReference(),
+            methodReference -> ArtProfileMethodRule.builder().setMethod(method.getReference()));
+
+    // Setup the rule.
+    methodRuleBuilder.acceptMethodRuleInfoBuilder(
+        methodRuleInfoBuilder ->
+            config.configureMethodRuleInfo(methodRuleInfoBuilder, contextMethodRule));
+    return true;
+  }
+
+  ArtProfile createNewArtProfile() {
+    if (!hasAdditions()) {
+      return artProfile;
+    }
+
+    // Add existing rules to new profile.
+    ArtProfile.Builder artProfileBuilder = ArtProfile.builder();
+    artProfile.forEachRule(artProfileBuilder::addRule);
+
+    // Sort and add additions to new profile. Sorting is needed since the additions to this
+    // collection may be concurrent.
+    List<ArtProfileRule> ruleAdditionsSorted =
+        new ArrayList<>(classRuleAdditions.size() + methodRuleAdditions.size());
+    classRuleAdditions
+        .values()
+        .forEach(classRuleBuilder -> ruleAdditionsSorted.add(classRuleBuilder.build()));
+    methodRuleAdditions
+        .values()
+        .forEach(methodRuleBuilder -> ruleAdditionsSorted.add(methodRuleBuilder.build()));
+    ruleAdditionsSorted.sort(ArtProfileRule::compareTo);
+    artProfileBuilder.addRules(ruleAdditionsSorted);
+
+    return artProfileBuilder.build();
+  }
+
+  boolean hasAdditions() {
+    return !classRuleAdditions.isEmpty() || !methodRuleAdditions.isEmpty();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileCollectionAdditions.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileCollectionAdditions.java
new file mode 100644
index 0000000..f4ea621
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileCollectionAdditions.java
@@ -0,0 +1,42 @@
+// 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.AppView;
+import com.android.tools.r8.profile.art.ArtProfileCollection;
+
+/**
+ * Interface for adding (synthetic) items to an existing ArtProfileCollection.
+ *
+ * <p>The interface will be implemented by {@link NopArtProfileCollectionAdditions} when the
+ * compilation does not contain any ART profiles, for minimal performance overhead.
+ *
+ * <p>When one or more ART profiles are present, this is implemented by {@link
+ * ConcreteArtProfileCollectionAdditions}.
+ */
+public abstract class ArtProfileCollectionAdditions {
+
+  public static ArtProfileCollectionAdditions create(AppView<?> appView) {
+    ArtProfileCollection artProfileCollection = appView.getArtProfileCollection();
+    if (artProfileCollection.isNonEmpty()) {
+      return new ConcreteArtProfileCollectionAdditions(artProfileCollection.asNonEmpty());
+    }
+    return nop();
+  }
+
+  public static NopArtProfileCollectionAdditions nop() {
+    return NopArtProfileCollectionAdditions.getInstance();
+  }
+
+  public abstract void commit(AppView<?> appView);
+
+  boolean isNop() {
+    return false;
+  }
+
+  ConcreteArtProfileCollectionAdditions asConcrete() {
+    return null;
+  }
+}
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 2386676..58a8a1e 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
@@ -17,17 +17,24 @@
 public class ArtProfileRewritingCfInstructionDesugaringEventConsumer
     extends CfInstructionDesugaringEventConsumer {
 
+  private final ConcreteArtProfileCollectionAdditions additionsCollection;
   private final CfInstructionDesugaringEventConsumer parent;
 
-  public ArtProfileRewritingCfInstructionDesugaringEventConsumer(
+  private ArtProfileRewritingCfInstructionDesugaringEventConsumer(
+      ConcreteArtProfileCollectionAdditions additionsCollection,
       CfInstructionDesugaringEventConsumer parent) {
+    this.additionsCollection = additionsCollection;
     this.parent = parent;
   }
 
-  public static CfInstructionDesugaringEventConsumer attachIfNeeded(
+  public static CfInstructionDesugaringEventConsumer attach(
+      ArtProfileCollectionAdditions artProfileCollectionAdditions,
       CfInstructionDesugaringEventConsumer eventConsumer) {
-    // TODO(b/265729283): Attach this wrapper when there is a baseline profile that needs rewriting.
-    return eventConsumer;
+    if (artProfileCollectionAdditions.isNop()) {
+      return eventConsumer;
+    }
+    return new ArtProfileRewritingCfInstructionDesugaringEventConsumer(
+        artProfileCollectionAdditions.asConcrete(), eventConsumer);
   }
 
   @Override
@@ -123,6 +130,7 @@
 
   @Override
   public void acceptOutlinedMethod(ProgramMethod outlinedMethod, ProgramMethod context) {
+    additionsCollection.addMethodRuleIfContextIsInProfile(outlinedMethod, context);
     parent.acceptOutlinedMethod(outlinedMethod, context);
   }
 
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
new file mode 100644
index 0000000..7b1b1c6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/ConcreteArtProfileCollectionAdditions.java
@@ -0,0 +1,59 @@
+// 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.AppView;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.profile.art.ArtProfile;
+import com.android.tools.r8.profile.art.ArtProfileCollection;
+import com.android.tools.r8.profile.art.NonEmptyArtProfileCollection;
+import com.google.common.collect.Iterables;
+import java.util.ArrayList;
+import java.util.List;
+
+public class ConcreteArtProfileCollectionAdditions extends ArtProfileCollectionAdditions {
+
+  private final List<ArtProfileAdditions> additionsCollection = new ArrayList<>();
+
+  ConcreteArtProfileCollectionAdditions(NonEmptyArtProfileCollection artProfileCollection) {
+    for (ArtProfile artProfile : artProfileCollection) {
+      additionsCollection.add(new ArtProfileAdditions(artProfile));
+    }
+    assert !additionsCollection.isEmpty();
+  }
+
+  void addMethodRuleIfContextIsInProfile(ProgramMethod method, ProgramMethod context) {
+    for (ArtProfileAdditions artProfileAdditions : additionsCollection) {
+      if (artProfileAdditions.addMethodRuleIfContextIsInProfile(method, context)) {
+        artProfileAdditions.addClassRule(method.getHolder());
+      }
+    }
+  }
+
+  @Override
+  ConcreteArtProfileCollectionAdditions asConcrete() {
+    return this;
+  }
+
+  @Override
+  public void commit(AppView<?> appView) {
+    if (hasAdditions()) {
+      appView.setArtProfileCollection(createNewArtProfileCollection());
+    }
+  }
+
+  private ArtProfileCollection createNewArtProfileCollection() {
+    assert hasAdditions();
+    List<ArtProfile> newArtProfiles = new ArrayList<>(additionsCollection.size());
+    for (ArtProfileAdditions additions : additionsCollection) {
+      newArtProfiles.add(additions.createNewArtProfile());
+    }
+    return new NonEmptyArtProfileCollection(newArtProfiles);
+  }
+
+  private boolean hasAdditions() {
+    return Iterables.any(additionsCollection, ArtProfileAdditions::hasAdditions);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/MethodRuleAdditionConfig.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/MethodRuleAdditionConfig.java
new file mode 100644
index 0000000..5f4f17f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/MethodRuleAdditionConfig.java
@@ -0,0 +1,38 @@
+// 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.profile.art.ArtProfileMethodRule;
+import com.android.tools.r8.profile.art.ArtProfileMethodRuleInfoImpl;
+
+public abstract class MethodRuleAdditionConfig {
+
+  public static MethodRuleAdditionConfig getDefault() {
+    return DefaultMethodRuleAdditionConfig.getInstance();
+  }
+
+  public abstract void configureMethodRuleInfo(
+      ArtProfileMethodRuleInfoImpl.Builder methodRuleInfoBuilder,
+      ArtProfileMethodRule contextMethodRule);
+
+  private static class DefaultMethodRuleAdditionConfig extends MethodRuleAdditionConfig {
+
+    private static final DefaultMethodRuleAdditionConfig INSTANCE =
+        new DefaultMethodRuleAdditionConfig();
+
+    private DefaultMethodRuleAdditionConfig() {}
+
+    static DefaultMethodRuleAdditionConfig getInstance() {
+      return INSTANCE;
+    }
+
+    @Override
+    public void configureMethodRuleInfo(
+        ArtProfileMethodRuleInfoImpl.Builder methodRuleInfoBuilder,
+        ArtProfileMethodRule contextMethodRule) {
+      methodRuleInfoBuilder.joinFlags(contextMethodRule.getMethodRuleInfo());
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/NopArtProfileCollectionAdditions.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/NopArtProfileCollectionAdditions.java
new file mode 100644
index 0000000..c26a098
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/NopArtProfileCollectionAdditions.java
@@ -0,0 +1,29 @@
+// 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.AppView;
+
+public class NopArtProfileCollectionAdditions extends ArtProfileCollectionAdditions {
+
+  private static final NopArtProfileCollectionAdditions INSTANCE =
+      new NopArtProfileCollectionAdditions();
+
+  private NopArtProfileCollectionAdditions() {}
+
+  public static NopArtProfileCollectionAdditions getInstance() {
+    return INSTANCE;
+  }
+
+  @Override
+  public void commit(AppView<?> appView) {
+    // Intentionally empty.
+  }
+
+  @Override
+  boolean isNop() {
+    return true;
+  }
+}
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 ea03644..11a9fb6 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -126,6 +126,7 @@
 import com.android.tools.r8.naming.identifiernamestring.IdentifierNameStringLookupResult;
 import com.android.tools.r8.naming.identifiernamestring.IdentifierNameStringTypeLookupResult;
 import com.android.tools.r8.position.Position;
+import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions;
 import com.android.tools.r8.shaking.AnnotationMatchResult.MatchedAnnotation;
 import com.android.tools.r8.shaking.DelayedRootSetActionItem.InterfaceMethodSyntheticBridgeAction;
 import com.android.tools.r8.shaking.EnqueuerEvent.ClassEnqueuerEvent;
@@ -469,6 +470,8 @@
 
   private final Thread mainThreadForTesting = Thread.currentThread();
 
+  private final ArtProfileCollectionAdditions artProfileCollectionAdditions;
+
   Enqueuer(
       AppView<? extends AppInfoWithClassHierarchy> appView,
       ExecutorService executorService,
@@ -479,6 +482,7 @@
     InternalOptions options = appView.options();
     this.appInfo = appView.appInfo();
     this.appView = appView.withClassHierarchy();
+    this.artProfileCollectionAdditions = ArtProfileCollectionAdditions.create(appView);
     this.deferredTracing = EnqueuerDeferredTracing.create(appView, this, mode);
     this.executorService = executorService;
     this.subtypingInfo = subtypingInfo;
@@ -3673,6 +3677,7 @@
     }
     timing.begin("Create result");
     EnqueuerResult result = createEnqueuerResult(appInfo, timing);
+    artProfileCollectionAdditions.commit(appView);
     timing.end();
     return result;
   }
@@ -4086,6 +4091,7 @@
     CfInstructionDesugaringEventConsumer eventConsumer =
         CfInstructionDesugaringEventConsumer.createForR8(
             appView,
+            artProfileCollectionAdditions,
             lambdaCallback,
             this::recordConstantDynamicSynthesizingContext,
             this::recordTwrCloseResourceMethodSynthesizingContext,
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/ApiOutlineProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/ApiOutlineProfileRewritingTest.java
index f5959e8..599d9c9 100644
--- a/src/test/java/com/android/tools/r8/profile/art/completeness/ApiOutlineProfileRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/ApiOutlineProfileRewritingTest.java
@@ -109,10 +109,14 @@
     MethodSubject apiOutlineMethodSubject = apiOutlineClassSubject.uniqueMethod();
     assertThat(apiOutlineMethodSubject, notIf(isPresent(), isLibraryClassAlwaysPresent()));
 
-    // TODO(b/265729283): When outlining the residual profile should include the outline method and
-    //  its holder.
+    // Verify the residual profile contains the outline method and its holder when present.
     profileInspector
         .assertContainsMethodRule(MethodReferenceUtils.mainMethod(Main.class))
+        .applyIf(
+            !isLibraryClassAlwaysPresent(),
+            i ->
+                i.assertContainsClassRule(apiOutlineClassSubject)
+                    .assertContainsMethodRule(apiOutlineMethodSubject))
         .assertContainsNoOtherRules();
   }
 
diff --git a/src/test/java/com/android/tools/r8/profile/art/utils/ArtProfileInspector.java b/src/test/java/com/android/tools/r8/profile/art/utils/ArtProfileInspector.java
index 507dd67..d498e18 100644
--- a/src/test/java/com/android/tools/r8/profile/art/utils/ArtProfileInspector.java
+++ b/src/test/java/com/android/tools/r8/profile/art/utils/ArtProfileInspector.java
@@ -30,6 +30,13 @@
     this.artProfile = artProfile;
   }
 
+  public ArtProfileInspector applyIf(boolean condition, Consumer<ArtProfileInspector> fn) {
+    if (condition) {
+      fn.accept(this);
+    }
+    return this;
+  }
+
   public ArtProfileInspector assertEmpty() {
     assertEquals(0, artProfile.size());
     return this;