Merge "Add latest gmscore build to x20 dependencies."
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLense.java b/src/main/java/com/android/tools/r8/graph/GraphLense.java
index e6c2d00..b9173dd 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLense.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLense.java
@@ -6,6 +6,19 @@
 import java.util.IdentityHashMap;
 import java.util.Map;
 
+/**
+ * A GraphLense implements a virtual view on top of the graph, used to delay global rewrites until
+ * later IR processing stages.
+ * <p>
+ * Valid remappings are limited to the following operations:
+ * <ul>
+ * <li>Mapping a classes type to one of the super/subtypes.</li>
+ * <li>Renaming private methods/fields.</li>
+ * <li>Moving methods/fields to a super/subclass.</li>
+ * <li>Replacing method/field references by the same method/field on a super/subtype</li>
+ * </ul>
+ * Note that the latter two have to take visibility into account.
+ */
 public abstract class GraphLense {
 
   public static class Builder {
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
index 8bb31d2..2bef174 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
@@ -694,7 +694,7 @@
       StringBuilder builder, List<BasicBlock> list, Function<BasicBlock, String> postfix) {
     if (list.size() > 0) {
       for (BasicBlock block : list) {
-        builder.append(block.getNumber());
+        builder.append(block.number >= 0 ? block.number : "<unknown>");
         builder.append(postfix.apply(block));
         builder.append(" ");
       }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
index 7e47ed2..b0ab940 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
@@ -277,7 +277,7 @@
       // Check that we don't ever have instructions that can throw and have targets.
       assert !dex.canThrow();
       for (int relativeOffset : targets) {
-        builder.ensureSuccessorBlock(offset + relativeOffset);
+        builder.ensureNormalSuccessorBlock(offset, offset + relativeOffset);
       }
       return true;
     }
@@ -297,7 +297,7 @@
         builder.ensureBlockWithoutEnqueuing(tryRangeStartAddress);
         // Edge to exceptional successors.
         for (Integer handlerOffset : getUniqueTryHandlerOffsets(tryRange)) {
-          builder.ensureSuccessorBlock(handlerOffset);
+          builder.ensureExceptionalSuccessorBlock(offset, handlerOffset);
         }
         // If the following instruction is a move-result include it in this (the invokes) block.
         if (index + 1 < code.instructions.length && isMoveResult(code.instructions[index + 1])) {
@@ -307,7 +307,7 @@
         }
         // Edge to normal successor if any (fallthrough).
         if (!(dex instanceof Throw)) {
-          builder.ensureSuccessorBlock(dex.getOffset() + dex.getSize());
+          builder.ensureNormalSuccessorBlock(offset, dex.getOffset() + dex.getSize());
         }
         return true;
       }
@@ -319,9 +319,9 @@
       switchPayloadResolver.addPayloadUser(dex);
 
       for (int target : switchPayloadResolver.absoluteTargets(dex)) {
-        builder.ensureSuccessorBlock(target);
+        builder.ensureNormalSuccessorBlock(offset, target);
       }
-      builder.ensureSuccessorBlock(offset + dex.getSize());
+      builder.ensureNormalSuccessorBlock(offset, offset + dex.getSize());
       return true;
     }
     // TODO(zerny): Remove this from block computation.
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index bd8a469..b3d5266 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -83,7 +83,14 @@
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.StringUtils.BraceType;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceAVLTreeMap;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMap;
+import it.unimi.dsi.fastutil.ints.IntArraySet;
+import it.unimi.dsi.fastutil.ints.IntIterator;
+import it.unimi.dsi.fastutil.ints.IntSet;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedList;
@@ -101,6 +108,8 @@
  */
 public class IRBuilder {
 
+  private static final int INITIAL_BLOCK_OFFSET = -1;
+
   // SSA construction uses a worklist of basic blocks reachable from the entry and their
   // instruction offsets.
   private static class WorklistItem {
@@ -158,8 +167,64 @@
     }
   }
 
+  private static class BlockInfo {
+    BasicBlock block = new BasicBlock();
+    IntSet normalPredecessors = new IntArraySet();
+    IntSet normalSuccessors = new IntArraySet();
+    IntSet exceptionalPredecessors = new IntArraySet();
+    IntSet exceptionalSuccessors = new IntArraySet();
+
+    void addNormalPredecessor(int offset) {
+      normalPredecessors.add(offset);
+    }
+
+    void addNormalSuccessor(int offset) {
+      normalSuccessors.add(offset);
+    }
+
+    void replaceNormalPredecessor(int existing, int replacement) {
+      normalPredecessors.remove(existing);
+      normalPredecessors.add(replacement);
+    }
+
+    void addExceptionalPredecessor(int offset) {
+      exceptionalPredecessors.add(offset);
+    }
+
+    void addExceptionalSuccessor(int offset) {
+      exceptionalSuccessors.add(offset);
+    }
+
+    int predecessorCount() {
+      return normalPredecessors.size() + exceptionalPredecessors.size();
+    }
+
+    BlockInfo split(
+        int blockStartOffset, int fallthroughOffset, Int2ReferenceMap<BlockInfo> targets) {
+      BlockInfo fallthroughInfo = new BlockInfo();
+      fallthroughInfo.normalPredecessors = new IntArraySet(Collections.singleton(blockStartOffset));
+      fallthroughInfo.block.incrementUnfilledPredecessorCount();
+      // Move all normal successors to the fallthrough block.
+      IntIterator normalSuccessorIterator = normalSuccessors.iterator();
+      while (normalSuccessorIterator.hasNext()) {
+        BlockInfo normalSuccessor = targets.get(normalSuccessorIterator.nextInt());
+        normalSuccessor.replaceNormalPredecessor(blockStartOffset, fallthroughOffset);
+      }
+      fallthroughInfo.normalSuccessors = normalSuccessors;
+      normalSuccessors = new IntArraySet(Collections.singleton(fallthroughOffset));
+      // Copy all exceptional successors to the fallthrough block.
+      IntIterator exceptionalSuccessorIterator = fallthroughInfo.exceptionalSuccessors.iterator();
+      while (exceptionalSuccessorIterator.hasNext()) {
+        BlockInfo exceptionalSuccessor = targets.get(exceptionalSuccessorIterator.nextInt());
+        exceptionalSuccessor.addExceptionalPredecessor(fallthroughOffset);
+      }
+      fallthroughInfo.exceptionalSuccessors = new IntArraySet(this.exceptionalSuccessors);
+      return fallthroughInfo;
+    }
+  }
+
   // Mapping from instruction offsets to basic-block targets.
-  private final Map<Integer, BasicBlock> targets = new HashMap<>();
+  private final Int2ReferenceSortedMap<BlockInfo> targets = new Int2ReferenceAVLTreeMap<>();
 
   // Worklist of reachable blocks.
   private final Queue<Integer> traceBlocksWorklist = new LinkedList<>();
@@ -239,13 +304,8 @@
     assert source != null;
     source.setUp();
 
-    // Create entry block.
-    setCurrentBlock(new BasicBlock());
-
-    // If the method needs a prelude, the entry block must never be the target of other blocks.
-    if (!source.needsPrelude()) {
-      targets.put(0, currentBlock);
-    }
+    // Create entry block (at a non-targetable address).
+    targets.put(INITIAL_BLOCK_OFFSET, new BlockInfo());
 
     // Process reachable code paths starting from instruction 0.
     processedInstructions = new boolean[source.instructionCount()];
@@ -267,8 +327,8 @@
         // If the next instruction starts a block, fall through to it.
         if (index + 1 < source.instructionCount()) {
           int nextOffset = source.instructionOffset(index + 1);
-          if (getTarget(nextOffset) != null) {
-            ensureSuccessorBlock(nextOffset);
+          if (targets.get(nextOffset) != null) {
+            ensureNormalSuccessorBlock(startOfBlockOffset, nextOffset);
             break;
           }
         }
@@ -276,6 +336,7 @@
     }
     processedInstructions = null;
 
+    setCurrentBlock(targets.get(INITIAL_BLOCK_OFFSET).block);
     source.buildPrelude(this);
 
     // Process normal blocks reachable from the entry block using a worklist of reachable
@@ -340,11 +401,36 @@
 
   private boolean verifyFilledPredecessors() {
     for (BasicBlock block : blocks) {
-      assert block.verifyFilledPredecessors();
+      assert verifyFilledPredecessors(block);
     }
     return true;
   }
 
+  private boolean verifyFilledPredecessors(BasicBlock block) {
+    assert block.verifyFilledPredecessors();
+    // TODO(zerny): Consider moving the validation of the initial control-flow graph to after its
+    // construction and prior to building the IR.
+    for (BlockInfo info : targets.values()) {
+      if (info != null && info.block == block) {
+        assert info.predecessorCount() == block.getPredecessors().size();
+        assert info.normalSuccessors.size() == block.getNormalSucessors().size();
+        if (block.hasCatchHandlers()) {
+          assert info.exceptionalSuccessors.size()
+              == block.getCatchHandlers().getUniqueTargets().size();
+        } else {
+          assert !block.canThrow()
+              || info.exceptionalSuccessors.isEmpty()
+              || (info.exceptionalSuccessors.size() == 1
+                  && info.exceptionalSuccessors.iterator().nextInt() < 0);
+        }
+        return true;
+      }
+    }
+    // There are places where we add in new blocks that we do not represent in the initial CFG.
+    // TODO(zerny): Should we maintain the initial CFG after instruction building?
+    return true;
+  }
+
   private int processWorklist(int blockNumber) {
     for (WorklistItem item = ssaWorklist.poll(); item != null; item = ssaWorklist.poll()) {
       if (item.block.isFilled()) {
@@ -359,11 +445,11 @@
           source.closedCurrentBlock();
           break;
         }
-        BasicBlock block = getTarget(source.instructionOffset(i));
-        if (block != null && block != currentBlock) {
-          closeCurrentBlockWithFallThrough(block);
+        BlockInfo info = targets.get(source.instructionOffset(i));
+        if (info != null && info.block != currentBlock) {
+          closeCurrentBlockWithFallThrough(info.block);
           source.closedCurrentBlockWithFallthrough(i);
-          addToWorklist(block, i);
+          addToWorklist(info.block, i);
           break;
         }
         source.buildInstruction(this, i);
@@ -1151,7 +1237,8 @@
       // If this was a packed switch with only fallthrough cases we can make it a goto.
       // Oddly, this does happen.
       if (numberOfFallthroughs == numberOfTargets) {
-        targets.get(fallthroughOffset).decrementUnfilledPredecessorCount(numberOfFallthroughs);
+        BlockInfo info = targets.get(fallthroughOffset);
+        info.block.decrementUnfilledPredecessorCount(numberOfFallthroughs);
         addGoto(fallthroughOffset);
         return;
       }
@@ -1161,7 +1248,8 @@
       int bytesSaved = packedSwitchPayloadSize - sparseSwitchPayloadSize;
       // Perform the rewrite if we can reduce the payload size by more than 20%.
       if (bytesSaved > (packedSwitchPayloadSize / 5)) {
-        targets.get(fallthroughOffset).decrementUnfilledPredecessorCount(numberOfFallthroughs);
+        BlockInfo info = targets.get(fallthroughOffset);
+        info.block.decrementUnfilledPredecessorCount(numberOfFallthroughs);
         int nextCaseIndex = 0;
         int currentKey = keys[0];
         keys = new int[numberOfSparseTargets];
@@ -1458,10 +1546,16 @@
     BasicBlock block = new BasicBlock();
     blocks.add(block);
     block.incrementUnfilledPredecessorCount();
-    for (Integer offset : source.getCurrentCatchHandlers().getUniqueTargets()) {
-      BasicBlock target = getTarget(offset);
-      assert !target.isSealed();
-      target.incrementUnfilledPredecessorCount();
+    int freshOffset = INITIAL_BLOCK_OFFSET - 1;
+    while (targets.containsKey(freshOffset)) {
+      freshOffset--;
+    }
+    targets.put(freshOffset, null);
+    for (int offset : source.getCurrentCatchHandlers().getUniqueTargets()) {
+      BlockInfo target = targets.get(offset);
+      assert !target.block.isSealed();
+      target.block.incrementUnfilledPredecessorCount();
+      target.addExceptionalPredecessor(freshOffset);
     }
     addInstruction(new Goto());
     currentBlock.link(block);
@@ -1499,21 +1593,32 @@
   // Package (ie, SourceCode accessed) helpers.
 
   // Ensure there is a block starting at offset.
-  BasicBlock ensureBlockWithoutEnqueuing(int offset) {
-    BasicBlock block = targets.get(offset);
-    if (block == null) {
-      block = new BasicBlock();
-      targets.put(offset, block);
+  BlockInfo ensureBlockWithoutEnqueuing(int offset) {
+    assert offset != INITIAL_BLOCK_OFFSET;
+    BlockInfo info = targets.get(offset);
+    if (info == null) {
       // If this is a processed instruction, the block split and it has a fall-through predecessor.
       if (offset >= 0 && isOffsetProcessed(offset)) {
-        block.incrementUnfilledPredecessorCount();
+        int blockStartOffset = getBlockStartOffset(offset);
+        BlockInfo existing = targets.get(blockStartOffset);
+        info = existing.split(blockStartOffset, offset, targets);
+      } else {
+        info = new BlockInfo();
       }
+      targets.put(offset, info);
     }
-    return block;
+    return info;
+  }
+
+  private int getBlockStartOffset(int offset) {
+    if (targets.containsKey(offset)) {
+      return offset;
+    }
+    return targets.headMap(offset).lastIntKey();
   }
 
   // Ensure there is a block starting at offset and add it to the work-list if it needs processing.
-  private BasicBlock ensureBlock(int offset) {
+  private BlockInfo ensureBlock(int offset) {
     // We don't enqueue negative targets (these are special blocks, eg, an argument prelude).
     if (offset >= 0 && !isOffsetProcessed(offset)) {
       traceBlocksWorklist.add(offset);
@@ -1550,16 +1655,32 @@
   }
 
   // Ensure there is a block at offset and add a predecessor to it.
-  BasicBlock ensureSuccessorBlock(int offset) {
-    BasicBlock block = ensureBlock(offset);
-    block.incrementUnfilledPredecessorCount();
-    return block;
+  private void ensureSuccessorBlock(int sourceOffset, int targetOffset, boolean normal) {
+    BlockInfo targetInfo = ensureBlock(targetOffset);
+    int sourceStartOffset = getBlockStartOffset(sourceOffset);
+    BlockInfo sourceInfo = targets.get(sourceStartOffset);
+    if (normal) {
+      sourceInfo.addNormalSuccessor(targetOffset);
+      targetInfo.addNormalPredecessor(sourceStartOffset);
+    } else {
+      sourceInfo.addExceptionalSuccessor(targetOffset);
+      targetInfo.addExceptionalPredecessor(sourceStartOffset);
+    }
+    targetInfo.block.incrementUnfilledPredecessorCount();
+  }
+
+  void ensureNormalSuccessorBlock(int sourceOffset, int targetOffset) {
+    ensureSuccessorBlock(sourceOffset, targetOffset, true);
+  }
+
+  void ensureExceptionalSuccessorBlock(int sourceOffset, int targetOffset) {
+    ensureSuccessorBlock(sourceOffset, targetOffset, false);
   }
 
   // Private block helpers.
 
   private BasicBlock getTarget(int offset) {
-    return targets.get(offset);
+    return targets.get(offset).block;
   }
 
   private void closeCurrentBlock() {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
index 5647878..1d8f18d 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
@@ -66,7 +66,6 @@
 import org.objectweb.asm.util.Textifier;
 import org.objectweb.asm.util.TraceMethodVisitor;
 
-
 public class JarSourceCode implements SourceCode {
 
   // Try-catch block wrapper containing resolved offsets.
@@ -144,7 +143,7 @@
   // thus that the monitor-entry prelude (part of the argument block which must not have a try-catch
   // successor) is not joined with the first instruction block (which likely will have a try-catch
   // successor).
-  private static final int EXCEPTIONAL_SYNC_EXIT_OFFSET = -1;
+  private static final int EXCEPTIONAL_SYNC_EXIT_OFFSET = -2;
   private static final TryCatchBlock EXCEPTIONAL_SYNC_EXIT =
       new TryCatchBlock(EXCEPTIONAL_SYNC_EXIT_OFFSET, 0, Integer.MAX_VALUE, null);
 
@@ -534,7 +533,7 @@
     if (targets != NO_TARGETS) {
       assert !canThrow(insn);
       for (int target : targets) {
-        builder.ensureSuccessorBlock(target);
+        builder.ensureNormalSuccessorBlock(index, target);
       }
       return true;
     }
@@ -549,12 +548,12 @@
           int handler = tryCatchBlock.getHandler();
           if (!seenHandlerOffsets.contains(handler)) {
             seenHandlerOffsets.add(handler);
-            builder.ensureSuccessorBlock(handler);
+            builder.ensureExceptionalSuccessorBlock(index, handler);
           }
         }
         // Edge to normal successor if any (fallthrough).
         if (!isThrow(insn)) {
-          builder.ensureSuccessorBlock(getOffset(insn.getNext()));
+          builder.ensureNormalSuccessorBlock(index, getOffset(insn.getNext()));
         }
         return true;
       }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
index b122d23..df5cb27 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
@@ -236,8 +236,7 @@
   private boolean isFieldRead(DexEncodedField field, boolean isStatic) {
     // Without live set information we cannot tell and assume true.
     if (liveSet == null
-        || isStatic && liveSet.staticFieldsRead.contains(field.field)
-        || !isStatic && liveSet.instanceFieldsRead.contains(field.field)
+        || liveSet.fieldsRead.contains(field.field)
         || liveSet.pinnedItems.contains(field)) {
       return true;
     }
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
index 88d371b..17da59b 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
@@ -4,7 +4,6 @@
 package com.android.tools.r8.optimize;
 
 import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.Descriptor;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
@@ -98,11 +97,6 @@
     return searchClass.type;
   }
 
-  private boolean isNotFromLibrary(Descriptor item) {
-    DexClass clazz = appInfo.definitionFor(item.getHolder());
-    return clazz != null && !clazz.isLibraryClass();
-  }
-
   private DexEncodedMethod virtualLookup(DexMethod method) {
     return appInfo.lookupVirtualDefinition(method.getHolder(), method);
   }
@@ -190,13 +184,23 @@
     for (DexField field : fields) {
       field = lense.lookupField(field, null);
       DexEncodedField target = lookup.apply(field.getHolder(), field);
-      // Rebind to the lowest library class or program class.
-      if (target != null && target.field != field) {
+      // Rebind to the lowest library class or program class. Do not rebind accesses to fields that
+      // are not public, as this might lead to access violation errors.
+      if (target != null && target.field != field && isVisibleFromOtherClasses(target)) {
         builder.map(field, validTargetFor(target.field, field, lookupTargetOnClass));
       }
     }
   }
 
+  private boolean isVisibleFromOtherClasses(DexEncodedField field) {
+    // If the field is not public, the visibility on the class can not be a further constraint.
+    if (!field.accessFlags.isPublic()) {
+      return true;
+    }
+    // If the field is public, then a non-public holder class will further constrain visibility.
+    return appInfo.definitionFor(field.field.getHolder()).accessFlags.isPublic();
+  }
+
   private static void privateMethodsCheck(DexProgramClass clazz, DexEncodedMethod method) {
     throw new Unreachable("Direct invokes should not require forwarding.");
   }
@@ -211,9 +215,9 @@
     computeMethodRebinding(appInfo.staticInvokes, appInfo::lookupStaticTarget,
         DexClass::findDirectTarget, DexProgramClass::addStaticMethod);
 
-    computeFieldRebinding(Sets.union(appInfo.staticFieldsRead, appInfo.staticFieldsWritten),
+    computeFieldRebinding(Sets.union(appInfo.staticFieldReads, appInfo.staticFieldWrites),
         appInfo::lookupStaticTarget, DexClass::findStaticTarget);
-    computeFieldRebinding(Sets.union(appInfo.instanceFieldsRead, appInfo.instanceFieldsWritten),
+    computeFieldRebinding(Sets.union(appInfo.instanceFieldReads, appInfo.instanceFieldWrites),
         appInfo::lookupInstanceTarget, DexClass::findInstanceTarget);
     return builder.build(lense, appInfo.dexItemFactory);
   }
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 2dab755..abdda65 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -37,6 +37,7 @@
 import com.google.common.collect.Sets;
 import com.google.common.collect.Sets.SetView;
 import java.util.ArrayDeque;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Deque;
 import java.util.HashMap;
@@ -47,6 +48,7 @@
 import java.util.Queue;
 import java.util.Set;
 import java.util.function.BiFunction;
+import java.util.function.Function;
 import java.util.stream.Collectors;
 
 /**
@@ -856,9 +858,8 @@
   }
 
   private Set<DexField> collectFields(Map<DexType, Set<DexField>> map) {
-    Set<DexField> set = Sets.newIdentityHashSet();
-    map.values().forEach(set::addAll);
-    return set;
+    return map.values().stream().flatMap(Collection::stream)
+        .collect(Collectors.toCollection(Sets::newIdentityHashSet));
   }
 
   Set<DexField> collectInstanceFieldsRead() {
@@ -877,6 +878,32 @@
     return Collections.unmodifiableSet(collectFields(staticFieldsWritten));
   }
 
+  private Set<DexField> collectReachedFields(Map<DexType, Set<DexField>> map,
+      Function<DexField, DexField> lookup) {
+    return map.values().stream().flatMap(set -> set.stream().map(lookup))
+        .collect(Collectors.toCollection(Sets::newIdentityHashSet));
+  }
+
+  private DexField tryLookupInstanceField(DexField field) {
+    DexEncodedField target = appInfo.lookupInstanceTarget(field.clazz, field);
+    return target == null ? field : target.field;
+  }
+
+  private DexField tryLookupStaticField(DexField field) {
+    DexEncodedField target = appInfo.lookupStaticTarget(field.clazz, field);
+    return target == null ? field : target.field;
+  }
+
+  Set<DexField> collectFieldsRead() {
+    return Sets.union(collectReachedFields(instanceFieldsRead, this::tryLookupInstanceField),
+        collectReachedFields(staticFieldsRead, this::tryLookupStaticField));
+  }
+
+  Set<DexField> collectFieldsWritten() {
+    return Sets.union(collectReachedFields(instanceFieldsWritten, this::tryLookupInstanceField),
+        collectReachedFields(staticFieldsWritten, this::tryLookupStaticField));
+  }
+
   private static class Action {
 
     final Kind kind;
@@ -981,31 +1008,29 @@
      */
     final Set<DexField> liveFields;
     /**
-     * Set of all fields which are read. Combines {@link #instanceFieldsRead} and
-     * {@link #staticFieldsRead}.
+     * Set of all fields which may be touched by a get operation. This is actual field definitions.
      */
     public final Set<DexField> fieldsRead;
     /**
-     * Set of all fields which are written. Combines {@link #instanceFieldsWritten} and
-     * {@link #staticFieldsWritten}.
+     * Set of all fields which may be touched by a put operation. This is actual field definitions.
      */
     public final Set<DexField> fieldsWritten;
     /**
-     * Set of all instance fields which are read.
+     * Set of all field ids used in instance field reads.
      */
-    public final Set<DexField> instanceFieldsRead;
+    public final Set<DexField> instanceFieldReads;
     /**
-     * Set of all instance fields which are written.
+     * Set of all field ids used in instance field writes.
      */
-    public final Set<DexField> instanceFieldsWritten;
+    public final Set<DexField> instanceFieldWrites;
     /**
-     * Set of all static fields which are read.
+     * Set of all field ids used in static static field reads.
      */
-    public final Set<DexField> staticFieldsRead;
+    public final Set<DexField> staticFieldReads;
     /**
-     * Set of all static fields which are written.
+     * Set of all field ids used in static field writes.
      */
-    public final Set<DexField> staticFieldsWritten;
+    public final Set<DexField> staticFieldWrites;
     /**
      * Set of all methods referenced in virtual invokes;
      */
@@ -1042,12 +1067,12 @@
       this.targetedMethods = toDescriptorSet(enqueuer.targetedMethods.getItems());
       this.liveMethods = toDescriptorSet(enqueuer.liveMethods.getItems());
       this.liveFields = toDescriptorSet(enqueuer.liveFields.getItems());
-      this.instanceFieldsRead = enqueuer.collectInstanceFieldsRead();
-      this.instanceFieldsWritten = enqueuer.collectInstanceFieldsWritten();
-      this.staticFieldsRead = enqueuer.collectStaticFieldsRead();
-      this.staticFieldsWritten = enqueuer.collectStaticFieldsWritten();
-      this.fieldsRead = Sets.union(staticFieldsRead, instanceFieldsRead);
-      this.fieldsWritten = Sets.union(staticFieldsWritten, instanceFieldsWritten);
+      this.instanceFieldReads = enqueuer.collectInstanceFieldsRead();
+      this.instanceFieldWrites = enqueuer.collectInstanceFieldsWritten();
+      this.staticFieldReads = enqueuer.collectStaticFieldsRead();
+      this.staticFieldWrites = enqueuer.collectStaticFieldsWritten();
+      this.fieldsRead = enqueuer.collectFieldsRead();
+      this.fieldsWritten = enqueuer.collectFieldsWritten();
       this.pinnedItems = Collections.unmodifiableSet(enqueuer.pinnedItems);
       this.virtualInvokes = joinInvokedMethods(enqueuer.virtualInvokes);
       this.superInvokes = joinInvokedMethods(enqueuer.superInvokes);
@@ -1055,8 +1080,8 @@
       this.staticInvokes = joinInvokedMethods(enqueuer.staticInvokes);
       this.noSideEffects = enqueuer.rootSet.noSideEffects;
       this.assumedValues = enqueuer.rootSet.assumedValues;
-      assert Sets.intersection(instanceFieldsRead, staticFieldsRead).size() == 0;
-      assert Sets.intersection(instanceFieldsWritten, staticFieldsWritten).size() == 0;
+      assert Sets.intersection(instanceFieldReads, staticFieldReads).size() == 0;
+      assert Sets.intersection(instanceFieldWrites, staticFieldWrites).size() == 0;
     }
 
     private AppInfoWithLiveness(AppInfoWithLiveness previous, DexApplication application) {
@@ -1066,11 +1091,12 @@
       this.targetedMethods = previous.targetedMethods;
       this.liveMethods = previous.liveMethods;
       this.liveFields = previous.liveFields;
-      this.instanceFieldsRead = previous.instanceFieldsRead;
-      this.instanceFieldsWritten = previous.instanceFieldsWritten;
-      this.staticFieldsRead = previous.staticFieldsRead;
-      this.staticFieldsWritten = previous.staticFieldsWritten;
+      this.instanceFieldReads = previous.instanceFieldReads;
+      this.instanceFieldWrites = previous.instanceFieldWrites;
+      this.staticFieldReads = previous.staticFieldReads;
+      this.staticFieldWrites = previous.staticFieldWrites;
       this.fieldsRead = previous.fieldsRead;
+      // TODO(herhut): We remove fields that are only written, so maybe update this.
       this.fieldsWritten = previous.fieldsWritten;
       this.pinnedItems = previous.pinnedItems;
       this.noSideEffects = previous.noSideEffects;
@@ -1079,8 +1105,8 @@
       this.superInvokes = previous.superInvokes;
       this.directInvokes = previous.directInvokes;
       this.staticInvokes = previous.staticInvokes;
-      assert Sets.intersection(instanceFieldsRead, staticFieldsRead).size() == 0;
-      assert Sets.intersection(instanceFieldsWritten, staticFieldsWritten).size() == 0;
+      assert Sets.intersection(instanceFieldReads, staticFieldReads).size() == 0;
+      assert Sets.intersection(instanceFieldWrites, staticFieldWrites).size() == 0;
     }
 
     private AppInfoWithLiveness(AppInfoWithLiveness previous, GraphLense lense) {
@@ -1090,12 +1116,12 @@
       this.targetedMethods = rewriteItems(previous.targetedMethods, lense::lookupMethod);
       this.liveMethods = rewriteItems(previous.liveMethods, lense::lookupMethod);
       this.liveFields = rewriteItems(previous.liveFields, lense::lookupField);
-      this.instanceFieldsRead = rewriteItems(previous.instanceFieldsRead, lense::lookupField);
-      this.instanceFieldsWritten = rewriteItems(previous.instanceFieldsWritten, lense::lookupField);
-      this.staticFieldsRead = rewriteItems(previous.staticFieldsRead, lense::lookupField);
-      this.staticFieldsWritten = rewriteItems(previous.staticFieldsWritten, lense::lookupField);
-      this.fieldsRead = Sets.union(staticFieldsRead, instanceFieldsRead);
-      this.fieldsWritten = Sets.union(staticFieldsWritten, instanceFieldsWritten);
+      this.instanceFieldReads = rewriteItems(previous.instanceFieldReads, lense::lookupField);
+      this.instanceFieldWrites = rewriteItems(previous.instanceFieldWrites, lense::lookupField);
+      this.staticFieldReads = rewriteItems(previous.staticFieldReads, lense::lookupField);
+      this.staticFieldWrites = rewriteItems(previous.staticFieldWrites, lense::lookupField);
+      this.fieldsRead = rewriteItems(previous.fieldsRead, lense::lookupField);
+      this.fieldsWritten = rewriteItems(previous.fieldsWritten, lense::lookupField);
       // TODO(herhut): Migrate these to Descriptors, as well.
       this.pinnedItems = previous.pinnedItems;
       this.noSideEffects = previous.noSideEffects;
@@ -1104,8 +1130,8 @@
       this.superInvokes = rewriteItems(previous.superInvokes, lense::lookupMethod);
       this.directInvokes = rewriteItems(previous.directInvokes, lense::lookupMethod);
       this.staticInvokes = rewriteItems(previous.staticInvokes, lense::lookupMethod);
-      assert Sets.intersection(instanceFieldsRead, staticFieldsRead).size() == 0;
-      assert Sets.intersection(instanceFieldsWritten, staticFieldsWritten).size() == 0;
+      assert Sets.intersection(instanceFieldReads, staticFieldReads).size() == 0;
+      assert Sets.intersection(instanceFieldWrites, staticFieldWrites).size() == 0;
     }
 
     private Set<DexMethod> joinInvokedMethods(Map<DexType, Set<DexMethod>> invokes) {
diff --git a/src/main/java/com/android/tools/r8/shaking/SimpleClassMerger.java b/src/main/java/com/android/tools/r8/shaking/SimpleClassMerger.java
index a2864e4..d03c74b 100644
--- a/src/main/java/com/android/tools/r8/shaking/SimpleClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/SimpleClassMerger.java
@@ -27,9 +27,7 @@
 import com.android.tools.r8.utils.Timing;
 import com.google.common.base.Equivalence;
 import com.google.common.base.Equivalence.Wrapper;
-import com.google.common.collect.Iterables;
 import com.google.common.collect.Iterators;
-import com.google.common.collect.Sets;
 import it.unimi.dsi.fastutil.ints.Int2IntMap;
 import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
 import it.unimi.dsi.fastutil.objects.Reference2IntMap;
@@ -44,6 +42,7 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.function.BiFunction;
+import java.util.stream.Collectors;
 
 /**
  * Merges Supertypes with a single implementation into their single subtype.
@@ -61,7 +60,7 @@
   private final GraphLense.Builder renamedMembersLense = GraphLense.builder();
   private final Map<DexType, DexType> mergedClasses = new IdentityHashMap<>();
   private final Timing timing;
-  private Set<DexMethod> invokes;
+  private Collection<DexMethod> invokes;
   private int numberOfMerges = 0;
 
   public SimpleClassMerger(DexApplication application, AppInfoWithLiveness appInfo,
@@ -81,25 +80,51 @@
         && clazz.type.getSingleSubtype() != null;
   }
 
-  private Set<DexMethod> getInvokes() {
-    if (invokes == null) {
+  private void addProgramMethods(Set<Wrapper<DexMethod>> set, DexMethod method,
+      Equivalence<DexMethod> equivalence) {
+    DexClass definition = appInfo.definitionFor(method.holder);
+    if (definition != null && definition.isProgramClass()) {
+      set.add(equivalence.wrap(method));
+    }
+  }
 
-      // TODO(herhut): Ignore invokes into the library, as those can only reference library types.
-      invokes = Sets.newIdentityHashSet();
-      invokes.addAll(appInfo.directInvokes);
-      invokes.addAll(appInfo.staticInvokes);
-      invokes.addAll(appInfo.superInvokes);
-      invokes.addAll(appInfo.virtualInvokes);
-      invokes.addAll(appInfo.targetedMethods);
-      invokes.addAll(appInfo.liveMethods);
-      for (DexEncodedMethod method : Iterables
-          .filter(appInfo.pinnedItems, DexEncodedMethod.class)) {
-        invokes.add(method.method);
-      }
+  private Collection<DexMethod> getInvokes() {
+    if (invokes == null) {
+      // Collect all reachable methods that are not within a library class. Those defined on
+      // library classes are known not to have program classes in their signature.
+      // Also filter methods that only use types from library classes in their signatures. We
+      // know that those won't conflict.
+      Set<Wrapper<DexMethod>> filteredInvokes = new HashSet<>();
+      Equivalence<DexMethod> equivalence = MethodSignatureEquivalence.get();
+      appInfo.targetedMethods.forEach(m -> addProgramMethods(filteredInvokes, m, equivalence));
+      invokes = filteredInvokes.stream().map(Wrapper::get).filter(this::removeNonProgram)
+          .collect(Collectors.toList());
     }
     return invokes;
   }
 
+  private boolean isProgramClass(DexType type) {
+    if (type.isArrayType()) {
+      type = type.toBaseType(appInfo.dexItemFactory);
+    }
+    if (type.isClassType()) {
+      DexClass clazz = appInfo.definitionFor(type);
+      if (clazz != null && clazz.isProgramClass()) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  private boolean removeNonProgram(DexMethod dexMethod) {
+    for (DexType type : dexMethod.proto.parameters.values) {
+      if (isProgramClass(type)) {
+        return true;
+      }
+    }
+    return isProgramClass(dexMethod.proto.returnType);
+  }
+
   public GraphLense run() {
     timing.begin("merge");
     GraphLense mergingGraphLense = mergeClasses(graphLense);
@@ -426,7 +451,8 @@
         if (previous != null) {
           if (!previous.accessFlags.isBridge()) {
             if (!method.accessFlags.isBridge()) {
-              throw new CompilationError("Class merging produced invalid result.");
+              throw new CompilationError(
+                  "Class merging produced invalid result on: " + previous.toSourceString());
             } else {
               filtered.put(previous.method, previous);
             }
diff --git a/src/test/examples/memberrebinding/Test.java b/src/test/examples/memberrebinding/Memberrebinding.java
similarity index 97%
rename from src/test/examples/memberrebinding/Test.java
rename to src/test/examples/memberrebinding/Memberrebinding.java
index 62a2440..74c99fd 100644
--- a/src/test/examples/memberrebinding/Test.java
+++ b/src/test/examples/memberrebinding/Memberrebinding.java
@@ -6,7 +6,7 @@
 import memberrebinding.subpackage.PublicClass;
 import memberrebindinglib.AnIndependentInterface;
 
-public class Test {
+public class Memberrebinding {
 
   public static void main(String[] args) {
     ClassAtBottomOfChain bottomInstance = new ClassAtBottomOfChain();
diff --git a/src/test/examples/memberrebinding2/Test.java b/src/test/examples/memberrebinding2/Memberrebinding.java
similarity index 96%
rename from src/test/examples/memberrebinding2/Test.java
rename to src/test/examples/memberrebinding2/Memberrebinding.java
index d4b005e..13cde8c 100644
--- a/src/test/examples/memberrebinding2/Test.java
+++ b/src/test/examples/memberrebinding2/Memberrebinding.java
@@ -5,7 +5,7 @@
 
 import memberrebinding2.subpackage.PublicClass;
 
-public class Test {
+public class Memberrebinding {
 
   public static void main(String[] args) {
     ClassAtBottomOfChain bottomInstance = new ClassAtBottomOfChain();
diff --git a/src/test/examples/memberrebinding2/keep-rules.txt b/src/test/examples/memberrebinding2/keep-rules.txt
new file mode 100644
index 0000000..ae40c43
--- /dev/null
+++ b/src/test/examples/memberrebinding2/keep-rules.txt
@@ -0,0 +1,12 @@
+# Copyright (c) 2016, 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.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class memberrebinding2.Memberrebinding {
+  public static void main(...);
+}
+
+# Remove once b/62048823 is fixed.
+-allowaccessmodification
\ No newline at end of file
diff --git a/src/test/examples/memberrebinding3/Test.java b/src/test/examples/memberrebinding3/Memberrebinding.java
similarity index 78%
rename from src/test/examples/memberrebinding3/Test.java
rename to src/test/examples/memberrebinding3/Memberrebinding.java
index d37f263..83d8bbb 100644
--- a/src/test/examples/memberrebinding3/Test.java
+++ b/src/test/examples/memberrebinding3/Memberrebinding.java
@@ -3,16 +3,19 @@
 // BSD-style license that can be found in the LICENSE file.
 package memberrebinding3;
 
-public class Test extends ClassAtBottomOfChain {
+public class Memberrebinding extends ClassAtBottomOfChain {
 
+  @Override
   void bottomMethod() {
 
   }
 
+  @Override
   void middleMethod() {
 
   }
 
+  @Override
   void topMethod() {
 
   }
@@ -24,6 +27,6 @@
   }
 
   public static void main(String[] args) {
-    new Test().test();
+    new Memberrebinding().test();
   }
 }
diff --git a/src/test/examplesAndroidN/memberrebinding4/Test.java b/src/test/examplesAndroidN/memberrebinding4/Memberrebinding.java
similarity index 93%
rename from src/test/examplesAndroidN/memberrebinding4/Test.java
rename to src/test/examplesAndroidN/memberrebinding4/Memberrebinding.java
index 6fc2ad5..a699537 100644
--- a/src/test/examplesAndroidN/memberrebinding4/Test.java
+++ b/src/test/examplesAndroidN/memberrebinding4/Memberrebinding.java
@@ -5,7 +5,7 @@
 
 import memberrebinding4.subpackage.PublicInterface;
 
-public class Test {
+public class Memberrebinding {
 
   static class Inner implements PublicInterface {
 
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
index a264f48..9be6930 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
@@ -103,8 +103,8 @@
         "regress_37875803.Regress",
         "regress_37955340.Regress",
         "regress_62300145.Regress",
-        "memberrebinding2.Test",
-        "memberrebinding3.Test",
+        "memberrebinding2.Memberrebinding",
+        "memberrebinding3.Memberrebinding",
         "minification.Minification",
         "enclosingmethod.Main",
         "interfaceinlining.Main",
@@ -123,7 +123,7 @@
   private static String[] makeTest(
       DexTool tool, CompilerUnderTest compiler, CompilationMode mode, String clazz) {
     String pkg = clazz.substring(0, clazz.lastIndexOf('.'));
-    return new String[] {pkg, tool.name(), compiler.name(), mode.name(), clazz};
+    return new String[]{pkg, tool.name(), compiler.name(), mode.name(), clazz};
   }
 
   @Rule
diff --git a/src/test/java/com/android/tools/r8/dex/ExtraFileTest.java b/src/test/java/com/android/tools/r8/dex/ExtraFileTest.java
index 26067c7..376577c 100644
--- a/src/test/java/com/android/tools/r8/dex/ExtraFileTest.java
+++ b/src/test/java/com/android/tools/r8/dex/ExtraFileTest.java
@@ -25,7 +25,7 @@
   private static final String EXAMPLE_DIR = ToolHelper.EXAMPLES_BUILD_DIR;
   private static final String EXAMPLE_DEX = "memberrebinding/classes.dex";
   private static final String EXAMPLE_LIB = "memberrebindinglib/classes.dex";
-  private static final String EXAMPLE_CLASS = "memberrebinding.Test";
+  private static final String EXAMPLE_CLASS = "memberrebinding.Memberrebinding";
   private static final String EXAMPLE_PACKAGE_MAP = "memberrebinding/package.map";
   private static final String EXAMPLE_PROGUARD_MAP = "memberrebinding/proguard.map";
 
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingTest.java b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingTest.java
index 55bd373..c617c25 100644
--- a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingTest.java
+++ b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingTest.java
@@ -105,7 +105,8 @@
   }
 
   private static void inspectOriginalMain(DexInspector inspector) {
-    MethodSubject main = inspector.clazz("memberrebinding.Test").method(DexInspector.MAIN);
+    MethodSubject main = inspector.clazz("memberrebinding.Memberrebinding")
+        .method(DexInspector.MAIN);
     Iterator<InvokeInstructionSubject> iterator =
         main.iterateInstructions(MemberRebindingTest::coolInvokes);
     assertTrue(iterator.next().holder().is("memberrebinding.ClassAtBottomOfChain"));
@@ -132,7 +133,8 @@
   }
 
   private static void inspectMain(DexInspector inspector) {
-    MethodSubject main = inspector.clazz("memberrebinding.Test").method(DexInspector.MAIN);
+    MethodSubject main = inspector.clazz("memberrebinding.Memberrebinding")
+        .method(DexInspector.MAIN);
     Iterator<InvokeInstructionSubject> iterator =
         main.iterateInstructions(MemberRebindingTest::coolInvokes);
     assertTrue(iterator.next().holder().is("memberrebinding.ClassAtBottomOfChain"));
@@ -162,7 +164,8 @@
   }
 
   private static void inspectOriginalMain2(DexInspector inspector) {
-    MethodSubject main = inspector.clazz("memberrebinding2.Test").method(DexInspector.MAIN);
+    MethodSubject main = inspector.clazz("memberrebinding2.Memberrebinding")
+        .method(DexInspector.MAIN);
     Iterator<FieldAccessInstructionSubject> iterator =
         main.iterateInstructions(InstructionSubject::isFieldAccess);
     // Run through instance put, static put, instance get and instance get.
@@ -177,7 +180,8 @@
   }
 
   private static void inspectMain2(DexInspector inspector) {
-    MethodSubject main = inspector.clazz("memberrebinding2.Test").method(DexInspector.MAIN);
+    MethodSubject main = inspector.clazz("memberrebinding2.Memberrebinding")
+        .method(DexInspector.MAIN);
     Iterator<FieldAccessInstructionSubject> iterator =
         main.iterateInstructions(InstructionSubject::isFieldAccess);
     // Run through instance put, static put, instance get and instance get.
@@ -185,7 +189,7 @@
       assertTrue(iterator.next().holder().is("memberrebinding2.ClassAtBottomOfChain"));
       assertTrue(iterator.next().holder().is("memberrebinding2.ClassInMiddleOfChain"));
       assertTrue(iterator.next().holder().is("memberrebinding2.SuperClassOfAll"));
-      assertTrue(iterator.next().holder().is("memberrebinding2.subpackage.PackagePrivateClass"));
+      assertTrue(iterator.next().holder().is("memberrebinding2.subpackage.PublicClass"));
     }
     assertTrue(iterator.next().holder().is("java.lang.System"));
     assertFalse(iterator.hasNext());
@@ -195,7 +199,7 @@
       new MethodSignature("test", "void", new String[]{});
 
   private static void inspectOriginal3(DexInspector inspector) {
-    MethodSubject main = inspector.clazz("memberrebinding3.Test").method(TEST);
+    MethodSubject main = inspector.clazz("memberrebinding3.Memberrebinding").method(TEST);
     Iterator<InvokeInstructionSubject> iterator =
         main.iterateInstructions(InstructionSubject::isInvoke);
     assertTrue(iterator.next().holder().is("memberrebinding3.ClassAtBottomOfChain"));
@@ -205,7 +209,7 @@
   }
 
   private static void inspect3(DexInspector inspector) {
-    MethodSubject main = inspector.clazz("memberrebinding3.Test").method(TEST);
+    MethodSubject main = inspector.clazz("memberrebinding3.Memberrebinding").method(TEST);
     Iterator<InvokeInstructionSubject> iterator =
         main.iterateInstructions(InstructionSubject::isInvoke);
     assertTrue(iterator.next().holder().is("memberrebinding3.ClassAtBottomOfChain"));
@@ -215,19 +219,19 @@
   }
 
   private static void inspectOriginal4(DexInspector inspector) {
-    MethodSubject main = inspector.clazz("memberrebinding4.Test").method(TEST);
+    MethodSubject main = inspector.clazz("memberrebinding4.Memberrebinding").method(TEST);
     Iterator<InvokeInstructionSubject> iterator =
         main.iterateInstructions(InstructionSubject::isInvoke);
-    assertTrue(iterator.next().holder().is("memberrebinding4.Test$Inner"));
+    assertTrue(iterator.next().holder().is("memberrebinding4.Memberrebinding$Inner"));
     assertTrue(iterator.next().holder().is("memberrebinding4.subpackage.PublicInterface"));
     assertFalse(iterator.hasNext());
   }
 
   private static void inspect4(DexInspector inspector) {
-    MethodSubject main = inspector.clazz("memberrebinding4.Test").method(TEST);
+    MethodSubject main = inspector.clazz("memberrebinding4.Memberrebinding").method(TEST);
     Iterator<InvokeInstructionSubject> iterator =
         main.iterateInstructions(InstructionSubject::isInvoke);
-    assertTrue(iterator.next().holder().is("memberrebinding4.Test$Inner"));
+    assertTrue(iterator.next().holder().is("memberrebinding4.Memberrebinding$Inner"));
     assertTrue(iterator.next().holder().is("memberrebinding4.subpackage.PublicInterface"));
     assertFalse(iterator.hasNext());
   }
diff --git a/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java b/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
index 4530e2e..6a8cfb8 100644
--- a/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
@@ -542,7 +542,9 @@
             "assumevalues3",
             "assumevalues4",
             "assumevalues5",
-            "annotationremoval");
+            "annotationremoval",
+            "memberrebinding2",
+            "memberrebinding3");
 
     // Keys can be the name of the test or the name of the test followed by a colon and the name
     // of the keep file.