Add a test for not computing object state in parameter propagation

Bug: b/296030319
Change-Id: Iac232c4a2bc4e1636605658497b2231d96bc75db
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java
index e6ac132..b9f074c 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java
@@ -35,7 +35,8 @@
     this.appView = appView;
     this.fieldBitAccessAnalysis =
         options.enableFieldBitAccessAnalysis ? new FieldBitAccessAnalysis() : null;
-    this.fieldAssignmentTracker = new FieldAssignmentTracker(appView);
+    this.fieldAssignmentTracker =
+        options.enableFieldAssignmentTracker ? new FieldAssignmentTracker(appView) : null;
     this.fieldReadForInvokeReceiverAnalysis = new FieldReadForInvokeReceiverAnalysis(appView);
     this.fieldReadForWriteAnalysis = new FieldReadForWriteAnalysis(appView);
   }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
index c1a2b62..1a64882 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
@@ -16,6 +16,7 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DexValue;
 import com.android.tools.r8.graph.DexValue.DexValueNull;
+import com.android.tools.r8.ir.analysis.fieldvalueanalysis.StaticFieldValues.EmptyStaticValues;
 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.value.AbstractValue;
@@ -64,6 +65,9 @@
     assert appView.appInfo().hasLiveness();
     assert appView.enableWholeProgramOptimizations();
     assert code.context().getDefinition().isClassInitializer();
+    if (!appView.options().enableFieldValueAnalysis) {
+      return EmptyStaticValues.getInstance();
+    }
     timing.begin("Analyze class initializer");
     StaticFieldValues result =
         new StaticFieldValueAnalysis(appView.withLiveness(), code, feedback)
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java
index 7de5536..035690f 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java
@@ -73,7 +73,7 @@
     numberUnboxer.prepareForPrimaryOptimizationPass(timing, executorService);
     outliner.prepareForPrimaryOptimizationPass(graphLensForPrimaryOptimizationPass);
 
-    if (fieldAccessAnalysis != null) {
+    if (fieldAccessAnalysis != null && fieldAccessAnalysis.fieldAssignmentTracker() != null) {
       fieldAccessAnalysis.fieldAssignmentTracker().initialize();
     }
 
@@ -252,11 +252,12 @@
     onWaveDoneActions = Collections.synchronizedList(new ArrayList<>());
   }
 
-  public void waveDone(ProgramMethodSet wave, ExecutorService executorService)
-      throws ExecutionException {
+  public void waveDone(ProgramMethodSet wave, ExecutorService executorService) {
     delayedOptimizationFeedback.refineAppInfoWithLiveness(appView.appInfo().withLiveness());
     delayedOptimizationFeedback.updateVisibleOptimizationInfo();
-    fieldAccessAnalysis.fieldAssignmentTracker().waveDone(wave, delayedOptimizationFeedback);
+    if (fieldAccessAnalysis.fieldAssignmentTracker() != null) {
+      fieldAccessAnalysis.fieldAssignmentTracker().waveDone(wave, delayedOptimizationFeedback);
+    }
     appView.withArgumentPropagator(ArgumentPropagator::publishDelayedReprocessingCriteria);
     if (appView.options().protoShrinking().enableRemoveProtoEnumSwitchMap()) {
       appView.protoShrinker().protoEnumSwitchMapRemover.updateVisibleStaticFieldValues();
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index b8b3ddf..7b83b69 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -401,6 +401,8 @@
   // Optimization-related flags. These should conform to -dontoptimize and disableAllOptimizations.
   public boolean enableFieldBitAccessAnalysis =
       System.getProperty("com.android.tools.r8.fieldBitAccessAnalysis") != null;
+  public boolean enableFieldAssignmentTracker = true;
+  public boolean enableFieldValueAnalysis = true;
   public boolean enableUnusedInterfaceRemoval = true;
   public boolean enableDevirtualization = true;
   public boolean enableEnumUnboxing = true;
diff --git a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/FieldStateArgumentPropagationTest.java b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/FieldStateArgumentPropagationTest.java
new file mode 100644
index 0000000..728cd78
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/FieldStateArgumentPropagationTest.java
@@ -0,0 +1,101 @@
+// 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;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class FieldStateArgumentPropagationTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(Main.class)
+        .addOptionsModification(
+            options -> {
+              options.enableFieldAssignmentTracker = false;
+              options.enableFieldValueAnalysis = false;
+            })
+        .enableInliningAnnotations()
+        .setMinApi(parameters)
+        .compile()
+        .inspect(
+            inspector -> {
+              ClassSubject mainClassSubject = inspector.clazz(Main.class);
+              assertThat(mainClassSubject, isPresent());
+
+              MethodSubject printMethodSubject =
+                  mainClassSubject.uniqueMethodWithOriginalName("print");
+              assertThat(printMethodSubject, isPresent());
+              // TODO(b/296030319): Should be 0.
+              assertEquals(1, printMethodSubject.getProgramMethod().getArity());
+
+              MethodSubject printlnMethodSubject =
+                  mainClassSubject.uniqueMethodWithOriginalName("println");
+              assertThat(printlnMethodSubject, isPresent());
+              // TODO(b/296030319): Should be 0.
+              assertEquals(1, printlnMethodSubject.getProgramMethod().getArity());
+            })
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("Hello, world!");
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      print(Greetings.HELLO.f);
+      println(Greetings.WORLD.f);
+    }
+
+    @NeverInline
+    static void print(String message) {
+      System.out.print(message);
+    }
+
+    @NeverInline
+    static void println(String message) {
+      System.out.println(message);
+    }
+  }
+
+  static class Greetings {
+
+    static final Greetings HELLO;
+    static final Greetings WORLD;
+
+    static {
+      HELLO = new Greetings("Hello");
+      WORLD = new Greetings(", world!");
+    }
+
+    final String f;
+
+    Greetings(String f) {
+      this.f = f;
+    }
+  }
+}