[LIR] Add support for int-switch.
This also moves legacy tests invoke, invokeempty and instanceofstring.
Bug: b/225838009
Bug: b/167145686
Change-Id: I61314aebf2fb89a785164928a227cac4d4efc4cf
diff --git a/src/main/java/com/android/tools/r8/ir/code/IntSwitch.java b/src/main/java/com/android/tools/r8/ir/code/IntSwitch.java
index 15712e1..de5ce5a 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IntSwitch.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IntSwitch.java
@@ -17,6 +17,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.lightir.LirBuilder;
import com.android.tools.r8.utils.IntObjConsumer;
import com.android.tools.r8.utils.InternalOutputMode;
import it.unimi.dsi.fastutil.ints.Int2ReferenceAVLTreeMap;
@@ -35,6 +36,11 @@
}
@Override
+ public void buildLir(LirBuilder<Value, ?> builder) {
+ builder.addIntSwitch(value(), getKeys(), getKeyToTargetMap(), fallthroughBlock());
+ }
+
+ @Override
public int opcode() {
return Opcodes.INT_SWITCH;
}
diff --git a/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java b/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java
index 0583876..bda771a 100644
--- a/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java
+++ b/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java
@@ -38,6 +38,7 @@
import com.android.tools.r8.ir.code.InstanceOf;
import com.android.tools.r8.ir.code.InstancePut;
import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.IntSwitch;
import com.android.tools.r8.ir.code.InvokeDirect;
import com.android.tools.r8.ir.code.InvokeInterface;
import com.android.tools.r8.ir.code.InvokeNewArray;
@@ -68,9 +69,12 @@
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.ir.code.Xor;
import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
+import com.android.tools.r8.lightir.LirBuilder.IntSwitchPayload;
import com.android.tools.r8.lightir.LirCode.PositionEntry;
import com.android.tools.r8.utils.ListUtils;
import com.google.common.collect.ImmutableList;
+import it.unimi.dsi.fastutil.ints.Int2IntMap;
+import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntArrayList;
@@ -477,6 +481,39 @@
}
@Override
+ public void onIntSwitch(EV value, IntSwitchPayload payload) {
+ // keys is the 'value' -> 'target index' mapping.
+ int[] keys = payload.keys;
+ // successorIndices is the 'target index' to 'IR successor index'.
+ int[] successorIndices = new int[keys.length];
+ List<BasicBlock> successorBlocks = new ArrayList<>();
+ {
+ // The mapping from instruction to successor is a temp mapping to track if any targets
+ // point to the same block.
+ Int2IntMap instructionToSuccessor = new Int2IntOpenHashMap();
+ for (int i = 0; i < successorIndices.length; i++) {
+ int instructionIndex = payload.targets[i];
+ if (instructionToSuccessor.containsKey(instructionIndex)) {
+ successorIndices[i] = instructionToSuccessor.get(instructionIndex);
+ } else {
+ int successorIndex = successorBlocks.size();
+ successorIndices[i] = successorIndex;
+ instructionToSuccessor.put(instructionIndex, successorIndex);
+ successorBlocks.add(getBasicBlock(instructionIndex));
+ }
+ }
+ }
+ int fallthrough = successorBlocks.size();
+ addInstruction(new IntSwitch(getValue(value), keys, successorIndices, fallthrough));
+ // The call to addInstruction will ensure the current block so don't amend to it before here.
+ // If the block has successors then the index mappings are not valid / need to be offset.
+ assert currentBlock.getSuccessors().isEmpty();
+ successorBlocks.forEach(currentBlock::link);
+ currentBlock.link(getBasicBlock(nextInstructionIndex));
+ closeCurrentBlock();
+ }
+
+ @Override
public void onInvokeDirect(DexMethod target, List<EV> arguments, boolean isInterface) {
Value dest = getInvokeInstructionOutputValue(target);
List<Value> ssaArgumentValues = getValues(arguments);
diff --git a/src/main/java/com/android/tools/r8/lightir/LirBuilder.java b/src/main/java/com/android/tools/r8/lightir/LirBuilder.java
index 54bdd09..7a88ae2 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirBuilder.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirBuilder.java
@@ -37,6 +37,7 @@
import com.google.common.collect.ImmutableList;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMap;
import it.unimi.dsi.fastutil.objects.Reference2IntMap;
import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
import java.util.ArrayList;
@@ -83,12 +84,25 @@
private int[] valueIndexBuffer = new int[MAX_VALUE_COUNT];
/**
+ * Internal "DexItem" for the instruction payloads such that they can be put in the pool.
+ *
+ * <p>The instruction encoding assumes the instruction operand payload size is u1, so this allows
+ * the data payload to be stored in the constant pool instead.
+ */
+ public abstract static class InstructionPayload extends DexItem {
+ @Override
+ protected final void collectMixedSectionItems(MixedSectionCollection collection) {
+ throw new Unreachable();
+ }
+ }
+
+ /**
* Internal "DexItem" for the fill-array payloads such that they can be put in the pool.
*
* <p>The instruction encoding assumes the instruction operand payload size is u1, so the data
* payload is stored in the constant pool instead.
*/
- public static class FillArrayPayload extends DexItem {
+ public static class FillArrayPayload extends InstructionPayload {
public final int element_width;
public final long size;
public final short[] data;
@@ -98,10 +112,16 @@
this.size = size;
this.data = data;
}
+ }
- @Override
- protected void collectMixedSectionItems(MixedSectionCollection collection) {
- throw new Unreachable();
+ public static class IntSwitchPayload extends InstructionPayload {
+ public final int[] keys;
+ public final int[] targets;
+
+ public IntSwitchPayload(int[] keys, int[] targets) {
+ assert keys.length == targets.length;
+ this.keys = keys;
+ this.targets = targets;
}
}
@@ -489,6 +509,22 @@
return this;
}
+ public LirBuilder<V, EV> addIntSwitch(
+ V value,
+ int[] keys,
+ Int2ReferenceSortedMap<BasicBlock> keyToTargetMap,
+ BasicBlock fallthroughBlock) {
+ int[] targets = new int[keys.length];
+ for (int i = 0; i < keys.length; i++) {
+ targets[i] = getBlockIndex(keyToTargetMap.get(keys[i]));
+ }
+ IntSwitchPayload payload = new IntSwitchPayload(keys, targets);
+ return addInstructionTemplate(
+ LirOpcodes.TABLESWITCH,
+ Collections.singletonList(payload),
+ Collections.singletonList(value));
+ }
+
public LirBuilder<V, EV> addIf(
IfType ifKind, ValueType valueType, V value, BasicBlock trueTarget) {
int opcode;
diff --git a/src/main/java/com/android/tools/r8/lightir/LirParsedInstructionCallback.java b/src/main/java/com/android/tools/r8/lightir/LirParsedInstructionCallback.java
index fa36ce6..d418d96 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirParsedInstructionCallback.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirParsedInstructionCallback.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.ir.code.MemberType;
import com.android.tools.r8.ir.code.NumericType;
import com.android.tools.r8.lightir.LirBuilder.FillArrayPayload;
+import com.android.tools.r8.lightir.LirBuilder.IntSwitchPayload;
import java.util.ArrayList;
import java.util.List;
@@ -288,6 +289,10 @@
onInstruction();
}
+ public void onIntSwitch(EV value, IntSwitchPayload payload) {
+ onInstruction();
+ }
+
public void onFallthrough() {
onInstruction();
}
@@ -831,6 +836,14 @@
onGoto(blockIndex);
return;
}
+ case LirOpcodes.TABLESWITCH:
+ {
+ IntSwitchPayload payload =
+ (IntSwitchPayload) getConstantItem(view.getNextConstantOperand());
+ EV value = getNextValueOperand(view);
+ onIntSwitch(value, payload);
+ return;
+ }
case LirOpcodes.INVOKEDIRECT:
case LirOpcodes.INVOKEDIRECT_ITF:
{
diff --git a/src/main/java/com/android/tools/r8/lightir/LirPrinter.java b/src/main/java/com/android/tools/r8/lightir/LirPrinter.java
index c3c06b1..e19661e 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirPrinter.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirPrinter.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.ir.code.IfType;
import com.android.tools.r8.ir.code.MemberType;
import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.lightir.LirBuilder.IntSwitchPayload;
import com.android.tools.r8.utils.StringUtils;
import java.util.Arrays;
import java.util.List;
@@ -202,6 +203,12 @@
}
@Override
+ public void onIntSwitch(EV value, IntSwitchPayload payload) {
+ appendValueArguments(value);
+ // TODO(b/225838009): Consider printing the switch payload info.
+ }
+
+ @Override
public void onFallthrough() {
// Nothing to append.
}
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
index 418dad3..cac9b82 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
@@ -25,46 +25,43 @@
@Parameters(name = "{0}_{1}_{2}_{3}_{5}_{6}")
public static Collection<String[]> data() {
String[] tests = {
- "arithmetic.Arithmetic",
- "inlining.Inlining",
- "instanceofstring.InstanceofString",
- "invoke.Invoke",
- "invokeempty.InvokeEmpty",
- "jumbostring.JumboString",
- "loadconst.LoadConst",
- "loop.UdpServer",
- "nestedtrycatches.NestedTryCatches",
- "regalloc.RegAlloc",
- "returns.Returns",
- "staticfield.StaticField",
- "stringbuilding.StringBuilding",
- "switches.Switches",
- "sync.Sync",
- "throwing.Throwing",
- "trivial.Trivial",
- "trycatch.TryCatch",
- "trycatchmany.TryCatchMany",
- "regress.Regress",
- "regress2.Regress2",
- "regress_37726195.Regress",
- "regress_37658666.Regress",
- "regress_37875803.Regress",
- "regress_37955340.Regress",
- "regress_62300145.Regress",
- "regress_64881691.Regress",
- "regress_65104300.Regress",
- "regress_70703087.Test",
- "regress_70736958.Test",
- "regress_70737019.Test",
- "regress_72361252.Test",
- "regress_110373181.Regress",
- "memberrebinding2.Memberrebinding",
- "memberrebinding3.Memberrebinding",
- "minification.Minification",
- "enclosingmethod.Main",
- "enclosingmethod_proguarded.Main",
- "switchmaps.Switches",
- "uninitializedfinal.UninitializedFinalFieldLeak",
+ "arithmetic.Arithmetic",
+ "inlining.Inlining",
+ "jumbostring.JumboString",
+ "loadconst.LoadConst",
+ "loop.UdpServer",
+ "nestedtrycatches.NestedTryCatches",
+ "regalloc.RegAlloc",
+ "returns.Returns",
+ "staticfield.StaticField",
+ "stringbuilding.StringBuilding",
+ "switches.Switches",
+ "sync.Sync",
+ "throwing.Throwing",
+ "trivial.Trivial",
+ "trycatch.TryCatch",
+ "trycatchmany.TryCatchMany",
+ "regress.Regress",
+ "regress2.Regress2",
+ "regress_37726195.Regress",
+ "regress_37658666.Regress",
+ "regress_37875803.Regress",
+ "regress_37955340.Regress",
+ "regress_62300145.Regress",
+ "regress_64881691.Regress",
+ "regress_65104300.Regress",
+ "regress_70703087.Test",
+ "regress_70736958.Test",
+ "regress_70737019.Test",
+ "regress_72361252.Test",
+ "regress_110373181.Regress",
+ "memberrebinding2.Memberrebinding",
+ "memberrebinding3.Memberrebinding",
+ "minification.Minification",
+ "enclosingmethod.Main",
+ "enclosingmethod_proguarded.Main",
+ "switchmaps.Switches",
+ "uninitializedfinal.UninitializedFinalFieldLeak",
};
List<String[]> fullTestList = new ArrayList<>(tests.length * 2);
diff --git a/src/test/java/com/android/tools/r8/debug/ExamplesDebugTest.java b/src/test/java/com/android/tools/r8/debug/ExamplesDebugTest.java
index fe9bed4..25caafc 100644
--- a/src/test/java/com/android/tools/r8/debug/ExamplesDebugTest.java
+++ b/src/test/java/com/android/tools/r8/debug/ExamplesDebugTest.java
@@ -60,16 +60,6 @@
}
@Test
- public void testInstanceofString() throws Exception {
- testDebugging("instanceofstring", "InstanceofString");
- }
-
- @Test
- public void testInvoke() throws Exception {
- testDebugging("invoke", "Invoke");
- }
-
- @Test
public void testJumboString() throws Exception {
testDebugging("jumbostring", "JumboString");
}
diff --git a/src/test/java/com/android/tools/r8/examples/instanceofstring/InstanceOfStringTestRunner.java b/src/test/java/com/android/tools/r8/examples/instanceofstring/InstanceOfStringTestRunner.java
new file mode 100644
index 0000000..14b2620
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/examples/instanceofstring/InstanceOfStringTestRunner.java
@@ -0,0 +1,50 @@
+// Copyright (c) 2023, 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.instanceofstring;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.examples.ExamplesTestBase;
+import com.android.tools.r8.utils.StringUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class InstanceOfStringTestRunner extends ExamplesTestBase {
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().enableApiLevelsForCf().build();
+ }
+
+ public InstanceOfStringTestRunner(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Override
+ public Class<?> getMainClass() {
+ return InstanceofString.class;
+ }
+
+ @Override
+ public String getExpected() {
+ return StringUtils.lines("is-string:true");
+ }
+
+ @Test
+ public void testDesugaring() throws Exception {
+ runTestDesugaring();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ runTestR8();
+ }
+
+ @Test
+ public void testDebug() throws Exception {
+ runTestDebugComparator();
+ }
+}
diff --git a/src/test/examples/instanceofstring/InstanceofString.java b/src/test/java/com/android/tools/r8/examples/instanceofstring/InstanceofString.java
similarity index 86%
rename from src/test/examples/instanceofstring/InstanceofString.java
rename to src/test/java/com/android/tools/r8/examples/instanceofstring/InstanceofString.java
index c7ff011..8073d0a 100644
--- a/src/test/examples/instanceofstring/InstanceofString.java
+++ b/src/test/java/com/android/tools/r8/examples/instanceofstring/InstanceofString.java
@@ -1,7 +1,7 @@
// Copyright (c) 2016, 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 instanceofstring;
+package com.android.tools.r8.examples.instanceofstring;
class InstanceofString {
public static void main(String[] args) {
diff --git a/src/test/examples/invoke/Invoke.java b/src/test/java/com/android/tools/r8/examples/invoke/Invoke.java
similarity index 99%
rename from src/test/examples/invoke/Invoke.java
rename to src/test/java/com/android/tools/r8/examples/invoke/Invoke.java
index d936140..6625471 100644
--- a/src/test/examples/invoke/Invoke.java
+++ b/src/test/java/com/android/tools/r8/examples/invoke/Invoke.java
@@ -5,7 +5,7 @@
// This code is not run directly. It needs to be compiled to dex code.
// 'invoke.dex' is what is run.
-package invoke;
+package com.android.tools.r8.examples.invoke;
public class Invoke extends SuperClass implements InvokeInterface {
diff --git a/src/test/examples/invoke/InvokeInterface.java b/src/test/java/com/android/tools/r8/examples/invoke/InvokeInterface.java
similarity index 91%
rename from src/test/examples/invoke/InvokeInterface.java
rename to src/test/java/com/android/tools/r8/examples/invoke/InvokeInterface.java
index 7f5a08e..bba0c12 100644
--- a/src/test/examples/invoke/InvokeInterface.java
+++ b/src/test/java/com/android/tools/r8/examples/invoke/InvokeInterface.java
@@ -2,7 +2,7 @@
// 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 invoke;
+package com.android.tools.r8.examples.invoke;
public interface InvokeInterface {
public void interface0();
diff --git a/src/test/java/com/android/tools/r8/examples/invoke/InvokeTestRunner.java b/src/test/java/com/android/tools/r8/examples/invoke/InvokeTestRunner.java
new file mode 100644
index 0000000..5e22ac6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/examples/invoke/InvokeTestRunner.java
@@ -0,0 +1,117 @@
+// Copyright (c) 2023, 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.invoke;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.examples.ExamplesTestBase;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class InvokeTestRunner extends ExamplesTestBase {
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().enableApiLevelsForCf().build();
+ }
+
+ public InvokeTestRunner(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Override
+ public Class<?> getMainClass() {
+ return Invoke.class;
+ }
+
+ @Override
+ public List<Class<?>> getTestClasses() {
+ return ImmutableList.of(Invoke.class, InvokeInterface.class, SuperClass.class);
+ }
+
+ @Override
+ public String getExpected() {
+ return StringUtils.lines(
+ "static0",
+ "static1 1",
+ "static2 1 2",
+ "static3 1 2 3",
+ "static4 1 2 3 4",
+ "static5 1 2 3 4 5",
+ "staticRange 1 2 3 4 5 6",
+ "staticDouble0 0.1",
+ "staticDouble2 0.1 0.2",
+ "staticDoubleRange 0.1 0.2 0.3",
+ "direct0",
+ "direct1 1",
+ "direct2 1 2",
+ "direct3 1 2 3",
+ "direct4 1 2 3 4",
+ "directRange 1 2 3 4 5 6",
+ "interface0",
+ "interface1 1",
+ "interface2 1 2",
+ "interface3 1 2 3",
+ "interface4 1 2 3 4",
+ "interfaceRange 1 2 3 4 5 6",
+ "virtual0",
+ "virtual1 1",
+ "virtual2 1 2",
+ "virtual3 1 2 3",
+ "virtual4 1 2 3 4",
+ "virtualRange 1 2 3 4 5 6",
+ "super0",
+ "super1 1",
+ "super2 1 2",
+ "super3 1 2 3",
+ "super4 1 2 3 4",
+ "superRange 1 2 3 4 5 6",
+ "rangeInvoke0 i 0 j 2 d 42.42 e 43.43 l 64424509440",
+ "rangeInvoke0 i 0 j 2 d 42.42 e 43.43 l 64424509440",
+ "rangeInvoke1 i 0 j 2 d 42.42 e 43.43 l 64424509440",
+ "rangeInvoke1 i 0 j 2 d 42.42 e 43.43 l 64424509440",
+ "rangeInvoke2 i 0 j 2 d 42.42 e 43.43 l 64424509440",
+ "rangeInvoke2 i 0 j 2 d 42.42 e 43.43 l 64424509440",
+ "oneArgumentMethod 0",
+ "oneArgumentMethod 3",
+ "oneArgumentMethod 16",
+ "twoArgumentMethod 16 9",
+ "twoArgumentMethod 16 10",
+ "twoArgumentMethod 16 11",
+ "oneDoubleArgumentMethod 1.1",
+ "oneDoubleArgumentMethod 4.4",
+ "oneDoubleArgumentMethod 16.6",
+ "twoDoubleArgumentMethod 16.6 9.9",
+ "twoDoubleArgumentMethod 16.6 10.1",
+ "twoDoubleArgumentMethod 16.6 11.2",
+ "rangeInvokesRepeatedArgument0 0 1 0 1 0 1 0 1",
+ "rangeInvokesRepeatedArgument0 0 1 1 1 1 1 1 1",
+ "int: 42",
+ "double: 42.42",
+ "int: 32",
+ "double: 32.32",
+ "519",
+ "15");
+ }
+
+ @Test
+ public void testDesugaring() throws Exception {
+ runTestDesugaring();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ runTestR8();
+ }
+
+ @Test
+ public void testDebug() throws Exception {
+ runTestDebugComparator();
+ }
+}
diff --git a/src/test/examples/invoke/SuperClass.java b/src/test/java/com/android/tools/r8/examples/invoke/SuperClass.java
similarity index 95%
rename from src/test/examples/invoke/SuperClass.java
rename to src/test/java/com/android/tools/r8/examples/invoke/SuperClass.java
index c4f7ae5..9b1deb2 100644
--- a/src/test/examples/invoke/SuperClass.java
+++ b/src/test/java/com/android/tools/r8/examples/invoke/SuperClass.java
@@ -2,7 +2,7 @@
// 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 invoke;
+package com.android.tools.r8.examples.invoke;
public class SuperClass {
public void super0() {
diff --git a/src/test/examples/invokeempty/ClassA.java b/src/test/java/com/android/tools/r8/examples/invokeempty/ClassA.java
similarity index 86%
rename from src/test/examples/invokeempty/ClassA.java
rename to src/test/java/com/android/tools/r8/examples/invokeempty/ClassA.java
index 50ac63c..248643f 100644
--- a/src/test/examples/invokeempty/ClassA.java
+++ b/src/test/java/com/android/tools/r8/examples/invokeempty/ClassA.java
@@ -2,7 +2,7 @@
// 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 invokeempty;
+package com.android.tools.r8.examples.invokeempty;
public class ClassA {
diff --git a/src/test/examples/invokeempty/ClassB.java b/src/test/java/com/android/tools/r8/examples/invokeempty/ClassB.java
similarity index 86%
rename from src/test/examples/invokeempty/ClassB.java
rename to src/test/java/com/android/tools/r8/examples/invokeempty/ClassB.java
index 305baa6..dd96e04 100644
--- a/src/test/examples/invokeempty/ClassB.java
+++ b/src/test/java/com/android/tools/r8/examples/invokeempty/ClassB.java
@@ -2,7 +2,7 @@
// 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 invokeempty;
+package com.android.tools.r8.examples.invokeempty;
public class ClassB {
diff --git a/src/test/examples/invokeempty/InvokeEmpty.java b/src/test/java/com/android/tools/r8/examples/invokeempty/InvokeEmpty.java
similarity index 88%
rename from src/test/examples/invokeempty/InvokeEmpty.java
rename to src/test/java/com/android/tools/r8/examples/invokeempty/InvokeEmpty.java
index 6160eb2..e1969bf 100644
--- a/src/test/examples/invokeempty/InvokeEmpty.java
+++ b/src/test/java/com/android/tools/r8/examples/invokeempty/InvokeEmpty.java
@@ -2,7 +2,7 @@
// 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 invokeempty;
+package com.android.tools.r8.examples.invokeempty;
public class InvokeEmpty {
diff --git a/src/test/java/com/android/tools/r8/examples/invokeempty/InvokeEmptyTestRunner.java b/src/test/java/com/android/tools/r8/examples/invokeempty/InvokeEmptyTestRunner.java
new file mode 100644
index 0000000..967fba6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/examples/invokeempty/InvokeEmptyTestRunner.java
@@ -0,0 +1,56 @@
+// Copyright (c) 2023, 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.invokeempty;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.examples.ExamplesTestBase;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class InvokeEmptyTestRunner extends ExamplesTestBase {
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().enableApiLevelsForCf().build();
+ }
+
+ public InvokeEmptyTestRunner(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Override
+ public Class<?> getMainClass() {
+ return InvokeEmpty.class;
+ }
+
+ @Override
+ public List<Class<?>> getTestClasses() {
+ return ImmutableList.of(getMainClass(), ClassA.class, ClassB.class);
+ }
+
+ @Override
+ public String getExpected() {
+ return "AB";
+ }
+
+ @Test
+ public void testDesugaring() throws Exception {
+ runTestDesugaring();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ runTestR8();
+ }
+
+ @Test
+ public void testDebug() throws Exception {
+ runTestDebugComparator();
+ }
+}