1 /*
   2  * Copyright (c) 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 
  24 /*
  25  * @test
  26  * @bug 6912521
  27  * @summary small array copy as loads/stores
  28  * @run main/othervm -XX:-BackgroundCompilation -XX:-UseOnStackReplacement -XX:CompileCommand=dontinline,TestArrayCopyAsLoadsStores::m* -XX:TypeProfileLevel=200 TestArrayCopyAsLoadsStores
  29  * @run main/othervm -XX:-BackgroundCompilation -XX:-UseOnStackReplacement -XX:CompileCommand=dontinline,TestArrayCopyAsLoadsStores::m* -XX:+IgnoreUnrecognizedVMOptions -XX:+StressArrayCopyMacroNode -XX:TypeProfileLevel=200 TestArrayCopyAsLoadsStores
  30  *
  31  */
  32 
  33 import java.lang.annotation.*;
  34 import java.lang.reflect.*;
  35 import java.util.*;
  36 
  37 public class TestArrayCopyAsLoadsStores {
  38 
  39     public enum ArraySrc {
  40         SMALL,
  41         LARGE,
  42         ZERO
  43     }
  44 
  45     public enum ArrayDst {
  46         NONE,
  47         NEW,
  48         SRC
  49     }
  50 
  51     static class A {
  52     }
  53 
  54     static class B extends A {
  55     }
  56 
  57     static final A[] small_object_src = new A[5];
  58     static final A[] large_object_src = new A[10];
  59     static final A[] zero_object_src = new A[0];
  60     static final int[] small_int_src = new int[5];
  61     static final int[] large_int_src = new int[10];
  62     static final int[] zero_int_src = new int[0];
  63     static Object src;
  64 
  65     @Retention(RetentionPolicy.RUNTIME)
  66     @interface Args {
  67         ArraySrc src();
  68         ArrayDst dst() default ArrayDst.NONE;
  69         int[] extra_args() default {};
  70     }
  71 
  72     // array clone should be compiled as loads/stores
  73     @Args(src=ArraySrc.SMALL)
  74     static A[] m1() throws CloneNotSupportedException {
  75         return (A[])small_object_src.clone();
  76     }
  77 
  78     @Args(src=ArraySrc.SMALL)
  79     static int[] m2() throws CloneNotSupportedException {
  80         return (int[])small_int_src.clone();
  81     }
  82 
  83     // new array allocation should be optimized out
  84     @Args(src=ArraySrc.SMALL)
  85     static int m3() throws CloneNotSupportedException {
  86         int[] array_clone = (int[])small_int_src.clone();
  87         return array_clone[0] + array_clone[1] + array_clone[2] +
  88             array_clone[3] + array_clone[4];
  89     }
  90 
  91     // should not be compiled as loads/stores
  92     @Args(src=ArraySrc.LARGE)
  93     static int[] m4() throws CloneNotSupportedException {
  94         return (int[])large_int_src.clone();
  95     }
  96 
  97     // check that array of length 0 is handled correctly
  98     @Args(src=ArraySrc.ZERO)
  99     static int[] m5() throws CloneNotSupportedException {
 100         return (int[])zero_int_src.clone();
 101     }
 102 
 103     // array copy should be compiled as loads/stores
 104     @Args(src=ArraySrc.SMALL, dst=ArrayDst.NEW)
 105     static void m6(int[] src, int[] dest) {
 106         System.arraycopy(src, 0, dest, 0, 5);
 107     }
 108 
 109     // array copy should not be compiled as loads/stores
 110     @Args(src=ArraySrc.LARGE, dst=ArrayDst.NEW)
 111     static void m7(int[] src, int[] dest) {
 112         System.arraycopy(src, 0, dest, 0, 10);
 113     }
 114 
 115     // array copy should be compiled as loads/stores
 116     @Args(src=ArraySrc.SMALL)
 117     static A[] m8(A[] src) {
 118         src[0] = src[0]; // force null check
 119         A[] dest = new A[5];
 120         System.arraycopy(src, 0, dest, 0, 5);
 121         return dest;
 122     }
 123 
 124     // array copy should not be compiled as loads/stores: we would
 125     // need to emit GC barriers
 126     @Args(src=ArraySrc.SMALL, dst=ArrayDst.NEW)
 127     static void m9(A[] src, A[] dest) {
 128         System.arraycopy(src, 0, dest, 0, 5);
 129     }
 130 
 131     // overlapping array regions: copy backward
 132     @Args(src=ArraySrc.SMALL, dst=ArrayDst.SRC)
 133     static void m10(int[] src, int[] dest) {
 134         System.arraycopy(src, 0, dest, 1, 4);
 135     }
 136 
 137     static boolean m10_check(int[] src, int[] dest) {
 138         boolean failure = false;
 139         for (int i = 0; i < 5; i++) {
 140             int j = Math.max(i - 1, 0);
 141             if (dest[i] != src[j]) {
 142                 System.out.println("Test m10 failed for " + i + " src[" + j +"]=" + src[j] + ", dest[" + i + "]=" + dest[i]);
 143                 failure = true;
 144             }
 145         }
 146         return failure;
 147     }
 148 
 149     // overlapping array regions: copy forward
 150     @Args(src=ArraySrc.SMALL, dst=ArrayDst.SRC)
 151     static void m11(int[] src, int[] dest) {
 152         System.arraycopy(src, 1, dest, 0, 4);
 153     }
 154 
 155     static boolean m11_check(int[] src, int[] dest) {
 156         boolean failure = false;
 157         for (int i = 0; i < 5; i++) {
 158             int j = Math.min(i + 1, 4);
 159             if (dest[i] != src[j]) {
 160                 System.out.println("Test m11 failed for " + i + " src[" + j +"]=" + src[j] + ", dest[" + i + "]=" + dest[i]);
 161                 failure = true;
 162             }
 163         }
 164         return failure;
 165     }
 166 
 167     // overlapping array region with unknown src/dest offsets: compiled must include both forward and backward copies
 168     @Args(src=ArraySrc.SMALL, dst=ArrayDst.SRC, extra_args={0,1})
 169     static void m12(int[] src, int[] dest, int srcPos, int destPos) {
 170         System.arraycopy(src, srcPos, dest, destPos, 4);
 171     }
 172 
 173     static boolean m12_check(int[] src, int[] dest) {
 174         boolean failure = false;
 175         for (int i = 0; i < 5; i++) {
 176             int j = Math.max(i - 1, 0);
 177             if (dest[i] != src[j]) {
 178                 System.out.println("Test m10 failed for " + i + " src[" + j +"]=" + src[j] + ", dest[" + i + "]=" + dest[i]);
 179                 failure = true;
 180             }
 181         }
 182         return failure;
 183     }
 184 
 185     // Array allocation and copy should optimize out
 186     @Args(src=ArraySrc.SMALL)
 187     static int m13(int[] src) {
 188         int[] dest = new int[5];
 189         System.arraycopy(src, 0, dest, 0, 5);
 190         return dest[0] + dest[1] + dest[2] + dest[3] + dest[4];
 191     }
 192 
 193     // Check that copy of length 0 is handled correctly
 194     @Args(src=ArraySrc.ZERO, dst=ArrayDst.NEW)
 195     static void m14(int[] src, int[] dest) {
 196         System.arraycopy(src, 0, dest, 0, 0);
 197     }
 198 
 199     // copyOf should compile to loads/stores
 200     @Args(src=ArraySrc.SMALL)
 201     static A[] m15() {
 202         return Arrays.copyOf(small_object_src, 5, A[].class);
 203     }
 204 
 205     static Object[] helper16(int i) {
 206         Object[] arr = null; 
 207         if ((i%2) == 0) {
 208             arr = small_object_src;
 209         } else {
 210             arr = new Object[5];
 211         }
 212         return arr;
 213     }
 214 
 215     // CopyOf may need subtype check 
 216     @Args(src=ArraySrc.SMALL, dst=ArrayDst.NONE, extra_args={0})
 217     static A[] m16(A[] unused_src, int i) {
 218         Object[] arr = helper16(i);
 219         return Arrays.copyOf(arr, 5, A[].class);
 220     }
 221 
 222     static Object[] helper17_1(int i) {
 223         Object[] arr = null; 
 224         if ((i%2) == 0) {
 225             arr = small_object_src;
 226         } else {
 227             arr = new Object[5];
 228         }
 229         return arr;
 230     }
 231 
 232     static A[] helper17_2(Object[] arr) {
 233         return Arrays.copyOf(arr, 5, A[].class);
 234     }
 235 
 236     // CopyOf may leverage type speculation
 237     @Args(src=ArraySrc.SMALL, dst=ArrayDst.NONE, extra_args={0})
 238     static A[] m17(A[] unused_src, int i) {
 239         Object[] arr = helper17_1(i);
 240         return helper17_2(arr);
 241     }
 242 
 243     static Object[] helper18_1(int i) {
 244         Object[] arr = null; 
 245         if ((i%2) == 0) {
 246             arr = small_object_src;
 247         } else {
 248             arr = new Object[5];
 249         }
 250         return arr;
 251     }
 252 
 253     static Object[] helper18_2(Object[] arr) {
 254         return Arrays.copyOf(arr, 5, Object[].class);
 255     }
 256 
 257     // CopyOf should not attempt to use type speculation if it's not needed
 258     @Args(src=ArraySrc.SMALL, dst=ArrayDst.NONE, extra_args={0})
 259     static Object[] m18(A[] unused_src, int i) {
 260         Object[] arr = helper18_1(i);
 261         return helper18_2(arr);
 262     }
 263 
 264     static Object[] helper19(int i) {
 265         Object[] arr = null; 
 266         if ((i%2) == 0) {
 267             arr = small_object_src;
 268         } else {
 269             arr = new Object[5];
 270         }
 271         return arr;
 272     }
 273 
 274     // CopyOf may need subtype check. Test is run to make type check
 275     // fail and cause deoptimization. Next compilation should not
 276     // compile as loads/stores because failed first compilation.
 277     @Args(src=ArraySrc.SMALL, dst=ArrayDst.NONE, extra_args={0})
 278     static A[] m19(A[] unused_src, int i) {
 279         Object[] arr = helper19(i);
 280         return Arrays.copyOf(arr, 5, A[].class);
 281     }
 282 
 283     // copyOf for large array should not compile to loads/stores
 284     @Args(src=ArraySrc.LARGE)
 285     static A[] m20() {
 286         return Arrays.copyOf(large_object_src, 10, A[].class);
 287     }
 288 
 289     // check zero length copyOf is handled correctly
 290     @Args(src=ArraySrc.ZERO)
 291     static A[] m21() {
 292         return Arrays.copyOf(zero_object_src, 0, A[].class);
 293     }
 294 
 295     // Run with srcPos=0 for a 1st compile, then with incorrect value
 296     // of srcPos to cause deoptimization, then with srcPos=0 for a 2nd
 297     // compile. The 2nd compile shouldn't turn arraycopy into
 298     // loads/stores because input arguments are no longer known to be
 299     // valid.
 300     @Args(src=ArraySrc.SMALL, dst=ArrayDst.NEW, extra_args={0})
 301     static void m22(int[] src, int[] dest, int srcPos) {
 302         System.arraycopy(src, srcPos, dest, 0, 5);
 303     }
 304 
 305     // copyOf should compile to loads/stores
 306     @Args(src=ArraySrc.SMALL)
 307     static A[] m23() {
 308         return Arrays.copyOfRange(small_object_src, 1, 4, A[].class);
 309     }
 310 
 311     static boolean m23_check(A[] src, A[] dest) {
 312         boolean failure = false;
 313         for (int i = 0; i < 3; i++) {
 314             if (src[i+1] != dest[i]) {
 315                 System.out.println("Test m23 failed for " + i + " src[" + (i+1) +"]=" + dest[i] + ", dest[" + i + "]=" + dest[i]);
 316                 failure = true;
 317             }
 318         }
 319         return failure;
 320     }
 321     
 322     final HashMap<String,Method> tests = new HashMap<>();
 323     {
 324         for (Method m : this.getClass().getDeclaredMethods()) {
 325             if (m.getName().matches("m[0-9]+(_check)?")) {
 326                 assert(Modifier.isStatic(m.getModifiers())) : m;
 327                 tests.put(m.getName(), m);
 328             }
 329         }
 330     }
 331 
 332     boolean success = true;
 333 
 334     void doTest(String name) throws Exception {
 335         Method m = tests.get(name);
 336         Method m_check = tests.get(name + "_check");
 337         Class[] paramTypes = m.getParameterTypes();
 338         Object[] params = new Object[paramTypes.length];
 339         Class retType = m.getReturnType();
 340         boolean isIntArray = (retType.isPrimitive() && !retType.equals(Void.TYPE)) ||
 341             (retType.equals(Void.TYPE) && paramTypes[0].getComponentType().isPrimitive()) ||
 342             (retType.isArray() && retType.getComponentType().isPrimitive());
 343 
 344         Args args = m.getAnnotation(Args.class);
 345 
 346         Object src = null;
 347         switch(args.src()) {
 348         case SMALL: {
 349             if (isIntArray) {
 350                 src = small_int_src;
 351             } else {
 352                 src = small_object_src;
 353             }
 354             break;
 355         }
 356         case LARGE: {
 357             if (isIntArray) {
 358                 src = large_int_src;
 359             } else {
 360                 src = large_object_src;
 361             }
 362             break;
 363         }
 364         case ZERO: {
 365             assert isIntArray;
 366             if (isIntArray) {
 367                 src = zero_int_src;
 368             } else {
 369                 src = zero_object_src;
 370             }
 371             break;
 372         }
 373         }
 374 
 375         for (int i = 0; i < 20000; i++) {
 376             boolean failure = false;
 377 
 378             int p = 0;
 379 
 380             if (params.length > 0) {
 381                 if (isIntArray) {
 382                     params[0] = ((int[])src).clone();
 383                 } else {
 384                     params[0] = ((A[])src).clone();
 385                 }
 386                 p++;
 387             }
 388 
 389             if (params.length > 1) {
 390                 switch(args.dst()) {
 391                 case NEW: {
 392                     if (isIntArray) {
 393                         params[1] = new int[((int[])params[0]).length];
 394                     } else {
 395                         params[1] = new A[((A[])params[0]).length];
 396                     }
 397                     p++;
 398                     break;
 399                 }
 400                 case SRC: {
 401                     params[1] = params[0];
 402                     p++;
 403                     break;
 404                 }
 405                 case NONE: break;
 406                 }
 407             }
 408 
 409             for (int j = 0; j < args.extra_args().length; j++) {
 410                 params[p+j] = args.extra_args()[j];
 411             }
 412 
 413             Object res = m.invoke(null, params);
 414 
 415             if (retType.isPrimitive() && !retType.equals(Void.TYPE)) {
 416                 int s = (int)res;
 417                 int sum = 0;
 418                 int[] int_res = (int[])src;
 419                 for (int j = 0; j < int_res.length; j++) {
 420                     sum += int_res[j];
 421                 }
 422                 failure = (s != sum);
 423                 if (failure) {
 424                     System.out.println("Test " + name + " failed: result = " + s + " != " + sum);
 425                 }
 426             } else {
 427                 Object dest = null;
 428                 if (!retType.equals(Void.TYPE)) {
 429                     dest = res;
 430                 } else {
 431                     dest = params[1];
 432                 }
 433 
 434                 if (m_check != null) {
 435                     failure = (boolean)m_check.invoke(null,  new Object[] { src, dest });
 436                 } else {
 437                     if (isIntArray) {
 438                         int[] int_res = (int[])src;
 439                         int[] int_dest = (int[])dest;
 440                         for (int j = 0; j < int_res.length; j++) {
 441                             if (int_res[j] != int_dest[j]) {
 442                                 System.out.println("Test " + name + " failed for " + j + " src[" + j +"]=" + int_res[j] + ", dest[" + j + "]=" + int_dest[j]);
 443                                 failure = true;
 444                             }
 445                         }
 446                     } else {
 447                         Object[] object_res = (Object[])src;
 448                         Object[] object_dest = (Object[])dest;
 449                         for (int j = 0; j < object_res.length; j++) {
 450                             if (object_res[j] != object_dest[j]) {
 451                                 System.out.println("Test " + name + " failed for " + j + " src[" + j +"]=" + object_res[j] + ", dest[" + j + "]=" + object_dest[j]);
 452                                 failure = true;
 453                             }
 454                         }
 455                     }
 456                 }
 457             }
 458 
 459             if (failure) {
 460                 success = false;
 461                 break;
 462             }
 463         }
 464     }
 465 
 466     public static void main(String[] args) throws Exception {
 467         for (int i = 0; i < small_object_src.length; i++) {
 468             small_object_src[i] = new A();
 469         }
 470 
 471         for (int i = 0; i < small_int_src.length; i++) {
 472             small_int_src[i] = i;
 473         }
 474 
 475         for (int i = 0; i < large_int_src.length; i++) {
 476             large_int_src[i] = i;
 477         }
 478 
 479         TestArrayCopyAsLoadsStores test = new TestArrayCopyAsLoadsStores();
 480 
 481         test.doTest("m1");
 482         test.doTest("m2");
 483         test.doTest("m3");
 484         test.doTest("m4");
 485         test.doTest("m5");
 486         test.doTest("m6");
 487         test.doTest("m7");
 488         test.doTest("m8");
 489         test.doTest("m9");
 490         test.doTest("m10");
 491         test.doTest("m11");
 492         test.doTest("m12");
 493         test.doTest("m13");
 494         test.doTest("m14");
 495         test.doTest("m15");
 496 
 497         // make both branches of the If appear taken
 498         for (int i = 0; i < 20000; i++) {
 499             helper16(i);
 500         }
 501 
 502         test.doTest("m16");
 503 
 504         // load class B so type check in m17 would not be simple comparison
 505         B b = new B();
 506         // make both branches of the If appear taken
 507         for (int i = 0; i < 20000; i++) {
 508             helper17_1(i);
 509         }
 510         
 511         test.doTest("m17");
 512 
 513         // make both branches of the If appear taken
 514         for (int i = 0; i < 20000; i++) {
 515             helper18_1(i);
 516         }
 517         test.doTest("m18");
 518 
 519         // make both branches of the If appear taken
 520         for (int i = 0; i < 20000; i++) {
 521             helper19(i);
 522         }
 523 
 524         // Compile
 525         for (int i = 0; i < 20000; i++) {
 526             m19(null, 0);
 527         }
 528 
 529         // force deopt
 530         for (int i = 0; i < 10; i++) {
 531             m19(null, 1);
 532         }
 533 
 534         test.doTest("m19");
 535 
 536         test.doTest("m20");
 537         test.doTest("m21");
 538 
 539         // Compile
 540         int[] dst = new int[small_int_src.length];
 541         for (int i = 0; i < 20000; i++) {
 542             m22(small_int_src, dst, 0);
 543         }
 544 
 545         // force deopt
 546         for (int i = 0; i < 10; i++) {
 547             try {
 548                 m22(small_int_src, dst, 5);
 549             } catch(ArrayIndexOutOfBoundsException aioobe) {}
 550         }
 551 
 552         test.doTest("m22");
 553 
 554         test.doTest("m23");
 555 
 556         if (!test.success) {
 557             throw new RuntimeException("some tests failed");
 558         }
 559     }
 560 }