--- /dev/null 2017-11-16 08:17:56.803999947 +0100 +++ new/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.jtt/src/org/graalvm/compiler/jtt/optimize/SwitchHashTableTest.java 2019-03-09 03:58:01.877483068 +0100 @@ -0,0 +1,635 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +package org.graalvm.compiler.jtt.optimize; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Random; +import java.util.Set; +import java.util.stream.Collectors; + +import org.graalvm.compiler.jtt.JTTTest; +import org.graalvm.compiler.lir.hashing.HashFunction; +import org.graalvm.compiler.lir.hashing.Hasher; +import org.junit.Test; + +import jdk.vm.ci.meta.JavaConstant; + +/* + * Tests optimization of hash table switches. + * Code generated by `SwitchHashTableTest.TestGenerator.main` + */ +public class SwitchHashTableTest extends JTTTest { + @Test + public void checkHashFunctionInstances() { + List coveredByTestCases = Arrays.asList("val >> min", "val", "val >> (val & min)", "(val >> min) ^ val", "val - min", "rotateRight(val, prime)", "rotateRight(val, prime) ^ val", + "rotateRight(val, prime) + val", "(val >> min) * val", "(val * prime) >> min"); + Set functions = HashFunction.instances().stream().map(Object::toString).collect(Collectors.toSet()); + functions.removeAll(coveredByTestCases); + assertTrue("The following hash functions are not covered by the `Switch03` test: " + functions + + ". Re-run the `Switch03.TestGenerator.main` and update the test class.", functions.isEmpty()); + } + + // Hasher[function=rotateRight(val, prime), effort=4, cardinality=16] + public static int test1(int arg) { + switch (arg) { + case 3080012: + return 3080012; + case 3080017: + return 3080017; + case 3080029: + return 3080029; + case 3080037: + return 3080037; + case 3080040: + return 3080040; + case 3080054: + return 3080054; + case 3080060: + return 3080060; + case 3080065: + return 3080065; + case 3080073: + return 3080073; + case 3080082: + return 3080082; + case 3080095: + return 3080095; + case 3080103: + return 3080103; + case 3080116: + return 3080116; + case 3080127: + return 3080127; + case 3080130: + return 3080130; + default: + return -1; + } + } + + @Test + public void run1() throws Throwable { + runTest("test1", 0); // zero + runTest("test1", 3080011); // bellow + runTest("test1", 3080012); // first + runTest("test1", 3080065); // middle + runTest("test1", 3080130); // last + runTest("test1", 3080131); // above + runTest("test1", 3080013); // miss + } + + // Hasher[function=rotateRight(val, prime) ^ val, effort=5, cardinality=28] + public static int test2(int arg) { + switch (arg) { + case 718707335: + return 718707335; + case 718707336: + return 718707336; + case 718707347: + return 718707347; + case 718707359: + return 718707359; + case 718707366: + return 718707366; + case 718707375: + return 718707375; + case 718707378: + return 718707378; + case 718707386: + return 718707386; + case 718707396: + return 718707396; + case 718707401: + return 718707401; + case 718707408: + return 718707408; + case 718707409: + return 718707409; + case 718707420: + return 718707420; + case 718707431: + return 718707431; + case 718707436: + return 718707436; + default: + return -1; + } + } + + @Test + public void run2() throws Throwable { + runTest("test2", 0); // zero + runTest("test2", 718707334); // bellow + runTest("test2", 718707335); // first + runTest("test2", 718707386); // middle + runTest("test2", 718707436); // last + runTest("test2", 718707437); // above + runTest("test2", 718707337); // miss + } + + // Hasher[function=(val * prime) >> min, effort=4, cardinality=16] + public static int test3(int arg) { + switch (arg) { + case 880488712: + return 880488712; + case 880488723: + return 880488723; + case 880488737: + return 880488737; + case 880488744: + return 880488744; + case 880488752: + return 880488752; + case 880488757: + return 880488757; + case 880488767: + return 880488767; + case 880488777: + return 880488777; + case 880488781: + return 880488781; + case 880488794: + return 880488794; + case 880488795: + return 880488795; + case 880488807: + return 880488807; + case 880488814: + return 880488814; + case 880488821: + return 880488821; + case 880488831: + return 880488831; + default: + return -1; + } + } + + @Test + public void run3() throws Throwable { + runTest("test3", 0); // zero + runTest("test3", 880488711); // bellow + runTest("test3", 880488712); // first + runTest("test3", 880488777); // middle + runTest("test3", 880488831); // last + runTest("test3", 880488832); // above + runTest("test3", 880488713); // miss + } + + // Hasher[function=rotateRight(val, prime) + val, effort=5, cardinality=28] + public static int test4(int arg) { + switch (arg) { + case 189404658: + return 189404658; + case 189404671: + return 189404671; + case 189404678: + return 189404678; + case 189404680: + return 189404680; + case 189404687: + return 189404687; + case 189404698: + return 189404698; + case 189404699: + return 189404699; + case 189404711: + return 189404711; + case 189404724: + return 189404724; + case 189404725: + return 189404725; + case 189404732: + return 189404732; + case 189404739: + return 189404739; + case 189404748: + return 189404748; + case 189404754: + return 189404754; + case 189404765: + return 189404765; + default: + return -1; + } + } + + @Test + public void run4() throws Throwable { + runTest("test4", 0); // zero + runTest("test4", 189404657); // bellow + runTest("test4", 189404658); // first + runTest("test4", 189404711); // middle + runTest("test4", 189404765); // last + runTest("test4", 189404766); // above + runTest("test4", 189404659); // miss + } + + // Hasher[function=val - min, effort=2, cardinality=24] + public static int test5(int arg) { + switch (arg) { + case 527674226: + return 527674226; + case 527674235: + return 527674235; + case 527674236: + return 527674236; + case 527674247: + return 527674247; + case 527674251: + return 527674251; + case 527674253: + return 527674253; + case 527674257: + return 527674257; + case 527674263: + return 527674263; + case 527674265: + return 527674265; + case 527674272: + return 527674272; + case 527674286: + return 527674286; + case 527674293: + return 527674293; + case 527674294: + return 527674294; + case 527674306: + return 527674306; + case 527674308: + return 527674308; + default: + return -1; + } + } + + @Test + public void run5() throws Throwable { + runTest("test5", 0); // zero + runTest("test5", 527674225); // bellow + runTest("test5", 527674226); // first + runTest("test5", 527674263); // middle + runTest("test5", 527674308); // last + runTest("test5", 527674309); // above + runTest("test5", 527674227); // miss + } + + // Hasher[function=val, effort=1, cardinality=24] + public static int test6(int arg) { + switch (arg) { + case 676979121: + return 676979121; + case 676979128: + return 676979128; + case 676979135: + return 676979135; + case 676979146: + return 676979146; + case 676979148: + return 676979148; + case 676979156: + return 676979156; + case 676979158: + return 676979158; + case 676979169: + return 676979169; + case 676979175: + return 676979175; + case 676979179: + return 676979179; + case 676979182: + return 676979182; + case 676979194: + return 676979194; + case 676979200: + return 676979200; + case 676979205: + return 676979205; + case 676979219: + return 676979219; + default: + return -1; + } + } + + @Test + public void run6() throws Throwable { + runTest("test6", 0); // zero + runTest("test6", 676979120); // bellow + runTest("test6", 676979121); // first + runTest("test6", 676979169); // middle + runTest("test6", 676979219); // last + runTest("test6", 676979220); // above + runTest("test6", 676979122); // miss + } + + // Hasher[function=(val >> min) ^ val, effort=3, cardinality=16] + public static int test7(int arg) { + switch (arg) { + case 634218696: + return 634218696; + case 634218710: + return 634218710; + case 634218715: + return 634218715; + case 634218720: + return 634218720; + case 634218724: + return 634218724; + case 634218732: + return 634218732; + case 634218737: + return 634218737; + case 634218749: + return 634218749; + case 634218751: + return 634218751; + case 634218758: + return 634218758; + case 634218767: + return 634218767; + case 634218772: + return 634218772; + case 634218786: + return 634218786; + case 634218792: + return 634218792; + case 634218795: + return 634218795; + default: + return -1; + } + } + + @Test + public void run7() throws Throwable { + runTest("test7", 0); // zero + runTest("test7", 634218695); // bellow + runTest("test7", 634218696); // first + runTest("test7", 634218749); // middle + runTest("test7", 634218795); // last + runTest("test7", 634218796); // above + runTest("test7", 634218697); // miss + } + + // Hasher[function=val >> min, effort=2, cardinality=16] + public static int test8(int arg) { + switch (arg) { + case 473982403: + return 473982403; + case 473982413: + return 473982413; + case 473982416: + return 473982416; + case 473982425: + return 473982425; + case 473982439: + return 473982439; + case 473982445: + return 473982445; + case 473982459: + return 473982459; + case 473982468: + return 473982468; + case 473982479: + return 473982479; + case 473982482: + return 473982482; + case 473982494: + return 473982494; + case 473982501: + return 473982501; + case 473982505: + return 473982505; + case 473982519: + return 473982519; + case 473982523: + return 473982523; + default: + return -1; + } + } + + @Test + public void run8() throws Throwable { + runTest("test8", 0); // zero + runTest("test8", 473982402); // bellow + runTest("test8", 473982403); // first + runTest("test8", 473982468); // middle + runTest("test8", 473982523); // last + runTest("test8", 473982524); // above + runTest("test8", 473982404); // miss + } + + // Hasher[function=val >> (val & min), effort=3, cardinality=16] + public static int test9(int arg) { + switch (arg) { + case 15745090: + return 15745090; + case 15745093: + return 15745093; + case 15745102: + return 15745102; + case 15745108: + return 15745108; + case 15745122: + return 15745122; + case 15745131: + return 15745131; + case 15745132: + return 15745132; + case 15745146: + return 15745146; + case 15745151: + return 15745151; + case 15745163: + return 15745163; + case 15745169: + return 15745169; + case 15745182: + return 15745182; + case 15745191: + return 15745191; + case 15745198: + return 15745198; + case 15745207: + return 15745207; + default: + return -1; + } + } + + @Test + public void run9() throws Throwable { + runTest("test9", 0); // zero + runTest("test9", 15745089); // bellow + runTest("test9", 15745090); // first + runTest("test9", 15745146); // middle + runTest("test9", 15745207); // last + runTest("test9", 15745208); // above + runTest("test9", 15745091); // miss + } + + // Hasher[function=(val >> min) * val, effort=4, cardinality=28] + public static int test10(int arg) { + switch (arg) { + case 989358996: + return 989358996; + case 989359010: + return 989359010; + case 989359022: + return 989359022; + case 989359030: + return 989359030; + case 989359038: + return 989359038; + case 989359047: + return 989359047; + case 989359053: + return 989359053; + case 989359059: + return 989359059; + case 989359061: + return 989359061; + case 989359072: + return 989359072; + case 989359073: + return 989359073; + case 989359087: + return 989359087; + case 989359097: + return 989359097; + case 989359099: + return 989359099; + case 989359108: + return 989359108; + default: + return -1; + } + } + + @Test + public void run10() throws Throwable { + runTest("test10", 0); // zero + runTest("test10", 989358995); // bellow + runTest("test10", 989358996); // first + runTest("test10", 989359059); // middle + runTest("test10", 989359108); // last + runTest("test10", 989359109); // above + runTest("test10", 989358997); // miss + } + + public static class TestGenerator { + + private static int nextId = 0; + private static final int size = 15; + private static double minDensity = 0.5; + + // test code generator + public static void main(String[] args) { + + Random r = new Random(0); + Set seen = new HashSet<>(); + Set all = HashFunction.instances().stream().map(Object::toString).collect(Collectors.toSet()); + + println("@Test"); + println("public void checkHashFunctionInstances() {"); + println(" List coveredByTestCases = Arrays.asList(" + String.join(", ", all.stream().map(s -> "\"" + s + "\"").collect(Collectors.toSet())) + ");"); + println(" Set functions = HashFunction.instances().stream().map(Object::toString).collect(Collectors.toSet());"); + println(" functions.removeAll(coveredByTestCases);"); + println(" assertTrue(\"The following hash functions are not covered by the `Switch03` test: \" + functions +"); + println(" \". Re-run the `Switch03.TestGenerator.main` and update the test class.\", functions.isEmpty());"); + println("}"); + + while (seen.size() < all.size()) { + int v = r.nextInt(Integer.MAX_VALUE / 2); + List keys = new ArrayList<>(); + while (keys.size() < 15) { + keys.add(v); + v += r.nextInt(15); + } + keys.sort(Integer::compare); + double density = ((double) keys.size() + 1) / (keys.get(keys.size() - 1) - keys.get(0)); + if (density < minDensity) { + Hasher.forKeys(toConstants(keys), minDensity).ifPresent(h -> { + String f = h.function().toString(); + if (!seen.contains(f)) { + gen(keys, h); + seen.add(f); + } + }); + } + } + } + + private static void gen(List keys, Hasher hasher) { + int id = ++nextId; + + println("// " + hasher + ""); + println("public static int test" + id + "(int arg) {"); + println(" switch (arg) {"); + + for (Integer key : keys) { + println(" case " + key + ": return " + key + ";"); + } + + println(" default: return -1;"); + println(" }"); + println("}"); + + int miss = keys.get(0) + 1; + while (keys.contains(miss)) { + miss++; + } + + println("@Test"); + println("public void run" + id + "() throws Throwable {"); + println(" runTest(\"test" + id + "\", 0); // zero "); + println(" runTest(\"test" + id + "\", " + (keys.get(0) - 1) + "); // bellow "); + println(" runTest(\"test" + id + "\", " + keys.get(0) + "); // first "); + println(" runTest(\"test" + id + "\", " + keys.get(size / 2) + "); // middle "); + println(" runTest(\"test" + id + "\", " + keys.get(size - 1) + "); // last "); + println(" runTest(\"test" + id + "\", " + (keys.get(size - 1) + 1) + "); // above "); + println(" runTest(\"test" + id + "\", " + miss + "); // miss "); + println("}"); + } + + private static void println(String s) { + System.out.println(s); + } + + private static JavaConstant[] toConstants(List keys) { + JavaConstant[] ckeys = new JavaConstant[keys.size()]; + + for (int i = 0; i < keys.size(); i++) { + ckeys[i] = JavaConstant.forInt(keys.get(i)); + } + return ckeys; + } + } +}