Path: blob/master/test/hotspot/jtreg/compiler/rangechecks/TestExplicitRangeChecks.java
41152 views
/*1* Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223/*24* @test25* @bug 807348026* @summary explicit range checks should be recognized by C227* @library /test/lib /28* @modules java.base/jdk.internal.misc:+open29* @build sun.hotspot.WhiteBox30* @run driver jdk.test.lib.helpers.ClassFileInstaller sun.hotspot.WhiteBox31* @run main/othervm -ea -Xmixed -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI32* -XX:-BackgroundCompilation -XX:-UseOnStackReplacement33* -XX:CompileCommand=compileonly,compiler.rangechecks.TestExplicitRangeChecks::test*34* compiler.rangechecks.TestExplicitRangeChecks35*36*/3738package compiler.rangechecks;3940import compiler.whitebox.CompilerWhiteBoxTest;41import jdk.internal.misc.Unsafe;42import jdk.test.lib.Platform;43import sun.hotspot.WhiteBox;4445import java.lang.annotation.Retention;46import java.lang.annotation.RetentionPolicy;47import java.lang.reflect.Field;48import java.lang.reflect.Method;49import java.lang.reflect.Modifier;50import java.util.HashMap;5152public class TestExplicitRangeChecks {53private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();54private static final int TIERED_STOP_AT_LEVEL = WHITE_BOX.getIntxVMFlag("TieredStopAtLevel").intValue();55private static int[] array = new int[10];56private static boolean success = true;5758@Retention(RetentionPolicy.RUNTIME)59@interface Args {60int[] compile();61int[] good();62int[] bad();63boolean deoptimize() default true;64}6566// Should be compiled as a single unsigned comparison67// 0 <= index < array.length68@Args(compile = {5,}, good = {0, 9}, bad = {-1, 10})69static boolean test1_1(int index, int[] array) {70if (index < 0 || index >= array.length) {71return false;72}73return true;74}7576// same test but so we can compile with same optimization after trap in test1_177static boolean test1_2(int index, int[] array) {78if (index < 0 || index >= array.length) {79return false;80}81return true;82}8384// Shouldn't matter whether first or second test is the one85// against a constants86// 0 <= index < array.length87@Args(compile = {5,}, good = {0, 9}, bad = {-1, 10})88static boolean test2_1(int index, int[] array) {89if (index >= array.length || index < 0) {90return false;91}92return true;93}9495static boolean test2_2(int index, int[] array) {96if (index >= array.length || index < 0) {97return false;98}99return true;100}101102// 0 <= index <= array.length103@Args(compile = {5,}, good = {0, 10}, bad = {-1, 11})104static boolean test3_1(int index, int[] array) {105if (index < 0 || index > array.length) {106return false;107}108return true;109}110111static boolean test3_2(int index, int[] array) {112if (index < 0 || index > array.length) {113return false;114}115return true;116}117118// 0 <= index <= array.length119@Args(compile = {5,}, good = {0, 10}, bad = {-1, 11})120static boolean test4_1(int index, int[] array) {121if (index > array.length || index < 0 ) {122return false;123}124return true;125}126127static boolean test4_2(int index, int[] array) {128if (index > array.length || index < 0) {129return false;130}131return true;132}133134static int[] test5_helper(int i) {135return (i < 100) ? new int[10] : new int[5];136}137138// 0 < index < array.length139@Args(compile = {5,}, good = {1, 9}, bad = {0, 10})140static boolean test5_1(int index, int[] array) {141array = test5_helper(index); // array.length must be not constant greater than 1142if (index <= 0 || index >= array.length) {143return false;144}145return true;146}147148static boolean test5_2(int index, int[] array) {149array = test5_helper(index); // array.length must be not constant greater than 1150if (index <= 0 || index >= array.length) {151return false;152}153return true;154}155156// 0 < index < array.length157@Args(compile = {5,}, good = {1, 9}, bad = {0, 10})158static boolean test6_1(int index, int[] array) {159array = test5_helper(index); // array.length must be not constant greater than 1160if (index >= array.length || index <= 0 ) {161return false;162}163return true;164}165166static boolean test6_2(int index, int[] array) {167array = test5_helper(index); // array.length must be not constant greater than 1168if (index >= array.length || index <= 0) {169return false;170}171return true;172}173174// 0 < index <= array.length175@Args(compile = {5,}, good = {1, 10}, bad = {0, 11})176static boolean test7_1(int index, int[] array) {177if (index <= 0 || index > array.length) {178return false;179}180return true;181}182183static boolean test7_2(int index, int[] array) {184if (index <= 0 || index > array.length) {185return false;186}187return true;188}189190// 0 < index <= array.length191@Args(compile = {5,}, good = {1, 10}, bad = {0, 11})192static boolean test8_1(int index, int[] array) {193if (index > array.length || index <= 0 ) {194return false;195}196return true;197}198199static boolean test8_2(int index, int[] array) {200if (index > array.length || index <= 0) {201return false;202}203return true;204}205206static int[] test9_helper1(int i) {207return (i < 100) ? new int[1] : new int[2];208}209210static int[] test9_helper2(int i) {211return (i < 100) ? new int[10] : new int[11];212}213214// array1.length <= index < array2.length215@Args(compile = {5,}, good = {1, 9}, bad = {0, 10})216static boolean test9_1(int index, int[] array) {217int[] array1 = test9_helper1(index);218int[] array2 = test9_helper2(index);219if (index < array1.length || index >= array2.length) {220return false;221}222return true;223}224225static boolean test9_2(int index, int[] array) {226int[] array1 = test9_helper1(index);227int[] array2 = test9_helper2(index);228if (index < array1.length || index >= array2.length) {229return false;230}231return true;232}233234// Previously supported pattern235@Args(compile = {-5,5,15}, good = {0, 9}, bad = {-1, 10}, deoptimize=false)236static boolean test10_1(int index, int[] array) {237if (index < 0 || index >= 10) {238return false;239}240return true;241}242243static int[] array11 = new int[10];244@Args(compile = {5,}, good = {0, 9}, bad = {-1,})245static boolean test11_1(int index, int[] array) {246if (index < 0) {247return false;248}249int unused = array11[index];250// If this one is folded with the first test then we allow251// array access above to proceed even for out of bound array252// index and the method throws an253// ArrayIndexOutOfBoundsException.254if (index >= array.length) {255return false;256}257return true;258}259260static int[] array12 = {10, 10, 10, 10, 10, 10, 10, 10, 10, 10};261@Args(compile = {5,}, good = {0, 9}, bad = {-1,})262static boolean test12_1(int index, int[] array) {263// Cannot be folded otherwise would cause incorrect array264// access if the array12 range check is executed before the265// folded test.266if (index < 0 || index >= array12[index]) {267return false;268}269return true;270}271272// Same as test1_1 but pass null array when index < 0: shouldn't273// cause NPE.274@Args(compile = {5,}, good = {0, 9}, bad = {})275static boolean test13_1(int index, int[] array) {276if (index < 0 || index >= array.length) {277return false;278}279return true;280}281282// Same as test10 but with uncommon traps283@Args(compile = {5}, good = {0, 9}, bad = {-1, 10})284static boolean test14_1(int index, int[] array) {285if (index < 0 || index >= 10) {286return false;287}288return true;289}290291static boolean test14_2(int index, int[] array) {292if (index < 0 || index >= 10) {293return false;294}295return true;296}297298// Same as test13_1 but pass null array: null trap should be reported on first if299@Args(compile = {5,}, good = {0, 9}, bad = {})300static boolean test15_1(int index, int[] array) {301if (index < 0 || index >= array.length) {302return false;303}304return true;305}306307// Same as test1 but with no null check between the integer comparisons308@Args(compile = {5,}, good = {0, 9}, bad = {-1, 10})309static boolean test16_1(int index, int[] array) {310int l = array.length;311if (index < 0 || index >= l) {312return false;313}314return true;315}316317static boolean test16_2(int index, int[] array) {318int l = array.length;319if (index < 0 || index >= l) {320return false;321}322return true;323}324325// Same as test1 but bound check on array access should optimize326// out.327@Args(compile = {5,}, good = {0, 9}, bad = {-1, 10})328static boolean test17_1(int index, int[] array) {329if (index < 0 || index >= array.length) {330return false;331}332array[index] = 0;333return true;334}335336static boolean test17_2(int index, int[] array) {337if (index < 0 || index >= array.length) {338return false;339}340array[index] = 0;341return true;342}343344// Same as test1 but range check smearing should optimize345// 3rd range check out.346@Args(compile = {5,}, good = {}, bad = {})347static boolean test18_1(int index, int[] array) {348if (index < 0 || index >= array.length) {349return false;350}351array[index+2] = 0;352array[index+1] = 0;353return true;354}355356static boolean test19_helper1(int index) {357if (index < 12) {358return false;359}360return true;361}362363static boolean test19_helper2(int index) {364if (index > 8) {365return false;366}367return true;368}369370// Second test should be optimized out371static boolean test19(int index, int[] array) {372test19_helper1(index);373test19_helper2(index);374return true;375}376377final HashMap<String,Method> tests = new HashMap<>();378{379for (Method m : this.getClass().getDeclaredMethods()) {380if (m.getName().matches("test[0-9]+(_[0-9])?")) {381assert(Modifier.isStatic(m.getModifiers())) : m;382tests.put(m.getName(), m);383}384}385}386387void doTest(String name) throws Exception {388Method m = tests.get(name + "_1");389390Args anno = m.getAnnotation(Args.class);391int[] compile = anno.compile();392int[] good = anno.good();393int[] bad = anno.bad();394boolean deoptimize = anno.deoptimize();395396// Get compiled397for (int i = 0; i < 20000;) {398for (int j = 0; j < compile.length; j++) {399m.invoke(null, compile[j], array);400i++;401}402}403404if (!WHITE_BOX.isMethodCompiled(m)) {405System.out.println(name + "_1 not compiled");406success = false;407}408409// check that good values don't trigger exception or410// deoptimization411for (int i = 0; i < good.length; i++) {412boolean res = (boolean)m.invoke(null, good[i], array);413414if (!res) {415System.out.println(name + " bad result for good input " + good[i]);416success = false;417}418if (!WHITE_BOX.isMethodCompiled(m)) {419System.out.println(name + " deoptimized on valid access");420success = false;421}422}423424// check that bad values trigger exception and deoptimization425for (int i = 0; i < bad.length; i++) {426if (i > 0 && deoptimize) {427m = tests.get(name + "_" + (i+1));428for (int k = 0; k < 20000;) {429for (int j = 0; j < compile.length; j++) {430m.invoke(null, compile[j], array);431k++;432}433}434if (!WHITE_BOX.isMethodCompiled(m)) {435System.out.println(name + ("_" + (i+1)) + " not compiled");436success = false;437}438}439440boolean res = (boolean)m.invoke(null, bad[i], array);441442if (res) {443System.out.println(name + " bad result for bad input " + bad[i]);444success = false;445}446// Only perform these additional checks if C2 is available447if (Platform.isServer() && !Platform.isEmulatedClient() &&448TIERED_STOP_AT_LEVEL == CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION) {449if (deoptimize && WHITE_BOX.isMethodCompiled(m)) {450System.out.println(name + " not deoptimized on invalid access");451success = false;452} else if (!deoptimize && !WHITE_BOX.isMethodCompiled(m)) {453System.out.println(name + " deoptimized on invalid access");454success = false;455}456}457}458459}460461private static final Unsafe UNSAFE;462463static {464try {465Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");466unsafeField.setAccessible(true);467UNSAFE = (Unsafe) unsafeField.get(null);468}469catch (Exception e) {470throw new AssertionError(e);471}472}473474// On x64, int to long conversion should optimize away in address computation475static int test20(int[] a) {476int sum = 0;477for (int i = 0; i < a.length; i++) {478sum += test20_helper(a, i);479}480return sum;481}482483static int test20_helper(int[] a, int i) {484if (i < 0 || i >= a.length)485throw new ArrayIndexOutOfBoundsException();486487long address = (((long) i) << 2) + UNSAFE.ARRAY_INT_BASE_OFFSET;488return UNSAFE.getInt(a, address);489}490491static int test21(int[] a) {492int sum = 0;493for (int i = 0; i < a.length; i++) {494sum += test20_helper(a, i);495}496return sum;497}498499static int test21_helper(int[] a, int i) {500if (i < 0 || i >= a.length)501throw new ArrayIndexOutOfBoundsException();502503long address = (((long) i) << 2) + UNSAFE.ARRAY_INT_BASE_OFFSET;504return UNSAFE.getIntVolatile(a, address);505}506507static public void main(String[] args) throws Exception {508509if (WHITE_BOX.getBooleanVMFlag("BackgroundCompilation")) {510throw new AssertionError("Background compilation enabled");511}512513TestExplicitRangeChecks test = new TestExplicitRangeChecks();514515test.doTest("test1");516test.doTest("test2");517test.doTest("test3");518test.doTest("test4");519520// pollute branch profile521for (int i = 0; i < 10000; i++) {522test5_helper((i%2 == 0) ? 0 : 1000);523}524525test.doTest("test5");526test.doTest("test6");527test.doTest("test7");528test.doTest("test8");529530// pollute branch profile531for (int i = 0; i < 10000; i++) {532test9_helper1((i%2 == 0) ? 0 : 1000);533test9_helper2((i%2 == 0) ? 0 : 1000);534}535536test.doTest("test9");537test.doTest("test10");538test.doTest("test11");539test.doTest("test12");540541test.doTest("test13");542{543Method m = test.tests.get("test13_1");544for (int i = 0; i < 1; i++) {545test13_1(-1, null);546if (!WHITE_BOX.isMethodCompiled(m)) {547break;548}549}550}551test.doTest("test13");552{553Method m = test.tests.get("test13_1");554for (int i = 0; i < 10; i++) {555test13_1(-1, null);556if (!WHITE_BOX.isMethodCompiled(m)) {557break;558}559}560}561562test.doTest("test14");563564test.doTest("test15");565{566Method m = test.tests.get("test15_1");567for (int i = 0; i < 10; i++) {568try {569test15_1(5, null);570} catch(NullPointerException npe) {}571if (!WHITE_BOX.isMethodCompiled(m)) {572break;573}574}575}576test.doTest("test15");577test.doTest("test16");578test.doTest("test17");579test.doTest("test18");580581for (int i = 0; i < 20000; i++) {582test19_helper1(20);583test19_helper2(5);584}585586{587Method m = test.tests.get("test19");588WHITE_BOX.enqueueMethodForCompilation(m, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION);589}590591for (int i = 0; i < 20000; i++) {592test20(array);593}594595for (int i = 0; i < 20000; i++) {596test21(array);597}598599if (!success) {600throw new RuntimeException("some tests failed");601}602}603}604605606