Desugar multianewarray instructions in new CF front-end.
Bug: 109789539
Change-Id: I86efad06e8f765ce4302a786cd2e9558f70e9ee2
diff --git a/src/main/java/com/android/tools/r8/cf/CfPrinter.java b/src/main/java/com/android/tools/r8/cf/CfPrinter.java
index 3866f5f..0248418 100644
--- a/src/main/java/com/android/tools/r8/cf/CfPrinter.java
+++ b/src/main/java/com/android/tools/r8/cf/CfPrinter.java
@@ -317,7 +317,7 @@
public void print(CfConstClass constClass) {
indent();
builder.append("ldc ");
- appendClass(constClass.getType());
+ appendType(constClass.getType());
}
public void print(CfReturnVoid ret) {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java b/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java
index e2f7336..94f7223 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.cf.code;
import com.android.tools.r8.cf.CfPrinter;
-import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.conversion.CfSourceCode;
@@ -55,10 +54,7 @@
@Override
public void buildIR(IRBuilder builder, CfState state, CfSourceCode code) {
InternalOptions options = builder.appView.options();
- if (options.isGeneratingDex()) {
- // TODO(b/109789539): Implement this case (see JarSourceCode.buildPrelude()/buildPostlude()).
- throw new Unimplemented("CfMultiANewArray to DEX backend");
- }
+ assert !options.isGeneratingDex();
int[] dimensions = state.popReverse(this.dimensions);
builder.addMultiNewArray(type, state.push(type).register, dimensions);
}
diff --git a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
index 2a10b2d..0df8ee4 100644
--- a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
@@ -783,7 +783,47 @@
@Override
public void visitMultiANewArrayInsn(String desc, int dims) {
- instructions.add(new CfMultiANewArray(factory.createType(desc), dims));
+ if (!application.options.isGeneratingDex()) {
+ instructions.add(new CfMultiANewArray(factory.createType(desc), dims));
+ return;
+ }
+ // When generating DEX code a multianewarray is desugared to a reflective creation.
+ // The stack transformation is:
+ // ..., count1, ..., countN (where N = dims)
+ // ->
+ // ..., arrayref(of type : desc)
+ //
+ // This is unfolded to a call to java.lang.reflect.Array.newInstance to the same effect:
+ // ..., count1, ..., countN
+ visitLdcInsn(dims);
+ // ..., count1, ..., countN, dims
+ visitIntInsn(Opcodes.NEWARRAY, Opcodes.T_INT);
+ // ..., count1, ..., countN, dim-array
+ for (int i = dims - 1; i >= 0; i--) {
+ visitInsn(Opcodes.DUP_X1);
+ // ..., count1, ..., dim-array, countN, dim-array
+ visitInsn(Opcodes.SWAP);
+ // ..., count1, ..., dim-array, dim-array, countN
+ visitLdcInsn(i);
+ // ..., count1, ..., dim-array, dim-array, countN, index
+ visitInsn(Opcodes.SWAP);
+ // ..., count1, ..., dim-array, dim-array, index, countN
+ visitInsn(Opcodes.IASTORE);
+ // ..., count1, ..., dim-array
+ }
+ visitLdcInsn(Type.getType(desc.substring(dims)));
+ // ..., dim-array, dim-member-type
+ visitInsn(Opcodes.SWAP);
+ // ..., dim-member-type, dim-array
+ visitMethodInsn(
+ Opcodes.INVOKESTATIC,
+ "java/lang/reflect/Array",
+ "newInstance",
+ "(Ljava/lang/Class;[I)Ljava/lang/Object;",
+ false);
+ // ..., ref
+ visitTypeInsn(Opcodes.CHECKCAST, desc);
+ // ..., arrayref(of type : desc)
}
@Override
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
index b3f104b..1f02b9e 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
@@ -48,7 +48,6 @@
"loadconst.LoadConst",
"loop.UdpServer",
"nestedtrycatches.NestedTryCatches",
- "newarray.NewArray",
"regalloc.RegAlloc",
"returns.Returns",
"staticfield.StaticField",
@@ -158,11 +157,6 @@
@Override
protected Set<String> getFailingCompileCfToDex() {
return new ImmutableSet.Builder<String>()
- // TODO(b/109789539): Implement CfMultiANewArray.buildIR() for DEX backend.
- .add("newarray.NewArray")
- .add("trycatch.TryCatch")
- .add("regress_70737019.Test")
- .add("regress_72361252.Test")
.build();
}
diff --git a/src/test/examples/newarray/NewArray.java b/src/test/java/com/android/tools/r8/examples/newarray/NewArray.java
similarity index 97%
rename from src/test/examples/newarray/NewArray.java
rename to src/test/java/com/android/tools/r8/examples/newarray/NewArray.java
index b652324..b1f2693 100644
--- a/src/test/examples/newarray/NewArray.java
+++ b/src/test/java/com/android/tools/r8/examples/newarray/NewArray.java
@@ -1,7 +1,7 @@
-// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// Copyright (c) 2019, 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 newarray;
+package com.android.tools.r8.examples.newarray;
class NewArray {
diff --git a/src/test/java/com/android/tools/r8/examples/newarray/NewArrayTestRunner.java b/src/test/java/com/android/tools/r8/examples/newarray/NewArrayTestRunner.java
new file mode 100644
index 0000000..3810a92
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/examples/newarray/NewArrayTestRunner.java
@@ -0,0 +1,78 @@
+// Copyright (c) 2019, 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.examples.newarray;
+
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime;
+import com.android.tools.r8.utils.BooleanUtils;
+import java.util.List;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class NewArrayTestRunner extends TestBase {
+
+ static final Class<?> CLASS = NewArray.class;
+
+ private final TestParameters parameters;
+ private final CompilationMode mode;
+ private final boolean newCfFrontend;
+
+ private static String referenceOut;
+
+ @Parameterized.Parameters(name = "{0}, {1}, new-cf:{2}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withAllRuntimes().withAllApiLevels().build(),
+ CompilationMode.values(),
+ BooleanUtils.values());
+ }
+
+ public NewArrayTestRunner(
+ TestParameters parameters, CompilationMode mode, boolean newCfFrontend) {
+ this.parameters = parameters;
+ this.mode = mode;
+ this.newCfFrontend = newCfFrontend;
+ }
+
+ @BeforeClass
+ public static void runReference() throws Exception {
+ referenceOut =
+ testForJvm(getStaticTemp())
+ .addProgramClassesAndInnerClasses(CLASS)
+ .run(TestRuntime.getDefaultJavaRuntime(), CLASS)
+ .assertSuccess()
+ .getStdOut();
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ testForD8()
+ .addProgramClassesAndInnerClasses(CLASS)
+ .setMinApi(parameters.getApiLevel())
+ .addOptionsModification(options -> options.enableCfFrontend = newCfFrontend)
+ .setMode(mode)
+ .run(parameters.getRuntime(), CLASS)
+ .assertSuccessWithOutput(referenceOut);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClassesAndInnerClasses(CLASS)
+ .addKeepMainRule(CLASS)
+ .setMinApi(parameters.getApiLevel())
+ .addOptionsModification(options -> options.enableCfFrontend = newCfFrontend)
+ .setMode(mode)
+ .run(parameters.getRuntime(), CLASS)
+ .assertSuccessWithOutput(referenceOut);
+ }
+}