package org.graalvm.compiler.hotspot.test;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.HashMap;
import org.graalvm.compiler.api.directives.GraalDirectives;
import org.graalvm.compiler.core.test.GraalCompilerTest;
import org.graalvm.compiler.graph.Node;
import org.graalvm.compiler.nodes.DirectCallTargetNode;
import org.graalvm.compiler.nodes.Invoke;
import org.graalvm.compiler.nodes.LoweredCallTargetNode;
import org.graalvm.compiler.nodes.StructuredGraph;
import org.graalvm.compiler.options.OptionValues;
import org.graalvm.compiler.replacements.arraycopy.ArrayCopySnippets;
import org.junit.Assert;
import org.junit.Test;
import jdk.vm.ci.code.InstalledCode;
import jdk.vm.ci.meta.JavaMethod;
import jdk.vm.ci.meta.ResolvedJavaMethod;
public class ArrayCopyIntrinsificationTest extends GraalCompilerTest {
@Override
protected InstalledCode getCode(ResolvedJavaMethod method, StructuredGraph g, boolean forceCompile, boolean installAsDefault, OptionValues options) {
StructuredGraph graph = g == null ? parseForCompile(method) : g;
int nodeCount = graph.getNodeCount();
InstalledCode result = super.getCode(method, graph, forceCompile, installAsDefault, options);
boolean graphWasProcessed = nodeCount != graph.getNodeCount();
if (graphWasProcessed) {
if (mustIntrinsify) {
for (Node node : graph.getNodes()) {
if (node instanceof Invoke) {
Invoke invoke = (Invoke) node;
Assert.assertTrue(invoke.callTarget() instanceof DirectCallTargetNode);
LoweredCallTargetNode directCall = (LoweredCallTargetNode) invoke.callTarget();
JavaMethod callee = directCall.targetMethod();
if (callee.getDeclaringClass().equals(getMetaAccess().lookupJavaType(System.class)) && callee.getName().equals("arraycopy")) {
} else {
Assert.assertTrue(callee.toString(), callee.getName().equals("<init>"));
Assert.assertTrue(getMetaAccess().lookupJavaType(ArrayIndexOutOfBoundsException.class).equals(callee.getDeclaringClass()) ||
getMetaAccess().lookupJavaType(NullPointerException.class).equals(callee.getDeclaringClass()));
}
}
}
} else {
boolean found = false;
for (Node node : graph.getNodes()) {
if (node instanceof Invoke) {
Invoke invoke = (Invoke) node;
LoweredCallTargetNode directCall = (LoweredCallTargetNode) invoke.callTarget();
JavaMethod callee = directCall.targetMethod();
if (callee.getDeclaringClass().equals(getMetaAccess().lookupJavaType(System.class)) && callee.getName().equals("arraycopy")) {
found = true;
} else {
fail("found invoke to some method other than arraycopy: " + callee);
}
}
}
Assert.assertTrue("did not find invoke to arraycopy", found);
}
}
return result;
}
boolean mustIntrinsify = true;
@Test
public void test0() {
test("genericArraycopy", new Object(), 0, new Object[0], 0, 0);
test("genericArraycopy", new Object[0], 0, new Object(), 0, 0);
}
@Test
public void test1() {
String name = "intArraycopy";
int[] src = {234, 5345, 756, 23, 8, 345, 873, 440};
test(name, null, 0, src, 0, 0);
test(name, src, 0, null, 0, 0);
test(name, src, 0, src, 0, -1);
test(name, src, 0, src, 0, src.length + 1);
}
@Test
public void testByte() {
byte[] src = {-1, 0, 1, 2, 3, 4};
testHelper("byteArraycopy", src);
}
@Test
public void testChar() {
char[] src = "some string of chars".toCharArray();
testHelper("charArraycopy", src);
}
@Test
public void testShort() {
short[] src = {234, 5345, 756, 23, 8, 345, 873, 440};
testHelper("shortArraycopy", src);
}
@Test
public void testInt() {
int[] src = {234, 5345, 756, 23, 8, 345, 873, 440};
testHelper("intArraycopy", src);
}
@Test
public void testFloat() {
float[] src = {234, 5345, 756, 23, 8, 345, 873, 440};
testHelper("floatArraycopy", src);
}
@Test
public void testLong() {
long[] src = {234, 5345, 756, 23, 8, 345, 873, 440};
testHelper("longArraycopy", src);
}
@Test
public void testDouble() {
double[] src = {234, 5345, 756, 23, 8, 345, 873, 440};
testHelper("doubleArraycopy", src);
}
@Test
public void testObject() {
Object[] src = {"one", "two", "three", new ArrayList<>(), new HashMap<>()};
testHelper("objectArraycopy", src);
}
@Test
public void testArrayStoreException() {
Object[] src = {"one", "two", "three", new ArrayList<>(), new HashMap<>()};
Object[] dst = new CharSequence[src.length];
test("objectArraycopy", src, 0, dst, 0, src.length);
}
@Test
public void testDisjointObject() {
Integer[] src1 = {1, 2, 3, 4};
test("objectArraycopy", src1, 0, src1, 1, src1.length - 1);
Integer[] src2 = {1, 2, 3, 4};
test("objectArraycopy", src2, 1, src2, 0, src2.length - 1);
}
@Test
public void testObjectExact() {
Integer[] src = {1, 2, 3, 4};
testHelper("objectArraycopyExact", src);
}
private static Object newArray(Object proto, int length) {
assert proto != null;
assert proto.getClass().isArray();
return Array.newInstance(proto.getClass().getComponentType(), length);
}
private void testHelper(String name, Object src) {
int srcLength = Array.getLength(src);
test(name, src, 0, newArray(src, srcLength), 0, srcLength);
for (int length : new int[]{0, 1, srcLength - 1, srcLength}) {
test(name, src, 0, newArray(src, length), 0, length);
test(name, src, srcLength - length, newArray(src, length), 0, length);
test(name, src, 0, newArray(src, srcLength), 0, length);
}
if (srcLength > 1) {
test(name, src, 0, src, 1, srcLength - 1);
}
}
public static Object genericArraycopy(Object src, int srcPos, Object dst, int dstPos, int length) {
System.arraycopy(src, srcPos, dst, dstPos, length);
return dst;
}
public static Object genericArraycopyCatchNullException(Object src, int srcPos, Object dst, int dstPos, int length) {
boolean caught = false;
try {
System.arraycopy(src, srcPos, dst, dstPos, length);
} catch (NullPointerException e) {
caught = true;
}
return caught;
}
public static Object genericArraycopyCatchArrayStoreException(Object src, int srcPos, Object dst, int dstPos, int length) {
boolean caught = false;
try {
System.arraycopy(src, srcPos, dst, dstPos, length);
} catch (ArrayStoreException e) {
caught = true;
}
return caught;
}
public static Object genericArraycopyCatchArrayIndexException(Object src, int srcPos, Object dst, int dstPos, int length) {
boolean caught = false;
try {
System.arraycopy(src, srcPos, dst, dstPos, length);
} catch (ArrayIndexOutOfBoundsException e) {
caught = true;
}
return caught;
}
public static Object[] objectArraycopy(Object[] src, int srcPos, Object[] dst, int dstPos, int length) {
System.arraycopy(src, srcPos, dst, dstPos, length);
return dst;
}
public static Object objectArraycopyCatchNullException(Object[] src, int srcPos, Object[] dst, int dstPos, int length) {
boolean caught = false;
try {
System.arraycopy(src, srcPos, dst, dstPos, length);
} catch (NullPointerException e) {
caught = true;
}
return caught;
}
public static Object objectArraycopyCatchArrayStoreException(Object[] src, int srcPos, Object[] dst, int dstPos, int length) {
boolean caught = false;
try {
System.arraycopy(src, srcPos, dst, dstPos, length);
} catch (ArrayStoreException e) {
caught = true;
}
return caught;
}
public static Object objectArraycopyCatchArrayIndexException(Object[] src, int srcPos, Object[] dst, int dstPos, int length) {
boolean caught = false;
try {
System.arraycopy(src, srcPos, dst, dstPos, length);
} catch (ArrayIndexOutOfBoundsException e) {
caught = true;
}
return caught;
}
public static Object[] objectArraycopyExact(Integer[] src, int srcPos, Integer[] dst, int dstPos, int length) {
System.arraycopy(src, srcPos, dst, dstPos, length);
return dst;
}
public static boolean[] booleanArraycopy(boolean[] src, int srcPos, boolean[] dst, int dstPos, int length) {
System.arraycopy(src, srcPos, dst, dstPos, length);
return dst;
}
public static byte[] byteArraycopy(byte[] src, int srcPos, byte[] dst, int dstPos, int length) {
System.arraycopy(src, srcPos, dst, dstPos, length);
return dst;
}
public static char[] charArraycopy(char[] src, int srcPos, char[] dst, int dstPos, int length) {
System.arraycopy(src, srcPos, dst, dstPos, length);
return dst;
}
public static short[] shortArraycopy(short[] src, int srcPos, short[] dst, int dstPos, int length) {
System.arraycopy(src, srcPos, dst, dstPos, length);
return dst;
}
public static int[] intArraycopy(int[] src, int srcPos, int[] dst, int dstPos, int length) {
System.arraycopy(src, srcPos, dst, dstPos, length);
return dst;
}
public static float[] floatArraycopy(float[] src, int srcPos, float[] dst, int dstPos, int length) {
System.arraycopy(src, srcPos, dst, dstPos, length);
return dst;
}
public static long[] longArraycopy(long[] src, int srcPos, long[] dst, int dstPos, int length) {
System.arraycopy(src, srcPos, dst, dstPos, length);
return dst;
}
public static double[] doubleArraycopy(double[] src, int srcPos, double[] dst, int dstPos, int length) {
System.arraycopy(src, srcPos, dst, dstPos, length);
return dst;
}
@Test
public void testCopyRows() {
Object[][] rows = {{"a1", "a2", "a3", "a4"}, {"b1", "b2", "b3", "b4"}, {"c1", "c2", "c3", "c4"}};
test("copyRows", rows, 4, Integer.valueOf(rows.length));
}
public static Object[][] copyRows(Object[][] rows, int rowSize, Integer rowCount) {
Object[][] copy = new Object[rows.length][rowSize];
for (int i = 0; i < rowCount.intValue(); i++) {
System.arraycopy(rows[i], 0, copy[i], 0, rows[i].length);
}
return copy;
}
@Test
public void testGenericExceptions() {
test("genericArraycopyCatchNullException", null, 4, new byte[128], 2, 3);
test("genericArraycopyCatchNullException", new byte[128], 4, null, 2, 3);
Object[] integerDest = new Integer[128];
Object[] longSource = new Object[128];
for (int i = 0; i < longSource.length; i++) {
longSource[i] = Long.valueOf(i);
}
test("genericArraycopyCatchArrayStoreException", longSource, 4, integerDest, 2, 3);
test("genericArraycopyCatchArrayIndexException", new int[128], 0, new int[128], Integer.MAX_VALUE, 1);
}
@Test
public void testObjectArrayExceptions() {
test("objectArraycopyCatchNullException", null, 4, new Byte[128], 2, 3);
test("objectArraycopyCatchNullException", new Byte[128], 4, null, 2, 3);
Object[] integerDest = new Integer[128];
Object[] longSource = new Object[128];
for (int i = 0; i < longSource.length; i++) {
longSource[i] = Long.valueOf(i);
}
test("objectArraycopyCatchArrayStoreException", longSource, 4, integerDest, 2, 3);
test("objectArraycopyCatchArrayIndexException", new Integer[128], 0, new Integer[128], Integer.MAX_VALUE, 1);
}
@Test
public void testArraycopyDeoptWithSideEffect() {
ArgSupplier s = () -> new int[4];
int[] b = new int[]{1, 1, 1, 1};
int[] c = new int[]{2, 2, 2, 2};
test("arraycopyAndDeopt", s, b, c);
}
public static int[] arraycopyAndDeopt(int[] a, int[] b, int[] c) {
if (a[0] == 0) {
System.arraycopy(b, 0, a, 0, b.length);
GraalDirectives.deoptimize();
} else {
System.arraycopy(c, 0, a, 0, b.length);
}
return a;
}
}