Merge "Add assert when reading invokespecial method handle"
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 7dc77de..527f27b 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -162,7 +162,7 @@
       "Ldalvik/annotation/SourceDebugExtension;");
   public final DexType annotationThrows = createType("Ldalvik/annotation/Throws;");
 
-  public void clearSubtypeInformation() {
+  public synchronized void clearSubtypeInformation() {
     types.values().forEach(DexType::clearSubtypeInformation);
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLense.java b/src/main/java/com/android/tools/r8/graph/GraphLense.java
index b9173dd..3abe7c5 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLense.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLense.java
@@ -118,18 +118,21 @@
     @Override
     public DexType lookupType(DexType type, DexEncodedMethod context) {
       if (type.isArrayType()) {
-        DexType result = arrayTypeCache.get(type);
-        if (result == null) {
-          DexType baseType = type.toBaseType(dexItemFactory);
-          DexType newType = lookupType(baseType, context);
-          if (baseType == newType) {
-            result = type;
-          } else {
-            result = type.replaceBaseType(newType, dexItemFactory);
+        synchronized(this) {
+          // This block need to be synchronized due to arrayTypeCache.
+          DexType result = arrayTypeCache.get(type);
+          if (result == null) {
+            DexType baseType = type.toBaseType(dexItemFactory);
+            DexType newType = lookupType(baseType, context);
+            if (baseType == newType) {
+              result = type;
+            } else {
+              result = type.replaceBaseType(newType, dexItemFactory);
+            }
+            arrayTypeCache.put(type, result);
           }
-          arrayTypeCache.put(type, result);
+          return result;
         }
-        return result;
       }
       DexType previous = previousLense.lookupType(type, context);
       return typeMap.getOrDefault(previous, previous);
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java b/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
index e376361..ef5a025 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
@@ -26,7 +26,6 @@
     // are all for fixed register values. All other values that are used as the destination for
     // const number instructions should be marked as constants.
     assert dest.isFixedRegisterValue() || dest.definition.isConstNumber();
-    assert type != ConstType.OBJECT;
     this.type = type;
     this.value = value;
   }
@@ -50,7 +49,7 @@
   }
 
   public int getIntValue() {
-    assert type == ConstType.INT || type == ConstType.INT_OR_FLOAT;
+    assert type == ConstType.INT || type == ConstType.INT_OR_FLOAT || type == ConstType.OBJECT;
     return (int) value;
   }
 
@@ -101,7 +100,7 @@
     }
 
     int register = builder.allocatedRegister(dest(), getNumber());
-    if (MoveType.fromConstType(type) == MoveType.SINGLE) {
+    if (MoveType.fromConstType(type) == MoveType.SINGLE || type == ConstType.OBJECT) {
       assert NumberUtils.is32Bit(value);
       if ((register & 0xf) == register && NumberUtils.is4Bit(value)) {
         builder.add(this, new Const4(register, (int) value));
diff --git a/src/main/java/com/android/tools/r8/ir/code/Div.java b/src/main/java/com/android/tools/r8/ir/code/Div.java
index 8fd9bf0..7dc5e75 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Div.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Div.java
@@ -88,7 +88,7 @@
 
   @Override
   public boolean canBeFolded() {
-    return super.canBeFolded() && !rightValue().getConstInstruction().asConstNumber().isZero();
+    return super.canBeFolded() && !rightValue().isZero();
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Phi.java b/src/main/java/com/android/tools/r8/ir/code/Phi.java
index 20dc3c7..65995ff 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Phi.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Phi.java
@@ -273,9 +273,9 @@
   }
 
   private boolean isSingleConstZero(Value value) {
-    return value.definition != null && value.definition.isConstNumber() &&
-        value.definition.asConstNumber().isZero() &&
-        value.outType() == MoveType.SINGLE;
+    return value.definition != null
+        && value.outType() == MoveType.SINGLE
+        && value.isZero();
   }
 
   private MoveType computeOutType(Set<Phi> active) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/Rem.java b/src/main/java/com/android/tools/r8/ir/code/Rem.java
index 6521ae7..a257109 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Rem.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Rem.java
@@ -88,7 +88,7 @@
 
   @Override
   public boolean canBeFolded() {
-    return super.canBeFolded() && !rightValue().getConstInstruction().asConstNumber().isZero();
+    return super.canBeFolded() && !rightValue().isZero();
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Value.java b/src/main/java/com/android/tools/r8/ir/code/Value.java
index 62a1152..2880c87 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Value.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Value.java
@@ -614,4 +614,10 @@
     }
     return true;
   }
+
+  public boolean isZero() {
+    return isConstant()
+        && getConstInstruction().isConstNumber()
+        && getConstInstruction().asConstNumber().isZero();
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index 32159c1..01ce6a4 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -724,7 +724,7 @@
   }
 
   public void addNullConst(int dest, long value) {
-    canonicalizeAndAddConst(ConstType.INT, dest, value, nullConstants);
+    canonicalizeAndAddConst(ConstType.OBJECT, dest, value, nullConstants);
   }
 
   public void addConstClass(int dest, DexType type) {
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 59106c8..ec61f43 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
@@ -87,6 +87,7 @@
 import java.util.Comparator;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.IdentityHashMap;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
@@ -825,7 +826,7 @@
                   assert (invoke.outType() == argument.outType()) ||
                       (invoke.outType() == MoveType.OBJECT
                           && argument.outType() == MoveType.SINGLE
-                          && argument.getConstInstruction().asConstNumber().isZero());
+                          && argument.isZero());
                   invoke.outValue().replaceUsers(argument);
                   invoke.setOutValue(null);
                 }
@@ -948,8 +949,7 @@
           if (field.getHolder() == method.method.getHolder()) {
             if (put.inValue().isConstant()) {
               if ((field.type.isClassType() || field.type.isArrayType())
-                  && put.inValue().getConstInstruction().isConstNumber() &&
-                  put.inValue().getConstInstruction().asConstNumber().isZero()) {
+                  && put.inValue().isZero()) {
                 // Collect put of zero as a potential default value.
                 puts.put(put.getField(), put);
               } else if (field.type.isPrimitiveType() || field.type == dexItemFactory.stringType) {
@@ -979,8 +979,8 @@
         DexEncodedField encodedField = appInfo.definitionFor(field);
         if (field.type == dexItemFactory.stringType) {
           if (put.inValue().isConstant()) {
-            if (put.inValue().getConstInstruction().isConstNumber()) {
-              assert put.inValue().getConstInstruction().asConstNumber().isZero();
+            if (put.inValue().isConstNumber()) {
+              assert put.inValue().isZero();
               encodedField.staticValue = DexValueNull.NULL;
             } else {
               ConstString cnst = put.inValue().getConstInstruction().asConstString();
@@ -999,8 +999,7 @@
             }
           }
         } else if (field.type.isClassType() || field.type.isArrayType()) {
-          if (put.inValue().getConstInstruction().isConstNumber()
-              && put.inValue().getConstInstruction().asConstNumber().isZero()) {
+          if (put.inValue().isZero()) {
             encodedField.staticValue = DexValueNull.NULL;
           } else {
             throw new Unreachable("Unexpected default value for field type " + field.type + ".");
@@ -1124,7 +1123,7 @@
         if (current.isInvoke() && current.asInvoke().requiredArgumentRegisters() > 5) {
           Invoke invoke = current.asInvoke();
           it.previous();
-          Map<ConstNumber, ConstNumber> oldToNew = new HashMap<>();
+          Map<ConstNumber, ConstNumber> oldToNew = new IdentityHashMap<>();
           for (int i = 0; i < invoke.inValues().size(); i++) {
             Value value = invoke.inValues().get(i);
             if (value.isConstNumber() && value.numberOfUsers() > 1) {
diff --git a/src/main/java/com/android/tools/r8/shaking/protolite/ProtoLitePruner.java b/src/main/java/com/android/tools/r8/shaking/protolite/ProtoLitePruner.java
index 4610f41..09bbad6 100644
--- a/src/main/java/com/android/tools/r8/shaking/protolite/ProtoLitePruner.java
+++ b/src/main/java/com/android/tools/r8/shaking/protolite/ProtoLitePruner.java
@@ -137,8 +137,7 @@
   }
 
   private boolean isDefinedAsNull(Value value) {
-    return value.definition != null && value.definition.isConstNumber()
-        && value.definition.asConstNumber().isZero();
+    return value.definition != null && value.isZero();
   }
 
   private boolean isComputeSizeMethod(DexMethod invokedMethod) {