Merge "Allow gradle benchmarks to run without downloading"
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 4a26889..39bdd96 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -25,6 +25,7 @@
 import java.io.IOException;
 import java.io.OutputStreamWriter;
 import java.nio.charset.StandardCharsets;
+import java.util.Collections;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 
@@ -135,6 +136,9 @@
     if (options.hasMarker()) {
       return options.getMarker();
     }
+    if (options.testing.dontCreateMarkerInD8) {
+      return null;
+    }
     Marker marker = new Marker(Tool.D8)
         .setVersion(Version.LABEL)
         .setMinApi(options.minApiLevel);
@@ -163,7 +167,14 @@
         options.methodsFilter.forEach((m) -> System.out.println("  - " + m));
       }
       Marker marker = getMarker(options);
-      new ApplicationWriter(app, options, marker, null, NamingLens.getIdentityLens(), null, null)
+      new ApplicationWriter(
+              app,
+              options,
+              marker == null ? null : Collections.singletonList(marker),
+              null,
+              NamingLens.getIdentityLens(),
+              null,
+              null)
           .write(executor);
       options.printWarnings();
     } catch (ExecutionException e) {
diff --git a/src/main/java/com/android/tools/r8/DexFileMergerHelper.java b/src/main/java/com/android/tools/r8/DexFileMergerHelper.java
index 2e121c3..06f6d03 100644
--- a/src/main/java/com/android/tools/r8/DexFileMergerHelper.java
+++ b/src/main/java/com/android/tools/r8/DexFileMergerHelper.java
@@ -6,6 +6,7 @@
 
 import com.android.tools.r8.dex.ApplicationReader;
 import com.android.tools.r8.dex.ApplicationWriter;
+import com.android.tools.r8.dex.Marker;
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexProgramClass;
@@ -16,6 +17,7 @@
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
 import java.io.IOException;
+import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
@@ -86,9 +88,10 @@
         AppInfo appInfo = new AppInfo(app);
         app = D8.optimize(app, appInfo, options, timing, executor);
 
+        List<Marker> markers = app.dexItemFactory.extractMarkers();
+
         assert !options.hasMethodsFilter();
-        new ApplicationWriter(
-                app, options, D8.getMarker(options), null, NamingLens.getIdentityLens(), null, null)
+        new ApplicationWriter(app, options, markers, null, NamingLens.getIdentityLens(), null, null)
             .write(executor);
         options.printWarnings();
       } catch (ExecutionException e) {
@@ -101,4 +104,11 @@
       executor.shutdown();
     }
   }
+
+  public static void runD8ForTesting(D8Command command, boolean dontCreateMarkerInD8)
+      throws IOException, CompilationException {
+    InternalOptions options = command.getInternalOptions();
+    options.testing.dontCreateMarkerInD8 = dontCreateMarkerInD8;
+    D8.runForTesting(command.getInputApp(), options);
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/DexSplitterHelper.java b/src/main/java/com/android/tools/r8/DexSplitterHelper.java
index 8573e0f..ee6c012 100644
--- a/src/main/java/com/android/tools/r8/DexSplitterHelper.java
+++ b/src/main/java/com/android/tools/r8/DexSplitterHelper.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.DexIndexedConsumer.DirectoryConsumer;
 import com.android.tools.r8.dex.ApplicationReader;
 import com.android.tools.r8.dex.ApplicationWriter;
+import com.android.tools.r8.dex.Marker;
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexApplication.Builder;
@@ -23,6 +24,7 @@
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.concurrent.ExecutionException;
@@ -51,6 +53,7 @@
         DexApplication app =
             new ApplicationReader(command.getInputApp(), options, timing).read(null, executor);
 
+        List<Marker> markers = app.dexItemFactory.extractMarkers();
 
         ClassNameMapper mapper = null;
         if (proguardMap != null) {
@@ -77,7 +80,7 @@
             new ApplicationWriter(
                     featureApp,
                     options,
-                    D8.getMarker(options),
+                    markers,
                     null,
                     NamingLens.getIdentityLens(),
                     null,
@@ -119,4 +122,11 @@
     }
     return applications;
   }
+
+  public static void runD8ForTesting(D8Command command, boolean dontCreateMarkerInD8)
+      throws IOException, CompilationException {
+    InternalOptions options = command.getInternalOptions();
+    options.testing.dontCreateMarkerInD8 = dontCreateMarkerInD8;
+    D8.runForTesting(command.getInputApp(), options);
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 4f12ce1..6821c4e 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -70,6 +70,7 @@
 import java.io.PrintStream;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.StandardOpenOption;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.Set;
 import java.util.concurrent.ExecutionException;
@@ -195,7 +196,7 @@
         new ApplicationWriter(
                 application,
                 options,
-                marker,
+                marker == null ? null : Collections.singletonList(marker),
                 deadCode,
                 namingLens,
                 proguardSeedsData,
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 210a632..cde7e93 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
 
   // This field is accessed from release scripts using simple pattern matching.
   // Therefore, changing this field could break our release scripts.
-  public static final String LABEL = "v1.2.9-dev";
+  public static final String LABEL = "v1.2.10-dev";
 
   private Version() {
   }
diff --git a/src/main/java/com/android/tools/r8/cf/CfRegisterAllocator.java b/src/main/java/com/android/tools/r8/cf/CfRegisterAllocator.java
index c2cfe42..ae43001 100644
--- a/src/main/java/com/android/tools/r8/cf/CfRegisterAllocator.java
+++ b/src/main/java/com/android/tools/r8/cf/CfRegisterAllocator.java
@@ -97,6 +97,11 @@
   }
 
   @Override
+  public InternalOptions getOptions() {
+    return options;
+  }
+
+  @Override
   public void allocateRegisters(boolean debug) {
     assert options.debug == debug;
     allocateRegisters();
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index 30664a2..d5a7013 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -46,7 +46,7 @@
   public final NamingLens namingLens;
   public final String proguardSeedsData;
   public final InternalOptions options;
-  public DexString markerString;
+  public List<DexString> markerStrings;
   public DexIndexedConsumer programConsumer;
   public final ProguardMapSupplier proguardMapSupplier;
 
@@ -110,7 +110,7 @@
   public ApplicationWriter(
       DexApplication application,
       InternalOptions options,
-      Marker marker,
+      List<Marker> markers,
       String deadCode,
       NamingLens namingLens,
       String proguardSeedsData,
@@ -118,7 +118,7 @@
     this(
         application,
         options,
-        marker,
+        markers,
         deadCode,
         namingLens,
         proguardSeedsData,
@@ -129,7 +129,7 @@
   public ApplicationWriter(
       DexApplication application,
       InternalOptions options,
-      Marker marker,
+      List<Marker> markers,
       String deadCode,
       NamingLens namingLens,
       String proguardSeedsData,
@@ -139,9 +139,12 @@
     this.application = application;
     assert options != null;
     this.options = options;
-    this.markerString = (marker == null)
-        ? null
-        : application.dexItemFactory.createString(marker.toString());
+    if (markers != null && !markers.isEmpty()) {
+      this.markerStrings = new ArrayList<>();
+      for (Marker marker : markers) {
+        this.markerStrings.add(application.dexItemFactory.createString(marker.toString()));
+      }
+    }
     this.deadCode = deadCode;
     this.namingLens = namingLens;
     this.proguardSeedsData = proguardSeedsData;
@@ -174,7 +177,9 @@
       insertAttributeAnnotations();
 
       application.dexItemFactory.sort(namingLens);
-      assert this.markerString == null || application.dexItemFactory.extractMarker() != null;
+      assert this.markerStrings == null
+          || this.markerStrings.isEmpty()
+          || application.dexItemFactory.extractMarker() != null;
 
       SortAnnotations sortAnnotations = new SortAnnotations();
       application.classes().forEach((clazz) -> clazz.addDependencies(sortAnnotations));
diff --git a/src/main/java/com/android/tools/r8/dex/VirtualFile.java b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
index 5369069..ae6acd9 100644
--- a/src/main/java/com/android/tools/r8/dex/VirtualFile.java
+++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -275,8 +275,10 @@
       mainDexFile = new VirtualFile(0, writer.namingLens);
       assert virtualFiles.isEmpty();
       virtualFiles.add(mainDexFile);
-      if (writer.markerString != null) {
-        mainDexFile.transaction.addString(writer.markerString);
+      if (writer.markerStrings != null && !writer.markerStrings.isEmpty()) {
+        for (DexString markerString : writer.markerStrings) {
+          mainDexFile.transaction.addString(markerString);
+        }
         mainDexFile.commitTransaction();
       }
 
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 5fe9134..364c0ee 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -483,7 +483,7 @@
   }
 
   // Debugging support to extract marking string.
-  synchronized public Marker extractMarker() {
+  public synchronized Marker extractMarker() {
     // This is slow but it is not needed for any production code yet.
     for (DexString dexString : strings.keySet()) {
       Marker result = Marker.parse(dexString.toString());
@@ -494,6 +494,20 @@
     return null;
   }
 
+  // Debugging support to extract marking string.
+  // Find all markers.
+  public synchronized List<Marker> extractMarkers() {
+    // This is slow but it is not needed for any production code yet.
+    List<Marker> markers = new ArrayList<>();
+    for (DexString dexString : strings.keySet()) {
+      Marker marker = Marker.parse(dexString.toString());
+      if (marker != null) {
+        markers.add(marker);
+      }
+    }
+    return markers;
+  }
+
   synchronized public DexType createType(DexString descriptor) {
     assert !sorted;
     assert descriptor != null;
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 163c20b..60703de 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
@@ -495,6 +495,8 @@
 
   public void removePhi(Phi phi) {
     phis.remove(phi);
+    assert currentDefinitions == null || !currentDefinitions.containsValue(phi)
+        : "Attempt to remove Phi " + phi + " which is present in currentDefinitions";
   }
 
   public void add(Instruction next) {
@@ -678,7 +680,17 @@
   public void replaceCurrentDefinitions(Value oldValue, Value newValue) {
     assert oldValue.definition.getBlock() == this;
     assert !oldValue.isUsed();
-    currentDefinitions.replaceAll((index, value) -> value == oldValue ? newValue : value);
+    for (Entry<Integer, Value> entry : currentDefinitions.entrySet()) {
+      if (entry.getValue() == oldValue) {
+        if (oldValue.isPhi()) {
+          oldValue.asPhi().removeDefinitionsUser(currentDefinitions);
+        }
+        entry.setValue(newValue);
+        if (newValue.isPhi()) {
+          newValue.asPhi().addDefinitionsUser(currentDefinitions);
+        }
+      }
+    }
   }
 
   public void updateCurrentDefinition(int register, Value value, EdgeType readingEdge) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/Binop.java b/src/main/java/com/android/tools/r8/ir/code/Binop.java
index f751669..c2f21ac 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Binop.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Binop.java
@@ -26,8 +26,13 @@
   public Binop(NumericType type, Value dest, Value left, Value right) {
     super(dest);
     this.type = type;
-    addInValue(left);
-    addInValue(right);
+    if (isCommutative() && (!right.isConstNumber() && left.isConstNumber())) {
+      addInValue(right);
+      addInValue(left);
+    } else {
+      addInValue(left);
+      addInValue(right);
+    }
   }
 
   public NumericType getNumericType() {
@@ -53,7 +58,8 @@
       return ((leftRegister == destRegister) ||
           (isCommutative() && rightRegister == destRegister)) &&
           leftRegister <= U4BIT_MAX &&
-          rightRegister <= U4BIT_MAX;
+          rightRegister <= U4BIT_MAX &&
+          !(allocator.getOptions().canHaveMul2AddrBug() && isMul());
     }
     return false;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Invoke.java b/src/main/java/com/android/tools/r8/ir/code/Invoke.java
index af6d5a3..6f535bb 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Invoke.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Invoke.java
@@ -103,7 +103,7 @@
   }
 
   protected int argumentRegisterValue(int i, DexBuilder builder) {
-    assert needsRangedInvoke(builder);
+    assert needsRangedInvoke();
     if (i < arguments().size()) {
       // If argument values flow into ranged invokes, all the ranged invoke arguments
       // are arguments to this method in order. Therefore, we use the incoming registers
@@ -116,9 +116,11 @@
   }
 
   protected int fillArgumentRegisters(DexBuilder builder, int[] registers) {
+    assert !needsRangedInvoke();
     int i = 0;
     for (Value value : arguments()) {
       int register = builder.allocatedRegister(value, getNumber());
+      assert register <= Constants.U4BIT_MAX;
       for (int j = 0; j < value.requiredRegisters(); j++) {
         assert i < 5;
         registers[i++] = register++;
@@ -127,15 +129,6 @@
     return i;
   }
 
-  protected boolean hasHighArgumentRegister(DexBuilder builder) {
-    for (Value value : arguments()) {
-      if (builder.argumentValueUsesHighRegister(value, getNumber())) {
-        return true;
-      }
-    }
-    return false;
-  }
-
   protected boolean argumentsConsecutive(DexBuilder builder) {
     Value value = arguments().get(0);
     int next = builder.argumentOrAllocateRegister(value, getNumber()) + value.requiredRegisters();
@@ -206,19 +199,8 @@
     return true;
   }
 
-  private boolean argumentsAreConsecutiveInputArgumentsWithHighRegisters(
-      DexBuilder builder) {
-    if (!argumentsAreConsecutiveInputArguments()) {
-      return false;
-    }
-    Value lastArgument = arguments().get(arguments().size() - 1);
-    return builder.argumentOrAllocateRegister(lastArgument, getNumber()) > Constants.U4BIT_MAX;
-  }
-
-  protected boolean needsRangedInvoke(DexBuilder builder) {
-    return requiredArgumentRegisters() > 5
-        || hasHighArgumentRegister(builder)
-        || argumentsAreConsecutiveInputArgumentsWithHighRegisters(builder);
+  protected boolean needsRangedInvoke() {
+    return requiredArgumentRegisters() > 5 || argumentsAreConsecutiveInputArguments();
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java b/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
index 0579362..9d95514 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
@@ -54,7 +54,7 @@
     com.android.tools.r8.code.Instruction instruction;
     int argumentRegisters = requiredArgumentRegisters();
     builder.requestOutgoingRegisters(argumentRegisters);
-    if (needsRangedInvoke(builder)) {
+    if (needsRangedInvoke()) {
       assert argumentsConsecutive(builder);
       int firstRegister = argumentRegisterValue(0, builder);
       instruction = new InvokeCustomRange(firstRegister, argumentRegisters, getCallSite());
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
index fa85845..12c991d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
@@ -52,7 +52,7 @@
     com.android.tools.r8.code.Instruction instruction;
     int argumentRegisters = requiredArgumentRegisters();
     builder.requestOutgoingRegisters(argumentRegisters);
-    if (needsRangedInvoke(builder)) {
+    if (needsRangedInvoke()) {
       assert argumentsConsecutive(builder);
       int firstRegister = argumentRegisterValue(0, builder);
       instruction = new InvokeDirectRange(firstRegister, argumentRegisters, getInvokedMethod());
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java b/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
index d29e3eb..e886f27 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
@@ -46,7 +46,7 @@
     com.android.tools.r8.code.Instruction instruction;
     int argumentRegisters = requiredArgumentRegisters();
     builder.requestOutgoingRegisters(argumentRegisters);
-    if (needsRangedInvoke(builder)) {
+    if (needsRangedInvoke()) {
       assert argumentsConsecutive(builder);
       int firstRegister = argumentRegisterValue(0, builder);
       instruction = new InvokeInterfaceRange(firstRegister, argumentRegisters, getInvokedMethod());
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java b/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java
index 41e4ccf..746f87a 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java
@@ -56,7 +56,7 @@
     com.android.tools.r8.code.Instruction instruction;
     int argumentRegisters = requiredArgumentRegisters();
     builder.requestOutgoingRegisters(argumentRegisters);
-    if (needsRangedInvoke(builder)) {
+    if (needsRangedInvoke()) {
       assert argumentsConsecutive(builder);
       int firstRegister = argumentRegisterValue(0, builder);
       instruction = new FilledNewArrayRange(firstRegister, argumentRegisters, type);
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java b/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
index 0e63cc7..624c534 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
@@ -60,7 +60,7 @@
     com.android.tools.r8.code.Instruction instruction;
     int argumentRegisters = requiredArgumentRegisters();
     builder.requestOutgoingRegisters(argumentRegisters);
-    if (needsRangedInvoke(builder)) {
+    if (needsRangedInvoke()) {
       assert argumentsConsecutive(builder);
       int firstRegister = argumentRegisterValue(0, builder);
       instruction = new InvokePolymorphicRange(
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
index 896fca9..1930948 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
@@ -53,7 +53,7 @@
     com.android.tools.r8.code.Instruction instruction;
     int argumentRegisters = requiredArgumentRegisters();
     builder.requestOutgoingRegisters(argumentRegisters);
-    if (needsRangedInvoke(builder)) {
+    if (needsRangedInvoke()) {
       assert argumentsConsecutive(builder);
       int firstRegister = argumentRegisterValue(0, builder);
       instruction = new InvokeStaticRange(firstRegister, argumentRegisters, getInvokedMethod());
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java b/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
index c761184..3a70bac 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
@@ -58,7 +58,7 @@
     com.android.tools.r8.code.Instruction instruction;
     int argumentRegisters = requiredArgumentRegisters();
     builder.requestOutgoingRegisters(argumentRegisters);
-    if (needsRangedInvoke(builder)) {
+    if (needsRangedInvoke()) {
       assert argumentsConsecutive(builder);
       int firstRegister = argumentRegisterValue(0, builder);
       instruction = new InvokeSuperRange(firstRegister, argumentRegisters, getInvokedMethod());
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
index 5ef74b8..50b83c6 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
@@ -46,7 +46,7 @@
     com.android.tools.r8.code.Instruction instruction;
     int argumentRegisters = requiredArgumentRegisters();
     builder.requestOutgoingRegisters(argumentRegisters);
-    if (needsRangedInvoke(builder)) {
+    if (needsRangedInvoke()) {
       assert argumentsConsecutive(builder);
       int firstRegister = argumentRegisterValue(0, builder);
       instruction = new InvokeVirtualRange(firstRegister, argumentRegisters, getInvokedMethod());
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index a54208d..67a4783 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -1549,8 +1549,7 @@
     insertAt.nextUntil(i ->
         i.inValues().contains(instruction.outValue())
         || i.isJumpInstruction()
-        || (hasCatchHandlers && i.instructionTypeCanThrow())
-        || (options.canHaveBoundsCheckEliminationBug() && i.isArrayLength()));
+        || (hasCatchHandlers && i.instructionTypeCanThrow()));
     Instruction next = insertAt.previous();
     instruction.forceSetPosition(
         next.isGoto() ? next.asGoto().getTarget().getPosition() : next.getPosition());
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index 678ddae..7eb771c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -205,7 +205,7 @@
         }
         return NEVER;
       } else {
-        /* package-private */
+      /* package-private */
         return targetHolder.isSamePackage(contextHolder) ? PACKAGE : NEVER;
       }
     }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroup.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroup.java
index b4a1865..c4e2ab6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroup.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroup.java
@@ -160,8 +160,9 @@
     int validateInstanceInitializerEpilogue(
         com.android.tools.r8.code.Instruction[] instructions, int index)
         throws LambdaStructureError {
-      if (!(instructions[index] instanceof com.android.tools.r8.code.InvokeDirect) ||
-          instructions[index].getMethod() != kotlin.factory.objectMethods.constructor) {
+      if (!(instructions[index] instanceof com.android.tools.r8.code.InvokeDirect
+              || instructions[index] instanceof com.android.tools.r8.code.InvokeDirectRange)
+          || instructions[index].getMethod() != kotlin.factory.objectMethods.constructor) {
         throw structureError(LAMBDA_INIT_CODE_VERIFICATION_FAILED);
       }
       index++;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroup.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroup.java
index 46c14e9..46add5f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroup.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroup.java
@@ -170,8 +170,9 @@
         throw structureError(LAMBDA_INIT_CODE_VERIFICATION_FAILED);
       }
       index++;
-      if (!(instructions[index] instanceof com.android.tools.r8.code.InvokeDirect) ||
-          instructions[index].getMethod() != kotlin.functional.lambdaInitializerMethod) {
+      if (!(instructions[index] instanceof com.android.tools.r8.code.InvokeDirect
+              || instructions[index] instanceof com.android.tools.r8.code.InvokeDirectRange)
+          || instructions[index].getMethod() != kotlin.functional.lambdaInitializerMethod) {
         throw structureError(LAMBDA_INIT_CODE_VERIFICATION_FAILED);
       }
       index++;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaClassValidator.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaClassValidator.java
index 3410703..2385f35 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaClassValidator.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaClassValidator.java
@@ -225,8 +225,9 @@
         ((com.android.tools.r8.code.NewInstance) instructions[0]).getType() != lambda.type) {
       throw structureError(LAMBDA_CLINIT_CODE_VERIFICATION_FAILED);
     }
-    if (!(instructions[1] instanceof com.android.tools.r8.code.InvokeDirect) ||
-        !isLambdaInitializerMethod(lambda, instructions[1].getMethod())) {
+    if (!(instructions[1] instanceof com.android.tools.r8.code.InvokeDirect
+            || instructions[1] instanceof com.android.tools.r8.code.InvokeDirectRange)
+        || !isLambdaInitializerMethod(lambda, instructions[1].getMethod())) {
       throw structureError(LAMBDA_CLINIT_CODE_VERIFICATION_FAILED);
     }
     if (!(instructions[2] instanceof SputObject) ||
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
index c6477f3..8ad7a32 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
@@ -75,7 +75,6 @@
 
   public static final int REGISTER_CANDIDATE_NOT_FOUND = -1;
   public static final int MIN_CONSTANT_FREE_FOR_POSITIONS = 5;
-  private static final int RESERVED_MOVE_EXCEPTION_REGISTER = 0;
 
   private enum ArgumentReuseMode {
     ALLOW_ARGUMENT_REUSE,
@@ -113,8 +112,6 @@
 
   // The max register number that will fit in any dex instruction encoding.
   private static final int MAX_SMALL_REGISTER = Constants.U4BIT_MAX;
-  // We put one sentinel in front of the argument chain and one after the argument chain.
-  private static final int NUMBER_OF_SENTINEL_REGISTERS = 2;
 
   // The code for which to allocate registers.
   private final IRCode code;
@@ -125,8 +122,8 @@
 
   // Mapping from basic blocks to the set of values live at entry to that basic block.
   private Map<BasicBlock, Set<Value>> liveAtEntrySets;
-  // The sentinel value starting the chain of linked argument values.
-  private Value preArgumentSentinelValue = null;
+  // The value of the first argument, or null if the method has no arguments.
+  private Value firstArgumentValue;
 
   // The set of registers that are free for allocation.
   private TreeSet<Integer> freeRegisters = new TreeSet<>();
@@ -157,9 +154,10 @@
   // register.
   private boolean hasDedicatedMoveExceptionRegister = false;
 
+  // We allocate a dedicated move exception register right after the arguments.
   private int getMoveExceptionRegister() {
-    return numberOfArgumentRegisters + NUMBER_OF_SENTINEL_REGISTERS +
-        RESERVED_MOVE_EXCEPTION_REGISTER;
+    assert hasDedicatedMoveExceptionRegister;
+    return numberOfArgumentRegisters;
   }
 
   private int getNextUnusedRegisterNumber() {
@@ -300,10 +298,11 @@
     ranges.sort(LocalRange::compareTo);
 
     // At each instruction compute the changes to live locals.
-    boolean localsChanged = false;
     LinkedList<LocalRange> openRanges = new LinkedList<>();
     Iterator<LocalRange> rangeIterator = ranges.iterator();
     LocalRange nextStartingRange = rangeIterator.next();
+    Int2ReferenceMap<DebugLocalInfo> ending = new Int2ReferenceOpenHashMap<>();
+    Int2ReferenceMap<DebugLocalInfo> starting = new Int2ReferenceOpenHashMap<>();
 
     for (BasicBlock block : blocks) {
       // Skip past all spill moves to obtain the instruction number of the actual first instruction.
@@ -349,42 +348,52 @@
           instruction.clearDebugValues();
           instructionIterator.remove();
         }
-        if (isSpillInstruction(instruction)) {
+        if (!instructionIterator.hasNext()) {
+          break;
+        }
+        Instruction nextInstruction = instructionIterator.peekNext();
+        if (isSpillInstruction(nextInstruction)) {
+          // No need to insert a DebugLocalsChange instruction before a spill instruction.
           continue;
         }
-        int index = instruction.getNumber();
+        int index = nextInstruction.getNumber();
         ListIterator<LocalRange> it = openRanges.listIterator(0);
-        Int2ReferenceMap<DebugLocalInfo> ending = new Int2ReferenceOpenHashMap<>();
-        Int2ReferenceMap<DebugLocalInfo> starting = new Int2ReferenceOpenHashMap<>();
         while (it.hasNext()) {
           LocalRange openRange = it.next();
-          // Any local change is inserted after the instruction so end is inclusive.
-          if (openRange.end <= index) {
+          // Close ranges up-to but excluding the first instruction.
+          if (!isLocalLiveAtInstruction(nextInstruction, openRange)) {
             it.remove();
             assert currentLocals.get(openRange.register) == openRange.local;
             currentLocals.remove(openRange.register);
-            localsChanged = true;
             ending.put(openRange.register, openRange.local);
           }
         }
-        while (nextStartingRange != null && nextStartingRange.start <= index) {
+        while (nextStartingRange != null && nextStartingRange.start < index) {
           // If the range is live at this index open it.
-          if (index < nextStartingRange.end) {
+          if (isLocalLiveAtInstruction(nextInstruction, nextStartingRange)) {
             openRanges.add(nextStartingRange);
             assert !currentLocals.containsKey(nextStartingRange.register);
             currentLocals.put(nextStartingRange.register, nextStartingRange.local);
             starting.put(nextStartingRange.register, nextStartingRange.local);
-            localsChanged = true;
           }
           nextStartingRange = rangeIterator.hasNext() ? rangeIterator.next() : null;
         }
-        if (localsChanged && instruction.getBlock().exit() != instruction) {
-          DebugLocalsChange change = createLocalsChange(ending, starting);
-          if (change != null) {
-            instructionIterator.add(change);
+        // Compute the final change in locals and insert it before nextInstruction.
+        boolean localsChanged = !ending.isEmpty() || !starting.isEmpty();
+        if (localsChanged) {
+          boolean skipChange =
+              nextInstruction == nextInstruction.getBlock().exit() && nextInstruction.isGoto();
+          if (!skipChange) {
+            DebugLocalsChange change = createLocalsChange(ending, starting);
+            if (change != null) {
+              // Insert the DebugLocalsChange instruction before nextInstruction.
+              instructionIterator.add(change);
+            }
           }
+          // Create new maps for the next DebugLocalsChange instruction.
+          ending = new Int2ReferenceOpenHashMap<>();
+          starting = new Int2ReferenceOpenHashMap<>();
         }
-        localsChanged = false;
       }
     }
   }
@@ -393,11 +402,6 @@
     return isLocalLiveAtInstruction(instruction, range.start, range.end, range.value);
   }
 
-  public static boolean isLocalLiveAtInstruction(
-      Instruction instruction, LiveRange range, Value value) {
-    return isLocalLiveAtInstruction(instruction, range.start, range.end, value);
-  }
-
   private static boolean isLocalLiveAtInstruction(
       Instruction instruction, int start, int end, Value value) {
     int number = instruction.getNumber();
@@ -515,12 +519,9 @@
     // Compute the set of registers that is used based on all live intervals.
     Set<Integer> usedRegisters = new HashSet<>();
     for (LiveIntervals intervals : liveIntervals) {
-      // Argument sentinel values do not occupy any registers.
-      if (!isArgumentSentinelIntervals(intervals)) {
-        addRegisterIfUsed(usedRegisters, intervals);
-        for (LiveIntervals childIntervals : intervals.getSplitChildren()) {
-          addRegisterIfUsed(usedRegisters, childIntervals);
-        }
+      addRegisterIfUsed(usedRegisters, intervals);
+      for (LiveIntervals childIntervals : intervals.getSplitChildren()) {
+        addRegisterIfUsed(usedRegisters, childIntervals);
       }
     }
     // Additionally, we have used temporary registers for parallel move scheduling, those
@@ -540,11 +541,6 @@
     unusedRegisters = computed;
   }
 
-  private boolean isArgumentSentinelIntervals(LiveIntervals intervals) {
-    return intervals.isArgumentInterval() &&
-            (intervals.getPreviousConsecutive() == null || intervals.getNextConsecutive() == null);
-  }
-
   private void addRegisterIfUsed(Set<Integer> used, LiveIntervals intervals) {
     boolean unused = intervals.isSpilledAndRematerializable(this);
     if (!unused) {
@@ -567,7 +563,7 @@
 
   @Override
   public int registersUsed() {
-    int numberOfRegister = maxRegisterNumber + 1 - NUMBER_OF_SENTINEL_REGISTERS;
+    int numberOfRegister = maxRegisterNumber + 1;
     if (unusedRegisters != null) {
       return numberOfRegister - unusedRegisters[unusedRegisters.length - 1];
     }
@@ -594,6 +590,11 @@
     return getRegisterForValue(value, instructionNumber);
   }
 
+  @Override
+  public InternalOptions getOptions() {
+    return options;
+  }
+
   private ImmutableList<BasicBlock> computeLivenessInformation() {
     ImmutableList<BasicBlock> blocks = code.numberInstructions();
     liveAtEntrySets = code.computeLiveAtEntrySets();
@@ -627,7 +628,7 @@
   // for all uses and thereby avoid moving argument values around.
   private boolean unsplitArguments() {
     boolean argumentRegisterUnsplit = false;
-    Value current = preArgumentSentinelValue;
+    Value current = firstArgumentValue;
     while (current != null) {
       LiveIntervals intervals = current.getLiveIntervals();
       assert intervals.getRegisterLimit() == Constants.U16BIT_MAX;
@@ -701,15 +702,12 @@
     assert allocated != NO_REGISTER;
     assert allocated >= 0;
     int register;
-    if (allocated < numberOfArgumentRegisters + NUMBER_OF_SENTINEL_REGISTERS) {
+    if (allocated < numberOfArgumentRegisters) {
       // For the |numberOfArguments| first registers map to the correct argument register.
-      // Due to the sentinel register in front of the arguments, the register number returned is
-      // the argument number starting from one.
-      register = maxRegisterNumber - (numberOfArgumentRegisters - allocated)
-          - NUMBER_OF_SENTINEL_REGISTERS;
+      register = maxRegisterNumber - (numberOfArgumentRegisters - allocated - 1);
     } else {
       // For everything else use the lower numbers.
-      register = allocated - numberOfArgumentRegisters - NUMBER_OF_SENTINEL_REGISTERS;
+      register = allocated - numberOfArgumentRegisters;
     }
     return register;
   }
@@ -730,8 +728,10 @@
 
   private boolean performLinearScan(ArgumentReuseMode mode) {
     unhandled.addAll(liveIntervals);
-    LiveIntervals argumentInterval = preArgumentSentinelValue.getLiveIntervals();
-    while (argumentInterval != null) {
+
+    Value argumentValue = firstArgumentValue;
+    while (argumentValue != null) {
+      LiveIntervals argumentInterval = argumentValue.getLiveIntervals();
       assert argumentInterval.getRegister() != NO_REGISTER;
       unhandled.remove(argumentInterval);
       if (mode == ArgumentReuseMode.ALLOW_ARGUMENT_REUSE) {
@@ -758,7 +758,7 @@
           }
         }
       }
-      argumentInterval = argumentInterval.getNextConsecutive();
+      argumentValue = argumentValue.getNextConsecutive();
     }
 
     // We have to be careful when it comes to the register allocated for a move exception
@@ -783,7 +783,6 @@
             unhandled.remove(intervals);
             moveExceptionIntervals.add(intervals);
             if (moveExceptionRegister == NO_REGISTER) {
-              assert RESERVED_MOVE_EXCEPTION_REGISTER == 0;
               moveExceptionRegister = getFreeConsecutiveRegisters(1);
               assert moveExceptionRegister == getMoveExceptionRegister();
               assert !freeRegisters.contains(moveExceptionRegister);
@@ -957,6 +956,7 @@
           allocateLinkedIntervals(destIntervals);
           // Restore the register allocation state.
           freeRegisters = savedFreeRegisters;
+          // In case maxRegisterNumber has changed, update freeRegisters.
           for (int i = savedUnusedRegisterNumber, j = getNextUnusedRegisterNumber(); i < j; i++) {
             freeRegisters.add(i);
           }
@@ -993,7 +993,6 @@
     // Exclude move exception register if the first interval overlaps a move exception interval.
     if (overlapsMoveExceptionInterval(current) &&
         freeRegisters.remove(getMoveExceptionRegister())) {
-      assert RESERVED_MOVE_EXCEPTION_REGISTER == 0;
       excludedRegisters.add(getMoveExceptionRegister());
     }
     // Select registers.
@@ -1260,13 +1259,9 @@
     }
 
     if (registerConstraint < Constants.U16BIT_MAX) {
-      // We always have argument sentinels that will not actually occupy registers. Therefore, we
-      // allow the use of NUMBER_OF_SENTINEL_REGISTERS more than the limit.
-      registerConstraint += NUMBER_OF_SENTINEL_REGISTERS;
       if (mode == ArgumentReuseMode.DISALLOW_ARGUMENT_REUSE) {
         // We know that none of the argument registers will be reused. Therefore, we allow the
-        // use of number of arguments more registers. (We will never use number of arguments +
-        // number of sentinel registers of them.)
+        // use of number of arguments more registers.
         registerConstraint += numberOfArgumentRegisters;
       }
     }
@@ -1275,26 +1270,18 @@
     RegisterPositions freePositions = new RegisterPositions(registerConstraint + 1);
 
     if (mode == ArgumentReuseMode.ALLOW_ARGUMENT_REUSE) {
-      // The sentinel registers cannot be used and we block them.
-      freePositions.set(0, 0);
       if (options.debug && !code.method.accessFlags.isStatic()) {
         // If we are generating debug information, we pin the this value register since the
         // debugger expects to always be able to find it in the input register.
         assert numberOfArgumentRegisters > 0;
-        assert preArgumentSentinelValue.getNextConsecutive().requiredRegisters() == 1;
-        freePositions.set(1, 0);
-      }
-      int lastSentinelRegister = numberOfArgumentRegisters + NUMBER_OF_SENTINEL_REGISTERS - 1;
-      if (lastSentinelRegister <= registerConstraint) {
-        freePositions.set(lastSentinelRegister, 0);
+        assert firstArgumentValue != null && firstArgumentValue.requiredRegisters() == 1;
+        freePositions.set(0, 0);
       }
     } else {
       // Argument reuse is not allowed and we block all the argument registers so that
       // arguments are never free.
-      for (int i = 0; i < numberOfArgumentRegisters + NUMBER_OF_SENTINEL_REGISTERS; i++) {
-        if (i <= registerConstraint) {
-          freePositions.set(i, 0);
-        }
+      for (int i = 0; i < numberOfArgumentRegisters && i <= registerConstraint; i++) {
+        freePositions.set(i, 0);
       }
     }
 
@@ -1541,8 +1528,11 @@
     active.add(unhandledInterval);
   }
 
-  private int getLargestCandidate(int registerConstraint, RegisterPositions freePositions,
-      boolean needsRegisterPair, RegisterPositions.Type type) {
+  private int getLargestCandidate(
+      int registerConstraint,
+      RegisterPositions freePositions,
+      boolean needsRegisterPair,
+      RegisterPositions.Type type) {
     int candidate = REGISTER_CANDIDATE_NOT_FOUND;
     int largest = -1;
 
@@ -1552,6 +1542,10 @@
       }
       int freePosition = freePositions.get(i);
       if (needsRegisterPair) {
+        if (i == numberOfArgumentRegisters - 1) {
+          // The last register of the method is |i|, so we cannot use the pair (|i|, |i+1|).
+          continue;
+        }
         if (i >= registerConstraint) {
           break;
         }
@@ -1615,11 +1609,8 @@
   private void allocateBlockedRegister(LiveIntervals unhandledInterval) {
     int registerConstraint = unhandledInterval.getRegisterLimit();
     if (registerConstraint < Constants.U16BIT_MAX) {
-      // We always have argument sentinels that will not actually occupy registers. Therefore, we
-      // allow the use of NUMBER_OF_SENTINEL_REGISTERS more than the limit. Additionally, we never
-      // reuse argument registers and therefore allow the use of numberOfArgumentRegisters as well.
+      // We never reuse argument registers and therefore allow the use of numberOfArgumentRegisters.
       registerConstraint += numberOfArgumentRegisters;
-      registerConstraint += NUMBER_OF_SENTINEL_REGISTERS;
     }
 
     // Initialize all candidate registers to Integer.MAX_VALUE.
@@ -1657,7 +1648,7 @@
 
     // Disallow the reuse of argument registers by always treating them as being used
     // at instruction number 0.
-    for (int i = 0; i < numberOfArgumentRegisters + NUMBER_OF_SENTINEL_REGISTERS; i++) {
+    for (int i = 0; i < numberOfArgumentRegisters; i++) {
       usePositions.set(i, 0);
     }
 
@@ -1949,8 +1940,7 @@
   }
 
   private void insertMoves() {
-    SpillMoveSet spillMoves =
-        new SpillMoveSet(this, code, numberOfArgumentRegisters + NUMBER_OF_SENTINEL_REGISTERS);
+    SpillMoveSet spillMoves = new SpillMoveSet(this, code, numberOfArgumentRegisters);
     for (LiveIntervals intervals : liveIntervals) {
       if (intervals.hasSplits()) {
         LiveIntervals current = intervals;
@@ -2261,53 +2251,36 @@
     return true;
   }
 
-  private Value createSentinelRegisterValue() {
-    return createValue(ValueType.OBJECT);
-  }
-
-  private LiveIntervals createSentinelLiveInterval(Value sentinelValue) {
-    LiveIntervals sentinelInterval = new LiveIntervals(sentinelValue);
-    sentinelInterval.addRange(LiveRange.INFINITE);
-    liveIntervals.add(sentinelInterval);
-    return sentinelInterval;
-  }
-
-  private void linkArgumentValues() {
-    Value current = preArgumentSentinelValue = createSentinelRegisterValue();
-    for (Value argument : code.collectArguments()) {
-      current.linkTo(argument);
-      current = argument;
-    }
-    Value endSentinel = createSentinelRegisterValue();
-    current.linkTo(endSentinel);
-  }
-
-  private void setupArgumentLiveIntervals() {
-    Value current = preArgumentSentinelValue;
-    LiveIntervals currentIntervals = createSentinelLiveInterval(current);
-    current = current.getNextConsecutive();
+  private void createArgumentLiveIntervals(List<Value> arguments) {
     int index = 0;
-    while (current.getNextConsecutive() != null) {
+    for (Value argument : arguments) {
       // Add a live range to this value from the beginning of the block up to the argument
       // instruction to avoid dead arguments without a range. This may create an actually empty
       // range like [0,0[ but that works, too.
-      LiveIntervals argumentInterval = new LiveIntervals(current);
-      argumentInterval.addRange(new LiveRange(0, index * INSTRUCTION_NUMBER_DELTA));
-      index++;
+      LiveIntervals argumentInterval = new LiveIntervals(argument);
+      argumentInterval.addRange(new LiveRange(0, index));
       liveIntervals.add(argumentInterval);
-      currentIntervals.link(argumentInterval);
-      currentIntervals = argumentInterval;
-      current = current.getNextConsecutive();
+      index += INSTRUCTION_NUMBER_DELTA;
     }
-    currentIntervals.link(createSentinelLiveInterval(current));
+  }
+
+  private void linkArgumentValuesAndIntervals(List<Value> arguments) {
+    if (!arguments.isEmpty()) {
+      Value last = firstArgumentValue = arguments.get(0);
+      for (int i = 1; i < arguments.size(); ++i) {
+        Value next = arguments.get(i);
+        last.linkTo(next);
+        last.getLiveIntervals().link(next.getLiveIntervals());
+        last = next;
+      }
+    }
   }
 
   private void insertArgumentMoves() {
     // Record the constraint that incoming arguments are in consecutive registers.
-    // We also insert sentinel registers before and after the arguments at this
-    // point.
-    linkArgumentValues();
-    setupArgumentLiveIntervals();
+    List<Value> arguments = code.collectArguments();
+    createArgumentLiveIntervals(arguments);
+    linkArgumentValuesAndIntervals(arguments);
     for (BasicBlock block : code.blocks) {
       InstructionListIterator it = block.listIterator();
       while (it.hasNext()) {
@@ -2336,30 +2309,32 @@
 
   private void pinArgumentRegisters() {
     // Special handling for arguments. Pin their register.
-    int register = 0;
-    Value current = preArgumentSentinelValue;
-    while (current != null) {
-      LiveIntervals argumentLiveInterval = current.getLiveIntervals();
-      // Pin the argument register. We use getFreeConsecutiveRegisters to make sure that we update
-      // the max register number.
-      register = getFreeConsecutiveRegisters(argumentLiveInterval.requiredRegisters());
-      assignRegister(argumentLiveInterval, register);
-      current = current.getNextConsecutive();
+    if (firstArgumentValue != null) {
+      int register = 0;
+      Value current = firstArgumentValue;
+      while (current != null) {
+        LiveIntervals argumentLiveInterval = current.getLiveIntervals();
+        // Pin the argument register. We use getFreeConsecutiveRegisters to make sure that we update
+        // the max register number.
+        register = getFreeConsecutiveRegisters(argumentLiveInterval.requiredRegisters());
+        assignRegister(argumentLiveInterval, register);
+        current = current.getNextConsecutive();
+      }
+      // If the last argument is wide, then its register will be numberOfArgumentRegisters - 2.
+      assert register == numberOfArgumentRegisters - 1 || register == numberOfArgumentRegisters - 2;
     }
-    assert register == numberOfArgumentRegisters + NUMBER_OF_SENTINEL_REGISTERS - 1;
   }
 
-  private int getFreeConsecutiveRegisters(int numberOfRegister) {
-    List<Integer> unused = new ArrayList<>();
-    int first = getNextFreeRegister();
+  private int getFreeConsecutiveRegisters(int numberOfRegisters) {
+    Iterator<Integer> freeRegistersIterator = freeRegisters.iterator();
+    int first = getNextFreeRegister(freeRegistersIterator);
     int current = first;
-    while ((current - first + 1) != numberOfRegister) {
-      for (int i = 0; i < numberOfRegister - 1; i++) {
-        int next = getNextFreeRegister();
-        if (next != current + 1) {
-          for (int j = first; j <= current; j++) {
-            unused.add(j);
-          }
+    while (current - first + 1 != numberOfRegisters) {
+      for (int i = 0; i < numberOfRegisters - 1; i++) {
+        int next = getNextFreeRegister(freeRegistersIterator);
+        // We cannot allow that some are argument registers and some or not, because they will no
+        // longer be consecutive if we later decide to increment maxRegisterNumber.
+        if (next != current + 1 || next == numberOfArgumentRegisters) {
           first = next;
           current = first;
           break;
@@ -2367,13 +2342,21 @@
         current++;
       }
     }
-    freeRegisters.addAll(unused);
+    for (int register = first; register < first + numberOfRegisters; ++register) {
+      freeRegisters.remove(register);
+    }
+    // Either all the consecutive registers are from the argument registers, or all are from the
+    // non-argument registers.
+    assert (first < numberOfArgumentRegisters
+            && first + numberOfRegisters - 1 < numberOfArgumentRegisters)
+        || (first >= numberOfArgumentRegisters
+            && first + numberOfRegisters - 1 >= numberOfArgumentRegisters);
     return first;
   }
 
-  private int getNextFreeRegister() {
-    if (freeRegisters.size() > 0) {
-      return freeRegisters.pollFirst();
+  private int getNextFreeRegister(Iterator<Integer> freeRegistersIterator) {
+    if (freeRegistersIterator.hasNext()) {
+      return freeRegistersIterator.next();
     }
     maxRegisterNumber = getNextUnusedRegisterNumber();
     return maxRegisterNumber;
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/RegisterAllocator.java b/src/main/java/com/android/tools/r8/ir/regalloc/RegisterAllocator.java
index 79ba8db..8fcc6b0 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/RegisterAllocator.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/RegisterAllocator.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8.ir.regalloc;
 
 import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.utils.InternalOptions;
 
 public interface RegisterAllocator {
   void allocateRegisters(boolean debug);
@@ -11,4 +12,5 @@
   int getRegisterForValue(Value value, int instructionNumber);
   boolean argumentValueUsesHighRegister(Value value, int instructionNumber);
   int getArgumentOrAllocateRegisterForValue(Value value, int instructionNumber);
+  InternalOptions getOptions();
 }
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/SpillMoveSet.java b/src/main/java/com/android/tools/r8/ir/regalloc/SpillMoveSet.java
index 2dbc015..3dc151d 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/SpillMoveSet.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/SpillMoveSet.java
@@ -296,8 +296,18 @@
         scheduler.addMove(
             new RegisterMove(move.to.getRegister(), move.type, move.from.getValue().definition));
       } else if (move.to.getRegister() != move.from.getRegister()) {
-        scheduler.addMove(
-            new RegisterMove(move.to.getRegister(), move.from.getRegister(), move.type));
+        // In case the runtime might have a bound-check elimination bug we make sure to define all
+        // indexing constants with an actual const instruction rather than a move. This appears to
+        // avoid a bug where the index variable could end up being uninitialized.
+        if (code.options.canHaveBoundsCheckEliminationBug()
+            && move.from.getValue().isConstNumber()
+            && move.type == MoveType.SINGLE) {
+          scheduler.addMove(
+              new RegisterMove(move.to.getRegister(), move.type, move.from.getValue().definition));
+        } else {
+          scheduler.addMove(
+              new RegisterMove(move.to.getRegister(), move.from.getRegister(), move.type));
+        }
       }
     }
     scheduler.schedule();
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 3372a20..de171a3 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
@@ -11,9 +11,8 @@
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.GraphLense;
-import com.android.tools.r8.ir.optimize.Inliner.Constraint;
 import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
-import java.util.Map;
+import com.google.common.collect.Sets;
 import java.util.Set;
 import java.util.function.BiFunction;
 import java.util.function.Function;
@@ -125,7 +124,7 @@
       // Rebind to the lowest library class or program class.
       if (target != null && target.method != method) {
         DexClass targetClass = appInfo.definitionFor(target.method.holder);
-        // If the target class is not public but the targeted method is, we might run into
+        // If the targetclass is not public but the targeted method is, we might run into
         // visibility problems when rebinding.
         if (!targetClass.accessFlags.isPublic() && target.accessFlags.isPublic()) {
           // If the original class is public and this method is public, it might have been called
@@ -179,37 +178,27 @@
     return null;
   }
 
-  private void computeFieldRebinding(Map<DexField, Set<DexEncodedMethod>> fields,
+  private void computeFieldRebinding(Set<DexField> fields,
       BiFunction<DexType, DexField, DexEncodedField> lookup,
       BiFunction<DexClass, DexField, DexEncodedField> lookupTargetOnClass) {
-    for (Map.Entry<DexField, Set<DexEncodedMethod>> entry : fields.entrySet()) {
-      DexField field = entry.getKey();
+    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. Do not rebind accesses to fields that
-      // are not visible from the access context.
-      Set<DexEncodedMethod> contexts = entry.getValue();
-      if (target != null && target.field != field && contexts.stream().allMatch(context ->
-          isVisibleFromOriginalContext(context.method.getHolder(), target))) {
+      // 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 isVisibleFromOriginalContext(DexType context, DexEncodedField field) {
-    DexType holderType = field.field.getHolder();
-    DexClass holder = appInfo.definitionFor(holderType);
-    if (holder == null) {
-      return false;
+  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;
     }
-    Constraint classVisibility =
-        Constraint.deriveConstraint(context, holderType, holder.accessFlags, appInfo);
-    if (classVisibility == Constraint.NEVER) {
-      return false;
-    }
-    Constraint fieldVisibility =
-        Constraint.deriveConstraint(context, holderType, field.accessFlags, appInfo);
-    return fieldVisibility != Constraint.NEVER;
+    // If the field is public, then a non-public holder class will further constrain visibility.
+    return appInfo.definitionFor(field.field.getHolder()).accessFlags.isPublic();
   }
 
   public GraphLense run() {
@@ -224,15 +213,10 @@
     // Likewise static invokes.
     computeMethodRebinding(appInfo.staticInvokes, this::anyLookup);
 
-    computeFieldRebinding(appInfo.staticFieldReads,
+    computeFieldRebinding(Sets.union(appInfo.staticFieldReads, appInfo.staticFieldWrites),
         appInfo::resolveFieldOn, DexClass::lookupField);
-    computeFieldRebinding(appInfo.staticFieldWrites,
+    computeFieldRebinding(Sets.union(appInfo.instanceFieldReads, appInfo.instanceFieldWrites),
         appInfo::resolveFieldOn, DexClass::lookupField);
-    computeFieldRebinding(appInfo.instanceFieldReads,
-        appInfo::resolveFieldOn, DexClass::lookupField);
-    computeFieldRebinding(appInfo.instanceFieldWrites,
-        appInfo::resolveFieldOn, DexClass::lookupField);
-
     return builder.build(appInfo.dexItemFactory, lense);
   }
 }
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 cd04989..3a31203 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -102,14 +102,10 @@
       Maps.newIdentityHashMap();
   private final Map<DexType, Set<DexMethod>> directInvokes = Maps.newIdentityHashMap();
   private final Map<DexType, Set<DexMethod>> staticInvokes = Maps.newIdentityHashMap();
-  private final Map<DexType, Set<TargetWithContext<DexField>>> instanceFieldsWritten =
-      Maps.newIdentityHashMap();
-  private final Map<DexType, Set<TargetWithContext<DexField>>> instanceFieldsRead =
-      Maps.newIdentityHashMap();
-  private final Map<DexType, Set<TargetWithContext<DexField>>> staticFieldsRead =
-      Maps.newIdentityHashMap();
-  private final Map<DexType, Set<TargetWithContext<DexField>>> staticFieldsWritten =
-      Maps.newIdentityHashMap();
+  private final Map<DexType, Set<DexField>> instanceFieldsWritten = Maps.newIdentityHashMap();
+  private final Map<DexType, Set<DexField>> instanceFieldsRead = Maps.newIdentityHashMap();
+  private final Map<DexType, Set<DexField>> staticFieldsRead = Maps.newIdentityHashMap();
+  private final Map<DexType, Set<DexField>> staticFieldsWritten = Maps.newIdentityHashMap();
 
   private final ProtoLiteExtension protoLiteExtension;
   private final Set<DexField> protoLiteFields = Sets.newIdentityHashSet();
@@ -368,7 +364,7 @@
 
     @Override
     public boolean registerInstanceFieldWrite(DexField field) {
-      if (!registerItemWithTargetAndContext(instanceFieldsWritten, field, currentMethod)) {
+      if (!registerItemWithTarget(instanceFieldsWritten, field)) {
         return false;
       }
       if (Log.ENABLED) {
@@ -381,7 +377,7 @@
 
     @Override
     public boolean registerInstanceFieldRead(DexField field) {
-      if (!registerItemWithTargetAndContext(instanceFieldsRead, field, currentMethod)) {
+      if (!registerItemWithTarget(instanceFieldsRead, field)) {
         return false;
       }
       if (Log.ENABLED) {
@@ -399,7 +395,7 @@
 
     @Override
     public boolean registerStaticFieldRead(DexField field) {
-      if (!registerItemWithTargetAndContext(staticFieldsRead, field, currentMethod)) {
+      if (!registerItemWithTarget(staticFieldsRead, field)) {
         return false;
       }
       if (Log.ENABLED) {
@@ -411,7 +407,7 @@
 
     @Override
     public boolean registerStaticFieldWrite(DexField field) {
-      if (!registerItemWithTargetAndContext(staticFieldsWritten, field, currentMethod)) {
+      if (!registerItemWithTarget(staticFieldsWritten, field)) {
         return false;
       }
       if (Log.ENABLED) {
@@ -1239,41 +1235,34 @@
     }
   }
 
-  private Map<DexField, Set<DexEncodedMethod>> collectFields(
-      Map<DexType, Set<TargetWithContext<DexField>>> map) {
-    Map<DexField, Set<DexEncodedMethod>> result = new IdentityHashMap<>();
-    for (Map.Entry<DexType, Set<TargetWithContext<DexField>>> entry : map.entrySet()) {
-      for (TargetWithContext<DexField> fieldWithContext : entry.getValue()) {
-        DexField field = fieldWithContext.getTarget();
-        DexEncodedMethod context = fieldWithContext.getContext();
-        result.computeIfAbsent(field, k -> Sets.newIdentityHashSet())
-            .add(context);
-      }
-    }
-    return result;
+  private Set<DexField> collectFields(Map<DexType, Set<DexField>> map) {
+    return map.values().stream().flatMap(Collection::stream)
+        .collect(Collectors.toCollection(Sets::newIdentityHashSet));
   }
 
-  Map<DexField, Set<DexEncodedMethod>> collectInstanceFieldsRead() {
-    return Collections.unmodifiableMap(collectFields(instanceFieldsRead));
+  SortedSet<DexField> collectInstanceFieldsRead() {
+    return ImmutableSortedSet.copyOf(
+        PresortedComparable<DexField>::slowCompareTo, collectFields(instanceFieldsRead));
   }
 
-  Map<DexField, Set<DexEncodedMethod>> collectInstanceFieldsWritten() {
-    return Collections.unmodifiableMap(collectFields(instanceFieldsWritten));
+  SortedSet<DexField> collectInstanceFieldsWritten() {
+    return ImmutableSortedSet.copyOf(
+        PresortedComparable<DexField>::slowCompareTo, collectFields(instanceFieldsWritten));
   }
 
-  Map<DexField, Set<DexEncodedMethod>> collectStaticFieldsRead() {
-    return Collections.unmodifiableMap(collectFields(staticFieldsRead));
+  SortedSet<DexField> collectStaticFieldsRead() {
+    return ImmutableSortedSet.copyOf(
+        PresortedComparable<DexField>::slowCompareTo, collectFields(staticFieldsRead));
   }
 
-  Map<DexField, Set<DexEncodedMethod>> collectStaticFieldsWritten() {
-    return Collections.unmodifiableMap(collectFields(staticFieldsWritten));
+  SortedSet<DexField> collectStaticFieldsWritten() {
+    return ImmutableSortedSet.copyOf(
+        PresortedComparable<DexField>::slowCompareTo, collectFields(staticFieldsWritten));
   }
 
-  private Set<DexField> collectReachedFields(
-      Set<DexField> set, Function<DexField, DexField> lookup) {
-    return set.stream()
-        .map(lookup)
-        .filter(Objects::nonNull)
+  private Set<DexField> collectReachedFields(Map<DexType, Set<DexField>> map,
+      Function<DexField, DexField> lookup) {
+    return map.values().stream().flatMap(set -> set.stream().map(lookup).filter(Objects::nonNull))
         .collect(Collectors.toCollection(Sets::newIdentityHashSet));
   }
 
@@ -1287,11 +1276,16 @@
     return target == null ? null : target.field;
   }
 
-  SortedSet<DexField> mergeFieldAccesses(Set<DexField> instanceFields, Set<DexField> staticFields) {
+  SortedSet<DexField> collectFieldsRead() {
     return ImmutableSortedSet.copyOf(PresortedComparable<DexField>::slowCompareTo,
-        Sets.union(
-            collectReachedFields(instanceFields, this::tryLookupInstanceField),
-            collectReachedFields(staticFields, this::tryLookupStaticField)));
+        Sets.union(collectReachedFields(instanceFieldsRead, this::tryLookupInstanceField),
+            collectReachedFields(staticFieldsRead, this::tryLookupStaticField)));
+  }
+
+  SortedSet<DexField> collectFieldsWritten() {
+    return ImmutableSortedSet.copyOf(PresortedComparable<DexField>::slowCompareTo,
+        Sets.union(collectReachedFields(instanceFieldsWritten, this::tryLookupInstanceField),
+            collectReachedFields(staticFieldsWritten, this::tryLookupStaticField)));
   }
 
   private void markClassAsInstantiatedWithCompatRule(DexClass clazz) {
@@ -1462,21 +1456,21 @@
      */
     public final SortedSet<DexField> fieldsWritten;
     /**
-     * Set of all field ids used in instance field reads, along with access context.
+     * Set of all field ids used in instance field reads.
      */
-    public final Map<DexField, Set<DexEncodedMethod>> instanceFieldReads;
+    public final SortedSet<DexField> instanceFieldReads;
     /**
-     * Set of all field ids used in instance field writes, along with access context.
+     * Set of all field ids used in instance field writes.
      */
-    public final Map<DexField, Set<DexEncodedMethod>> instanceFieldWrites;
+    public final SortedSet<DexField> instanceFieldWrites;
     /**
-     * Set of all field ids used in static static field reads, along with access context.
+     * Set of all field ids used in static static field reads.
      */
-    public final Map<DexField, Set<DexEncodedMethod>> staticFieldReads;
+    public final SortedSet<DexField> staticFieldReads;
     /**
-     * Set of all field ids used in static field writes, along with access context.
+     * Set of all field ids used in static field writes.
      */
-    public final Map<DexField, Set<DexEncodedMethod>> staticFieldWrites;
+    public final SortedSet<DexField> staticFieldWrites;
     /**
      * Set of all methods referenced in virtual invokes;
      */
@@ -1548,10 +1542,8 @@
       this.instanceFieldWrites = enqueuer.collectInstanceFieldsWritten();
       this.staticFieldReads = enqueuer.collectStaticFieldsRead();
       this.staticFieldWrites = enqueuer.collectStaticFieldsWritten();
-      this.fieldsRead = enqueuer.mergeFieldAccesses(
-          instanceFieldReads.keySet(), staticFieldReads.keySet());
-      this.fieldsWritten = enqueuer.mergeFieldAccesses(
-          instanceFieldWrites.keySet(), staticFieldWrites.keySet());
+      this.fieldsRead = enqueuer.collectFieldsRead();
+      this.fieldsWritten = enqueuer.collectFieldsWritten();
       this.pinnedItems = rewritePinnedItemsToDescriptors(enqueuer.pinnedItems);
       this.virtualInvokes = joinInvokedMethods(enqueuer.virtualInvokes);
       this.interfaceInvokes = joinInvokedMethods(enqueuer.interfaceInvokes);
@@ -1567,8 +1559,8 @@
       this.prunedTypes = Collections.emptySet();
       this.switchMaps = Collections.emptyMap();
       this.ordinalsMaps = Collections.emptyMap();
-      assert Sets.intersection(instanceFieldReads.keySet(), staticFieldReads.keySet()).isEmpty();
-      assert Sets.intersection(instanceFieldWrites.keySet(), staticFieldWrites.keySet()).isEmpty();
+      assert Sets.intersection(instanceFieldReads, staticFieldReads).size() == 0;
+      assert Sets.intersection(instanceFieldWrites, staticFieldWrites).size() == 0;
     }
 
     private AppInfoWithLiveness(AppInfoWithLiveness previous, DexApplication application,
@@ -1601,8 +1593,8 @@
       this.prunedTypes = mergeSets(previous.prunedTypes, removedClasses);
       this.switchMaps = previous.switchMaps;
       this.ordinalsMaps = previous.ordinalsMaps;
-      assert Sets.intersection(instanceFieldReads.keySet(), staticFieldReads.keySet()).isEmpty();
-      assert Sets.intersection(instanceFieldWrites.keySet(), staticFieldWrites.keySet()).isEmpty();
+      assert Sets.intersection(instanceFieldReads, staticFieldReads).size() == 0;
+      assert Sets.intersection(instanceFieldWrites, staticFieldWrites).size() == 0;
     }
 
     private AppInfoWithLiveness(AppInfoWithLiveness previous,
@@ -1614,14 +1606,10 @@
       this.targetedMethods = rewriteItems(previous.targetedMethods, lense::lookupMethod);
       this.liveMethods = rewriteItems(previous.liveMethods, lense::lookupMethod);
       this.liveFields = rewriteItems(previous.liveFields, lense::lookupField);
-      this.instanceFieldReads =
-          rewriteKeysWhileMergingValues(previous.instanceFieldReads, lense::lookupField);
-      this.instanceFieldWrites =
-          rewriteKeysWhileMergingValues(previous.instanceFieldWrites, lense::lookupField);
-      this.staticFieldReads =
-          rewriteKeysWhileMergingValues(previous.staticFieldReads, lense::lookupField);
-      this.staticFieldWrites =
-          rewriteKeysWhileMergingValues(previous.staticFieldWrites, lense::lookupField);
+      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);
       this.pinnedItems = rewriteMixedItems(previous.pinnedItems, lense);
@@ -1647,8 +1635,8 @@
       this.ordinalsMaps = rewriteKeys(previous.ordinalsMaps, lense::lookupType);
       this.protoLiteFields = previous.protoLiteFields;
       // Sanity check sets after rewriting.
-      assert Sets.intersection(instanceFieldReads.keySet(), staticFieldReads.keySet()).isEmpty();
-      assert Sets.intersection(instanceFieldWrites.keySet(), staticFieldWrites.keySet()).isEmpty();
+      assert Sets.intersection(instanceFieldReads, staticFieldReads).isEmpty();
+      assert Sets.intersection(instanceFieldWrites, staticFieldWrites).isEmpty();
     }
 
     public AppInfoWithLiveness(AppInfoWithLiveness previous,
@@ -1783,18 +1771,6 @@
       return builder.build();
     }
 
-    private static <T extends PresortedComparable<T>, S> Map<T, Set<S>>
-        rewriteKeysWhileMergingValues(
-            Map<T, Set<S>> original, BiFunction<T, DexEncodedMethod, T> rewrite) {
-      Map<T, Set<S>> result = new IdentityHashMap<>();
-      for (T item : original.keySet()) {
-        T rewrittenKey = rewrite.apply(item, null);
-        result.computeIfAbsent(rewrittenKey, k -> Sets.newIdentityHashSet())
-            .addAll(original.get(item));
-      }
-      return Collections.unmodifiableMap(result);
-    }
-
     private static ImmutableSet<DexItem> rewriteMixedItems(
         Set<DexItem> original, GraphLense lense) {
       ImmutableSet.Builder<DexItem> builder = ImmutableSet.builder();
@@ -2146,10 +2122,6 @@
       return target;
     }
 
-    public DexEncodedMethod getContext() {
-      return context;
-    }
-
     @Override
     public int hashCode() {
       return target.hashCode() * 31 + context.hashCode();
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 04c5f72..4099912 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -411,6 +411,7 @@
 
     public boolean invertConditionals = false;
     public boolean placeExceptionalBlocksLast = false;
+    public boolean dontCreateMarkerInD8 = false;
   }
 
   public boolean canUseInvokePolymorphicOnVarHandle() {
@@ -487,18 +488,16 @@
   // Some Lollipop versions of Art found in the wild perform invalid bounds
   // check elimination. There is a fast path of loops and a slow path.
   // The bailout to the slow path is performed too early and therefore
-  // the loop variable might not be defined in the slow path code leading
+  // the array-index variable might not be defined in the slow path code leading
   // to use of undefined registers as indices into arrays. The result
   // is ArrayIndexOutOfBounds exceptions.
   //
-  // In an attempt to help these Art VMs get the loop variable initialized
-  // early, we do not lower constants past array-length instructions when
-  // building for Lollipop or below.
+  // In an attempt to help these Art VMs, all single-width constants are initialized and not moved.
   //
   // There is no guarantee that this works, but it does make the problem
   // disappear on the one known instance of this problem.
   //
-  // See b/69364976.
+  // See b/69364976 and b/77996377.
   public boolean canHaveBoundsCheckEliminationBug() {
     return minApiLevel <= AndroidApiLevel.L.getLevel();
   }
@@ -557,4 +556,26 @@
   public boolean canHaveCmpLongBug() {
     return minApiLevel < AndroidApiLevel.L.getLevel();
   }
+
+  // Some Lollipop VMs incorrectly optimize code with mul2addr instructions. In particular,
+  // the following hash code method produces wrong results after optimizations:
+  //
+  //    0:   0x00: IgetObject          v0, v3, Field java.lang.Class MultiClassKey.first
+  //    1:   0x02: InvokeVirtual       { v0 } Ljava/lang/Object;->hashCode()I
+  //    2:   0x05: MoveResult          v0
+  //    3:   0x06: Const16             v1, 0x001f (31)
+  //    4:   0x08: MulInt2Addr         v1, v0
+  //    5:   0x09: IgetObject          v2, v3, Field java.lang.Class MultiClassKey.second
+  //    6:   0x0b: InvokeVirtual       { v2 } Ljava/lang/Object;->hashCode()I
+  //    7:   0x0e: MoveResult          v2
+  //    8:   0x0f: AddInt2Addr         v1, v2
+  //    9:   0x10: Return              v1
+  //
+  // It seems that the issue is the MulInt2Addr instructions. Avoiding that, the VM computes
+  // hash codes correctly also after optimizations.
+  //
+  // This issue has only been observed on a Verizon Ellipsis 8 tablet. See b/76115465.
+  public boolean canHaveMul2AddrBug() {
+    return minApiLevel < AndroidApiLevel.M.getLevel();
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index 923398d..4d7240e 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -550,6 +550,24 @@
   // Tests where the output of R8 fails when run with Art.
   private static final Multimap<String, TestCondition> failingRunWithArt =
       new ImmutableListMultimap.Builder<String, TestCondition>()
+          // This test relies on specific field access patterns, which we rewrite.
+          .put("064-field-access",
+              TestCondition.match(
+                  TestCondition.R8DEX_NOT_AFTER_D8_COMPILER,
+                  TestCondition.runtimesUpTo(DexVm.Version.V4_4_4)))
+          .put("064-field-access",
+              TestCondition.match(
+                  TestCondition.R8DEX_COMPILER,
+                  TestCondition.runtimes(
+                      DexVm.Version.DEFAULT, DexVm.Version.V7_0_0, DexVm.Version.V6_0_1,
+                      DexVm.Version.V5_1_1)))
+          .put("064-field-access",
+              TestCondition.match(
+                  TestCondition.tools(DexTool.NONE),
+                  TestCondition.D8_AFTER_R8CF_COMPILER,
+                  TestCondition.runtimes(
+                      DexVm.Version.DEFAULT, DexVm.Version.V7_0_0, DexVm.Version.V6_0_1,
+                      DexVm.Version.V5_1_1)))
           // The growth limit test fails after processing by R8 because R8 will eliminate an
           // "unneeded" const store. The following reflective call to the VM's GC will then see the
           // large array as still live and the subsequent allocations will fail to reach the desired
diff --git a/src/test/java/com/android/tools/r8/dexfilemerger/DexFileMergerTests.java b/src/test/java/com/android/tools/r8/dexfilemerger/DexFileMergerTests.java
index e99e696..2b2a40b 100644
--- a/src/test/java/com/android/tools/r8/dexfilemerger/DexFileMergerTests.java
+++ b/src/test/java/com/android/tools/r8/dexfilemerger/DexFileMergerTests.java
@@ -8,12 +8,15 @@
 
 import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.D8;
 import com.android.tools.r8.D8Command;
+import com.android.tools.r8.DexFileMergerHelper;
+import com.android.tools.r8.ExtractMarker;
 import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.ResourceException;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.ArtCommandBuilder;
 import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.dex.Marker;
 import com.android.tools.r8.maindexlist.MainDexListTests;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
@@ -35,16 +38,57 @@
 
   @Rule public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
 
-  @Test
-  public void mergeTwoFiles() throws CompilationFailedException, IOException {
+  private Path createMergerInputWithTwoClasses(OutputMode outputMode, boolean addMarker)
+      throws CompilationFailedException, CompilationException, IOException {
     // Compile Class1 and Class2
     Path mergerInputZip = temp.newFolder().toPath().resolve("merger-input.zip");
-    D8.run(
+    D8Command command =
         D8Command.builder()
-            .setOutput(mergerInputZip, OutputMode.DexFilePerClassFile)
+            .setOutput(mergerInputZip, outputMode)
             .addProgramFiles(Paths.get(CLASS1_CLASS))
             .addProgramFiles(Paths.get(CLASS2_CLASS))
-            .build());
+            .build();
+
+    DexFileMergerHelper.runD8ForTesting(command, !addMarker);
+
+    return mergerInputZip;
+  }
+
+  private void testMarker(boolean addMarkerToInput)
+      throws CompilationFailedException, CompilationException, IOException, ResourceException,
+          ExecutionException {
+    Path mergerInputZip = createMergerInputWithTwoClasses(OutputMode.DexIndexed, addMarkerToInput);
+
+    Marker inputMarker = ExtractMarker.extractMarkerFromDexFile(mergerInputZip);
+    assertEquals(addMarkerToInput, inputMarker != null);
+
+    Path mergerOutputZip = temp.getRoot().toPath().resolve("merger-out.zip");
+    DexFileMerger.main(
+        new String[] {
+          "--input", mergerInputZip.toString(), "--output", mergerOutputZip.toString()
+        });
+
+    Marker outputMarker = ExtractMarker.extractMarkerFromDexFile(mergerOutputZip);
+    assertEquals(addMarkerToInput, outputMarker != null);
+  }
+
+  @Test
+  public void testMarkerPreserved()
+      throws CompilationFailedException, CompilationException, IOException, ResourceException,
+          ExecutionException {
+    testMarker(true);
+  }
+
+  @Test
+  public void testMarkerNotAdded()
+      throws CompilationFailedException, CompilationException, IOException, ResourceException,
+          ExecutionException {
+    testMarker(false);
+  }
+
+  @Test
+  public void mergeTwoFiles() throws CompilationFailedException, CompilationException, IOException {
+    Path mergerInputZip = createMergerInputWithTwoClasses(OutputMode.DexFilePerClassFile, false);
 
     Path mergerOutputZip = temp.getRoot().toPath().resolve("merger-out.zip");
     DexFileMerger.main(
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterTests.java b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterTests.java
index 2db8fe0..ed293f6 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterTests.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterTests.java
@@ -10,14 +10,16 @@
 
 import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.D8;
 import com.android.tools.r8.D8Command;
+import com.android.tools.r8.DexSplitterHelper;
+import com.android.tools.r8.ExtractMarker;
 import com.android.tools.r8.OutputMode;
 import com.android.tools.r8.R8;
 import com.android.tools.r8.R8Command;
 import com.android.tools.r8.ResourceException;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.ArtCommandBuilder;
+import com.android.tools.r8.dex.Marker;
 import com.android.tools.r8.dexsplitter.DexSplitter.Options;
 import com.android.tools.r8.utils.DexInspector;
 import com.android.tools.r8.utils.DexInspector.ClassSubject;
@@ -53,6 +55,65 @@
 
   @Rule public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
 
+  private Path createInput(boolean dontCreateMarkerInD8)
+      throws IOException, CompilationFailedException, CompilationException {
+    // Initial normal compile to create dex files.
+    Path inputZip = temp.newFolder().toPath().resolve("input.zip");
+    D8Command command =
+        D8Command.builder()
+            .setOutput(inputZip, OutputMode.DexIndexed)
+            .addProgramFiles(Paths.get(CLASS1_CLASS))
+            .addProgramFiles(Paths.get(CLASS2_CLASS))
+            .addProgramFiles(Paths.get(CLASS3_CLASS))
+            .addProgramFiles(Paths.get(CLASS3_INNER_CLASS))
+            .addProgramFiles(Paths.get(CLASS4_CLASS))
+            .addProgramFiles(Paths.get(CLASS4_LAMBDA_INTERFACE))
+            .build();
+
+    DexSplitterHelper.runD8ForTesting(command, dontCreateMarkerInD8);
+
+    return inputZip;
+  }
+
+  private void testMarker(boolean addMarkerToInput)
+      throws CompilationFailedException, CompilationException, IOException, ResourceException,
+          ExecutionException {
+    Path inputZip = createInput(!addMarkerToInput);
+
+    Path output = temp.newFolder().toPath().resolve("output");
+    Files.createDirectory(output);
+    Path splitSpec = createSplitSpec();
+
+    DexSplitter.main(
+        new String[] {
+          "--input", inputZip.toString(),
+          "--output", output.toString(),
+          "--feature-splits", splitSpec.toString()
+        });
+
+    Path base = output.resolve("base").resolve("classes.dex");
+    Path feature = output.resolve("feature1").resolve("classes.dex");
+
+    for (Path path : new Path[] {inputZip, base, feature}) {
+      Marker marker = ExtractMarker.extractMarkerFromDexFile(path);
+      assertEquals(addMarkerToInput, marker != null);
+    }
+  }
+
+  @Test
+  public void testMarkerPreserved()
+      throws CompilationFailedException, CompilationException, IOException, ResourceException,
+          ExecutionException {
+    testMarker(true);
+  }
+
+  @Test
+  public void testMarkerNotAdded()
+      throws CompilationFailedException, CompilationException, IOException, ResourceException,
+          ExecutionException {
+    testMarker(false);
+  }
+
   /**
    * To test the file splitting we have 3 classes that we distribute like this: Class1 -> base
    * Class2 -> feature1 Class3 -> feature1
@@ -71,19 +132,7 @@
   private void noObfuscation(boolean useOptions)
       throws IOException, CompilationFailedException, FeatureMappingException,
       ResourceException, ExecutionException, CompilationException {
-    // Initial normal compile to create dex files.
-    Path inputZip = temp.newFolder().toPath().resolve("input.zip");
-    D8.run(
-        D8Command.builder()
-            .setOutput(inputZip, OutputMode.DexIndexed)
-            .addProgramFiles(Paths.get(CLASS1_CLASS))
-            .addProgramFiles(Paths.get(CLASS2_CLASS))
-            .addProgramFiles(Paths.get(CLASS3_CLASS))
-            .addProgramFiles(Paths.get(CLASS3_INNER_CLASS))
-            .addProgramFiles(Paths.get(CLASS4_CLASS))
-            .addProgramFiles(Paths.get(CLASS4_LAMBDA_INTERFACE))
-            .build());
-
+    Path inputZip = createInput(false);
     Path output = temp.newFolder().toPath().resolve("output");
     Files.createDirectory(output);
     Path splitSpec = createSplitSpec();
@@ -189,19 +238,7 @@
   private void splitFromJars(boolean useOptions, boolean explicitBase)
       throws IOException, CompilationFailedException, FeatureMappingException, ResourceException,
       ExecutionException, CompilationException {
-    // Initial normal compile to create dex files.
-    Path inputZip = temp.newFolder().toPath().resolve("input.zip");
-    D8.run(
-        D8Command.builder()
-            .setOutput(inputZip, OutputMode.DexIndexed)
-            .addProgramFiles(Paths.get(CLASS1_CLASS))
-            .addProgramFiles(Paths.get(CLASS2_CLASS))
-            .addProgramFiles(Paths.get(CLASS3_CLASS))
-            .addProgramFiles(Paths.get(CLASS3_INNER_CLASS))
-            .addProgramFiles(Paths.get(CLASS4_CLASS))
-            .addProgramFiles(Paths.get(CLASS4_LAMBDA_INTERFACE))
-            .build());
-
+    Path inputZip = createInput(false);
     Path output = temp.newFolder().toPath().resolve("output");
     Files.createDirectory(output);
     Path baseJar = temp.getRoot().toPath().resolve("base.jar");
diff --git a/src/test/java/com/android/tools/r8/ir/PhiDefinitionsTest.java b/src/test/java/com/android/tools/r8/ir/PhiDefinitionsTest.java
new file mode 100644
index 0000000..98db705
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/PhiDefinitionsTest.java
@@ -0,0 +1,61 @@
+// Copyright (c) 2018, 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.ir;
+
+public class PhiDefinitionsTest {
+
+  public static Class[] CLASSES = {
+      PhiDefinitionsTest.class,
+      MethodWriter.class,
+  };
+
+  static class MethodWriter {
+    public int exceptionCount;
+  }
+
+  public static void main(String[] args) {
+    if (args.length >= 42) {
+      System.out.println(new PhiDefinitionsTest().readMethod(args.length));
+    }
+  }
+
+  private int readMethod(int u) {
+    u += 6;
+    int exception = 0;
+    for (int i = count(u); i > 0; --i) {
+      exception = count(u);
+      for (int j = count(); j > 0; --j) {
+        read(exception);
+        exception += 2;
+      }
+      u += 6 + count(u + 4);
+    }
+    u += 2;
+    MethodWriter mv = visitMethod();
+    if (cond() && cond() && cond()) {
+      MethodWriter mw = mv;
+      boolean sameExceptions = false;
+      if (count() == mw.exceptionCount) {
+        sameExceptions = true;
+        for (int j = count(); j >= 0; --j) {
+          exception -= 2;
+        }
+      }
+      if (cond()) {
+        return u;
+      }
+    }
+    return u;
+  }
+
+  private native MethodWriter visitMethod();
+
+  private native boolean cond();
+
+  private native String read(int i);
+
+  private native int count(int arg);
+
+  private native int count();
+}
diff --git a/src/test/java/com/android/tools/r8/ir/PhiDefinitionsTestDump.java b/src/test/java/com/android/tools/r8/ir/PhiDefinitionsTestDump.java
new file mode 100644
index 0000000..5a93117
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/PhiDefinitionsTestDump.java
@@ -0,0 +1,509 @@
+// Copyright (c) 2018, 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.ir;
+
+import org.objectweb.asm.*;
+
+public class PhiDefinitionsTestDump implements Opcodes {
+
+  private static final String SIMPLE_NAME = "PhiDefinitionsTest";
+  static final String INTERNAL_NAME = "com/android/tools/r8/ir/" + SIMPLE_NAME;
+  private static final String INNER_SIMPLE_NAME = "MethodWriter";
+  static final String INNER_INTERNAL_NAME = INTERNAL_NAME + "$" + INNER_SIMPLE_NAME;
+
+  // static String INTERNAL_NAME = "com/android/tools/r8/ir/PhiDefinitionsTest";
+  // private static String INNER_SIMPLE_NAME = "MethodWriter";
+  // static String INNER_INTERNAL_NAME = INTERNAL_NAME + '$' + INNER_SIMPLE_NAME;
+
+  public static byte[] dump() throws Exception {
+
+    ClassWriter cw = new ClassWriter(0);
+    FieldVisitor fv;
+    AnnotationVisitor av0;
+
+    cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, INTERNAL_NAME, null, "java/lang/Object", null);
+
+    cw.visitSource(SIMPLE_NAME + ".java", null);
+
+    cw.visitInnerClass(INNER_INTERNAL_NAME, INTERNAL_NAME, INNER_SIMPLE_NAME, ACC_STATIC);
+
+    method0(cw);
+    method1(cw);
+    method2(cw);
+    nativeMethod(cw, "visitMethod", "()L" + INTERNAL_NAME + "$" + INNER_SIMPLE_NAME + ";");
+    nativeMethod(cw, "cond", "()Z");
+    nativeMethod(cw, "read", "(I)Ljava/lang/String;");
+    nativeMethod(cw, "count", "(I)I");
+    nativeMethod(cw, "count", "()I");
+    cw.visitEnd();
+
+    return cw.toByteArray();
+  }
+
+  private static void nativeMethod(ClassWriter cw, String name, String desc) {
+    MethodVisitor mv;
+    mv = cw.visitMethod(ACC_PRIVATE + ACC_NATIVE, name, desc, null, null);
+    mv.visitEnd();
+  }
+
+  private static void method0(ClassWriter cw) {
+    MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
+    Label[] labels = new Label[2];
+    for (int i = 0; i < labels.length; i++) {
+      labels[i] = new Label();
+    }
+    mv.visitCode();
+    mv.visitLabel(labels[0]);
+    mv.visitLineNumber(6, labels[0]);
+    mv.visitVarInsn(ALOAD, 0);
+    mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
+    mv.visitInsn(RETURN);
+    mv.visitLabel(labels[1]);
+    mv.visitLocalVariable("this", "L" + INTERNAL_NAME + ";", null, labels[0], labels[1], 0);
+    mv.visitMaxs(1, 1);
+    mv.visitEnd();
+  }
+
+  private static void method1(ClassWriter cw) {
+    MethodVisitor mv =
+        cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
+    Label[] labels = new Label[4];
+    for (int i = 0; i < labels.length; i++) {
+      labels[i] = new Label();
+    }
+    mv.visitCode();
+    mv.visitLabel(labels[0]);
+    mv.visitLineNumber(18, labels[0]);
+    mv.visitLdcInsn(new Integer(42));
+    mv.visitVarInsn(ISTORE, 1);
+    mv.visitVarInsn(ALOAD, 0);
+    mv.visitInsn(ARRAYLENGTH);
+    mv.visitVarInsn(ILOAD, 1);
+    mv.visitJumpInsn(IF_ICMPLT, labels[1]);
+    mv.visitLabel(labels[2]);
+    mv.visitLineNumber(19, labels[2]);
+    mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
+    mv.visitVarInsn(ASTORE, 1);
+    mv.visitTypeInsn(NEW, INTERNAL_NAME);
+    mv.visitVarInsn(ASTORE, 2);
+    mv.visitVarInsn(ALOAD, 2);
+    mv.visitMethodInsn(INVOKESPECIAL, INTERNAL_NAME, "<init>", "()V", false);
+    mv.visitVarInsn(ALOAD, 0);
+    mv.visitInsn(ARRAYLENGTH);
+    mv.visitVarInsn(ISTORE, 3);
+    mv.visitVarInsn(ALOAD, 2);
+    mv.visitVarInsn(ILOAD, 3);
+    mv.visitMethodInsn(INVOKESPECIAL, INTERNAL_NAME, "readMethod", "(I)I", false);
+    mv.visitVarInsn(ISTORE, 2);
+    mv.visitVarInsn(ALOAD, 1);
+    mv.visitVarInsn(ILOAD, 2);
+    mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(I)V", false);
+    mv.visitLabel(labels[1]);
+    mv.visitLineNumber(21, labels[1]);
+    mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+    mv.visitInsn(RETURN);
+    mv.visitLabel(labels[3]);
+    mv.visitLocalVariable("args", "[Ljava/lang/String;", null, labels[0], labels[3], 0);
+    mv.visitMaxs(2, 4);
+    mv.visitEnd();
+  }
+
+  private static void method2(ClassWriter cw) {
+    MethodVisitor mv = cw.visitMethod(ACC_PRIVATE, "readMethod", "(I)I", null, null);
+    Label[] labels = new Label[31];
+    for (int i = 0; i < labels.length; i++) {
+      labels[i] = new Label();
+    }
+    mv.visitCode();
+    mv.visitLabel(labels[0]);
+    mv.visitLineNumber(24, labels[0]);
+    mv.visitLdcInsn(new Integer(6));
+    mv.visitInsn(POP);
+    mv.visitLdcInsn(new Integer(6));
+    mv.visitVarInsn(ISTORE, 2);
+    mv.visitVarInsn(ILOAD, 1);
+    mv.visitVarInsn(ILOAD, 2);
+    mv.visitInsn(IADD);
+    mv.visitVarInsn(ISTORE, 1);
+    mv.visitLabel(labels[1]);
+    mv.visitLineNumber(25, labels[1]);
+    mv.visitInsn(ICONST_0);
+    mv.visitVarInsn(ISTORE, 3);
+    mv.visitLabel(labels[2]);
+    mv.visitLineNumber(26, labels[2]);
+    mv.visitVarInsn(ALOAD, 0);
+    mv.visitVarInsn(ILOAD, 1);
+    mv.visitMethodInsn(INVOKESPECIAL, INTERNAL_NAME, "count", "(I)I", false);
+    mv.visitVarInsn(ISTORE, 4);
+    mv.visitVarInsn(ILOAD, 4);
+    mv.visitVarInsn(ILOAD, 1);
+    mv.visitVarInsn(ILOAD, 3);
+    mv.visitVarInsn(ISTORE, 4);
+    mv.visitVarInsn(ISTORE, 3);
+    mv.visitVarInsn(ISTORE, 1);
+    mv.visitLabel(labels[3]);
+    mv.visitFrame(
+        Opcodes.F_APPEND,
+        3,
+        new Object[] {Opcodes.INTEGER, Opcodes.INTEGER, Opcodes.INTEGER},
+        0,
+        null);
+    mv.visitVarInsn(ILOAD, 1);
+    mv.visitVarInsn(ISTORE, 1);
+    mv.visitVarInsn(ILOAD, 1);
+    mv.visitJumpInsn(IFLE, labels[4]);
+    mv.visitLabel(labels[5]);
+    mv.visitLineNumber(27, labels[5]);
+    mv.visitVarInsn(ALOAD, 0);
+    mv.visitVarInsn(ILOAD, 3);
+    mv.visitMethodInsn(INVOKESPECIAL, INTERNAL_NAME, "count", "(I)I", false);
+    mv.visitVarInsn(ISTORE, 4);
+    mv.visitLabel(labels[6]);
+    mv.visitLineNumber(28, labels[6]);
+    mv.visitVarInsn(ALOAD, 0);
+    mv.visitMethodInsn(INVOKESPECIAL, INTERNAL_NAME, "count", "()I", false);
+    mv.visitVarInsn(ISTORE, 5);
+    mv.visitVarInsn(ILOAD, 5);
+    mv.visitVarInsn(ILOAD, 4);
+    mv.visitVarInsn(ISTORE, 5);
+    mv.visitVarInsn(ISTORE, 4);
+    mv.visitLabel(labels[7]);
+    mv.visitFrame(Opcodes.F_APPEND, 1, new Object[] {Opcodes.INTEGER}, 0, null);
+    mv.visitVarInsn(ILOAD, 4);
+    mv.visitVarInsn(ISTORE, 4);
+    mv.visitVarInsn(ILOAD, 4);
+    mv.visitJumpInsn(IFLE, labels[8]);
+    mv.visitLabel(labels[9]);
+    mv.visitLineNumber(29, labels[9]);
+    mv.visitVarInsn(ALOAD, 0);
+    mv.visitVarInsn(ILOAD, 5);
+    mv.visitMethodInsn(INVOKESPECIAL, INTERNAL_NAME, "read", "(I)Ljava/lang/String;", false);
+    mv.visitInsn(POP);
+    mv.visitLabel(labels[10]);
+    mv.visitLineNumber(30, labels[10]);
+    mv.visitInsn(ICONST_2);
+    mv.visitInsn(POP);
+    mv.visitInsn(ICONST_2);
+    mv.visitVarInsn(ISTORE, 6);
+    mv.visitVarInsn(ILOAD, 5);
+    mv.visitVarInsn(ILOAD, 6);
+    mv.visitInsn(IADD);
+    mv.visitVarInsn(ISTORE, 5);
+    mv.visitLabel(labels[11]);
+    mv.visitLineNumber(28, labels[11]);
+    mv.visitInsn(ICONST_M1);
+    mv.visitInsn(POP);
+    mv.visitInsn(ICONST_M1);
+    mv.visitVarInsn(ISTORE, 6);
+    mv.visitVarInsn(ILOAD, 4);
+    mv.visitVarInsn(ILOAD, 6);
+    mv.visitInsn(IADD);
+    mv.visitVarInsn(ISTORE, 4);
+    mv.visitJumpInsn(GOTO, labels[7]);
+    mv.visitLabel(labels[8]);
+    mv.visitLineNumber(32, labels[8]);
+    mv.visitFrame(
+        Opcodes.F_FULL,
+        6,
+        new Object[] {
+          INTERNAL_NAME,
+          Opcodes.INTEGER,
+          Opcodes.INTEGER,
+          Opcodes.INTEGER,
+          Opcodes.TOP,
+          Opcodes.INTEGER
+        },
+        0,
+        new Object[] {});
+    mv.visitLdcInsn(new Integer(6));
+    mv.visitInsn(POP);
+    mv.visitInsn(ICONST_4);
+    mv.visitVarInsn(ISTORE, 4);
+    mv.visitVarInsn(ILOAD, 3);
+    mv.visitVarInsn(ILOAD, 4);
+    mv.visitInsn(IADD);
+    mv.visitVarInsn(ISTORE, 4);
+    mv.visitVarInsn(ALOAD, 0);
+    mv.visitVarInsn(ILOAD, 4);
+    mv.visitMethodInsn(INVOKESPECIAL, INTERNAL_NAME, "count", "(I)I", false);
+    mv.visitVarInsn(ISTORE, 4);
+    mv.visitVarInsn(ILOAD, 2);
+    mv.visitVarInsn(ILOAD, 4);
+    mv.visitInsn(IADD);
+    mv.visitVarInsn(ISTORE, 4);
+    mv.visitVarInsn(ILOAD, 3);
+    mv.visitVarInsn(ILOAD, 4);
+    mv.visitInsn(IADD);
+    mv.visitVarInsn(ISTORE, 3);
+    mv.visitLabel(labels[12]);
+    mv.visitLineNumber(26, labels[12]);
+    mv.visitInsn(ICONST_M1);
+    mv.visitInsn(POP);
+    mv.visitInsn(ICONST_M1);
+    mv.visitVarInsn(ISTORE, 4);
+    mv.visitVarInsn(ILOAD, 1);
+    mv.visitVarInsn(ILOAD, 4);
+    mv.visitInsn(IADD);
+    mv.visitVarInsn(ISTORE, 1);
+    mv.visitVarInsn(ILOAD, 5);
+    mv.visitVarInsn(ISTORE, 4);
+    mv.visitJumpInsn(GOTO, labels[3]);
+    mv.visitLabel(labels[4]);
+    mv.visitLineNumber(34, labels[4]);
+    mv.visitFrame(
+        Opcodes.F_FULL,
+        5,
+        new Object[] {INTERNAL_NAME, Opcodes.TOP, Opcodes.TOP, Opcodes.INTEGER, Opcodes.INTEGER},
+        0,
+        new Object[] {});
+    mv.visitInsn(ICONST_2);
+    mv.visitInsn(POP);
+    mv.visitInsn(ICONST_2);
+    mv.visitVarInsn(ISTORE, 1);
+    mv.visitVarInsn(ILOAD, 3);
+    mv.visitVarInsn(ILOAD, 1);
+    mv.visitInsn(IADD);
+    mv.visitVarInsn(ISTORE, 1);
+    mv.visitLabel(labels[13]);
+    mv.visitLineNumber(35, labels[13]);
+    mv.visitVarInsn(ALOAD, 0);
+    mv.visitMethodInsn(
+        INVOKESPECIAL,
+        INTERNAL_NAME,
+        "visitMethod",
+        "()L" + INTERNAL_NAME + "$" + INNER_SIMPLE_NAME + ";",
+        false);
+    mv.visitVarInsn(ASTORE, 2);
+    mv.visitLabel(labels[14]);
+    mv.visitLineNumber(36, labels[14]);
+    mv.visitVarInsn(ALOAD, 0);
+    mv.visitMethodInsn(INVOKESPECIAL, INTERNAL_NAME, "cond", "()Z", false);
+    mv.visitJumpInsn(IFEQ, labels[15]);
+    mv.visitVarInsn(ALOAD, 0);
+    mv.visitMethodInsn(INVOKESPECIAL, INTERNAL_NAME, "cond", "()Z", false);
+    mv.visitJumpInsn(IFEQ, labels[15]);
+    mv.visitVarInsn(ALOAD, 0);
+    mv.visitMethodInsn(INVOKESPECIAL, INTERNAL_NAME, "cond", "()Z", false);
+    mv.visitJumpInsn(IFEQ, labels[15]);
+    mv.visitLabel(labels[16]);
+    mv.visitLineNumber(37, labels[16]);
+    mv.visitVarInsn(ALOAD, 2);
+    mv.visitVarInsn(ASTORE, 3);
+    mv.visitLabel(labels[17]);
+    mv.visitLineNumber(38, labels[17]);
+    mv.visitInsn(ICONST_0);
+    mv.visitVarInsn(ISTORE, 5);
+    mv.visitLabel(labels[18]);
+    mv.visitLineNumber(39, labels[18]);
+    mv.visitVarInsn(ALOAD, 0);
+    mv.visitMethodInsn(INVOKESPECIAL, INTERNAL_NAME, "count", "()I", false);
+    mv.visitVarInsn(ISTORE, 6);
+    mv.visitVarInsn(ALOAD, 3);
+    mv.visitFieldInsn(GETFIELD, INNER_INTERNAL_NAME, "exceptionCount", "I");
+    mv.visitVarInsn(ISTORE, 7);
+    mv.visitVarInsn(ILOAD, 6);
+    mv.visitVarInsn(ILOAD, 7);
+    mv.visitJumpInsn(IF_ICMPNE, labels[19]);
+    mv.visitLabel(labels[20]);
+    mv.visitLineNumber(40, labels[20]);
+    mv.visitInsn(ICONST_1);
+    mv.visitVarInsn(ISTORE, 5);
+    mv.visitLabel(labels[21]);
+    mv.visitLineNumber(41, labels[21]);
+    mv.visitVarInsn(ALOAD, 0);
+    mv.visitMethodInsn(INVOKESPECIAL, INTERNAL_NAME, "count", "()I", false);
+    mv.visitVarInsn(ISTORE, 6);
+    mv.visitVarInsn(ILOAD, 6);
+    mv.visitVarInsn(ILOAD, 4);
+    mv.visitVarInsn(ISTORE, 6);
+    mv.visitVarInsn(ISTORE, 4);
+    mv.visitLabel(labels[22]);
+    mv.visitFrame(
+        Opcodes.F_FULL,
+        7,
+        new Object[] {
+          INTERNAL_NAME,
+          Opcodes.INTEGER,
+          INNER_INTERNAL_NAME,
+          INNER_INTERNAL_NAME,
+          Opcodes.INTEGER,
+          Opcodes.INTEGER,
+          Opcodes.INTEGER
+        },
+        0,
+        new Object[] {});
+    mv.visitVarInsn(ILOAD, 4);
+    mv.visitVarInsn(ISTORE, 4);
+    mv.visitVarInsn(ILOAD, 4);
+    mv.visitJumpInsn(IFLT, labels[23]);
+    mv.visitLabel(labels[24]);
+    mv.visitLineNumber(42, labels[24]);
+    mv.visitLdcInsn(new Integer(-2));
+    mv.visitVarInsn(ISTORE, 7);
+    mv.visitVarInsn(ILOAD, 6);
+    mv.visitVarInsn(ILOAD, 7);
+    mv.visitInsn(IADD);
+    mv.visitVarInsn(ISTORE, 6);
+    mv.visitLabel(labels[25]);
+    mv.visitLineNumber(41, labels[25]);
+    mv.visitInsn(ICONST_M1);
+    mv.visitInsn(POP);
+    mv.visitInsn(ICONST_M1);
+    mv.visitVarInsn(ISTORE, 7);
+    mv.visitVarInsn(ILOAD, 4);
+    mv.visitVarInsn(ILOAD, 7);
+    mv.visitInsn(IADD);
+    mv.visitVarInsn(ISTORE, 4);
+    mv.visitJumpInsn(GOTO, labels[22]);
+    mv.visitLabel(labels[23]);
+    mv.visitFrame(
+        Opcodes.F_FULL,
+        7,
+        new Object[] {
+          INTERNAL_NAME,
+          Opcodes.INTEGER,
+          INNER_INTERNAL_NAME,
+          INNER_INTERNAL_NAME,
+          Opcodes.TOP,
+          Opcodes.INTEGER,
+          Opcodes.INTEGER
+        },
+        0,
+        new Object[] {});
+    mv.visitVarInsn(ILOAD, 5);
+    mv.visitVarInsn(ILOAD, 6);
+    mv.visitVarInsn(ISTORE, 5);
+    mv.visitVarInsn(ISTORE, 4);
+    mv.visitJumpInsn(GOTO, labels[26]);
+    mv.visitLabel(labels[19]);
+    mv.visitFrame(
+        Opcodes.F_FULL,
+        6,
+        new Object[] {
+          INTERNAL_NAME,
+          Opcodes.INTEGER,
+          INNER_INTERNAL_NAME,
+          INNER_INTERNAL_NAME,
+          Opcodes.INTEGER,
+          Opcodes.INTEGER
+        },
+        0,
+        new Object[] {});
+    mv.visitVarInsn(ILOAD, 5);
+    mv.visitVarInsn(ILOAD, 4);
+    mv.visitVarInsn(ISTORE, 5);
+    mv.visitVarInsn(ISTORE, 4);
+    mv.visitLabel(labels[26]);
+    mv.visitLineNumber(45, labels[26]);
+    mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+    mv.visitVarInsn(ALOAD, 0);
+    mv.visitMethodInsn(INVOKESPECIAL, INTERNAL_NAME, "cond", "()Z", false);
+    mv.visitJumpInsn(IFEQ, labels[27]);
+    mv.visitLabel(labels[28]);
+    mv.visitLineNumber(46, labels[28]);
+    mv.visitVarInsn(ILOAD, 1);
+    mv.visitInsn(IRETURN);
+    mv.visitLabel(labels[27]);
+    mv.visitFrame(
+        Opcodes.F_FULL,
+        6,
+        new Object[] {
+          INTERNAL_NAME,
+          Opcodes.INTEGER,
+          INNER_INTERNAL_NAME,
+          Opcodes.TOP,
+          Opcodes.TOP,
+          Opcodes.INTEGER
+        },
+        0,
+        new Object[] {});
+    mv.visitVarInsn(ILOAD, 5);
+    mv.visitVarInsn(ISTORE, 3);
+    mv.visitJumpInsn(GOTO, labels[29]);
+    mv.visitLabel(labels[15]);
+    mv.visitFrame(
+        Opcodes.F_FULL,
+        5,
+        new Object[] {
+          INTERNAL_NAME, Opcodes.INTEGER, INNER_INTERNAL_NAME, Opcodes.TOP, Opcodes.INTEGER
+        },
+        0,
+        new Object[] {});
+    mv.visitVarInsn(ILOAD, 4);
+    mv.visitVarInsn(ISTORE, 3);
+    mv.visitLabel(labels[29]);
+    mv.visitLineNumber(49, labels[29]);
+    mv.visitFrame(
+        Opcodes.F_FULL,
+        4,
+        new Object[] {INTERNAL_NAME, Opcodes.INTEGER, INNER_INTERNAL_NAME, Opcodes.INTEGER},
+        0,
+        new Object[] {});
+    mv.visitVarInsn(ILOAD, 1);
+    mv.visitInsn(IRETURN);
+    mv.visitLabel(labels[30]);
+    mv.visitLocalVariable("u", "I", null, labels[0], labels[3], 1);
+    mv.visitLocalVariable("exception", "I", null, labels[2], labels[3], 3);
+    mv.visitLocalVariable("exception", "I", null, labels[3], labels[7], 4);
+    mv.visitLocalVariable("j", "I", null, labels[7], labels[8], 4);
+    mv.visitLocalVariable("i", "I", null, labels[3], labels[4], 1);
+    mv.visitLocalVariable("exception", "I", null, labels[7], labels[4], 5);
+    mv.visitLocalVariable("u", "I", null, labels[3], labels[13], 3);
+    mv.visitLocalVariable("exception", "I", null, labels[4], labels[22], 4);
+    mv.visitLocalVariable("j", "I", null, labels[22], labels[23], 4);
+    mv.visitLocalVariable("exception", "I", null, labels[22], labels[19], 6);
+    mv.visitLocalVariable("exception", "I", null, labels[19], labels[26], 4);
+    mv.visitLocalVariable("sameExceptions", "Z", null, labels[18], labels[26], 5);
+    mv.visitLocalVariable(
+        "mw", "L" + INTERNAL_NAME + "$" + INNER_SIMPLE_NAME + ";", null, labels[17], labels[27], 3);
+    mv.visitLocalVariable("sameExceptions", "Z", null, labels[26], labels[27], 4);
+    mv.visitLocalVariable("exception", "I", null, labels[26], labels[15], 5);
+    mv.visitLocalVariable("exception", "I", null, labels[15], labels[29], 4);
+    mv.visitLocalVariable("this", "L" + INTERNAL_NAME + ";", null, labels[0], labels[30], 0);
+    mv.visitLocalVariable("u", "I", null, labels[13], labels[30], 1);
+    mv.visitLocalVariable("exception", "I", null, labels[29], labels[30], 3);
+    mv.visitLocalVariable(
+        "mv", "L" + INTERNAL_NAME + "$" + INNER_SIMPLE_NAME + ";", null, labels[14], labels[30], 2);
+    mv.visitMaxs(3, 8);
+    mv.visitEnd();
+  }
+
+  public static byte[] dumpInner() throws Exception {
+
+    ClassWriter cw = new ClassWriter(0);
+    FieldVisitor fv;
+    MethodVisitor mv;
+    AnnotationVisitor av0;
+
+    cw.visit(V1_8, ACC_SUPER, INNER_INTERNAL_NAME, null, "java/lang/Object", null);
+
+    cw.visitSource(SIMPLE_NAME + ".java", null);
+
+    cw.visitInnerClass(INNER_INTERNAL_NAME, INTERNAL_NAME, INNER_SIMPLE_NAME, ACC_STATIC);
+
+    {
+      fv = cw.visitField(ACC_PUBLIC, "exceptionCount", "I", null, null);
+      fv.visitEnd();
+    }
+    {
+      mv = cw.visitMethod(0, "<init>", "()V", null, null);
+      mv.visitCode();
+      Label l0 = new Label();
+      mv.visitLabel(l0);
+      mv.visitLineNumber(13, l0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
+      mv.visitInsn(RETURN);
+      Label l1 = new Label();
+      mv.visitLabel(l1);
+      mv.visitLocalVariable(
+          "this", "L" + INTERNAL_NAME + "$" + INNER_SIMPLE_NAME + ";", null, l0, l1, 0);
+      mv.visitMaxs(1, 1);
+      mv.visitEnd();
+    }
+    cw.visitEnd();
+
+    return cw.toByteArray();
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/PhiDefinitionsTestRunner.java b/src/test/java/com/android/tools/r8/ir/PhiDefinitionsTestRunner.java
new file mode 100644
index 0000000..f9541e5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/PhiDefinitionsTestRunner.java
@@ -0,0 +1,126 @@
+// Copyright (c) 2018, 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.ir;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.ClassFileConsumer;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.DexIndexedConsumer;
+import com.android.tools.r8.ProgramConsumer;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.R8Command.Builder;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.utils.DescriptorUtils;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import org.junit.Test;
+
+public class PhiDefinitionsTestRunner extends TestBase {
+
+  private ProcessResult runInput;
+  private String className = PhiDefinitionsTest.class.getName();
+
+  private Path writeAndRunOriginal() throws IOException {
+    Path originalJar = temp.getRoot().toPath().resolve("originput.jar");
+    ClassFileConsumer consumer = new ClassFileConsumer.ArchiveConsumer(originalJar);
+    for (Class clazz : PhiDefinitionsTest.CLASSES) {
+      String descriptor = DescriptorUtils.javaTypeToDescriptor(clazz.getName());
+      consumer.accept(ToolHelper.getClassAsBytes(clazz), descriptor, null);
+    }
+    consumer.finished(null);
+    runOriginalJar(originalJar);
+    return originalJar;
+  }
+
+  private void runOriginalJar(Path originalJar) throws IOException {
+    runInput = ToolHelper.runJava(originalJar, className);
+    if (runInput.exitCode != 0) {
+      System.out.println(runInput);
+    }
+    assertEquals(0, runInput.exitCode);
+  }
+
+  private Path writeAndRunInputJar() throws Exception {
+    Path originalJar = writeAndRunOriginal();
+    Path inputJar = temp.getRoot().toPath().resolve("input.jar");
+    build(originalJar, new ClassFileConsumer.ArchiveConsumer(inputJar));
+    runCf(inputJar, className);
+    return inputJar;
+  }
+
+  private Path writeAndRunDumpJar() throws Exception {
+    Path dumpJar = temp.getRoot().toPath().resolve("dump.jar");
+    ClassFileConsumer consumer = new ClassFileConsumer.ArchiveConsumer(dumpJar);
+    String desc = 'L' + PhiDefinitionsTestDump.INTERNAL_NAME + ';';
+    consumer.accept(PhiDefinitionsTestDump.dump(), desc, null);
+    String innerDesc = 'L' + PhiDefinitionsTestDump.INNER_INTERNAL_NAME + ';';
+    consumer.accept(PhiDefinitionsTestDump.dumpInner(), innerDesc, null);
+    consumer.finished(null);
+    runOriginalJar(dumpJar);
+    return dumpJar;
+  }
+
+  @Test
+  public void testCf() throws Exception {
+    Path outCf = temp.getRoot().toPath().resolve("cf.zip");
+    build(writeAndRunInputJar(), new ClassFileConsumer.ArchiveConsumer(outCf));
+    runCf(outCf, className);
+  }
+
+  @Test
+  public void testDex() throws Exception {
+    Path outDex = temp.getRoot().toPath().resolve("dex.zip");
+    build(writeAndRunInputJar(), new DexIndexedConsumer.ArchiveConsumer(outDex));
+    runDex(outDex, className);
+  }
+
+  @Test
+  public void testCfDump() throws Exception {
+    Path outCf = temp.getRoot().toPath().resolve("dump-cf.zip");
+    build(writeAndRunDumpJar(), new ClassFileConsumer.ArchiveConsumer(outCf));
+    runCf(outCf, className);
+  }
+
+  @Test
+  public void testDexDump() throws Exception {
+    Path outDex = temp.getRoot().toPath().resolve("dump-dex.zip");
+    build(writeAndRunDumpJar(), new DexIndexedConsumer.ArchiveConsumer(outDex));
+    runDex(outDex, className);
+  }
+
+  private void runCf(Path outCf, String className) throws Exception {
+    ProcessResult runCf = ToolHelper.runJava(outCf, className);
+    assertEquals(runInput.toString(), runCf.toString());
+  }
+
+  private void runDex(Path outDex, String className) throws Exception {
+    ProcessResult runDex = ToolHelper.runArtNoVerificationErrorsRaw(outDex.toString(), className);
+    assertEquals(runInput.stdout, runDex.stdout);
+    assertEquals(runInput.exitCode, runDex.exitCode);
+  }
+
+  private void build(Path inputJar, ProgramConsumer consumer) throws Exception {
+    Builder builder =
+        R8Command.builder()
+            .setMode(CompilationMode.DEBUG)
+            .setProgramConsumer(consumer)
+            .addProgramFiles(inputJar);
+    if (consumer instanceof ClassFileConsumer) {
+      builder.addLibraryFiles(Paths.get(ToolHelper.JAVA_8_RUNTIME));
+    } else {
+      builder.addLibraryFiles(ToolHelper.getAndroidJar(ToolHelper.getMinApiLevelForDexVm()));
+    }
+    // TODO(b/75997473): Enable inlining when supported by CF backend
+    ToolHelper.runR8(
+        builder.build(),
+        options -> {
+          options.enableInlining = false;
+          options.invalidDebugInfoFatal = true;
+        });
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/MemberValuePropagationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/MemberValuePropagationTest.java
index f1a3c49..488b545 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/MemberValuePropagationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/MemberValuePropagationTest.java
@@ -13,6 +13,7 @@
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.code.Const4;
 import com.android.tools.r8.code.InvokeDirect;
+import com.android.tools.r8.code.InvokeDirectRange;
 import com.android.tools.r8.code.IputObject;
 import com.android.tools.r8.code.NewInstance;
 import com.android.tools.r8.code.ReturnVoid;
@@ -46,26 +47,27 @@
     Path processedApp = runR8(EXAMPLE_KEEP);
     DexInspector inspector = new DexInspector(processedApp);
     ClassSubject clazz = inspector.clazz(WRITE_ONLY_FIELD + ".WriteOnlyCls");
-    clazz.forAllMethods(methodSubject -> {
-      if (methodSubject.isClassInitializer()) {
-        DexEncodedMethod encodedMethod = methodSubject.getMethod();
-        DexCode code = encodedMethod.getCode().asDexCode();
-        assertEquals(4, code.instructions.length);
-        assertTrue(code.instructions[0] instanceof NewInstance);
-        assertTrue(code.instructions[1] instanceof Const4);
-        assertTrue(code.instructions[2] instanceof InvokeDirect);
-        assertTrue(code.instructions[3] instanceof ReturnVoid);
-      }
-      if (methodSubject.isInstanceInitializer()) {
-        DexEncodedMethod encodedMethod = methodSubject.getMethod();
-        DexCode code = encodedMethod.getCode().asDexCode();
-        assertEquals(4, code.instructions.length);
-        assertTrue(code.instructions[0] instanceof InvokeDirect);
-        assertTrue(code.instructions[1] instanceof NewInstance);
-        assertTrue(code.instructions[2] instanceof InvokeDirect);
-        assertTrue(code.instructions[3] instanceof ReturnVoid);
-      }
-    });
+    clazz.forAllMethods(
+        methodSubject -> {
+          if (methodSubject.isClassInitializer()) {
+            DexEncodedMethod encodedMethod = methodSubject.getMethod();
+            DexCode code = encodedMethod.getCode().asDexCode();
+            assertEquals(4, code.instructions.length);
+            assertTrue(code.instructions[0] instanceof NewInstance);
+            assertTrue(code.instructions[1] instanceof Const4);
+            assertTrue(code.instructions[2] instanceof InvokeDirect);
+            assertTrue(code.instructions[3] instanceof ReturnVoid);
+          }
+          if (methodSubject.isInstanceInitializer()) {
+            DexEncodedMethod encodedMethod = methodSubject.getMethod();
+            DexCode code = encodedMethod.getCode().asDexCode();
+            assertEquals(4, code.instructions.length);
+            assertTrue(code.instructions[0] instanceof InvokeDirectRange);
+            assertTrue(code.instructions[1] instanceof NewInstance);
+            assertTrue(code.instructions[2] instanceof InvokeDirect);
+            assertTrue(code.instructions[3] instanceof ReturnVoid);
+          }
+        });
   }
 
   @Test
@@ -73,28 +75,29 @@
     Path processedApp = runR8(DONT_OPTIMIZE);
     DexInspector inspector = new DexInspector(processedApp);
     ClassSubject clazz = inspector.clazz(WRITE_ONLY_FIELD + ".WriteOnlyCls");
-    clazz.forAllMethods(methodSubject -> {
-      if (methodSubject.isClassInitializer()) {
-        DexEncodedMethod encodedMethod = methodSubject.getMethod();
-        DexCode code = encodedMethod.getCode().asDexCode();
-        assertEquals(5, code.instructions.length);
-        assertTrue(code.instructions[0] instanceof NewInstance);
-        assertTrue(code.instructions[1] instanceof Const4);
-        assertTrue(code.instructions[2] instanceof InvokeDirect);
-        assertTrue(code.instructions[3] instanceof SputObject);
-        assertTrue(code.instructions[4] instanceof ReturnVoid);
-      }
-      if (methodSubject.isInstanceInitializer()) {
-        DexEncodedMethod encodedMethod = methodSubject.getMethod();
-        DexCode code = encodedMethod.getCode().asDexCode();
-        assertEquals(5, code.instructions.length);
-        assertTrue(code.instructions[0] instanceof InvokeDirect);
-        assertTrue(code.instructions[1] instanceof NewInstance);
-        assertTrue(code.instructions[2] instanceof InvokeDirect);
-        assertTrue(code.instructions[3] instanceof IputObject);
-        assertTrue(code.instructions[4] instanceof ReturnVoid);
-      }
-    });
+    clazz.forAllMethods(
+        methodSubject -> {
+          if (methodSubject.isClassInitializer()) {
+            DexEncodedMethod encodedMethod = methodSubject.getMethod();
+            DexCode code = encodedMethod.getCode().asDexCode();
+            assertEquals(5, code.instructions.length);
+            assertTrue(code.instructions[0] instanceof NewInstance);
+            assertTrue(code.instructions[1] instanceof Const4);
+            assertTrue(code.instructions[2] instanceof InvokeDirect);
+            assertTrue(code.instructions[3] instanceof SputObject);
+            assertTrue(code.instructions[4] instanceof ReturnVoid);
+          }
+          if (methodSubject.isInstanceInitializer()) {
+            DexEncodedMethod encodedMethod = methodSubject.getMethod();
+            DexCode code = encodedMethod.getCode().asDexCode();
+            assertEquals(5, code.instructions.length);
+            assertTrue(code.instructions[0] instanceof InvokeDirectRange);
+            assertTrue(code.instructions[1] instanceof NewInstance);
+            assertTrue(code.instructions[2] instanceof InvokeDirect);
+            assertTrue(code.instructions[3] instanceof IputObject);
+            assertTrue(code.instructions[4] instanceof ReturnVoid);
+          }
+        });
   }
 
   private Path runR8(Path proguardConfig)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
index 1c72986..d51d5c3 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
@@ -18,7 +18,7 @@
 import com.android.tools.r8.code.IfEqz;
 import com.android.tools.r8.code.Iget;
 import com.android.tools.r8.code.Instruction;
-import com.android.tools.r8.code.InvokeVirtual;
+import com.android.tools.r8.code.InvokeVirtualRange;
 import com.android.tools.r8.code.MoveResult;
 import com.android.tools.r8.code.MulInt2Addr;
 import com.android.tools.r8.code.PackedSwitch;
@@ -27,6 +27,7 @@
 import com.android.tools.r8.code.Throw;
 import com.android.tools.r8.graph.DexCode;
 import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.DexInspector;
 import com.android.tools.r8.utils.DexInspector.ClassSubject;
 import com.android.tools.r8.utils.DexInspector.MethodSubject;
@@ -96,6 +97,7 @@
     R8Command command =
         R8Command.builder()
             .addProgramFiles(getInputFile())
+            .setMinApiLevel(AndroidApiLevel.M.getLevel())
             .setOutput(out, OutputMode.DexIndexed)
             .addProguardConfigurationFiles(Paths.get(keepRulesFile))
             .build();
@@ -179,23 +181,27 @@
     MethodSubject m = clazz.method("int", "inlinable", ImmutableList.of("inlining.A"));
     assertTrue(m.isPresent());
     DexCode code = m.getMethod().getCode().asDexCode();
-    checkInstructions(code, ImmutableList.of(
-        Iget.class,
-        // TODO(b/70572176): below two could be replaced with Iget via inlining
-        InvokeVirtual.class,
-        MoveResult.class,
-        AddInt2Addr.class,
-        Return.class));
+    checkInstructions(
+        code,
+        ImmutableList.of(
+            Iget.class,
+            // TODO(b/70572176): below two could be replaced with Iget via inlining
+            InvokeVirtualRange.class,
+            MoveResult.class,
+            AddInt2Addr.class,
+            Return.class));
 
     m = clazz.method("int", "notInlinable", ImmutableList.of("inlining.A"));
     assertTrue(m.isPresent());
     code = m.getMethod().getCode().asDexCode();
-    checkInstructions(code, ImmutableList.of(
-        InvokeVirtual.class,
-        MoveResult.class,
-        Iget.class,
-        AddInt2Addr.class,
-        Return.class));
+    checkInstructions(
+        code,
+        ImmutableList.of(
+            InvokeVirtualRange.class,
+            MoveResult.class,
+            Iget.class,
+            AddInt2Addr.class,
+            Return.class));
 
     m = clazz.method("int", "notInlinableDueToMissingNpe", ImmutableList.of("inlining.A"));
     assertTrue(m.isPresent());
@@ -210,13 +216,15 @@
     m = clazz.method("int", "notInlinableDueToSideEffect", ImmutableList.of("inlining.A"));
     assertTrue(m.isPresent());
     code = m.getMethod().getCode().asDexCode();
-    checkInstructions(code, ImmutableList.of(
-        IfEqz.class,
-        InvokeVirtual.class,
-        MoveResult.class,
-        Goto.class,
-        Iget.class,
-        Return.class));
+    checkInstructions(
+        code,
+        ImmutableList.of(
+            IfEqz.class,
+            InvokeVirtualRange.class,
+            MoveResult.class,
+            Goto.class,
+            Iget.class,
+            Return.class));
 
     m = clazz.method("int", "notInlinableOnThrow", ImmutableList.of("java.lang.Throwable"));
     assertTrue(m.isPresent());
@@ -241,15 +249,17 @@
     MethodSubject m = clazz.method("int", "conditionalOperator", ImmutableList.of("inlining.A"));
     assertTrue(m.isPresent());
     DexCode code = m.getMethod().getCode().asDexCode();
-    checkInstructions(code, ImmutableList.of(
-        IfEqz.class,
-        // TODO(b/70794661): below two could be replaced with Iget via inlining if access
-        // modification is allowed.
-        InvokeVirtual.class,
-        MoveResult.class,
-        Goto.class,
-        Const4.class,
-        Return.class));
+    checkInstructions(
+        code,
+        ImmutableList.of(
+            IfEqz.class,
+            // TODO(b/70794661): below two could be replaced with Iget via inlining if access
+            // modification is allowed.
+            InvokeVirtualRange.class,
+            MoveResult.class,
+            Goto.class,
+            Const4.class,
+            Return.class));
 
     m = clazz.method("int", "moreControlFlows",
         ImmutableList.of("inlining.A", "inlining.Nullability$Factor"));
@@ -257,7 +267,7 @@
     code = m.getMethod().getCode().asDexCode();
     ImmutableList.Builder<Class<? extends Instruction>> builder = ImmutableList.builder();
     // Enum#ordinal
-    builder.add(InvokeVirtual.class);
+    builder.add(InvokeVirtualRange.class);
     builder.add(MoveResult.class);
     builder.add(PackedSwitch.class);
     for (int i = 0; i < 4; ++i) {
@@ -268,7 +278,7 @@
     builder.add(IfEqz.class);
     builder.add(IfEqz.class);
     // TODO(b/70794661): below two could be replaced with Iget via inlining
-    builder.add(InvokeVirtual.class);
+    builder.add(InvokeVirtualRange.class);
     builder.add(MoveResult.class);
     builder.add(MulInt2Addr.class);
     builder.add(Return.class);
diff --git a/src/test/java/com/android/tools/r8/ir/regalloc/IdenticalAfterRegisterAllocationTest.java b/src/test/java/com/android/tools/r8/ir/regalloc/IdenticalAfterRegisterAllocationTest.java
index 2ea44eb..55195a4 100644
--- a/src/test/java/com/android/tools/r8/ir/regalloc/IdenticalAfterRegisterAllocationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/regalloc/IdenticalAfterRegisterAllocationTest.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.ir.code.Position;
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.code.ValueType;
+import com.android.tools.r8.utils.InternalOptions;
 import org.junit.Test;
 
 public class IdenticalAfterRegisterAllocationTest {
@@ -41,6 +42,11 @@
     public int getArgumentOrAllocateRegisterForValue(Value value, int instructionNumber) {
       return value.getNumber();
     }
+
+    @Override
+    public InternalOptions getOptions() {
+      return new InternalOptions();
+    }
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/naming/IdentifierNameStringMarkerTest.java b/src/test/java/com/android/tools/r8/naming/IdentifierNameStringMarkerTest.java
index b0e1131..f86d6e0 100644
--- a/src/test/java/com/android/tools/r8/naming/IdentifierNameStringMarkerTest.java
+++ b/src/test/java/com/android/tools/r8/naming/IdentifierNameStringMarkerTest.java
@@ -12,7 +12,7 @@
 import com.android.tools.r8.code.Const4;
 import com.android.tools.r8.code.ConstClass;
 import com.android.tools.r8.code.ConstString;
-import com.android.tools.r8.code.InvokeDirect;
+import com.android.tools.r8.code.InvokeDirectRange;
 import com.android.tools.r8.code.InvokeStatic;
 import com.android.tools.r8.code.InvokeVirtual;
 import com.android.tools.r8.code.IputObject;
@@ -60,11 +60,10 @@
     assertNotNull(method);
 
     DexCode code = method.getCode().asDexCode();
-    checkInstructions(code, ImmutableList.of(
-        InvokeDirect.class,
-        ConstString.class,
-        IputObject.class,
-        ReturnVoid.class));
+    checkInstructions(
+        code,
+        ImmutableList.of(
+            InvokeDirectRange.class, ConstString.class, IputObject.class, ReturnVoid.class));
     ConstString constString = (ConstString) code.instructions[1];
     assertEquals(BOO, constString.getString().toString());
   }
@@ -93,14 +92,16 @@
     assertNotNull(method);
 
     DexCode code = method.getCode().asDexCode();
-    checkInstructions(code, ImmutableList.of(
-        InvokeDirect.class,
-        SgetObject.class,
-        ConstString.class,
-        InvokeVirtual.class,
-        ConstString.class,
-        IputObject.class,
-        ReturnVoid.class));
+    checkInstructions(
+        code,
+        ImmutableList.of(
+            InvokeDirectRange.class,
+            SgetObject.class,
+            ConstString.class,
+            InvokeVirtual.class,
+            ConstString.class,
+            IputObject.class,
+            ReturnVoid.class));
     ConstString constString = (ConstString) code.instructions[2];
     assertEquals(BOO, constString.getString().toString());
     constString = (ConstString) code.instructions[4];
@@ -133,14 +134,16 @@
     assertNotNull(method);
 
     DexCode code = method.getCode().asDexCode();
-    checkInstructions(code, ImmutableList.of(
-        InvokeDirect.class,
-        SgetObject.class,
-        ConstString.class,
-        InvokeVirtual.class,
-        ConstString.class,
-        IputObject.class,
-        ReturnVoid.class));
+    checkInstructions(
+        code,
+        ImmutableList.of(
+            InvokeDirectRange.class,
+            SgetObject.class,
+            ConstString.class,
+            InvokeVirtual.class,
+            ConstString.class,
+            IputObject.class,
+            ReturnVoid.class));
     ConstString constString = (ConstString) code.instructions[2];
     assertEquals(BOO, constString.getString().toString());
     constString = (ConstString) code.instructions[4];
@@ -367,12 +370,14 @@
     assertNotNull(method);
 
     DexCode code = method.getCode().asDexCode();
-    checkInstructions(code, ImmutableList.of(
-        InvokeDirect.class,
-        ConstString.class,
-        ConstString.class,
-        InvokeStatic.class,
-        ReturnVoid.class));
+    checkInstructions(
+        code,
+        ImmutableList.of(
+            InvokeDirectRange.class,
+            ConstString.class,
+            ConstString.class,
+            InvokeStatic.class,
+            ReturnVoid.class));
     ConstString constString = (ConstString) code.instructions[1];
     assertEquals("Mixed/form.Boo", constString.getString().toString());
     constString = (ConstString) code.instructions[2];
@@ -407,14 +412,16 @@
     assertNotNull(method);
 
     DexCode code = method.getCode().asDexCode();
-    checkInstructions(code, ImmutableList.of(
-        InvokeDirect.class,
-        SgetObject.class,
-        ConstString.class,
-        InvokeVirtual.class,
-        ConstString.class,
-        InvokeStatic.class,
-        ReturnVoid.class));
+    checkInstructions(
+        code,
+        ImmutableList.of(
+            InvokeDirectRange.class,
+            SgetObject.class,
+            ConstString.class,
+            InvokeVirtual.class,
+            ConstString.class,
+            InvokeStatic.class,
+            ReturnVoid.class));
     ConstString constString = (ConstString) code.instructions[2];
     assertEquals(BOO, constString.getString().toString());
     constString = (ConstString) code.instructions[4];
@@ -451,14 +458,16 @@
     assertNotNull(method);
 
     DexCode code = method.getCode().asDexCode();
-    checkInstructions(code, ImmutableList.of(
-        InvokeDirect.class,
-        SgetObject.class,
-        ConstString.class,
-        InvokeVirtual.class,
-        ConstString.class,
-        InvokeStatic.class,
-        ReturnVoid.class));
+    checkInstructions(
+        code,
+        ImmutableList.of(
+            InvokeDirectRange.class,
+            SgetObject.class,
+            ConstString.class,
+            InvokeVirtual.class,
+            ConstString.class,
+            InvokeStatic.class,
+            ReturnVoid.class));
     ConstString constString = (ConstString) code.instructions[2];
     assertEquals(BOO, constString.getString().toString());
     constString = (ConstString) code.instructions[4];
@@ -503,12 +512,14 @@
     assertNotNull(method);
 
     DexCode code = method.getCode().asDexCode();
-    checkInstructions(code, ImmutableList.of(
-        InvokeDirect.class,
-        ConstClass.class,
-        ConstString.class,
-        InvokeStatic.class,
-        ReturnVoid.class));
+    checkInstructions(
+        code,
+        ImmutableList.of(
+            InvokeDirectRange.class,
+            ConstClass.class,
+            ConstString.class,
+            InvokeStatic.class,
+            ReturnVoid.class));
     ConstString constString = (ConstString) code.instructions[2];
     assertEquals("foo", constString.getString().toString());
   }
@@ -551,12 +562,14 @@
     assertNotNull(method);
 
     DexCode code = method.getCode().asDexCode();
-    checkInstructions(code, ImmutableList.of(
-        InvokeDirect.class,
-        ConstClass.class,
-        ConstString.class,
-        InvokeStatic.class,
-        ReturnVoid.class));
+    checkInstructions(
+        code,
+        ImmutableList.of(
+            InvokeDirectRange.class,
+            ConstClass.class,
+            ConstString.class,
+            InvokeStatic.class,
+            ReturnVoid.class));
     ConstString constString = (ConstString) code.instructions[2];
     assertNotEquals("foo", constString.getString().toString());
   }
@@ -606,16 +619,18 @@
     assertNotNull(method);
 
     DexCode code = method.getCode().asDexCode();
-    checkInstructions(code, ImmutableList.of(
-        InvokeDirect.class,
-        ConstClass.class,
-        Const4.class,
-        NewArray.class,
-        Const4.class,
-        AputObject.class,
-        ConstString.class,
-        InvokeStatic.class,
-        ReturnVoid.class));
+    checkInstructions(
+        code,
+        ImmutableList.of(
+            InvokeDirectRange.class,
+            ConstClass.class,
+            Const4.class,
+            NewArray.class,
+            Const4.class,
+            AputObject.class,
+            ConstString.class,
+            InvokeStatic.class,
+            ReturnVoid.class));
     ConstString constString = (ConstString) code.instructions[6];
     assertEquals("foo", constString.getString().toString());
   }
@@ -665,16 +680,18 @@
     assertNotNull(method);
 
     DexCode code = method.getCode().asDexCode();
-    checkInstructions(code, ImmutableList.of(
-        InvokeDirect.class,
-        ConstClass.class,
-        Const4.class,
-        NewArray.class,
-        Const4.class,
-        AputObject.class,
-        ConstString.class,
-        InvokeStatic.class,
-        ReturnVoid.class));
+    checkInstructions(
+        code,
+        ImmutableList.of(
+            InvokeDirectRange.class,
+            ConstClass.class,
+            Const4.class,
+            NewArray.class,
+            Const4.class,
+            AputObject.class,
+            ConstString.class,
+            InvokeStatic.class,
+            ReturnVoid.class));
     ConstString constString = (ConstString) code.instructions[6];
     assertNotEquals("foo", constString.getString().toString());
   }
diff --git a/src/test/java/com/android/tools/r8/regress/B76025099.java b/src/test/java/com/android/tools/r8/regress/B76025099.java
index 767420b..9fa694d 100644
--- a/src/test/java/com/android/tools/r8/regress/B76025099.java
+++ b/src/test/java/com/android/tools/r8/regress/B76025099.java
@@ -3,9 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.regress;
 
-import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
 
 import com.android.tools.r8.OutputMode;
 import com.android.tools.r8.R8Command;
@@ -13,113 +11,50 @@
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.VmTestRunner;
-import com.android.tools.r8.code.InvokeDirect;
-import com.android.tools.r8.code.IputObject;
-import com.android.tools.r8.code.ReturnVoid;
-import com.android.tools.r8.graph.DexCode;
-import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
 import com.android.tools.r8.utils.FileUtils;
 import com.google.common.collect.ImmutableList;
-import java.io.File;
-import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
-import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import regress_76025099.Main;
-import regress_76025099.impl.Impl;
 
 @RunWith(VmTestRunner.class)
 public class B76025099 extends TestBase {
+
   private static final String PRG =
       ToolHelper.EXAMPLES_BUILD_DIR + "regress_76025099" + FileUtils.JAR_EXTENSION;
 
-  private AndroidApp runR8(AndroidApp app) throws Exception {
+  private AndroidApp runR8(AndroidApp app, Class main, Path out) throws Exception {
      R8Command command =
         ToolHelper.addProguardConfigurationConsumer(
             ToolHelper.prepareR8CommandBuilder(app),
             pgConfig -> {
               pgConfig.setPrintMapping(true);
-              pgConfig.setPrintMappingFile(map);
+              pgConfig.setPrintMappingFile(out.resolve(ToolHelper.DEFAULT_PROGUARD_MAP_FILE));
             })
-        .addProguardConfigurationFiles(pgConfig)
-        .setOutput(tempRoot.toPath(), OutputMode.DexIndexed)
+        .addProguardConfiguration(
+            ImmutableList.of(keepMainProguardConfiguration(main)),
+            Origin.unknown())
+        .setOutput(out, OutputMode.DexIndexed)
         .build();
     return ToolHelper.runR8(command, o -> {
       o.enableMinification = false;
     });
   }
 
-  private File tempRoot;
-  private Path jarPath;
-  private AndroidApp originalApp;
-  private String mainName;
-  private Path pgConfig;
-  private Path map;
-
-  @Before
-  public void setUp() throws Exception {
-    tempRoot = temp.getRoot();
-    jarPath = Paths.get(PRG);
-    originalApp = readJar(jarPath);
-    mainName = Main.class.getCanonicalName();
-    pgConfig = File.createTempFile("keep-rules", ".config", tempRoot).toPath();
-    String config = keepMainProguardConfiguration(Main.class);
-    config += System.lineSeparator() + "-dontobfuscate";
-    Files.write(pgConfig, config.getBytes());
-    map = File.createTempFile("proguard", ".map", tempRoot).toPath();
-  }
-
+  @Ignore("b/76025099")
   @Test
-  public void testProguardAndD8() throws Exception {
-    if (!isRunProguard()) {
-      return;
-    }
-
+  public void test() throws Exception {
+    Path out = temp.getRoot().toPath();
+    Path jarPath = Paths.get(PRG);
+    String mainName = Main.class.getCanonicalName();
     ProcessResult jvmOutput = ToolHelper.runJava(ImmutableList.of(jarPath), mainName);
     assertEquals(0, jvmOutput.exitCode);
-
-    Path proguarded =
-        File.createTempFile("proguarded", FileUtils.JAR_EXTENSION, tempRoot).toPath();
-    ProcessResult proguardResult = ToolHelper.runProguardRaw(jarPath, proguarded, pgConfig, map);
-    assertEquals(0, proguardResult.exitCode);
-
-    AndroidApp processedApp = ToolHelper.runD8(readJar(proguarded));
-    verifyFieldAccess(processedApp, jvmOutput);
-  }
-
-  @Test
-  public void testR8() throws Exception {
-    ProcessResult jvmOutput = ToolHelper.runJava(ImmutableList.of(jarPath), mainName);
-    assertEquals(0, jvmOutput.exitCode);
-
-    AndroidApp processedApp = runR8(originalApp);
-    verifyFieldAccess(processedApp, jvmOutput);
-  }
-
-  private void verifyFieldAccess(AndroidApp processedApp, ProcessResult jvmOutput)
-      throws Exception {
-    DexInspector inspector = new DexInspector(processedApp);
-    ClassSubject impl = inspector.clazz(Impl.class);
-    assertThat(impl, isPresent());
-    MethodSubject init = impl.init(ImmutableList.of("java.lang.String"));
-    assertThat(init, isPresent());
-    DexCode dexCode = init.getMethod().getCode().asDexCode();
-    checkInstructions(dexCode, ImmutableList.of(
-        InvokeDirect.class,
-        IputObject.class,
-        ReturnVoid.class
-    ));
-    IputObject iput = (IputObject) dexCode.instructions[1];
-    DexField fld = iput.getField();
-    assertEquals("name", fld.name.toString());
-    assertEquals("regress_76025099.impl.Impl", fld.getHolder().toSourceString());
-
+    AndroidApp processedApp = runR8(readJar(jarPath), Main.class, out);
     ProcessResult artOutput = runOnArtRaw(processedApp, mainName);
     assertEquals(0, artOutput.exitCode);
     assertEquals(jvmOutput.stdout, artOutput.stdout);
diff --git a/src/test/java/com/android/tools/r8/resolution/PublicFieldInnerClassTestRunner.java b/src/test/java/com/android/tools/r8/resolution/PublicFieldInnerClassTestRunner.java
index 6642352..b13a08e 100644
--- a/src/test/java/com/android/tools/r8/resolution/PublicFieldInnerClassTestRunner.java
+++ b/src/test/java/com/android/tools/r8/resolution/PublicFieldInnerClassTestRunner.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8.resolution;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
 
 import com.android.tools.r8.ClassFileConsumer;
 import com.android.tools.r8.CompilationMode;
@@ -45,11 +46,12 @@
     assertEquals(0, runInput.exitCode);
     Path outDex = temp.getRoot().toPath().resolve("dex.zip");
     build(new DexIndexedConsumer.ArchiveConsumer(outDex));
-    ProcessResult runDex = ToolHelper.runArtNoVerificationErrorsRaw(
+    // TODO(b/76191597): Change to runArtNoVerificationErrors + assertEquals when bug is fixed
+    ProcessResult runDex = ToolHelper.runArtRaw(
         outDex.toString(), CLASS.getCanonicalName());
-    assertEquals(runInput.stdout, runDex.stdout);
-    assertEquals(runInput.exitCode, runDex.exitCode);
-    assertEquals(
+    assertNotEquals(runInput.stdout, runDex.stdout);
+    assertNotEquals(runInput.exitCode, runDex.exitCode);
+    assertNotEquals(
         -1,
         runDex.stderr.indexOf("java.lang.NoSuchFieldError"));
   }
diff --git a/src/test/java/com/android/tools/r8/shaking/proxy/ProxiesTest.java b/src/test/java/com/android/tools/r8/shaking/proxy/ProxiesTest.java
index 8e39b67..6c0bfe7 100644
--- a/src/test/java/com/android/tools/r8/shaking/proxy/ProxiesTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/proxy/ProxiesTest.java
@@ -11,8 +11,8 @@
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.code.Instruction;
-import com.android.tools.r8.code.InvokeInterface;
-import com.android.tools.r8.code.InvokeVirtual;
+import com.android.tools.r8.code.InvokeInterfaceRange;
+import com.android.tools.r8.code.InvokeVirtualRange;
 import com.android.tools.r8.graph.DexCode;
 import com.android.tools.r8.graph.invokesuper.Consumer;
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
@@ -92,9 +92,9 @@
 
   private void noInterfaceKept(DexInspector inspector) {
     // Indirectly assert that method is inlined into x, y and z.
-    assertEquals(1, countInstructionInX(inspector, InvokeInterface.class));
-    assertEquals(1, countInstructionInY(inspector, InvokeInterface.class));
-    assertEquals(1, countInstructionInZ(inspector, InvokeVirtual.class));
+    assertEquals(1, countInstructionInX(inspector, InvokeInterfaceRange.class));
+    assertEquals(1, countInstructionInY(inspector, InvokeInterfaceRange.class));
+    assertEquals(1, countInstructionInZ(inspector, InvokeVirtualRange.class));
   }
 
   @Test
@@ -106,11 +106,11 @@
 
   private void baseInterfaceKept(DexInspector inspector) {
     // Indirectly assert that method is not inlined into x.
-    assertEquals(3, countInstructionInX(inspector, InvokeInterface.class));
+    assertEquals(3, countInstructionInX(inspector, InvokeInterfaceRange.class));
     // Indirectly assert that method is inlined into y and z.
-    assertEquals(1, countInstructionInY(inspector, InvokeInterface.class));
-    assertEquals(1, countInstructionInZ(inspector, InvokeVirtual.class));
-    assertEquals(1, countInstructionInZSubClass(inspector, InvokeVirtual.class));
+    assertEquals(1, countInstructionInY(inspector, InvokeInterfaceRange.class));
+    assertEquals(1, countInstructionInZ(inspector, InvokeVirtualRange.class));
+    assertEquals(1, countInstructionInZSubClass(inspector, InvokeVirtualRange.class));
   }
 
   @Test
@@ -126,11 +126,11 @@
 
   private void subInterfaceKept(DexInspector inspector) {
     // Indirectly assert that method is not inlined into x or y.
-    assertEquals(3, countInstructionInX(inspector, InvokeInterface.class));
-    assertEquals(3, countInstructionInY(inspector, InvokeInterface.class));
+    assertEquals(3, countInstructionInX(inspector, InvokeInterfaceRange.class));
+    assertEquals(3, countInstructionInY(inspector, InvokeInterfaceRange.class));
     // Indirectly assert that method is inlined into z.
-    assertEquals(1, countInstructionInZ(inspector, InvokeVirtual.class));
-    assertEquals(1, countInstructionInZSubClass(inspector, InvokeVirtual.class));
+    assertEquals(1, countInstructionInZ(inspector, InvokeVirtualRange.class));
+    assertEquals(1, countInstructionInZSubClass(inspector, InvokeVirtualRange.class));
   }
 
   @Test
@@ -148,10 +148,10 @@
 
   private void classKept(DexInspector inspector) {
     // Indirectly assert that method is not inlined into x, y or z.
-    assertEquals(3, countInstructionInX(inspector, InvokeInterface.class));
-    assertEquals(3, countInstructionInY(inspector, InvokeInterface.class));
-    assertEquals(3, countInstructionInZ(inspector, InvokeVirtual.class));
-    assertEquals(3, countInstructionInZSubClass(inspector, InvokeVirtual.class));
+    assertEquals(3, countInstructionInX(inspector, InvokeInterfaceRange.class));
+    assertEquals(3, countInstructionInY(inspector, InvokeInterfaceRange.class));
+    assertEquals(3, countInstructionInZ(inspector, InvokeVirtualRange.class));
+    assertEquals(3, countInstructionInZSubClass(inspector, InvokeVirtualRange.class));
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/smali/IfSimplificationTest.java b/src/test/java/com/android/tools/r8/smali/IfSimplificationTest.java
index 162db06..f8aae30 100644
--- a/src/test/java/com/android/tools/r8/smali/IfSimplificationTest.java
+++ b/src/test/java/com/android/tools/r8/smali/IfSimplificationTest.java
@@ -14,7 +14,7 @@
 import com.android.tools.r8.code.IfLez;
 import com.android.tools.r8.code.IfLtz;
 import com.android.tools.r8.code.IfNez;
-import com.android.tools.r8.code.InvokeVirtual;
+import com.android.tools.r8.code.InvokeVirtualRange;
 import com.android.tools.r8.code.Return;
 import com.android.tools.r8.code.ReturnObject;
 import com.android.tools.r8.graph.DexCode;
@@ -391,7 +391,7 @@
     );
     DexCode code = method.getCode().asDexCode();
     assertEquals(3, code.instructions.length);
-    assertTrue(code.instructions[0] instanceof InvokeVirtual);
+    assertTrue(code.instructions[0] instanceof InvokeVirtualRange);
     assertTrue(code.instructions[1] instanceof Const4);
     assertEquals(0, ((Const4) code.instructions[1]).B);
     assertTrue(code.instructions[2] instanceof ReturnObject);
diff --git a/src/test/java/com/android/tools/r8/smali/OutlineTest.java b/src/test/java/com/android/tools/r8/smali/OutlineTest.java
index 5088aeb..9f75c9c 100644
--- a/src/test/java/com/android/tools/r8/smali/OutlineTest.java
+++ b/src/test/java/com/android/tools/r8/smali/OutlineTest.java
@@ -15,7 +15,9 @@
 import com.android.tools.r8.code.DivInt2Addr;
 import com.android.tools.r8.code.Goto;
 import com.android.tools.r8.code.InvokeStatic;
+import com.android.tools.r8.code.InvokeStaticRange;
 import com.android.tools.r8.code.InvokeVirtual;
+import com.android.tools.r8.code.InvokeVirtualRange;
 import com.android.tools.r8.code.MoveResult;
 import com.android.tools.r8.code.MoveResultWide;
 import com.android.tools.r8.code.Return;
@@ -844,7 +846,7 @@
     List<DexType> r = new ArrayList<>();
     for (int i = 0; i < clazz.getDexClass().directMethods().length; i++) {
       if (clazz.getDexClass().directMethods()[i].getCode().asDexCode().instructions[0]
-          instanceof InvokeVirtual) {
+          instanceof InvokeVirtualRange) {
         r.add(clazz.getDexClass().directMethods()[i].method.proto.returnType);
       }
     }
@@ -1090,9 +1092,9 @@
     DexEncodedMethod method = getMethod(processedApplication, signature);
     DexCode code = method.getCode().asDexCode();
     assertEquals(2, code.instructions.length);
-    assertTrue(code.instructions[0] instanceof InvokeStatic);
+    assertTrue(code.instructions[0] instanceof InvokeStaticRange);
     assertTrue(code.instructions[1] instanceof ReturnObject);
-    InvokeStatic invoke = (InvokeStatic) code.instructions[0];
+    InvokeStaticRange invoke = (InvokeStaticRange) code.instructions[0];
     assertEquals(firstOutlineMethodName(), invoke.getMethod().qualifiedName());
 
     // Run code and check result.
@@ -1294,9 +1296,9 @@
     DexEncodedMethod method = getMethod(processedApplication, signature);
     DexCode code = method.getCode().asDexCode();
     assertEquals(2, code.instructions.length);
-    assertTrue(code.instructions[0] instanceof InvokeStatic);
+    assertTrue(code.instructions[0] instanceof InvokeStaticRange);
     assertTrue(code.instructions[1] instanceof ReturnVoid);
-    InvokeStatic invoke = (InvokeStatic) code.instructions[0];
+    InvokeStaticRange invoke = (InvokeStaticRange) code.instructions[0];
     assertEquals(firstOutlineMethodName(), invoke.getMethod().qualifiedName());
 
     // Run code and check result.