Report timing for each policy in horizontal class merging

Fixes: 168670937
Change-Id: I45987fc1befc61472c6b3ac7b735358ab509d71a
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index b77c8ed..56bdfda 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -496,7 +496,7 @@
           timing.begin("HorizontalClassMerger");
           HorizontalClassMerger merger = new HorizontalClassMerger(appViewWithLiveness);
           HorizontalClassMergerResult horizontalClassMergerResult =
-              merger.run(runtimeTypeCheckInfo);
+              merger.run(runtimeTypeCheckInfo, timing);
           if (horizontalClassMergerResult != null) {
             // Must rewrite AppInfoWithLiveness before pruning the merged classes, to ensure that
             // allocations sites, fields accesses, etc. are correctly transferred to the target
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
index 46df569..7085879 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -40,6 +40,7 @@
 import com.android.tools.r8.shaking.FieldAccessInfoCollectionModifier;
 import com.android.tools.r8.shaking.KeepInfoCollection;
 import com.android.tools.r8.shaking.RuntimeTypeCheckInfo;
+import com.android.tools.r8.utils.Timing;
 import com.google.common.collect.ImmutableList;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -55,14 +56,13 @@
     assert appView.options().enableInlining;
   }
 
-  public HorizontalClassMergerResult run(
-      RuntimeTypeCheckInfo runtimeTypeCheckInfo) {
+  public HorizontalClassMergerResult run(RuntimeTypeCheckInfo runtimeTypeCheckInfo, Timing timing) {
     MergeGroup initialGroup = new MergeGroup(appView.appInfo().classesWithDeterministicOrder());
 
     // Run the policies on all program classes to produce a final grouping.
     List<Policy> policies = getPolicies(runtimeTypeCheckInfo);
     Collection<MergeGroup> groups =
-        new SimplePolicyExecutor().run(Collections.singletonList(initialGroup), policies);
+        new PolicyExecutor().run(Collections.singletonList(initialGroup), policies, timing);
 
     // If there are no groups, then end horizontal class merging.
     if (groups.isEmpty()) {
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/Policy.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/Policy.java
index 4642229..b984ed5 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/Policy.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/Policy.java
@@ -14,6 +14,8 @@
 
   public void clear() {}
 
+  public abstract String getName();
+
   public boolean shouldSkipPolicy() {
     return false;
   }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyExecutor.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyExecutor.java
index b94adf8..33b6f7f 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyExecutor.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyExecutor.java
@@ -4,15 +4,88 @@
 
 package com.android.tools.r8.horizontalclassmerging;
 
+import com.android.tools.r8.utils.IterableUtils;
+import com.android.tools.r8.utils.Timing;
 import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedList;
 
-public abstract class PolicyExecutor {
+/**
+ * This is a simple policy executor that ensures regular sequential execution of policies. It should
+ * primarily be readable and correct. The SimplePolicyExecutor should be a reference implementation,
+ * against which more efficient policy executors can be compared.
+ */
+public class PolicyExecutor {
+
+  // TODO(b/165506334): if performing mutable operation ensure that linked lists are used
+  private void applySingleClassPolicy(SingleClassPolicy policy, LinkedList<MergeGroup> groups) {
+    Iterator<MergeGroup> i = groups.iterator();
+    while (i.hasNext()) {
+      MergeGroup group = i.next();
+      int previousNumberOfClasses = group.size();
+      group.removeIf(clazz -> !policy.canMerge(clazz));
+      policy.numberOfRemovedClasses += previousNumberOfClasses - group.size();
+      if (group.size() < 2) {
+        i.remove();
+      }
+    }
+  }
+
+  private LinkedList<MergeGroup> applyMultiClassPolicy(
+      MultiClassPolicy policy, LinkedList<MergeGroup> groups) {
+    // For each group apply the multi class policy and add all the new groups together.
+    LinkedList<MergeGroup> newGroups = new LinkedList<>();
+    groups.forEach(
+        group -> {
+          int previousNumberOfClasses = group.size();
+          Collection<MergeGroup> policyGroups = policy.apply(group);
+          policyGroups.forEach(newGroup -> newGroup.applyMetadataFrom(group));
+          policy.numberOfRemovedClasses +=
+              previousNumberOfClasses - IterableUtils.sumInt(policyGroups, MergeGroup::size);
+          newGroups.addAll(policyGroups);
+        });
+    return newGroups;
+  }
 
   /**
    * Given an initial collection of class groups which can potentially be merged, run all of the
    * policies registered to this policy executor on the class groups yielding a new collection of
    * class groups.
    */
-  public abstract Collection<MergeGroup> run(
-      Collection<MergeGroup> classes, Collection<Policy> policies);
+  public Collection<MergeGroup> run(
+      Collection<MergeGroup> inputGroups, Collection<Policy> policies, Timing timing) {
+    LinkedList<MergeGroup> linkedGroups;
+
+    if (inputGroups instanceof LinkedList) {
+      linkedGroups = (LinkedList<MergeGroup>) inputGroups;
+    } else {
+      linkedGroups = new LinkedList<>(inputGroups);
+    }
+
+    for (Policy policy : policies) {
+      if (policy.shouldSkipPolicy()) {
+        continue;
+      }
+
+      timing.begin(policy.getName());
+      if (policy instanceof SingleClassPolicy) {
+        applySingleClassPolicy((SingleClassPolicy) policy, linkedGroups);
+      } else {
+        assert policy instanceof MultiClassPolicy;
+        linkedGroups = applyMultiClassPolicy((MultiClassPolicy) policy, linkedGroups);
+      }
+      timing.end();
+
+      policy.clear();
+
+      if (linkedGroups.isEmpty()) {
+        break;
+      }
+
+      // Any policy should not return any trivial groups.
+      assert linkedGroups.stream().allMatch(group -> group.size() >= 2);
+    }
+
+    return linkedGroups;
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/SimplePolicyExecutor.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/SimplePolicyExecutor.java
deleted file mode 100644
index 3b96a76..0000000
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/SimplePolicyExecutor.java
+++ /dev/null
@@ -1,85 +0,0 @@
-// Copyright (c) 2020, 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.horizontalclassmerging;
-
-import com.android.tools.r8.utils.IterableUtils;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.LinkedList;
-
-/**
- * This is a simple policy executor that ensures regular sequential execution of policies. It should
- * primarily be readable and correct. The SimplePolicyExecutor should be a reference implementation,
- * against which more efficient policy executors can be compared.
- */
-public class SimplePolicyExecutor extends PolicyExecutor {
-
-  // TODO(b/165506334): if performing mutable operation ensure that linked lists are used
-  private LinkedList<MergeGroup> applySingleClassPolicy(
-      SingleClassPolicy policy, LinkedList<MergeGroup> groups) {
-    Iterator<MergeGroup> i = groups.iterator();
-    while (i.hasNext()) {
-      MergeGroup group = i.next();
-      int previousNumberOfClasses = group.size();
-      group.removeIf(clazz -> !policy.canMerge(clazz));
-      policy.numberOfRemovedClasses += previousNumberOfClasses - group.size();
-      if (group.size() < 2) {
-        i.remove();
-      }
-    }
-    return groups;
-  }
-
-  private LinkedList<MergeGroup> applyMultiClassPolicy(
-      MultiClassPolicy policy, LinkedList<MergeGroup> groups) {
-    // For each group apply the multi class policy and add all the new groups together.
-    LinkedList<MergeGroup> newGroups = new LinkedList<>();
-    groups.forEach(
-        group -> {
-          int previousNumberOfClasses = group.size();
-          Collection<MergeGroup> policyGroups = policy.apply(group);
-          policyGroups.forEach(newGroup -> newGroup.applyMetadataFrom(group));
-          policy.numberOfRemovedClasses +=
-              previousNumberOfClasses - IterableUtils.sumInt(policyGroups, MergeGroup::size);
-          newGroups.addAll(policyGroups);
-        });
-    return newGroups;
-  }
-
-  @Override
-  public Collection<MergeGroup> run(
-      Collection<MergeGroup> inputGroups, Collection<Policy> policies) {
-    LinkedList<MergeGroup> linkedGroups;
-
-    if (inputGroups instanceof LinkedList) {
-      linkedGroups = (LinkedList<MergeGroup>) inputGroups;
-    } else {
-      linkedGroups = new LinkedList<>(inputGroups);
-    }
-
-    for (Policy policy : policies) {
-      if (policy.shouldSkipPolicy()) {
-        continue;
-      }
-
-      if (policy instanceof SingleClassPolicy) {
-        linkedGroups = applySingleClassPolicy((SingleClassPolicy) policy, linkedGroups);
-      } else if (policy instanceof MultiClassPolicy) {
-        linkedGroups = applyMultiClassPolicy((MultiClassPolicy) policy, linkedGroups);
-      }
-
-      policy.clear();
-
-      if (linkedGroups.isEmpty()) {
-        break;
-      }
-
-      // Any policy should not return any trivial groups.
-      assert linkedGroups.stream().allMatch(group -> group.size() >= 2);
-    }
-
-    return linkedGroups;
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/AllInstantiatedOrUninstantiated.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/AllInstantiatedOrUninstantiated.java
index 75d61d9..38150db 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/AllInstantiatedOrUninstantiated.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/AllInstantiatedOrUninstantiated.java
@@ -21,4 +21,9 @@
   public Boolean getMergeKey(DexProgramClass clazz) {
     return appView.appInfo().isInstantiatedDirectlyOrIndirectly(clazz);
   }
+
+  @Override
+  public String getName() {
+    return "AllInstantiatedOrUninstantiated";
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/CheckAbstractClasses.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/CheckAbstractClasses.java
index 6e5c711..e78b196 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/CheckAbstractClasses.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/CheckAbstractClasses.java
@@ -25,6 +25,11 @@
   }
 
   @Override
+  public String getName() {
+    return "CheckAbstractClasses";
+  }
+
+  @Override
   public boolean shouldSkipPolicy() {
     // We can just make the target class non-abstract if one of the classes in the group
     // is non-abstract.
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/DontInlinePolicy.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/DontInlinePolicy.java
index f7d121a..e6e67e0 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/DontInlinePolicy.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/DontInlinePolicy.java
@@ -12,16 +12,14 @@
 import com.android.tools.r8.horizontalclassmerging.SingleClassPolicy;
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.shaking.MainDexInfo;
 import com.google.common.collect.Iterables;
 
 public class DontInlinePolicy extends SingleClassPolicy {
+
   private final AppView<AppInfoWithLiveness> appView;
-  private final MainDexInfo mainDexInfo;
 
   public DontInlinePolicy(AppView<AppInfoWithLiveness> appView) {
     this.appView = appView;
-    this.mainDexInfo = appView.appInfo().getMainDexInfo();
   }
 
   private boolean disallowInlining(ProgramMethod method) {
@@ -53,4 +51,9 @@
         program.directProgramMethods(),
         method -> method.getDefinition().isInstanceInitializer() && disallowInlining(method));
   }
+
+  @Override
+  public String getName() {
+    return "DontInlinePolicy";
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/DontMergeSynchronizedClasses.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/DontMergeSynchronizedClasses.java
index 4372869..e1cb9a7 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/DontMergeSynchronizedClasses.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/DontMergeSynchronizedClasses.java
@@ -56,4 +56,9 @@
 
     return synchronizedGroups;
   }
+
+  @Override
+  public String getName() {
+    return "DontMergeSynchronizedClasses";
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/LimitGroups.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/LimitGroups.java
index 33de8f3..80e1292 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/LimitGroups.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/LimitGroups.java
@@ -52,4 +52,9 @@
     newGroups.add(newGroup);
     return newGroup;
   }
+
+  @Override
+  public String getName() {
+    return "LimitGroups";
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/MinimizeFieldCasts.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/MinimizeFieldCasts.java
index 73e0786..1ef2f6a 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/MinimizeFieldCasts.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/MinimizeFieldCasts.java
@@ -70,4 +70,9 @@
     }
     return fieldTypes;
   }
+
+  @Override
+  public String getName() {
+    return "MinimizeFieldCasts";
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoAnnotationClasses.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoAnnotationClasses.java
index 1effb8b..9943010 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoAnnotationClasses.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoAnnotationClasses.java
@@ -12,4 +12,9 @@
   public boolean canMerge(DexProgramClass program) {
     return !program.isAnnotation();
   }
+
+  @Override
+  public String getName() {
+    return "NoAnnotationClasses";
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoClassAnnotationCollisions.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoClassAnnotationCollisions.java
index 9d3d730..1a96901 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoClassAnnotationCollisions.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoClassAnnotationCollisions.java
@@ -41,4 +41,9 @@
     }
     return removeTrivialGroups(newGroups);
   }
+
+  @Override
+  public String getName() {
+    return "NoClassAnnotationCollisions";
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoClassInitializerWithObservableSideEffects.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoClassInitializerWithObservableSideEffects.java
index f10d78c..b156a2e 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoClassInitializerWithObservableSideEffects.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoClassInitializerWithObservableSideEffects.java
@@ -28,4 +28,9 @@
     return program.getKotlinInfo().isSyntheticClass()
         && program.getKotlinInfo().asSyntheticClass().isLambda();
   }
+
+  @Override
+  public String getName() {
+    return "NoClassInitializerWithObservableSideEffects";
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDirectRuntimeTypeChecks.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDirectRuntimeTypeChecks.java
index b0f3766..17659f8 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDirectRuntimeTypeChecks.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDirectRuntimeTypeChecks.java
@@ -19,4 +19,9 @@
   public boolean canMerge(DexProgramClass clazz) {
     return !runtimeTypeCheckInfo.isRuntimeCheckType(clazz);
   }
+
+  @Override
+  public String getName() {
+    return "NoDirectRuntimeTypeChecks";
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoEnums.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoEnums.java
index c8a0aab..8bea8a4 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoEnums.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoEnums.java
@@ -27,6 +27,11 @@
   }
 
   @Override
+  public String getName() {
+    return "NoEnums";
+  }
+
+  @Override
   public boolean canMerge(DexProgramClass program) {
     if (program.isEnum()) {
       return false;
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoIndirectRuntimeTypeChecks.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoIndirectRuntimeTypeChecks.java
index 7446787..8de67d6 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoIndirectRuntimeTypeChecks.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoIndirectRuntimeTypeChecks.java
@@ -65,4 +65,9 @@
     }
     return false;
   }
+
+  @Override
+  public String getName() {
+    return "NoIndirectRuntimeTypeChecks";
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInnerClasses.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInnerClasses.java
index cc9f61c..1d26825 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInnerClasses.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInnerClasses.java
@@ -14,4 +14,9 @@
     // TODO(b/179018501): allow merging classes with inner/outer classes.
     return program.getInnerClasses().isEmpty();
   }
+
+  @Override
+  public String getName() {
+    return "NoInnerClasses";
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInstanceFieldAnnotations.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInstanceFieldAnnotations.java
index 610af7e..229c062 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInstanceFieldAnnotations.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInstanceFieldAnnotations.java
@@ -19,4 +19,9 @@
     }
     return true;
   }
+
+  @Override
+  public String getName() {
+    return "NoInstanceFieldAnnotations";
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInterfaces.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInterfaces.java
index 63df0de..d6032e7 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInterfaces.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInterfaces.java
@@ -13,4 +13,9 @@
   public boolean canMerge(DexProgramClass program) {
     return !program.isInterface();
   }
+
+  @Override
+  public String getName() {
+    return "NoInterfaces";
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoKeepRules.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoKeepRules.java
index e0a39eb..ec8a5aa 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoKeepRules.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoKeepRules.java
@@ -49,4 +49,9 @@
   public boolean canMerge(DexProgramClass program) {
     return !dontMergeTypes.contains(program.getType());
   }
+
+  @Override
+  public String getName() {
+    return "NoKeepRules";
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoKotlinMetadata.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoKotlinMetadata.java
index 1acad7a..7783f20 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoKotlinMetadata.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoKotlinMetadata.java
@@ -29,4 +29,9 @@
         .allMatch(member -> member.getKotlinMemberInfo().isNoKotlinInformation());
     return true;
   }
+
+  @Override
+  public String getName() {
+    return "NoKotlinMetadata";
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoNativeMethods.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoNativeMethods.java
index f1a278c..a32d083 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoNativeMethods.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoNativeMethods.java
@@ -14,4 +14,9 @@
   public boolean canMerge(DexProgramClass program) {
     return !Iterables.any(program.methods(), DexEncodedMethod::isNative);
   }
+
+  @Override
+  public String getName() {
+    return "NoNativeMethods";
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoServiceLoaders.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoServiceLoaders.java
index 1c6ae77..b1f0d99 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoServiceLoaders.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoServiceLoaders.java
@@ -26,4 +26,9 @@
     return !appView.appServices().allServiceTypes().contains(program.getType())
         && !allServiceImplementations.contains(program.getType());
   }
+
+  @Override
+  public String getName() {
+    return "NoServiceLoaders";
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NotMatchedByNoHorizontalClassMerging.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NotMatchedByNoHorizontalClassMerging.java
index bfe0f2a..8751afb 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NotMatchedByNoHorizontalClassMerging.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NotMatchedByNoHorizontalClassMerging.java
@@ -30,4 +30,9 @@
     return !deadEnumLiteMaps.contains(program.getType())
         && !appView.appInfo().isNoHorizontalClassMergingOfType(program.getType());
   }
+
+  @Override
+  public String getName() {
+    return "NotMatchedByNoHorizontalClassMerging";
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NotVerticallyMergedIntoSubtype.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NotVerticallyMergedIntoSubtype.java
index 67afbc3..92d50d1 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NotVerticallyMergedIntoSubtype.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NotVerticallyMergedIntoSubtype.java
@@ -23,4 +23,9 @@
     }
     return !appView.verticallyMergedClasses().hasBeenMergedIntoSubtype(program.type);
   }
+
+  @Override
+  public String getName() {
+    return "NotVerticallyMergedIntoSubtype";
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreserveMethodCharacteristics.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreserveMethodCharacteristics.java
index 4a789ba..c8bb5c6 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreserveMethodCharacteristics.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreserveMethodCharacteristics.java
@@ -28,6 +28,11 @@
  */
 public class PreserveMethodCharacteristics extends MultiClassPolicy {
 
+  @Override
+  public String getName() {
+    return "PreserveMethodCharacteristics";
+  }
+
   static class MethodCharacteristics {
 
     private final MethodAccessFlags accessFlags;
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventMergeIntoDifferentMainDexGroups.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventMergeIntoDifferentMainDexGroups.java
index 41c240f..38df5cf 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventMergeIntoDifferentMainDexGroups.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventMergeIntoDifferentMainDexGroups.java
@@ -29,4 +29,9 @@
         ? mainDexInfo.getMergeKey(clazz, synthetics)
         : ineligibleForClassMerging();
   }
+
+  @Override
+  public String getName() {
+    return "PreventMergeIntoDifferentMainDexGroups";
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventMethodImplementation.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventMethodImplementation.java
index c7c57c8..b182330 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventMethodImplementation.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventMethodImplementation.java
@@ -59,6 +59,11 @@
   private final ReservedInterfaceSignaturesFor reservedInterfaceSignaturesFor =
       new ReservedInterfaceSignaturesFor();
 
+  @Override
+  public String getName() {
+    return "PreventMethodImplementation";
+  }
+
   private abstract static class SignaturesCache<C extends DexClass> {
     private final Map<DexClass, DexMethodSignatureSet> memoizedSignatures = new IdentityHashMap<>();
 
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/RespectPackageBoundaries.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/RespectPackageBoundaries.java
index 914fc57..78cfe13 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/RespectPackageBoundaries.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/RespectPackageBoundaries.java
@@ -96,4 +96,9 @@
     groups.addAll(restrictedClasses.values());
     return groups;
   }
+
+  @Override
+  public String getName() {
+    return "RespectPackageBoundaries";
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameFeatureSplit.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameFeatureSplit.java
index 20caa50..b3ec017 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameFeatureSplit.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameFeatureSplit.java
@@ -24,4 +24,9 @@
         .getClassToFeatureSplitMap()
         .getFeatureSplit(clazz, appView.getSyntheticItems());
   }
+
+  @Override
+  public String getName() {
+    return "SameFeatureSplit";
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameInstanceFields.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameInstanceFields.java
index 13e0388..291aae5 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameInstanceFields.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameInstanceFields.java
@@ -34,6 +34,11 @@
     return fields;
   }
 
+  @Override
+  public String getName() {
+    return "SameInstanceFields";
+  }
+
   public static class InstanceFieldInfo {
 
     private final FieldAccessFlags accessFlags;
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameNestHost.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameNestHost.java
index 88d2984..fc8e39c 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameNestHost.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameNestHost.java
@@ -23,4 +23,9 @@
   public DexType getMergeKey(DexProgramClass clazz) {
     return clazz.isInANest() ? clazz.getNestHost() : dexItemFactory.objectType;
   }
+
+  @Override
+  public String getName() {
+    return "SameNestHost";
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameParentClass.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameParentClass.java
index 12c8e31..3afa656 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameParentClass.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameParentClass.java
@@ -14,4 +14,9 @@
   public DexType getMergeKey(DexProgramClass clazz) {
     return clazz.superType;
   }
+
+  @Override
+  public String getName() {
+    return "SameParentClass";
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SyntheticItemsPolicy.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SyntheticItemsPolicy.java
index b012897..cbc9b3d 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SyntheticItemsPolicy.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SyntheticItemsPolicy.java
@@ -48,4 +48,9 @@
     // Java lambda merging is disabled.
     return ineligibleForClassMerging();
   }
+
+  @Override
+  public String getName() {
+    return "SyntheticItemsPolicy";
+  }
 }