Ensure fields marked as dead will be eligible for removal

Bug: 183734568
Change-Id: Ib7c4e1aa763e0ebec6e884600e3c18b9ec1a71e2
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java
index b11431e..e98e0d2 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java
@@ -93,9 +93,19 @@
     timing.end(); // Clear reads from fields of interest
     timing.end(); // Trivial field accesses analysis
 
-    constantFields.forEach(OptimizationFeedbackSimple.getInstance()::markFieldAsDead);
-    readFields.keySet().forEach(OptimizationFeedbackSimple.getInstance()::markFieldAsDead);
-    writtenFields.keySet().forEach(OptimizationFeedbackSimple.getInstance()::markFieldAsDead);
+    constantFields.forEach(this::markFieldAsDead);
+    readFields.keySet().forEach(this::markFieldAsDead);
+    writtenFields.keySet().forEach(this::markFieldAsDead);
+  }
+
+  private void markFieldAsDead(DexEncodedField field) {
+    // Don't mark pinned fields as dead, since they need to remain in the app even if all reads and
+    // writes are removed.
+    if (appView.appInfo().isPinned(field)) {
+      assert field.getType().isAlwaysNull(appView);
+    } else {
+      OptimizationFeedbackSimple.getInstance().markFieldAsDead(field);
+    }
   }
 
   private void computeFieldsWithNonTrivialValue() {
@@ -236,7 +246,8 @@
       DexEncodedField field,
       boolean isWrite,
       FieldAccessInfoCollection<?> fieldAccessInfoCollection) {
-    assert !appView.appInfo().isPinned(field);
+    assert !appView.appInfo().isPinned(field) || field.getType().isAlwaysNull(appView);
+
     FieldAccessInfo fieldAccessInfo = fieldAccessInfoCollection.get(field.getReference());
     if (fieldAccessInfo == null) {
       assert false
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
index 606fbb6..573c0c5 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.ir.desugar.itf;
 
-import static com.android.tools.r8.utils.PredicateUtils.not;
 
 import com.android.tools.r8.cf.code.CfFieldInstruction;
 import com.android.tools.r8.cf.code.CfInstruction;
@@ -169,7 +168,10 @@
 
   private DexEncodedField findExistingStaticClinitFieldToTriggerInterfaceInitialization(
       DexProgramClass iface) {
-    for (DexEncodedField field : iface.staticFields(not(DexEncodedField::isPrivate))) {
+    // Don't select a field that has been marked dead, since we'll assert later that these fields
+    // have been dead code eliminated.
+    for (DexEncodedField field :
+        iface.staticFields(field -> !field.isPrivate() && !field.getOptimizationInfo().isDead())) {
       return field;
     }
     return null;
diff --git a/src/main/java/com/android/tools/r8/shaking/ClassInitFieldSynthesizer.java b/src/main/java/com/android/tools/r8/shaking/ClassInitFieldSynthesizer.java
index 03611d6..e8d52aa 100644
--- a/src/main/java/com/android/tools/r8/shaking/ClassInitFieldSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/shaking/ClassInitFieldSynthesizer.java
@@ -48,6 +48,10 @@
     // Use an existing static field if there is one.
     DexEncodedField encodedClinitField = null;
     for (DexEncodedField staticField : clazz.staticFields()) {
+      // Don't consider dead fields.
+      if (staticField.getOptimizationInfo().isDead()) {
+        continue;
+      }
       // We need the field to be accessible from the contexts in which it is accessed.
       if (!isMinimumRequiredVisibility(staticField, minimumRequiredVisibility)) {
         continue;