1 /*
   2  * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 package org.graalvm.compiler.hotspot.test;
  24 
  25 import java.lang.reflect.Array;
  26 import java.util.ArrayList;
  27 import java.util.HashMap;
  28 
  29 import org.graalvm.compiler.core.test.GraalCompilerTest;
  30 import org.graalvm.compiler.graph.Node;
  31 import org.graalvm.compiler.hotspot.replacements.arraycopy.ArrayCopySnippets;
  32 import org.graalvm.compiler.nodes.DirectCallTargetNode;
  33 import org.graalvm.compiler.nodes.Invoke;
  34 import org.graalvm.compiler.nodes.LoweredCallTargetNode;
  35 import org.graalvm.compiler.nodes.StructuredGraph;
  36 import org.graalvm.compiler.options.OptionValues;
  37 import org.junit.Assert;
  38 import org.junit.Test;
  39 
  40 import jdk.vm.ci.code.InstalledCode;
  41 import jdk.vm.ci.meta.JavaMethod;
  42 import jdk.vm.ci.meta.ResolvedJavaMethod;
  43 
  44 /**
  45  * Tests intrinsification of {@link System#arraycopy(Object, int, Object, int, int)}.
  46  */
  47 public class ArrayCopyIntrinsificationTest extends GraalCompilerTest {
  48 
  49     @Override
  50     protected InstalledCode getCode(ResolvedJavaMethod method, StructuredGraph g, boolean forceCompile, boolean installAsDefault, OptionValues options) {
  51         StructuredGraph graph = g == null ? parseForCompile(method) : g;
  52         int nodeCount = graph.getNodeCount();
  53         InstalledCode result = super.getCode(method, graph, forceCompile, installAsDefault, options);
  54         boolean graphWasProcessed = nodeCount != graph.getNodeCount();
  55         if (graphWasProcessed) {
  56             if (mustIntrinsify) {
  57                 for (Node node : graph.getNodes()) {
  58                     if (node instanceof Invoke) {
  59                         Invoke invoke = (Invoke) node;
  60                         Assert.assertTrue(invoke.callTarget() instanceof DirectCallTargetNode);
  61                         LoweredCallTargetNode directCall = (LoweredCallTargetNode) invoke.callTarget();
  62                         JavaMethod callee = directCall.targetMethod();
  63                         if (callee.getDeclaringClass().equals(getMetaAccess().lookupJavaType(System.class)) && callee.getName().equals("arraycopy")) {
  64                             // A partial snippet (e.g., ArrayCopySnippets.checkcastArraycopy) may
  65                             // call the original arraycopy method
  66                         } else {
  67                             Assert.assertTrue(callee.toString(), callee.getName().equals("<init>"));
  68                             Assert.assertTrue(getMetaAccess().lookupJavaType(ArrayIndexOutOfBoundsException.class).equals(callee.getDeclaringClass()) ||
  69                                             getMetaAccess().lookupJavaType(NullPointerException.class).equals(callee.getDeclaringClass()));
  70                         }
  71                     }
  72                 }
  73             } else {
  74                 boolean found = false;
  75                 for (Node node : graph.getNodes()) {
  76                     if (node instanceof Invoke) {
  77                         Invoke invoke = (Invoke) node;
  78                         LoweredCallTargetNode directCall = (LoweredCallTargetNode) invoke.callTarget();
  79                         JavaMethod callee = directCall.targetMethod();
  80                         if (callee.getDeclaringClass().equals(getMetaAccess().lookupJavaType(System.class)) && callee.getName().equals("arraycopy")) {
  81                             found = true;
  82                         } else {
  83                             fail("found invoke to some method other than arraycopy: " + callee);
  84                         }
  85                     }
  86                 }
  87                 Assert.assertTrue("did not find invoke to arraycopy", found);
  88             }
  89         }
  90         return result;
  91     }
  92 
  93     boolean mustIntrinsify = true;
  94 
  95     @Test
  96     public void test0() {
  97         // Array store checks
  98         test("genericArraycopy", new Object(), 0, new Object[0], 0, 0);
  99         test("genericArraycopy", new Object[0], 0, new Object(), 0, 0);
 100     }
 101 
 102     @Test
 103     public void test1() {
 104         String name = "intArraycopy";
 105         int[] src = {234, 5345, 756, 23, 8, 345, 873, 440};
 106         // Null checks
 107         test(name, null, 0, src, 0, 0);
 108         test(name, src, 0, null, 0, 0);
 109         // Bounds checks
 110         test(name, src, 0, src, 0, -1);
 111         test(name, src, 0, src, 0, src.length + 1);
 112     }
 113 
 114     @Test
 115     public void testByte() {
 116         byte[] src = {-1, 0, 1, 2, 3, 4};
 117         testHelper("byteArraycopy", src);
 118     }
 119 
 120     @Test
 121     public void testChar() {
 122         char[] src = "some string of chars".toCharArray();
 123         testHelper("charArraycopy", src);
 124     }
 125 
 126     @Test
 127     public void testShort() {
 128         short[] src = {234, 5345, 756, 23, 8, 345, 873, 440};
 129         testHelper("shortArraycopy", src);
 130     }
 131 
 132     @Test
 133     public void testInt() {
 134         int[] src = {234, 5345, 756, 23, 8, 345, 873, 440};
 135         testHelper("intArraycopy", src);
 136     }
 137 
 138     @Test
 139     public void testFloat() {
 140         float[] src = {234, 5345, 756, 23, 8, 345, 873, 440};
 141         testHelper("floatArraycopy", src);
 142     }
 143 
 144     @Test
 145     public void testLong() {
 146         long[] src = {234, 5345, 756, 23, 8, 345, 873, 440};
 147         testHelper("longArraycopy", src);
 148     }
 149 
 150     @Test
 151     public void testDouble() {
 152         double[] src = {234, 5345, 756, 23, 8, 345, 873, 440};
 153         testHelper("doubleArraycopy", src);
 154     }
 155 
 156     @Test
 157     public void testObject() {
 158         Object[] src = {"one", "two", "three", new ArrayList<>(), new HashMap<>()};
 159         testHelper("objectArraycopy", src);
 160     }
 161 
 162     /**
 163      * Tests {@link ArrayCopySnippets#checkcastArraycopyWork}.
 164      */
 165     @Test
 166     public void testArrayStoreException() {
 167         Object[] src = {"one", "two", "three", new ArrayList<>(), new HashMap<>()};
 168         Object[] dst = new CharSequence[src.length];
 169         // Will throw ArrayStoreException for 4th element
 170         test("objectArraycopy", src, 0, dst, 0, src.length);
 171     }
 172 
 173     @Test
 174     public void testDisjointObject() {
 175         Integer[] src1 = {1, 2, 3, 4};
 176         test("objectArraycopy", src1, 0, src1, 1, src1.length - 1);
 177 
 178         Integer[] src2 = {1, 2, 3, 4};
 179         test("objectArraycopy", src2, 1, src2, 0, src2.length - 1);
 180     }
 181 
 182     @Test
 183     public void testObjectExact() {
 184         Integer[] src = {1, 2, 3, 4};
 185         testHelper("objectArraycopyExact", src);
 186     }
 187 
 188     private static Object newArray(Object proto, int length) {
 189         assert proto != null;
 190         assert proto.getClass().isArray();
 191         return Array.newInstance(proto.getClass().getComponentType(), length);
 192     }
 193 
 194     private void testHelper(String name, Object src) {
 195         int srcLength = Array.getLength(src);
 196 
 197         // Complete array copy
 198         test(name, src, 0, newArray(src, srcLength), 0, srcLength);
 199 
 200         for (int length : new int[]{0, 1, srcLength - 1, srcLength}) {
 201             // Partial array copying
 202             test(name, src, 0, newArray(src, length), 0, length);
 203             test(name, src, srcLength - length, newArray(src, length), 0, length);
 204             test(name, src, 0, newArray(src, srcLength), 0, length);
 205         }
 206 
 207         if (srcLength > 1) {
 208             test(name, src, 0, src, 1, srcLength - 1);
 209         }
 210     }
 211 
 212     public static Object genericArraycopy(Object src, int srcPos, Object dst, int dstPos, int length) {
 213         System.arraycopy(src, srcPos, dst, dstPos, length);
 214         return dst;
 215     }
 216 
 217     public static Object[] objectArraycopy(Object[] src, int srcPos, Object[] dst, int dstPos, int length) {
 218         System.arraycopy(src, srcPos, dst, dstPos, length);
 219         return dst;
 220     }
 221 
 222     public static Object[] objectArraycopyExact(Integer[] src, int srcPos, Integer[] dst, int dstPos, int length) {
 223         System.arraycopy(src, srcPos, dst, dstPos, length);
 224         return dst;
 225     }
 226 
 227     public static boolean[] booleanArraycopy(boolean[] src, int srcPos, boolean[] dst, int dstPos, int length) {
 228         System.arraycopy(src, srcPos, dst, dstPos, length);
 229         return dst;
 230     }
 231 
 232     public static byte[] byteArraycopy(byte[] src, int srcPos, byte[] dst, int dstPos, int length) {
 233         System.arraycopy(src, srcPos, dst, dstPos, length);
 234         return dst;
 235     }
 236 
 237     public static char[] charArraycopy(char[] src, int srcPos, char[] dst, int dstPos, int length) {
 238         System.arraycopy(src, srcPos, dst, dstPos, length);
 239         return dst;
 240     }
 241 
 242     public static short[] shortArraycopy(short[] src, int srcPos, short[] dst, int dstPos, int length) {
 243         System.arraycopy(src, srcPos, dst, dstPos, length);
 244         return dst;
 245     }
 246 
 247     public static int[] intArraycopy(int[] src, int srcPos, int[] dst, int dstPos, int length) {
 248         System.arraycopy(src, srcPos, dst, dstPos, length);
 249         return dst;
 250     }
 251 
 252     public static float[] floatArraycopy(float[] src, int srcPos, float[] dst, int dstPos, int length) {
 253         System.arraycopy(src, srcPos, dst, dstPos, length);
 254         return dst;
 255     }
 256 
 257     public static long[] longArraycopy(long[] src, int srcPos, long[] dst, int dstPos, int length) {
 258         System.arraycopy(src, srcPos, dst, dstPos, length);
 259         return dst;
 260     }
 261 
 262     public static double[] doubleArraycopy(double[] src, int srcPos, double[] dst, int dstPos, int length) {
 263         System.arraycopy(src, srcPos, dst, dstPos, length);
 264         return dst;
 265     }
 266 
 267     /**
 268      * Test case derived from assertion while compiling <a href=
 269      * "https://code.google.com/r/baggiogamp-guava/source/browse/guava/src/com/google/common/collect/ArrayTable.java?r=d2e06112416223cb5437d43c12a989c0adc7345b#181"
 270      * > com.google.common.collect.ArrayTable(ArrayTable other)</a>.
 271      */
 272     @Test
 273     public void testCopyRows() {
 274         Object[][] rows = {{"a1", "a2", "a3", "a4"}, {"b1", "b2", "b3", "b4"}, {"c1", "c2", "c3", "c4"}};
 275         test("copyRows", rows, 4, new Integer(rows.length));
 276     }
 277 
 278     public static Object[][] copyRows(Object[][] rows, int rowSize, Integer rowCount) {
 279         Object[][] copy = new Object[rows.length][rowSize];
 280         for (int i = 0; i < rowCount.intValue(); i++) {
 281             System.arraycopy(rows[i], 0, copy[i], 0, rows[i].length);
 282         }
 283         return copy;
 284     }
 285 }