Share tracing for fields in Enqueuer

Change-Id: I01281a4b528c6bbc6b9818ffd4c01ddb78f2beee
diff --git a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java
index 39aa866..6e6c326 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AbstractAccessContexts.ConcreteAccessContexts;
 import com.android.tools.r8.graph.lens.GraphLens;
+import com.android.tools.r8.shaking.Enqueuer.FieldAccessKind;
 import com.android.tools.r8.utils.Timing;
 import com.android.tools.r8.utils.collections.ProgramMethodSet;
 import com.google.common.collect.Sets;
@@ -265,6 +266,14 @@
     return (flags & FLAG_IS_READ_FROM_METHOD_HANDLE) != 0;
   }
 
+  public void setAccessedFromMethodHandle(FieldAccessKind accessKind) {
+    if (accessKind.isRead()) {
+      setReadFromMethodHandle();
+    } else {
+      setWrittenFromMethodHandle();
+    }
+  }
+
   public void setReadFromMethodHandle() {
     flags |= FLAG_IS_READ_FROM_METHOD_HANDLE;
   }
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysisCollection.java b/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysisCollection.java
index 4873046..babfa95 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysisCollection.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysisCollection.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.graph.analysis;
 
+import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.ClasspathOrLibraryClass;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedMethod;
@@ -18,6 +19,7 @@
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.shaking.DefaultEnqueuerUseRegistry;
 import com.android.tools.r8.shaking.Enqueuer;
+import com.android.tools.r8.shaking.Enqueuer.FieldAccessKind;
 import com.android.tools.r8.shaking.EnqueuerWorklist;
 import com.android.tools.r8.utils.ArrayUtils;
 import com.android.tools.r8.utils.Timing;
@@ -152,6 +154,30 @@
     }
   }
 
+  public void traceFieldAccess(
+      DexField field,
+      SingleFieldResolutionResult<?> resolutionResult,
+      ProgramMethod context,
+      EnqueuerWorklist worklist,
+      FieldAccessKind accessKind) {
+    switch (accessKind) {
+      case INSTANCE_READ:
+        traceInstanceFieldRead(field, resolutionResult, context, worklist);
+        break;
+      case INSTANCE_WRITE:
+        traceInstanceFieldWrite(field, resolutionResult, context, worklist);
+        break;
+      case STATIC_READ:
+        traceStaticFieldRead(field, resolutionResult, context, worklist);
+        break;
+      case STATIC_WRITE:
+        traceStaticFieldWrite(field, resolutionResult, context, worklist);
+        break;
+      default:
+        throw new Unreachable();
+    }
+  }
+
   public void traceInstanceFieldRead(
       DexField field,
       FieldResolutionResult resolutionResult,
@@ -164,7 +190,7 @@
 
   public void traceInstanceFieldWrite(
       DexField field,
-      FieldResolutionResult resolutionResult,
+      SingleFieldResolutionResult<?> resolutionResult,
       ProgramMethod context,
       EnqueuerWorklist worklist) {
     for (TraceFieldAccessEnqueuerAnalysis analysis : fieldAccessAnalyses) {
@@ -184,7 +210,7 @@
 
   public void traceStaticFieldWrite(
       DexField field,
-      FieldResolutionResult resolutionResult,
+      SingleFieldResolutionResult<?> resolutionResult,
       ProgramMethod context,
       EnqueuerWorklist worklist) {
     for (TraceFieldAccessEnqueuerAnalysis analysis : fieldAccessAnalyses) {
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 4baf2a5..6dcfe4f 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -1117,6 +1117,15 @@
   // traversals.
   //
 
+  public boolean registerFieldAccess(
+      DexField field, ProgramMethod context, FieldAccessKind accessKind) {
+    if (accessKind.isRead()) {
+      registerFieldRead(field, context);
+    } else {
+      registerFieldWrite(field, context);
+    }
+  }
+
   public boolean registerFieldRead(DexField field, ProgramMethod context) {
     return registerFieldAccess(field, context, true, false);
   }
@@ -1758,13 +1767,13 @@
     traceInstanceFieldRead(field, currentMethod, FieldAccessMetadata.FROM_RECORD_METHOD_HANDLE);
   }
 
-  enum FieldAccessKind {
+  public enum FieldAccessKind {
     INSTANCE_READ,
     INSTANCE_WRITE,
     STATIC_READ,
     STATIC_WRITE;
 
-    boolean isRead() {
+    public boolean isRead() {
       return this == INSTANCE_READ || this == STATIC_READ;
     }
 
@@ -1855,61 +1864,10 @@
     }
   }
 
-  @SuppressWarnings("ReferenceEquality")
   void traceInstanceFieldRead(
       DexField fieldReference, ProgramMethod currentMethod, FieldAccessMetadata metadata) {
-    if (!metadata.isDeferred() && !registerFieldRead(fieldReference, currentMethod)) {
-      return;
-    }
-
-    FieldResolutionResult resolutionResult = resolveField(fieldReference, currentMethod);
-    if (deferredTracing.deferTracingOfFieldAccess(
-        fieldReference, resolutionResult, currentMethod, FieldAccessKind.INSTANCE_READ, metadata)) {
-      assert !metadata.isDeferred();
-      return;
-    }
-
-    resolutionResult.visitFieldResolutionResults(
-        singleResolutionResult -> {
-          analyses.traceInstanceFieldRead(
-              fieldReference, singleResolutionResult, currentMethod, worklist);
-
-          DexClassAndField classField = singleResolutionResult.getResolutionPair();
-          assert classField != null;
-
-          DexClass initialResolutionHolder = singleResolutionResult.getInitialResolutionHolder();
-          if (initialResolutionHolder != classField.getHolder()) {
-            // Mark the initial resolution holder as live. Note that this should only be done if
-            // the field is not a dead proto field (in which case we bail-out above).
-            markTypeAsLive(initialResolutionHolder, currentMethod);
-          }
-
-          ProgramField field = classField.asProgramField();
-          if (field == null) {
-            // No need to trace into the non-program code.
-            return;
-          }
-
-          assert !mode.isFinalTreeShaking() || !field.getDefinition().getOptimizationInfo().isDead()
-              : "Unexpected reference in `"
-                  + currentMethod.toSourceString()
-                  + "` to field marked dead: "
-                  + field.getReference().toSourceString();
-
-          if (metadata.isFromMethodHandle()) {
-            fieldAccessInfoCollection.get(field.getReference()).setReadFromMethodHandle();
-          } else if (metadata.isFromRecordMethodHandle()) {
-            fieldAccessInfoCollection.get(field.getReference()).setReadFromRecordInvokeDynamic();
-          }
-
-          worklist.enqueueMarkFieldAsReachableAction(
-              field, currentMethod, KeepReason.fieldReferencedIn(currentMethod));
-        },
-        failedResolution -> {
-          // Must trace the types from the field reference even if it does not exist.
-          traceFieldReference(fieldReference, currentMethod);
-          noClassMerging.add(fieldReference.getHolderType());
-        });
+    traceInstanceFieldAccess(
+        fieldReference, currentMethod, FieldAccessKind.INSTANCE_READ, metadata);
   }
 
   void traceInstanceFieldWrite(DexField field, ProgramMethod currentMethod) {
@@ -1920,28 +1878,32 @@
     traceInstanceFieldWrite(field, currentMethod, FieldAccessMetadata.FROM_METHOD_HANDLE);
   }
 
-  @SuppressWarnings("ReferenceEquality")
   void traceInstanceFieldWrite(
       DexField fieldReference, ProgramMethod currentMethod, FieldAccessMetadata metadata) {
-    if (!metadata.isDeferred() && !registerFieldWrite(fieldReference, currentMethod)) {
+    traceInstanceFieldAccess(
+        fieldReference, currentMethod, FieldAccessKind.INSTANCE_WRITE, metadata);
+  }
+
+  private void traceInstanceFieldAccess(
+      DexField fieldReference,
+      ProgramMethod currentMethod,
+      FieldAccessKind accessKind,
+      FieldAccessMetadata metadata) {
+    if (!metadata.isDeferred() && !registerFieldAccess(fieldReference, currentMethod, accessKind)) {
       return;
     }
 
     FieldResolutionResult resolutionResult = resolveField(fieldReference, currentMethod);
     if (deferredTracing.deferTracingOfFieldAccess(
-        fieldReference,
-        resolutionResult,
-        currentMethod,
-        FieldAccessKind.INSTANCE_WRITE,
-        metadata)) {
+        fieldReference, resolutionResult, currentMethod, accessKind, metadata)) {
       assert !metadata.isDeferred();
       return;
     }
 
     resolutionResult.visitFieldResolutionResults(
         singleResolutionResult -> {
-          analyses.traceInstanceFieldWrite(
-              fieldReference, singleResolutionResult, currentMethod, worklist);
+          analyses.traceFieldAccess(
+              fieldReference, singleResolutionResult, currentMethod, worklist, accessKind);
 
           DexClassAndField classField = singleResolutionResult.getResolutionPair();
           assert classField != null;
@@ -1966,7 +1928,11 @@
                   + field.getReference().toSourceString();
 
           if (metadata.isFromMethodHandle()) {
-            fieldAccessInfoCollection.get(field.getReference()).setWrittenFromMethodHandle();
+            fieldAccessInfoCollection
+                .get(field.getReference())
+                .setAccessedFromMethodHandle(accessKind);
+          } else if (metadata.isFromRecordMethodHandle() && accessKind.isRead()) {
+            fieldAccessInfoCollection.get(field.getReference()).setReadFromRecordInvokeDynamic();
           }
 
           KeepReason reason = KeepReason.fieldReferencedIn(currentMethod);
@@ -1991,10 +1957,30 @@
     traceStaticFieldRead(field, currentMethod, FieldAccessMetadata.FROM_SWITCH_METHOD_HANDLE);
   }
 
-  @SuppressWarnings("ReferenceEquality")
   void traceStaticFieldRead(
       DexField fieldReference, ProgramMethod currentMethod, FieldAccessMetadata metadata) {
-    if (!metadata.isDeferred() && !registerFieldRead(fieldReference, currentMethod)) {
+    traceStaticFieldAccess(fieldReference, currentMethod, FieldAccessKind.STATIC_READ, metadata);
+  }
+
+  void traceStaticFieldWrite(DexField field, ProgramMethod currentMethod) {
+    traceStaticFieldWrite(field, currentMethod, FieldAccessMetadata.DEFAULT);
+  }
+
+  void traceStaticFieldWriteFromMethodHandle(DexField field, ProgramMethod currentMethod) {
+    traceStaticFieldWrite(field, currentMethod, FieldAccessMetadata.FROM_METHOD_HANDLE);
+  }
+
+  void traceStaticFieldWrite(
+      DexField fieldReference, ProgramMethod currentMethod, FieldAccessMetadata metadata) {
+    traceStaticFieldAccess(fieldReference, currentMethod, FieldAccessKind.STATIC_WRITE, metadata);
+  }
+
+  private void traceStaticFieldAccess(
+      DexField fieldReference,
+      ProgramMethod currentMethod,
+      FieldAccessKind accessKind,
+      FieldAccessMetadata metadata) {
+    if (!metadata.isDeferred() && !registerFieldAccess(fieldReference, currentMethod, accessKind)) {
       return;
     }
 
@@ -2015,15 +2001,15 @@
     }
 
     if (deferredTracing.deferTracingOfFieldAccess(
-        fieldReference, resolutionResult, currentMethod, FieldAccessKind.STATIC_READ, metadata)) {
+        fieldReference, resolutionResult, currentMethod, accessKind, metadata)) {
       assert !metadata.isDeferred();
       return;
     }
 
     resolutionResult.visitFieldResolutionResults(
         singleResolutionResult -> {
-          analyses.traceStaticFieldRead(
-              fieldReference, singleResolutionResult, currentMethod, worklist);
+          analyses.traceFieldAccess(
+              fieldReference, singleResolutionResult, currentMethod, worklist, accessKind);
 
           DexClassAndField classField = singleResolutionResult.getResolutionPair();
           assert classField != null;
@@ -2048,8 +2034,11 @@
                   + field.getReference().toSourceString();
 
           if (metadata.isFromMethodHandle()) {
-            fieldAccessInfoCollection.get(field.getReference()).setReadFromMethodHandle();
+            fieldAccessInfoCollection
+                .get(field.getReference())
+                .setAccessedFromMethodHandle(accessKind);
           } else if (metadata.isFromSwitchMethodHandle()) {
+            assert accessKind.isRead();
             // TODO(b/340187630): This disables any optimization on such enum fields. We could
             //  support rewriting fields in switch method handles instead.
             keepInfo.joinClass(
@@ -2074,83 +2063,6 @@
         });
   }
 
-  void traceStaticFieldWrite(DexField field, ProgramMethod currentMethod) {
-    traceStaticFieldWrite(field, currentMethod, FieldAccessMetadata.DEFAULT);
-  }
-
-  void traceStaticFieldWriteFromMethodHandle(DexField field, ProgramMethod currentMethod) {
-    traceStaticFieldWrite(field, currentMethod, FieldAccessMetadata.FROM_METHOD_HANDLE);
-  }
-
-  @SuppressWarnings("ReferenceEquality")
-  void traceStaticFieldWrite(
-      DexField fieldReference, ProgramMethod currentMethod, FieldAccessMetadata metadata) {
-    if (!metadata.isDeferred() && !registerFieldWrite(fieldReference, currentMethod)) {
-      return;
-    }
-
-    FieldResolutionResult resolutionResult = resolveField(fieldReference, currentMethod);
-
-    if (appView.options().protoShrinking().enableGeneratedExtensionRegistryShrinking) {
-      // If it is a dead proto extension field, don't trace onwards.
-      boolean skipTracing =
-          appView.withGeneratedExtensionRegistryShrinker(
-              shrinker ->
-                  shrinker.isDeadProtoExtensionField(
-                      resolutionResult, fieldAccessInfoCollection, keepInfo),
-              false);
-      if (skipTracing) {
-        addDeadProtoTypeCandidate(resolutionResult.getSingleProgramField().getHolder());
-        return;
-      }
-    }
-
-    if (deferredTracing.deferTracingOfFieldAccess(
-        fieldReference, resolutionResult, currentMethod, FieldAccessKind.STATIC_WRITE, metadata)) {
-      assert !metadata.isDeferred();
-      return;
-    }
-
-    resolutionResult.visitFieldResolutionResults(
-        singleResolutionResult -> {
-          analyses.traceStaticFieldWrite(
-              fieldReference, singleResolutionResult, currentMethod, worklist);
-
-          DexClassAndField classField = singleResolutionResult.getResolutionPair();
-          assert classField != null;
-
-          DexClass initialResolutionHolder = singleResolutionResult.getInitialResolutionHolder();
-          if (initialResolutionHolder != classField.getHolder()) {
-            // Mark the initial resolution holder as live. Note that this should only be done if
-            // the field is not a dead proto field (in which case we bail-out above).
-            markTypeAsLive(initialResolutionHolder, currentMethod);
-          }
-
-          ProgramField field = classField.asProgramField();
-          if (field == null) {
-            // No need to trace into the non-program code.
-            return;
-          }
-
-          assert !mode.isFinalTreeShaking() || !field.getDefinition().getOptimizationInfo().isDead()
-              : "Unexpected reference in `"
-                  + currentMethod.toSourceString()
-                  + "` to field marked dead: "
-                  + field.getReference().toSourceString();
-
-          if (metadata.isFromMethodHandle()) {
-            fieldAccessInfoCollection.get(field.getReference()).setWrittenFromMethodHandle();
-          }
-
-          markFieldAsLive(field, currentMethod);
-        },
-        failedResolution -> {
-          // Must trace the types from the field reference even if it does not exist.
-          traceFieldReference(fieldReference, currentMethod);
-          noClassMerging.add(fieldReference.getHolderType());
-        });
-  }
-
   //
   // Actual actions performed.
   //