Refactor in-flow computation in code scanner

Bug: b/296030319
Change-Id: I7374afbecbc5ef5cc061c0de49361a8a9f44a8b3
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java
index 7cfd03a..5ec5473 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClassAndMethod;
+import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexMethodHandle;
 import com.android.tools.r8.graph.DexType;
@@ -16,7 +17,6 @@
 import com.android.tools.r8.ir.analysis.type.DynamicType;
 import com.android.tools.r8.ir.analysis.type.DynamicTypeWithUpperBound;
 import com.android.tools.r8.ir.analysis.type.Nullability;
-import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
 import com.android.tools.r8.ir.analysis.value.objectstate.ObjectState;
 import com.android.tools.r8.ir.code.AbstractValueSupplier;
@@ -30,6 +30,8 @@
 import com.android.tools.r8.ir.code.InvokeMethod;
 import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
 import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.AbstractFunction;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.BaseInFlow;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteArrayTypeValueState;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteClassTypeValueState;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteMonomorphicMethodState;
@@ -41,7 +43,6 @@
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteReceiverValueState;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteValueState;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.FieldStateCollection;
-import com.android.tools.r8.optimize.argumentpropagation.codescanner.FieldValue;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.FieldValueFactory;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.InFlow;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodParameter;
@@ -142,12 +143,19 @@
     return virtualRootMethods.get(method.getReference());
   }
 
+  // TODO(b/296030319): Allow lookups in the FieldStateCollection using DexField keys to avoid the
+  //  need for definitionFor here.
+  private boolean isFieldValueAlreadyUnknown(DexField field) {
+    return isFieldValueAlreadyUnknown(appView.definitionFor(field).asProgramField());
+  }
+
   private boolean isFieldValueAlreadyUnknown(ProgramField field) {
     return fieldStates.get(field).isUnknown();
   }
 
   protected boolean isMethodParameterAlreadyUnknown(
       MethodParameter methodParameter, ProgramMethod method) {
+    assert methodParameter.getMethod().isIdenticalTo(method.getReference());
     MethodState methodState =
         methodStates.get(
             method.getDefinition().belongsToDirectPool() || isMonomorphicVirtualMethod(method)
@@ -248,33 +256,9 @@
       ProgramField field,
       AbstractValueSupplier abstractValueSupplier,
       ProgramMethod context) {
-    Value valueRoot = fieldPut.value().getAliasedValue(aliasedValueConfiguration);
-    InFlow inFlow = null;
-    if (valueRoot.isArgument()) {
-      // If the value is an argument of the enclosing method, then clearly we have no information
-      // about its abstract value. Instead of treating this as having an unknown runtime value, we
-      // instead record a flow constraint that specifies that all values that flow into the
-      // parameter of this enclosing method also flows into the current field.
-      MethodParameter inParameter =
-          methodParameterFactory.create(context, valueRoot.getDefinition().asArgument().getIndex());
-      if (isMethodParameterAlreadyUnknown(inParameter, context)) {
-        return ValueState.unknown();
-      }
-      inFlow = inParameter;
-    } else if (valueRoot.isDefinedByInstructionSatisfying(Instruction::isFieldGet)) {
-      FieldGet fieldGet = valueRoot.getDefinition().asFieldGet();
-      ProgramField otherField = fieldGet.resolveField(appView, context).getProgramField();
-      if (otherField != null) {
-        FieldValue fieldValue = fieldValueFactory.create(otherField);
-        if (isFieldValueAlreadyUnknown(otherField)) {
-          return ValueState.unknown();
-        }
-        inFlow = fieldValue;
-      }
-    }
-
-    if (inFlow != null) {
-      return ConcreteValueState.create(field.getType(), inFlow);
+    NonEmptyValueState inFlowState = computeInFlowState(field.getType(), fieldPut.value(), context);
+    if (inFlowState != null) {
+      return inFlowState;
     }
 
     if (field.getType().isArrayType()) {
@@ -298,6 +282,53 @@
     }
   }
 
+  // If the value is an argument of the enclosing method or defined by a field-get, then clearly we
+  // have no information about its abstract value (yet). Instead of treating this as having an
+  // unknown runtime value, we instead record a flow constraint.
+  private InFlow computeInFlow(Value value, ProgramMethod context) {
+    Value valueRoot = value.getAliasedValue(aliasedValueConfiguration);
+    if (valueRoot.isArgument()) {
+      MethodParameter inParameter =
+          methodParameterFactory.create(context, valueRoot.getDefinition().asArgument().getIndex());
+      return widenBaseInFlow(inParameter, context);
+    } else if (valueRoot.isDefinedByInstructionSatisfying(Instruction::isFieldGet)) {
+      FieldGet fieldGet = valueRoot.getDefinition().asFieldGet();
+      ProgramField field = fieldGet.resolveField(appView, context).getProgramField();
+      if (field == null) {
+        return null;
+      }
+      return widenBaseInFlow(fieldValueFactory.create(field), context);
+    }
+    return null;
+  }
+
+  private InFlow widenBaseInFlow(BaseInFlow inFlow, ProgramMethod context) {
+    if (inFlow.isFieldValue()) {
+      if (isFieldValueAlreadyUnknown(inFlow.asFieldValue().getField())) {
+        return AbstractFunction.unknown();
+      }
+    } else {
+      assert inFlow.isMethodParameter();
+      if (isMethodParameterAlreadyUnknown(inFlow.asMethodParameter(), context)) {
+        return AbstractFunction.unknown();
+      }
+    }
+    return inFlow;
+  }
+
+  private NonEmptyValueState computeInFlowState(
+      DexType staticType, Value value, ProgramMethod context) {
+    InFlow inFlow = computeInFlow(value, context);
+    if (inFlow == null) {
+      return null;
+    }
+    if (inFlow.isUnknownAbstractFunction()) {
+      return ValueState.unknown();
+    }
+    assert inFlow.isBaseInFlow();
+    return ConcreteValueState.create(staticType, inFlow);
+  }
+
   // Strengthens the abstract value of static final fields to a (self-)SingleFieldValue when the
   // abstract value is unknown. The soundness of this is based on the fact that static final fields
   // will never have their value changed after the <clinit> finishes, so value in a static final
@@ -677,41 +708,17 @@
       return ValueState.unknown();
     }
 
-    Value argumentRoot = argument.getAliasedValue(aliasedValueConfiguration);
     DexType parameterType =
         invoke.getInvokedMethod().getArgumentType(argumentIndex, invoke.isInvokeStatic());
-    TypeElement parameterTypeElement = parameterType.toTypeElement(appView);
 
     // If the value is an argument of the enclosing method, then clearly we have no information
     // about its abstract value. Instead of treating this as having an unknown runtime value, we
     // instead record a flow constraint that specifies that all values that flow into the parameter
     // of this enclosing method also flows into the corresponding parameter of the methods
     // potentially called from this invoke instruction.
-    InFlow inFlow = null;
-    if (argumentRoot.isArgument()) {
-      MethodParameter forwardedParameter =
-          methodParameterFactory.create(
-              context, argumentRoot.getDefinition().asArgument().getIndex());
-      if (isMethodParameterAlreadyUnknown(forwardedParameter, context)) {
-        return ValueState.unknown();
-      }
-      inFlow = forwardedParameter;
-    } else if (argumentRoot.isDefinedByInstructionSatisfying(Instruction::isFieldGet)) {
-      // If the value is defined by a program field read, then record a flow constraint that
-      // specifies that all values that flow into the field also flows into the current parameter of
-      // this invoke instruction.
-      FieldGet fieldGet = argumentRoot.getDefinition().asFieldGet();
-      ProgramField field = fieldGet.resolveField(appView, context).getProgramField();
-      if (field != null) {
-        if (isFieldValueAlreadyUnknown(field)) {
-          return ValueState.unknown();
-        }
-        inFlow = fieldValueFactory.create(field);
-      }
-    }
-
-    if (inFlow != null) {
-      return ConcreteValueState.create(parameterType, inFlow);
+    NonEmptyValueState inFlowState = computeInFlowState(parameterType, argument, context);
+    if (inFlowState != null) {
+      return inFlowState;
     }
 
     // Only track the nullability for array types.
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/BaseInFlow.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/BaseInFlow.java
new file mode 100644
index 0000000..20ec5c7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/BaseInFlow.java
@@ -0,0 +1,17 @@
+// Copyright (c) 2024, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.optimize.argumentpropagation.codescanner;
+
+public interface BaseInFlow extends InFlow {
+
+  @Override
+  default boolean isBaseInFlow() {
+    return true;
+  }
+
+  @Override
+  default BaseInFlow asBaseInFlow() {
+    return this;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/FieldValue.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/FieldValue.java
index 24ad150..b049381 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/FieldValue.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/FieldValue.java
@@ -7,7 +7,7 @@
 
 // TODO(b/296030319): Change DexField to implement InFlow and use DexField in all places instead of
 //  FieldValue to avoid wrappers? This would also remove the need for the FieldValueFactory.
-public class FieldValue implements InFlow {
+public class FieldValue implements BaseInFlow {
 
   private final DexField field;
 
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/InFlow.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/InFlow.java
index d858bef..3cb0550 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/InFlow.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/InFlow.java
@@ -15,6 +15,14 @@
     return null;
   }
 
+  default boolean isBaseInFlow() {
+    return false;
+  }
+
+  default BaseInFlow asBaseInFlow() {
+    return null;
+  }
+
   default boolean isFieldValue() {
     return false;
   }
@@ -31,6 +39,10 @@
     return null;
   }
 
+  default boolean isUnknownAbstractFunction() {
+    return false;
+  }
+
   default boolean isUpdateChangedFlagsAbstractFunction() {
     return false;
   }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodParameter.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodParameter.java
index 9bfeab7..2fe1a2a 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodParameter.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodParameter.java
@@ -7,7 +7,7 @@
 import com.android.tools.r8.graph.DexMethod;
 import java.util.Objects;
 
-public class MethodParameter implements InFlow {
+public class MethodParameter implements BaseInFlow {
 
   private final DexMethod method;
   private final int index;
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/UnknownAbstractFunction.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/UnknownAbstractFunction.java
index 8ac1736..ccddf4d 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/UnknownAbstractFunction.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/UnknownAbstractFunction.java
@@ -24,4 +24,9 @@
   public InFlow getBaseInFlow() {
     throw new Unreachable();
   }
+
+  @Override
+  public boolean isUnknownAbstractFunction() {
+    return true;
+  }
 }