Always pop unused multianewarray return value

Bug: b/335663479
Change-Id: I86aa3e5b3bf49a228c842bcb2e7bb5d39ef5b85b
diff --git a/src/main/java/com/android/tools/r8/cf/LoadStoreHelper.java b/src/main/java/com/android/tools/r8/cf/LoadStoreHelper.java
index c7c4cc7..d7dbde7 100644
--- a/src/main/java/com/android/tools/r8/cf/LoadStoreHelper.java
+++ b/src/main/java/com/android/tools/r8/cf/LoadStoreHelper.java
@@ -167,10 +167,18 @@
     it.next();
   }
 
-  public void storeOutValue(Instruction instruction, InstructionListIterator it) {
-    if (!instruction.hasOutValue()) {
-      return;
+  public void storeOrPopOutValue(
+      DexType type, Instruction instruction, InstructionListIterator it) {
+    if (instruction.hasOutValue()) {
+      assert instruction.outValue().isUsed();
+      storeOutValue(instruction, it);
+    } else {
+      popOutType(type, instruction, it);
     }
+  }
+
+  public void storeOutValue(Instruction instruction, InstructionListIterator it) {
+    assert instruction.hasOutValue();
     assert !(instruction.outValue() instanceof StackValue);
     if (instruction.isConstInstruction()) {
       ConstInstruction constInstruction = instruction.asConstInstruction();
diff --git a/src/main/java/com/android/tools/r8/ir/code/Invoke.java b/src/main/java/com/android/tools/r8/ir/code/Invoke.java
index a2bb674..890516f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Invoke.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Invoke.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.code;
 
+import com.android.tools.r8.cf.LoadStoreHelper;
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.dex.code.DexInstruction;
 import com.android.tools.r8.dex.code.DexMoveResult;
@@ -294,4 +295,10 @@
   public boolean outTypeKnownToBeBoolean(Set<Phi> seen) {
     return getReturnType().isBooleanType();
   }
+
+  /**
+   * Subclasses must implement load and store handling and make sure to deal with a null out-value
+   */
+  @Override
+  public abstract void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper);
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java b/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
index 710fb47..0b24f8b 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
@@ -194,12 +194,7 @@
     if (getCallSite().methodProto.returnType.isVoidType()) {
       return;
     }
-    if (outValue == null) {
-      helper.popOutType(getCallSite().methodProto.returnType, this, it);
-    } else {
-      assert outValue.isUsed();
-      helper.storeOutValue(this, it);
-    }
+    helper.storeOrPopOutValue(getCallSite().methodProto.returnType, this, it);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
index 4a9b217..c1a5688 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
@@ -270,12 +270,7 @@
     if (getReturnType().isVoidType()) {
       return;
     }
-    if (outValue == null) {
-      helper.popOutType(getReturnType(), this, it);
-    } else {
-      assert outValue.isUsed();
-      helper.storeOutValue(this, it);
-    }
+    helper.storeOrPopOutValue(getReturnType(), this, it);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java
index 9b5eb96..b4256d2 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java
@@ -103,7 +103,7 @@
   @Override
   public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
     helper.loadInValues(this, it);
-    helper.storeOutValue(this, it);
+    helper.storeOrPopOutValue(type, this, it);
   }
 
   @Override
diff --git a/src/test/java/com/android/tools/r8/cf/UnusedMultiNewArrayTest.java b/src/test/java/com/android/tools/r8/cf/UnusedMultiNewArrayTest.java
index 2ec3d66..986256a 100644
--- a/src/test/java/com/android/tools/r8/cf/UnusedMultiNewArrayTest.java
+++ b/src/test/java/com/android/tools/r8/cf/UnusedMultiNewArrayTest.java
@@ -34,7 +34,10 @@
         .addKeepMainRule(TestClass.class)
         .addDontWarn(A.class)
         .setMinApi(parameters)
-        .compile();
+        .compile()
+        .addRunClasspathClasses(A.class)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput("");
   }
 
   static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/cf/UnusedMultiNewArrayWithOnStackValueTest.java b/src/test/java/com/android/tools/r8/cf/UnusedMultiNewArrayWithOnStackValueTest.java
new file mode 100644
index 0000000..1d8c60e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/UnusedMultiNewArrayWithOnStackValueTest.java
@@ -0,0 +1,55 @@
+// 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.cf;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/** Regression test for b/335663479. */
+@RunWith(Parameterized.class)
+public class UnusedMultiNewArrayWithOnStackValueTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public UnusedMultiNewArrayWithOnStackValueTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(TestClass.class)
+        .addKeepMainRule(TestClass.class)
+        .addDontWarn(A.class)
+        .setMinApi(parameters)
+        .compile()
+        .addRunClasspathClasses(A.class)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutputLines("42");
+  }
+
+  static class TestClass {
+
+    public static void main(String[] args) {
+      // The array instructions cannot be removed because A is not provided as a program class.
+      int dim = 42;
+      A[][] local = new A[dim][dim];
+      local = new A[dim][dim];
+      System.out.println(local.length);
+    }
+  }
+
+  static class A {}
+}