Merge "DebugLocalsChange instructions after spill moves"
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/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/regalloc/LinearScanRegisterAllocator.java b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
index dd13dfc..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
@@ -590,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();
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/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index a8ff665..d199f37 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -558,4 +558,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/ir/optimize/R8InliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
index 45b99e3..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
@@ -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();
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