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