[KeepAnno] Add tracing of inlining positions

Bug: b/325014359
Bug: b/323816623
Change-Id: Ica289493398558365d7322a092adc1dcddc3a067
diff --git a/src/main/java/com/android/tools/r8/graph/UseRegistry.java b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
index d7d1b0b..6238dd5 100644
--- a/src/main/java/com/android/tools/r8/graph/UseRegistry.java
+++ b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
@@ -9,6 +9,7 @@
 import com.android.tools.r8.graph.bytecodemetadata.BytecodeInstructionMetadata;
 import com.android.tools.r8.graph.lens.GraphLens;
 import com.android.tools.r8.ir.code.InvokeType;
+import com.android.tools.r8.ir.code.Position;
 import com.android.tools.r8.utils.TraversalContinuation;
 import java.util.ListIterator;
 
@@ -60,6 +61,10 @@
     return continuation;
   }
 
+  public void registerInliningPosition(Position position) {
+    assert position.hasCallerPosition();
+  }
+
   public void registerRecordFieldValues(DexField[] fields) {
     registerTypeReference(appView.dexItemFactory().objectArrayType);
   }
diff --git a/src/main/java/com/android/tools/r8/lightir/LirCode.java b/src/main/java/com/android/tools/r8/lightir/LirCode.java
index 6f93b21..5090346 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirCode.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirCode.java
@@ -585,6 +585,11 @@
   @Override
   public void registerCodeReferences(ProgramMethod method, UseRegistry registry) {
     assert registry.getTraversalContinuation().shouldContinue();
+    for (PositionEntry positionEntry : positionTable) {
+      if (positionEntry instanceof StructuredPositionEntry) {
+        registry.registerInliningPosition(((StructuredPositionEntry) positionEntry).position);
+      }
+    }
     LirUseRegistryCallback<EV> registryCallbacks = new LirUseRegistryCallback<>(this, registry);
     for (LirInstructionView view : this) {
       if (metadataMap != null) {
diff --git a/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java b/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
index 736f285..dd975f7 100644
--- a/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
@@ -19,6 +19,7 @@
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.code.Position;
 import java.util.ListIterator;
 
 public class DefaultEnqueuerUseRegistry extends ComputeApiLevelUseRegistry {
@@ -45,6 +46,12 @@
   }
 
   @Override
+  public void registerInliningPosition(Position position) {
+    super.registerInliningPosition(position);
+    enqueuer.traceMethodPosition(position, getContext());
+  }
+
+  @Override
   public void registerInitClass(DexType clazz) {
     super.registerInitClass(clazz);
     enqueuer.traceInitClass(clazz, getContext());
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 0dfda11..1d2e7b8 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -131,7 +131,6 @@
 import com.android.tools.r8.naming.identifiernamestring.IdentifierNameStringLookupResult;
 import com.android.tools.r8.naming.identifiernamestring.IdentifierNameStringTypeLookupResult;
 import com.android.tools.r8.origin.Origin;
-import com.android.tools.r8.position.Position;
 import com.android.tools.r8.profile.rewriting.ProfileCollectionAdditions;
 import com.android.tools.r8.shaking.AnnotationMatchResult.MatchedAnnotation;
 import com.android.tools.r8.shaking.DelayedRootSetActionItem.InterfaceMethodSyntheticBridgeAction;
@@ -315,6 +314,10 @@
    */
   private final SetWithReportedReason<DexProgramClass> liveTypes = new SetWithReportedReason<>();
 
+  /** Set of effectively live items from the original program. */
+  // TODO(b/323816623): Add reason tracking.
+  private final Set<DexReference> effectivelyLiveOriginalReferences = SetUtils.newIdentityHashSet();
+
   /** Set of interfaces that have been transitioned to being instantiated indirectly. */
   private final Set<DexProgramClass> interfacesTransitionedToInstantiated =
       Sets.newIdentityHashSet();
@@ -1631,6 +1634,27 @@
         analysis -> analysis.traceInvokeVirtual(invokedMethod, resolutionResult, context));
   }
 
+  void traceMethodPosition(com.android.tools.r8.ir.code.Position position, ProgramMethod context) {
+    if (!options.testing.enableExtractedKeepAnnotations) {
+      // Currently inlining is only intended for the evaluation of keep annotation edges.
+      return;
+    }
+    while (position.hasCallerPosition()) {
+      // Any inner position should not be non-synthetic user methods.
+      assert !position.isD8R8Synthesized();
+      DexMethod method = position.getMethod();
+      // TODO(b/325014359): It might be reasonable to reduce this map size by tracking which methods
+      //  actually are used in preconditions.
+      if (effectivelyLiveOriginalReferences.add(method)) {
+        effectivelyLiveOriginalReferences.add(method.getHolderType());
+      }
+      position = position.getCallerPosition();
+    }
+    // The outer-most position should be equal to the context.
+    // No need to trace this as the method is already traced since it is invoked.
+    assert context.getReference().isIdenticalTo(position.getMethod());
+  }
+
   void traceNewInstance(DexType type, ProgramMethod context) {
     boolean skipTracing =
         registerDeferredActionForDeadProtoBuilder(
@@ -3402,6 +3426,35 @@
     return liveTypes.contains(clazz);
   }
 
+  public boolean isEffectivelyLive(DexProgramClass clazz) {
+    if (isTypeLive(clazz)) {
+      return true;
+    }
+    if (mode.isInitialTreeShaking()) {
+      return false;
+    }
+    // TODO(b/325014359): Replace this by value tracking in instructions (akin to resource values).
+    for (DexEncodedField field : clazz.fields()) {
+      if (field.getOptimizationInfo().valueHasBeenPropagated()) {
+        return true;
+      }
+    }
+    // TODO(b/325014359): Replace this by value or position tracking.
+    //  We need to be careful not to throw away such values/positions.
+    for (DexEncodedMethod method : clazz.methods()) {
+      if (method.getOptimizationInfo().returnValueHasBeenPropagated()) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  public boolean isOriginalReferenceEffectivelyLive(DexReference reference) {
+    // The effectively-live original set contains types, fields and methods witnessed by
+    // instructions, such as method inlining positions.
+    return effectivelyLiveOriginalReferences.contains(reference);
+  }
+
   public boolean isNonProgramTypeLive(DexClass clazz) {
     assert !clazz.isProgramClass();
     return liveNonProgramTypes.contains(clazz);
@@ -4671,7 +4724,7 @@
                     context,
                     new InterfaceDesugarMissingTypeDiagnostic(
                         context.getOrigin(),
-                        Position.UNKNOWN,
+                        com.android.tools.r8.position.Position.UNKNOWN,
                         missing.asClassReference(),
                         context.getType().asClassReference(),
                         null)));
diff --git a/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java b/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java
index 933cc33..32ec312 100644
--- a/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java
+++ b/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java
@@ -12,7 +12,6 @@
 import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexDefinition;
 import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexReference;
@@ -178,23 +177,7 @@
   }
 
   private boolean isEffectivelyLive(DexProgramClass clazz) {
-    // A type is effectively live if (1) it is truly live, (2) the value of one of its fields has
-    // been inlined by the member value propagation, or (3) the return value of one of its methods
-    // has been forwarded by the member value propagation.
-    if (enqueuer.isTypeLive(clazz)) {
-      return true;
-    }
-    for (DexEncodedField field : clazz.fields()) {
-      if (field.getOptimizationInfo().valueHasBeenPropagated()) {
-        return true;
-      }
-    }
-    for (DexEncodedMethod method : clazz.methods()) {
-      if (method.getOptimizationInfo().returnValueHasBeenPropagated()) {
-        return true;
-      }
-    }
-    return false;
+    return enqueuer.isEffectivelyLive(clazz);
   }
 
   /** Determines if {@param clazz} satisfies the given if-rule class specification. */