Filter out common uses of const-class values from lock candidate set

Change-Id: If413ecc4104f5bdbb722cf0f88d0ed7093014d6b
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfCheckCast.java b/src/main/java/com/android/tools/r8/cf/code/CfCheckCast.java
index 4661e68..7af534e 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfCheckCast.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfCheckCast.java
@@ -21,6 +21,7 @@
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.InliningConstraints;
 import com.android.tools.r8.naming.NamingLens;
+import java.util.ListIterator;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
 
@@ -66,7 +67,8 @@
   }
 
   @Override
-  void internalRegisterUse(UseRegistry registry, DexClassAndMethod context) {
+  void internalRegisterUse(
+      UseRegistry registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) {
     registry.registerCheckCast(type);
   }
 
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstClass.java b/src/main/java/com/android/tools/r8/cf/code/CfConstClass.java
index 51f34fc..9d4ffa0 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstClass.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstClass.java
@@ -21,6 +21,7 @@
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.InliningConstraints;
 import com.android.tools.r8.naming.NamingLens;
+import java.util.ListIterator;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Type;
 
@@ -97,8 +98,9 @@
   }
 
   @Override
-  void internalRegisterUse(UseRegistry registry, DexClassAndMethod context) {
-    registry.registerConstClass(type);
+  void internalRegisterUse(
+      UseRegistry registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) {
+    registry.registerConstClass(type, iterator);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java
index 259a711..37d0ff4 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java
@@ -22,6 +22,7 @@
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.InliningConstraints;
 import com.android.tools.r8.naming.NamingLens;
+import java.util.ListIterator;
 import org.objectweb.asm.MethodVisitor;
 
 public class CfConstMethodHandle extends CfInstruction {
@@ -68,7 +69,8 @@
   }
 
   @Override
-  void internalRegisterUse(UseRegistry registry, DexClassAndMethod context) {
+  void internalRegisterUse(
+      UseRegistry registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) {
     registry.registerMethodHandle(handle, MethodHandleUse.NOT_ARGUMENT_TO_LAMBDA_METAFACTORY);
   }
 
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java
index 70417f6..b6a560f 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java
@@ -21,6 +21,7 @@
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.InliningConstraints;
 import com.android.tools.r8.naming.NamingLens;
+import java.util.ListIterator;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Type;
 
@@ -66,7 +67,8 @@
   }
 
   @Override
-  void internalRegisterUse(UseRegistry registry, DexClassAndMethod context) {
+  void internalRegisterUse(
+      UseRegistry registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) {
     registry.registerProto(type);
   }
 
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfDexItemBasedConstString.java b/src/main/java/com/android/tools/r8/cf/code/CfDexItemBasedConstString.java
index e9f11a8..b42194f 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfDexItemBasedConstString.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfDexItemBasedConstString.java
@@ -23,6 +23,7 @@
 import com.android.tools.r8.ir.optimize.InliningConstraints;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.naming.dexitembasedstring.NameComputationInfo;
+import java.util.ListIterator;
 import org.objectweb.asm.MethodVisitor;
 
 public class CfDexItemBasedConstString extends CfInstruction {
@@ -89,7 +90,8 @@
   }
 
   @Override
-  void internalRegisterUse(UseRegistry registry, DexClassAndMethod context) {
+  void internalRegisterUse(
+      UseRegistry registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) {
     if (nameComputationInfo.needsToRegisterReference()) {
       assert item.isDexType();
       registry.registerTypeReference(item.asDexType());
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
index 7c28eff..9b463fd 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
@@ -24,6 +24,7 @@
 import com.android.tools.r8.ir.optimize.InliningConstraints;
 import com.android.tools.r8.naming.NamingLens;
 import java.util.Comparator;
+import java.util.ListIterator;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
 
@@ -94,7 +95,8 @@
   }
 
   @Override
-  void internalRegisterUse(UseRegistry registry, DexClassAndMethod context) {
+  void internalRegisterUse(
+      UseRegistry registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) {
     switch (opcode) {
       case Opcodes.GETFIELD:
         registry.registerInstanceFieldRead(field);
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInitClass.java b/src/main/java/com/android/tools/r8/cf/code/CfInitClass.java
index e9c06e0..2c81fce 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInitClass.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInitClass.java
@@ -21,6 +21,7 @@
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.InliningConstraints;
 import com.android.tools.r8.naming.NamingLens;
+import java.util.ListIterator;
 import org.objectweb.asm.MethodVisitor;
 
 public class CfInitClass extends CfInstruction {
@@ -77,7 +78,8 @@
   }
 
   @Override
-  void internalRegisterUse(UseRegistry registry, DexClassAndMethod context) {
+  void internalRegisterUse(
+      UseRegistry registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) {
     registry.registerInitClass(clazz);
   }
 
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInstanceOf.java b/src/main/java/com/android/tools/r8/cf/code/CfInstanceOf.java
index 1300458..899ef31 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInstanceOf.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInstanceOf.java
@@ -20,6 +20,7 @@
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.InliningConstraints;
 import com.android.tools.r8.naming.NamingLens;
+import java.util.ListIterator;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
 
@@ -75,7 +76,8 @@
   }
 
   @Override
-  void internalRegisterUse(UseRegistry registry, DexClassAndMethod context) {
+  void internalRegisterUse(
+      UseRegistry registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) {
     registry.registerInstanceOf(type);
   }
 
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
index b8874e8..3c36d33 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8.cf.code;
 
 import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.code.CfOrDexInstruction;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.CfCompareHelper;
 import com.android.tools.r8.graph.ClasspathMethod;
@@ -21,9 +22,10 @@
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.InliningConstraints;
 import com.android.tools.r8.naming.NamingLens;
+import java.util.ListIterator;
 import org.objectweb.asm.MethodVisitor;
 
-public abstract class CfInstruction {
+public abstract class CfInstruction implements CfOrDexInstruction {
 
   public abstract void write(
       AppView<?> appView,
@@ -72,15 +74,18 @@
     return printer.toString();
   }
 
-  public void registerUse(UseRegistry registry, ProgramMethod context) {
-    internalRegisterUse(registry, context);
+  public void registerUse(
+      UseRegistry registry, ProgramMethod context, ListIterator<CfInstruction> iterator) {
+    internalRegisterUse(registry, context, iterator);
   }
 
-  public void registerUseForDesugaring(UseRegistry registry, ClasspathMethod context) {
-    internalRegisterUse(registry, context);
+  public void registerUseForDesugaring(
+      UseRegistry registry, ClasspathMethod context, ListIterator<CfInstruction> iterator) {
+    internalRegisterUse(registry, context, iterator);
   }
 
-  void internalRegisterUse(UseRegistry registry, DexClassAndMethod context) {
+  void internalRegisterUse(
+      UseRegistry registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) {
     // Intentionally empty.
   }
 
@@ -88,6 +93,16 @@
     return null;
   }
 
+  @Override
+  public CfInstruction asCfInstruction() {
+    return this;
+  }
+
+  @Override
+  public boolean isCfInstruction() {
+    return true;
+  }
+
   public CfConstString asConstString() {
     return null;
   }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
index 4765be5..4f5159b 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
@@ -34,6 +34,7 @@
 import com.android.tools.r8.ir.optimize.InliningConstraints;
 import com.android.tools.r8.naming.NamingLens;
 import java.util.Arrays;
+import java.util.ListIterator;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
 
@@ -111,7 +112,8 @@
   }
 
   @Override
-  void internalRegisterUse(UseRegistry registry, DexClassAndMethod context) {
+  void internalRegisterUse(
+      UseRegistry registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) {
     Type invokeType = getInvokeType(context);
     switch (invokeType) {
       case DIRECT:
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java b/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
index add492c..92384c8 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
@@ -28,6 +28,7 @@
 import com.android.tools.r8.naming.NamingLens;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.ListIterator;
 import org.objectweb.asm.Handle;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
@@ -112,7 +113,8 @@
   }
 
   @Override
-  void internalRegisterUse(UseRegistry registry, DexClassAndMethod context) {
+  void internalRegisterUse(
+      UseRegistry registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) {
     registry.registerCallSite(callSite);
   }
 
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java b/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java
index 5f405d3..d52e429 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java
@@ -22,6 +22,7 @@
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.utils.InternalOptions;
 import java.util.Comparator;
+import java.util.ListIterator;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
 
@@ -75,7 +76,8 @@
   }
 
   @Override
-  void internalRegisterUse(UseRegistry registry, DexClassAndMethod context) {
+  void internalRegisterUse(
+      UseRegistry registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) {
     registry.registerTypeReference(type);
   }
 
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNew.java b/src/main/java/com/android/tools/r8/cf/code/CfNew.java
index 60da1b2..a4be181 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfNew.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfNew.java
@@ -21,6 +21,7 @@
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.InliningConstraints;
 import com.android.tools.r8.naming.NamingLens;
+import java.util.ListIterator;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
 
@@ -66,7 +67,8 @@
   }
 
   @Override
-  void internalRegisterUse(UseRegistry registry, DexClassAndMethod context) {
+  void internalRegisterUse(
+      UseRegistry registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) {
     registry.registerNewInstance(type);
   }
 
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNewArray.java b/src/main/java/com/android/tools/r8/cf/code/CfNewArray.java
index 30671bd..794227e 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfNewArray.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfNewArray.java
@@ -23,6 +23,7 @@
 import com.android.tools.r8.ir.optimize.InliningConstraints;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.utils.DescriptorUtils;
+import java.util.ListIterator;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
 
@@ -112,7 +113,8 @@
   }
 
   @Override
-  void internalRegisterUse(UseRegistry registry, DexClassAndMethod context) {
+  void internalRegisterUse(
+      UseRegistry registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) {
     if (!type.isPrimitiveArrayType()) {
       registry.registerTypeReference(type);
     }
diff --git a/src/main/java/com/android/tools/r8/code/CfOrDexInstruction.java b/src/main/java/com/android/tools/r8/code/CfOrDexInstruction.java
new file mode 100644
index 0000000..e85c75c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/CfOrDexInstruction.java
@@ -0,0 +1,14 @@
+// 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.code;
+
+import com.android.tools.r8.cf.code.CfInstruction;
+
+public interface CfOrDexInstruction {
+
+  CfInstruction asCfInstruction();
+
+  boolean isCfInstruction();
+}
diff --git a/src/main/java/com/android/tools/r8/code/ConstClass.java b/src/main/java/com/android/tools/r8/code/ConstClass.java
index 3c6d03e..5562a69 100644
--- a/src/main/java/com/android/tools/r8/code/ConstClass.java
+++ b/src/main/java/com/android/tools/r8/code/ConstClass.java
@@ -72,7 +72,7 @@
 
   @Override
   public void registerUse(UseRegistry registry) {
-    registry.registerConstClass(getType());
+    registry.registerConstClass(getType(), null);
   }
 
   public DexType getType() {
diff --git a/src/main/java/com/android/tools/r8/code/Instruction.java b/src/main/java/com/android/tools/r8/code/Instruction.java
index 4ff81dc..52aa90d 100644
--- a/src/main/java/com/android/tools/r8/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/code/Instruction.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.code;
 
+import com.android.tools.r8.cf.code.CfInstruction;
 import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.errors.InternalCompilerError;
 import com.android.tools.r8.graph.DexCallSite;
@@ -21,7 +22,7 @@
 import java.nio.ShortBuffer;
 import java.util.function.BiPredicate;
 
-public abstract class Instruction implements Comparable<Instruction> {
+public abstract class Instruction implements CfOrDexInstruction, Comparable<Instruction> {
   public static final Instruction[] EMPTY_ARRAY = {};
 
   public final static int[] NO_TARGETS = null;
@@ -134,6 +135,16 @@
     this.offset = offset;
   }
 
+  @Override
+  public CfInstruction asCfInstruction() {
+    return null;
+  }
+
+  @Override
+  public boolean isCfInstruction() {
+    return false;
+  }
+
   public CheckCast asCheckCast() {
     return null;
   }
diff --git a/src/main/java/com/android/tools/r8/graph/CfCode.java b/src/main/java/com/android/tools/r8/graph/CfCode.java
index dbefe85..d6ae59f 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -56,6 +56,7 @@
 import java.util.Comparator;
 import java.util.IdentityHashMap;
 import java.util.List;
+import java.util.ListIterator;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.function.BiPredicate;
@@ -511,16 +512,20 @@
 
   @Override
   public void registerCodeReferences(ProgramMethod method, UseRegistry registry) {
-    for (CfInstruction instruction : instructions) {
-      instruction.registerUse(registry, method);
+    ListIterator<CfInstruction> iterator = instructions.listIterator();
+    while (iterator.hasNext()) {
+      CfInstruction instruction = iterator.next();
+      instruction.registerUse(registry, method, iterator);
     }
     tryCatchRanges.forEach(tryCatch -> tryCatch.internalRegisterUse(registry, method));
   }
 
   @Override
   public void registerCodeReferencesForDesugaring(ClasspathMethod method, UseRegistry registry) {
-    for (CfInstruction instruction : instructions) {
-      instruction.registerUseForDesugaring(registry, method);
+    ListIterator<CfInstruction> iterator = instructions.listIterator();
+    while (iterator.hasNext()) {
+      CfInstruction instruction = iterator.next();
+      instruction.registerUseForDesugaring(registry, method, iterator);
     }
     tryCatchRanges.forEach(tryCatch -> tryCatch.guards.forEach(registry::registerTypeReference));
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 13c4ac4..44314e5 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -29,6 +29,7 @@
 import com.android.tools.r8.ir.desugar.NestBasedAccessDesugaring;
 import com.android.tools.r8.kotlin.Kotlin;
 import com.android.tools.r8.utils.ArrayUtils;
+import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.LRUCacheTable;
 import com.google.common.base.Strings;
 import com.google.common.collect.BiMap;
@@ -343,6 +344,7 @@
       createStaticallyKnownType(referenceFieldUpdaterDescriptor);
 
   public final DexType classType = createStaticallyKnownType(classDescriptor);
+  public final DexType packageType = createStaticallyKnownType(Package.class);
   public final DexType classLoaderType = createStaticallyKnownType(classLoaderDescriptor);
   public final DexType fieldType = createStaticallyKnownType(fieldDescriptor);
   public final DexType methodType = createStaticallyKnownType(methodDescriptor);
@@ -1160,6 +1162,8 @@
     public final DexMethod desiredAssertionStatus;
     public final DexMethod forName;
     public final DexMethod forName3;
+    public final DexMethod getClassLoader =
+        createMethod(classType, createProto(classLoaderType), "getClassLoader");
     public final DexMethod getName;
     public final DexMethod getCanonicalName;
     public final DexMethod getSimpleName;
@@ -1169,6 +1173,8 @@
     public final DexMethod getDeclaredField;
     public final DexMethod getMethod;
     public final DexMethod getDeclaredMethod;
+    public final DexMethod getPackage =
+        createMethod(classType, createProto(packageType), "getPackage");
     public final DexMethod newInstance;
     private final Set<DexMethod> getMembers;
     public final Set<DexMethod> getNames;
@@ -1926,6 +1932,11 @@
     return createStaticallyKnownType(createString(descriptor));
   }
 
+  private DexType createStaticallyKnownType(Class<?> clazz) {
+    return createStaticallyKnownType(
+        createString(DescriptorUtils.javaTypeToDescriptor(clazz.getTypeName())));
+  }
+
   private DexType createStaticallyKnownType(DexString descriptor) {
     DexType type = internalCreateType(descriptor);
     // Conservatively add all statically known types to "compiler synthesized types set".
diff --git a/src/main/java/com/android/tools/r8/graph/UseRegistry.java b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
index 4a3327a..8f64090 100644
--- a/src/main/java/com/android/tools/r8/graph/UseRegistry.java
+++ b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
@@ -3,6 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.graph;
 
+import com.android.tools.r8.code.CfOrDexInstruction;
+import java.util.ListIterator;
 
 public abstract class UseRegistry {
 
@@ -67,7 +69,8 @@
 
   public abstract void registerInstanceOf(DexType type);
 
-  public void registerConstClass(DexType type) {
+  public void registerConstClass(
+      DexType type, ListIterator<? extends CfOrDexInstruction> iterator) {
     registerTypeReference(type);
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoEnqueuerUseRegistry.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoEnqueuerUseRegistry.java
index 2ef11c9..7dca039 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoEnqueuerUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoEnqueuerUseRegistry.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.ir.analysis.proto;
 
+import com.android.tools.r8.code.CfOrDexInstruction;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexType;
@@ -12,6 +13,7 @@
 import com.android.tools.r8.shaking.DefaultEnqueuerUseRegistry;
 import com.android.tools.r8.shaking.Enqueuer;
 import com.android.tools.r8.shaking.EnqueuerUseRegistryFactory;
+import java.util.ListIterator;
 
 public class ProtoEnqueuerUseRegistry extends DefaultEnqueuerUseRegistry {
 
@@ -30,19 +32,20 @@
   }
 
   /**
-   * Unlike {@link DefaultEnqueuerUseRegistry#registerConstClass(DexType)}, this method does not
-   * trace any const-class instructions in every implementation of dynamicMethod().
+   * Unlike {@link DefaultEnqueuerUseRegistry#registerConstClass(DexType, ListIterator)}, this
+   * method does not trace any const-class instructions in every implementation of dynamicMethod().
    *
    * <p>The const-class instructions that remain after the proto schema has been optimized will be
    * traced manually by {@link ProtoEnqueuerExtension#tracePendingInstructionsInDynamicMethods}.
    */
   @Override
-  public void registerConstClass(DexType type) {
+  public void registerConstClass(
+      DexType type, ListIterator<? extends CfOrDexInstruction> iterator) {
     if (references.isDynamicMethod(getContextMethod())) {
       enqueuer.addDeadProtoTypeCandidate(type);
       return;
     }
-    super.registerConstClass(type);
+    super.registerConstClass(type, iterator);
   }
 
   /**
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 a81b5dc..3a716e3 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -165,8 +165,10 @@
   private final Set<DexType> noHorizontalClassMerging;
   private final Set<DexType> noStaticClassMerging;
 
-  /** Set of const-class references. */
-  public final Set<DexType> constClassReferences;
+  /**
+   * Set of lock candidates (i.e., types whose class reference may flow to a monitor instruction).
+   */
+  public final Set<DexType> lockCandidates;
   /**
    * A map from seen init-class references to the minimum required visibility of the corresponding
    * static field.
@@ -234,7 +236,7 @@
       Set<DexType> prunedTypes,
       Map<DexField, Int2ReferenceMap<DexField>> switchMaps,
       EnumValueInfoMapCollection enumValueInfoMaps,
-      Set<DexType> constClassReferences,
+      Set<DexType> lockCandidates,
       Map<DexType, Visibility> initClassReferences) {
     super(syntheticItems, classToFeatureSplitMap, mainDexClasses);
     this.deadProtoTypes = deadProtoTypes;
@@ -273,7 +275,7 @@
     this.prunedTypes = prunedTypes;
     this.switchMaps = switchMaps;
     this.enumValueInfoMaps = enumValueInfoMaps;
-    this.constClassReferences = constClassReferences;
+    this.lockCandidates = lockCandidates;
     this.initClassReferences = initClassReferences;
   }
 
@@ -315,7 +317,7 @@
       Set<DexType> prunedTypes,
       Map<DexField, Int2ReferenceMap<DexField>> switchMaps,
       EnumValueInfoMapCollection enumValueInfoMaps,
-      Set<DexType> constClassReferences,
+      Set<DexType> lockCandidates,
       Map<DexType, Visibility> initClassReferences) {
     super(
         appInfoWithClassHierarchy.getSyntheticItems().commit(appInfoWithClassHierarchy.app()),
@@ -357,7 +359,7 @@
     this.prunedTypes = prunedTypes;
     this.switchMaps = switchMaps;
     this.enumValueInfoMaps = enumValueInfoMaps;
-    this.constClassReferences = constClassReferences;
+    this.lockCandidates = lockCandidates;
     this.initClassReferences = initClassReferences;
   }
 
@@ -404,7 +406,7 @@
         previous.prunedTypes,
         previous.switchMaps,
         previous.enumValueInfoMaps,
-        previous.constClassReferences,
+        previous.lockCandidates,
         previous.initClassReferences);
   }
 
@@ -457,7 +459,7 @@
             : CollectionUtils.mergeSets(previous.prunedTypes, removedClasses),
         previous.switchMaps,
         previous.enumValueInfoMaps,
-        previous.constClassReferences,
+        previous.lockCandidates,
         previous.initClassReferences);
     assert keepInfo.verifyNoneArePinned(removedClasses, previous);
   }
@@ -543,7 +545,7 @@
     this.prunedTypes = previous.prunedTypes;
     this.switchMaps = switchMaps;
     this.enumValueInfoMaps = enumValueInfoMaps;
-    this.constClassReferences = previous.constClassReferences;
+    this.lockCandidates = previous.lockCandidates;
     this.initClassReferences = previous.initClassReferences;
     previous.markObsolete();
   }
@@ -700,7 +702,7 @@
    * merge any const-class classes. More info at b/142438687.
    */
   public boolean isLockCandidate(DexType type) {
-    return constClassReferences.contains(type);
+    return lockCandidates.contains(type);
   }
 
   public Set<DexType> getDeadProtoTypes() {
@@ -1042,7 +1044,7 @@
         prunedTypes,
         lens.rewriteFieldKeys(switchMaps),
         enumValueInfoMaps.rewrittenWithLens(lens),
-        lens.rewriteTypes(constClassReferences),
+        lens.rewriteTypes(lockCandidates),
         lens.rewriteTypeKeys(initClassReferences));
   }
 
diff --git a/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java b/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
index b67d90d..18ae137 100644
--- a/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.shaking;
 
+import com.android.tools.r8.code.CfOrDexInstruction;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexCallSite;
 import com.android.tools.r8.graph.DexEncodedMethod;
@@ -14,6 +15,7 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.UseRegistry;
+import java.util.ListIterator;
 
 public class DefaultEnqueuerUseRegistry extends UseRegistry {
 
@@ -114,8 +116,9 @@
   }
 
   @Override
-  public void registerConstClass(DexType type) {
-    enqueuer.traceConstClass(type, context);
+  public void registerConstClass(
+      DexType type, ListIterator<? extends CfOrDexInstruction> iterator) {
+    enqueuer.traceConstClass(type, context, iterator);
   }
 
   @Override
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 7a2969e..b96a14f 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -10,6 +10,9 @@
 import static com.android.tools.r8.shaking.AnnotationRemover.shouldKeepAnnotation;
 
 import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.code.CfOrDexInstruction;
 import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
@@ -31,6 +34,7 @@
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItem;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexItemFactory.ClassMethods;
 import com.android.tools.r8.graph.DexLibraryClass;
 import com.android.tools.r8.graph.DexMember;
 import com.android.tools.r8.graph.DexMethod;
@@ -101,6 +105,7 @@
 import com.android.tools.r8.utils.DequeUtils;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.InternalOptions.DesugarState;
+import com.android.tools.r8.utils.IteratorUtils;
 import com.android.tools.r8.utils.MethodSignatureEquivalence;
 import com.android.tools.r8.utils.OptionalBool;
 import com.android.tools.r8.utils.Pair;
@@ -131,6 +136,7 @@
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.ListIterator;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
@@ -314,10 +320,10 @@
   private final MutableKeepInfoCollection keepInfo = new MutableKeepInfoCollection();
 
   /**
-   * A set of seen const-class references that both serve as an initial lock-candidate set and will
-   * prevent statically merging the classes referenced.
+   * A set of seen const-class references that serve as an initial lock-candidate set and will
+   * prevent class merging.
    */
-  private final Set<DexType> constClassReferences = Sets.newIdentityHashSet();
+  private final Set<DexType> lockCandidates = Sets.newIdentityHashSet();
 
   /**
    * A map from seen init-class references to the minimum required visibility of the corresponding
@@ -926,17 +932,63 @@
     traceConstClassOrCheckCast(type, currentMethod);
   }
 
-  void traceConstClass(DexType type, ProgramMethod currentMethod) {
+  void traceConstClass(
+      DexType type,
+      ProgramMethod currentMethod,
+      ListIterator<? extends CfOrDexInstruction> iterator) {
+    handleLockCandidate(type, currentMethod, iterator);
+    traceConstClassOrCheckCast(type, currentMethod);
+  }
+
+  private void handleLockCandidate(
+      DexType type,
+      ProgramMethod currentMethod,
+      ListIterator<? extends CfOrDexInstruction> iterator) {
     // We conservatively group T.class and T[].class to ensure that we do not merge T with S if
     // potential locks on T[].class and S[].class exists.
     DexType baseType = type.toBaseType(appView.dexItemFactory());
     if (baseType.isClassType()) {
       DexProgramClass baseClass = getProgramClassOrNull(baseType);
-      if (baseClass != null) {
-        constClassReferences.add(baseType);
+      if (baseClass != null && isConstClassMaybeUsedAsLock(currentMethod, iterator)) {
+        lockCandidates.add(baseType);
       }
     }
-    traceConstClassOrCheckCast(type, currentMethod);
+  }
+
+  /**
+   * Returns true if the const-class value may flow into a monitor instruction.
+   *
+   * <p>Some common usages of const-class values are handled, such as calls to Class.get*Name().
+   */
+  private boolean isConstClassMaybeUsedAsLock(
+      ProgramMethod currentMethod, ListIterator<? extends CfOrDexInstruction> iterator) {
+    if (iterator == null) {
+      return true;
+    }
+    boolean result = true;
+    if (currentMethod.getDefinition().getCode().isCfCode()) {
+      CfInstruction nextInstruction =
+          IteratorUtils.nextUntil(
+                  iterator,
+                  instruction ->
+                      !instruction.asCfInstruction().isLabel()
+                          && !instruction.asCfInstruction().isPosition())
+              .asCfInstruction();
+      assert nextInstruction != null;
+      if (nextInstruction.isInvoke()) {
+        CfInvoke invoke = nextInstruction.asInvoke();
+        DexMethod invokedMethod = invoke.getMethod();
+        ClassMethods classMethods = appView.dexItemFactory().classMethods;
+        if (classMethods.isReflectiveNameLookup(invokedMethod)
+            || invokedMethod == classMethods.desiredAssertionStatus
+            || invokedMethod == classMethods.getClassLoader
+            || invokedMethod == classMethods.getPackage) {
+          result = false;
+        }
+      }
+      iterator.previous();
+    }
+    return result;
   }
 
   private void traceConstClassOrCheckCast(DexType type, ProgramMethod currentMethod) {
@@ -3137,7 +3189,7 @@
             Collections.emptySet(),
             Collections.emptyMap(),
             EnumValueInfoMapCollection.empty(),
-            constClassReferences,
+            lockCandidates,
             initClassReferences);
     appInfo.markObsolete();
     return appInfoWithLiveness;
diff --git a/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java b/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
index 2bc8459..c368c21 100644
--- a/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
+++ b/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
@@ -176,7 +176,7 @@
 
     @Override
     public void run(Enqueuer enqueuer) {
-      enqueuer.traceConstClass(type, context);
+      enqueuer.traceConstClass(type, context, null);
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java b/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java
index 974fdb1..fe5a13a 100644
--- a/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java
@@ -337,7 +337,7 @@
         // We are not allowed to merge synchronized classes with synchronized methods.
         return;
       }
-      if (appView.appInfo().constClassReferences.contains(clazz.type)) {
+      if (appView.appInfo().lockCandidates.contains(clazz.type)) {
         // Since the type is const-class referenced (and the static merger does not create a lens
         // to map the merged type) the class will likely remain and there is no gain from merging.
         return;
diff --git a/src/main/java/com/android/tools/r8/utils/IteratorUtils.java b/src/main/java/com/android/tools/r8/utils/IteratorUtils.java
index f38c02a..b12de07 100644
--- a/src/main/java/com/android/tools/r8/utils/IteratorUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/IteratorUtils.java
@@ -50,6 +50,16 @@
     };
   }
 
+  public static <T> T nextUntil(Iterator<T> iterator, Predicate<T> predicate) {
+    while (iterator.hasNext()) {
+      T element = iterator.next();
+      if (predicate.test(element)) {
+        return element;
+      }
+    }
+    return null;
+  }
+
   public static <T> T peekPrevious(ListIterator<T> iterator) {
     T previous = iterator.previous();
     T next = iterator.next();
diff --git a/src/test/java/com/android/tools/r8/TestRunResult.java b/src/test/java/com/android/tools/r8/TestRunResult.java
index 85214af..e4640ec 100644
--- a/src/test/java/com/android/tools/r8/TestRunResult.java
+++ b/src/test/java/com/android/tools/r8/TestRunResult.java
@@ -13,7 +13,6 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.ExecutionException;
-import java.util.function.Consumer;
 import java.util.function.Function;
 import org.hamcrest.Matcher;
 
@@ -40,7 +39,7 @@
 
   public abstract RR disassemble() throws IOException, ExecutionException;
 
-  public RR apply(Consumer<RR> fn) {
+  public <E extends Throwable> RR apply(ThrowingConsumer<RR, E> fn) throws E {
     fn.accept(self());
     return self();
   }
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergingWithGetNameTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergingWithGetNameTest.java
new file mode 100644
index 0000000..df89c21
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergingWithGetNameTest.java
@@ -0,0 +1,77 @@
+// 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.classmerging.vertical;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+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.utils.codeinspector.ClassSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class VerticalClassMergingWithGetNameTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public VerticalClassMergingWithGetNameTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(Main.class)
+        .addVerticallyMergedClassesInspector(
+            inspector -> inspector.assertMergedIntoSubtype(A.class))
+        .enableInliningAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .run(parameters.getRuntime(), Main.class)
+        .apply(
+            runResult -> {
+              ClassSubject bClassSubject = runResult.inspector().clazz(B.class);
+              assertThat(bClassSubject, isPresent());
+              runResult.assertSuccessWithOutputLines(
+                  bClassSubject.getFinalName(), bClassSubject.getFinalName());
+            });
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      new B().print();
+    }
+  }
+
+  static class A {
+
+    @NeverInline
+    public void print() {
+      System.out.println(A.class.getName());
+    }
+  }
+
+  static class B extends A {
+
+    @NeverInline
+    public void print() {
+      super.print();
+      System.out.println(B.class.getName());
+    }
+  }
+}