Version 2.1.71

Cherry pick: Fix nondeterminism in redundant field load elimination
CL: https://r8-review.googlesource.com/c/r8/+/53900

Change-Id: I642e9b7a26929404307b84d12d03d2497cbb2bdd
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 0a23b2c..faebb92 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 = "2.1.70";
+  public static final String LABEL = "2.1.71";
 
   private Version() {
   }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java
index e71ef7d..edd1b91 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java
@@ -16,6 +16,7 @@
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.IdentityHashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Queue;
@@ -132,7 +133,10 @@
     builder.append(" {");
     Set<DexType> interfaces = getInterfaces();
     if (interfaces != null) {
-      builder.append(interfaces.stream().map(DexType::toString).collect(Collectors.joining(", ")));
+      List<DexType> sortedInterfaces = new ArrayList<>(interfaces);
+      sortedInterfaces.sort(DexType::slowCompareTo);
+      builder.append(
+          sortedInterfaces.stream().map(DexType::toString).collect(Collectors.joining(", ")));
     }
     builder.append("}");
     return builder.toString();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
index ef81097..6fe5c98 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
@@ -99,6 +99,11 @@
       it.removeOrReplaceByDebugLocalRead();
       value.uniquePhiUsers().forEach(Phi::removeTrivialPhi);
     }
+
+    @Override
+    public String toString() {
+      return "ExistingValue(v" + value.getNumber() + ")";
+    }
   }
 
   private class MaterializableValue implements FieldValue {
@@ -361,7 +366,7 @@
 
     InstanceFieldInitializationInfoCollection fieldInitializationInfos =
         instanceInitializerInfo.fieldInitializationInfos();
-    fieldInitializationInfos.forEach(
+    fieldInitializationInfos.forEachWithDeterministicOrder(
         appView,
         (field, info) -> {
           if (!appView.appInfo().withLiveness().mayPropagateValueFor(field.field)) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/EmptyInstanceFieldInitializationInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/EmptyInstanceFieldInitializationInfoCollection.java
index fd7a84f..271f225 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/field/EmptyInstanceFieldInitializationInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/EmptyInstanceFieldInitializationInfoCollection.java
@@ -35,6 +35,13 @@
   }
 
   @Override
+  public void forEachWithDeterministicOrder(
+      DexDefinitionSupplier definitions,
+      BiConsumer<DexEncodedField, InstanceFieldInitializationInfo> consumer) {
+    // Intentionally empty.
+  }
+
+  @Override
   public InstanceFieldInitializationInfo get(DexEncodedField field) {
     return UnknownInstanceFieldInitializationInfo.getInstance();
   }
@@ -49,4 +56,9 @@
       AppView<AppInfoWithLiveness> appView, GraphLense lens) {
     return this;
   }
+
+  @Override
+  public String toString() {
+    return "EmptyInstanceFieldInitializationInfoCollection";
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldArgumentInitializationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldArgumentInitializationInfo.java
index 757eec8..fee31d5 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldArgumentInitializationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldArgumentInitializationInfo.java
@@ -43,4 +43,9 @@
     // initialization info.
     return this;
   }
+
+  @Override
+  public String toString() {
+    return "InstanceFieldArgumentInitializationInfo(argumentIndex=" + argumentIndex + ")";
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfoCollection.java
index 3ff1799..6e5e45c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfoCollection.java
@@ -10,8 +10,7 @@
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.GraphLense;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import java.util.IdentityHashMap;
-import java.util.Map;
+import java.util.TreeMap;
 import java.util.function.BiConsumer;
 
 /**
@@ -31,6 +30,10 @@
       DexDefinitionSupplier definitions,
       BiConsumer<DexEncodedField, InstanceFieldInitializationInfo> consumer);
 
+  public abstract void forEachWithDeterministicOrder(
+      DexDefinitionSupplier definitions,
+      BiConsumer<DexEncodedField, InstanceFieldInitializationInfo> consumer);
+
   public abstract InstanceFieldInitializationInfo get(DexEncodedField field);
 
   public abstract boolean isEmpty();
@@ -40,7 +43,8 @@
 
   public static class Builder {
 
-    Map<DexField, InstanceFieldInitializationInfo> infos = new IdentityHashMap<>();
+    TreeMap<DexField, InstanceFieldInitializationInfo> infos =
+        new TreeMap<>(DexField::slowCompareTo);
 
     public void recordInitializationInfo(
         DexEncodedField field, InstanceFieldInitializationInfo info) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldTypeInitializationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldTypeInitializationInfo.java
index 3bfce16..8e3b0b9 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldTypeInitializationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldTypeInitializationInfo.java
@@ -87,4 +87,9 @@
     return Objects.equals(dynamicLowerBoundType, info.dynamicLowerBoundType)
         && Objects.equals(dynamicUpperBoundType, info.dynamicUpperBoundType);
   }
+
+  @Override
+  public String toString() {
+    return "InstanceFieldTypeInitializationInfo";
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/NonTrivialInstanceFieldInitializationInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/NonTrivialInstanceFieldInitializationInfoCollection.java
index 2fe8a19..672471c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/field/NonTrivialInstanceFieldInitializationInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/NonTrivialInstanceFieldInitializationInfoCollection.java
@@ -10,17 +10,20 @@
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.GraphLense;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import java.util.Map;
+import com.android.tools.r8.utils.StringUtils;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.TreeMap;
 import java.util.function.BiConsumer;
 
 /** See {@link InstanceFieldArgumentInitializationInfo}. */
 public class NonTrivialInstanceFieldInitializationInfoCollection
     extends InstanceFieldInitializationInfoCollection {
 
-  private final Map<DexField, InstanceFieldInitializationInfo> infos;
+  private final TreeMap<DexField, InstanceFieldInitializationInfo> infos;
 
   NonTrivialInstanceFieldInitializationInfoCollection(
-      Map<DexField, InstanceFieldInitializationInfo> infos) {
+      TreeMap<DexField, InstanceFieldInitializationInfo> infos) {
     assert !infos.isEmpty();
     assert infos.values().stream().noneMatch(InstanceFieldInitializationInfo::isUnknown);
     this.infos = infos;
@@ -42,6 +45,14 @@
   }
 
   @Override
+  public void forEachWithDeterministicOrder(
+      DexDefinitionSupplier definitions,
+      BiConsumer<DexEncodedField, InstanceFieldInitializationInfo> consumer) {
+    // We currently use a sorted backing and can therefore simply use forEach().
+    forEach(definitions, consumer);
+  }
+
+  @Override
   public InstanceFieldInitializationInfo get(DexEncodedField field) {
     return infos.getOrDefault(field.field, UnknownInstanceFieldInitializationInfo.getInstance());
   }
@@ -65,4 +76,13 @@
         });
     return builder.build();
   }
+
+  @Override
+  public String toString() {
+    List<String> strings = new ArrayList<>();
+    infos.forEach((field, info) -> strings.add(field.toSourceString() + " -> " + info));
+    return "NonTrivialInstanceFieldInitializationInfoCollection("
+        + StringUtils.join(strings, "; ")
+        + ")";
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/UnknownInstanceFieldInitializationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/UnknownInstanceFieldInitializationInfo.java
index b2b6ce9..5303c9e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/field/UnknownInstanceFieldInitializationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/UnknownInstanceFieldInitializationInfo.java
@@ -33,4 +33,9 @@
       AppView<AppInfoWithLiveness> appView, GraphLense lens) {
     return this;
   }
+
+  @Override
+  public String toString() {
+    return "UnknownInstanceFieldInitializationInfo";
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/DefaultInstanceInitializerInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/DefaultInstanceInitializerInfo.java
index 4065fb0..05ad1bd 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/DefaultInstanceInitializerInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/DefaultInstanceInitializerInfo.java
@@ -59,4 +59,9 @@
       AppView<AppInfoWithLiveness> appView, GraphLense lens) {
     return this;
   }
+
+  @Override
+  public String toString() {
+    return "DefaultInstanceInitializerInfo";
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/NonTrivialInstanceInitializerInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/NonTrivialInstanceInitializerInfo.java
index f549927..770b59c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/NonTrivialInstanceInitializerInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/NonTrivialInstanceInitializerInfo.java
@@ -92,6 +92,11 @@
         lens.getRenamedMethodSignature(parent));
   }
 
+  @Override
+  public String toString() {
+    return "NonTrivialInstanceInitializerInfo(" + fieldInitializationInfos + ")";
+  }
+
   public static class Builder {
 
     private final InstanceFieldInitializationInfoCollection instanceFieldInitializationInfos;