1 /*
   2  * Copyright (c) 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.jtt.optimize;
  26 
  27 import java.util.ArrayList;
  28 import java.util.Arrays;
  29 import java.util.HashSet;
  30 import java.util.List;
  31 import java.util.Random;
  32 import java.util.Set;
  33 import java.util.stream.Collectors;
  34 
  35 import org.graalvm.compiler.jtt.JTTTest;
  36 import org.graalvm.compiler.lir.hashing.HashFunction;
  37 import org.graalvm.compiler.lir.hashing.Hasher;
  38 import org.junit.Test;
  39 
  40 import jdk.vm.ci.meta.JavaConstant;
  41 
  42 /*
  43  * Tests optimization of hash table switches.
  44  * Code generated by `SwitchHashTableTest.TestGenerator.main`
  45  */
  46 public class SwitchHashTableTest extends JTTTest {
  47     @Test
  48     public void checkHashFunctionInstances() {
  49         List<String> coveredByTestCases = Arrays.asList("val >> min", "val", "val >> (val & min)", "(val >> min) ^ val", "val - min", "rotateRight(val, prime)", "rotateRight(val, prime) ^ val",
  50                         "rotateRight(val, prime) + val", "(val >> min) * val", "(val * prime) >> min");
  51         Set<String> functions = HashFunction.instances().stream().map(Object::toString).collect(Collectors.toSet());
  52         functions.removeAll(coveredByTestCases);
  53         assertTrue("The following hash functions are not covered by the `Switch03` test: " + functions +
  54                         ". Re-run the `Switch03.TestGenerator.main` and update the test class.", functions.isEmpty());
  55     }
  56 
  57     // Hasher[function=rotateRight(val, prime), effort=4, cardinality=16]
  58     public static int test1(int arg) {
  59         switch (arg) {
  60             case 3080012:
  61                 return 3080012;
  62             case 3080017:
  63                 return 3080017;
  64             case 3080029:
  65                 return 3080029;
  66             case 3080037:
  67                 return 3080037;
  68             case 3080040:
  69                 return 3080040;
  70             case 3080054:
  71                 return 3080054;
  72             case 3080060:
  73                 return 3080060;
  74             case 3080065:
  75                 return 3080065;
  76             case 3080073:
  77                 return 3080073;
  78             case 3080082:
  79                 return 3080082;
  80             case 3080095:
  81                 return 3080095;
  82             case 3080103:
  83                 return 3080103;
  84             case 3080116:
  85                 return 3080116;
  86             case 3080127:
  87                 return 3080127;
  88             case 3080130:
  89                 return 3080130;
  90             default:
  91                 return -1;
  92         }
  93     }
  94 
  95     @Test
  96     public void run1() throws Throwable {
  97         runTest("test1", 0); // zero
  98         runTest("test1", 3080011); // bellow
  99         runTest("test1", 3080012); // first
 100         runTest("test1", 3080065); // middle
 101         runTest("test1", 3080130); // last
 102         runTest("test1", 3080131); // above
 103         runTest("test1", 3080013); // miss
 104     }
 105 
 106     // Hasher[function=rotateRight(val, prime) ^ val, effort=5, cardinality=28]
 107     public static int test2(int arg) {
 108         switch (arg) {
 109             case 718707335:
 110                 return 718707335;
 111             case 718707336:
 112                 return 718707336;
 113             case 718707347:
 114                 return 718707347;
 115             case 718707359:
 116                 return 718707359;
 117             case 718707366:
 118                 return 718707366;
 119             case 718707375:
 120                 return 718707375;
 121             case 718707378:
 122                 return 718707378;
 123             case 718707386:
 124                 return 718707386;
 125             case 718707396:
 126                 return 718707396;
 127             case 718707401:
 128                 return 718707401;
 129             case 718707408:
 130                 return 718707408;
 131             case 718707409:
 132                 return 718707409;
 133             case 718707420:
 134                 return 718707420;
 135             case 718707431:
 136                 return 718707431;
 137             case 718707436:
 138                 return 718707436;
 139             default:
 140                 return -1;
 141         }
 142     }
 143 
 144     @Test
 145     public void run2() throws Throwable {
 146         runTest("test2", 0); // zero
 147         runTest("test2", 718707334); // bellow
 148         runTest("test2", 718707335); // first
 149         runTest("test2", 718707386); // middle
 150         runTest("test2", 718707436); // last
 151         runTest("test2", 718707437); // above
 152         runTest("test2", 718707337); // miss
 153     }
 154 
 155     // Hasher[function=(val * prime) >> min, effort=4, cardinality=16]
 156     public static int test3(int arg) {
 157         switch (arg) {
 158             case 880488712:
 159                 return 880488712;
 160             case 880488723:
 161                 return 880488723;
 162             case 880488737:
 163                 return 880488737;
 164             case 880488744:
 165                 return 880488744;
 166             case 880488752:
 167                 return 880488752;
 168             case 880488757:
 169                 return 880488757;
 170             case 880488767:
 171                 return 880488767;
 172             case 880488777:
 173                 return 880488777;
 174             case 880488781:
 175                 return 880488781;
 176             case 880488794:
 177                 return 880488794;
 178             case 880488795:
 179                 return 880488795;
 180             case 880488807:
 181                 return 880488807;
 182             case 880488814:
 183                 return 880488814;
 184             case 880488821:
 185                 return 880488821;
 186             case 880488831:
 187                 return 880488831;
 188             default:
 189                 return -1;
 190         }
 191     }
 192 
 193     @Test
 194     public void run3() throws Throwable {
 195         runTest("test3", 0); // zero
 196         runTest("test3", 880488711); // bellow
 197         runTest("test3", 880488712); // first
 198         runTest("test3", 880488777); // middle
 199         runTest("test3", 880488831); // last
 200         runTest("test3", 880488832); // above
 201         runTest("test3", 880488713); // miss
 202     }
 203 
 204     // Hasher[function=rotateRight(val, prime) + val, effort=5, cardinality=28]
 205     public static int test4(int arg) {
 206         switch (arg) {
 207             case 189404658:
 208                 return 189404658;
 209             case 189404671:
 210                 return 189404671;
 211             case 189404678:
 212                 return 189404678;
 213             case 189404680:
 214                 return 189404680;
 215             case 189404687:
 216                 return 189404687;
 217             case 189404698:
 218                 return 189404698;
 219             case 189404699:
 220                 return 189404699;
 221             case 189404711:
 222                 return 189404711;
 223             case 189404724:
 224                 return 189404724;
 225             case 189404725:
 226                 return 189404725;
 227             case 189404732:
 228                 return 189404732;
 229             case 189404739:
 230                 return 189404739;
 231             case 189404748:
 232                 return 189404748;
 233             case 189404754:
 234                 return 189404754;
 235             case 189404765:
 236                 return 189404765;
 237             default:
 238                 return -1;
 239         }
 240     }
 241 
 242     @Test
 243     public void run4() throws Throwable {
 244         runTest("test4", 0); // zero
 245         runTest("test4", 189404657); // bellow
 246         runTest("test4", 189404658); // first
 247         runTest("test4", 189404711); // middle
 248         runTest("test4", 189404765); // last
 249         runTest("test4", 189404766); // above
 250         runTest("test4", 189404659); // miss
 251     }
 252 
 253     // Hasher[function=val - min, effort=2, cardinality=24]
 254     public static int test5(int arg) {
 255         switch (arg) {
 256             case 527674226:
 257                 return 527674226;
 258             case 527674235:
 259                 return 527674235;
 260             case 527674236:
 261                 return 527674236;
 262             case 527674247:
 263                 return 527674247;
 264             case 527674251:
 265                 return 527674251;
 266             case 527674253:
 267                 return 527674253;
 268             case 527674257:
 269                 return 527674257;
 270             case 527674263:
 271                 return 527674263;
 272             case 527674265:
 273                 return 527674265;
 274             case 527674272:
 275                 return 527674272;
 276             case 527674286:
 277                 return 527674286;
 278             case 527674293:
 279                 return 527674293;
 280             case 527674294:
 281                 return 527674294;
 282             case 527674306:
 283                 return 527674306;
 284             case 527674308:
 285                 return 527674308;
 286             default:
 287                 return -1;
 288         }
 289     }
 290 
 291     @Test
 292     public void run5() throws Throwable {
 293         runTest("test5", 0); // zero
 294         runTest("test5", 527674225); // bellow
 295         runTest("test5", 527674226); // first
 296         runTest("test5", 527674263); // middle
 297         runTest("test5", 527674308); // last
 298         runTest("test5", 527674309); // above
 299         runTest("test5", 527674227); // miss
 300     }
 301 
 302     // Hasher[function=val, effort=1, cardinality=24]
 303     public static int test6(int arg) {
 304         switch (arg) {
 305             case 676979121:
 306                 return 676979121;
 307             case 676979128:
 308                 return 676979128;
 309             case 676979135:
 310                 return 676979135;
 311             case 676979146:
 312                 return 676979146;
 313             case 676979148:
 314                 return 676979148;
 315             case 676979156:
 316                 return 676979156;
 317             case 676979158:
 318                 return 676979158;
 319             case 676979169:
 320                 return 676979169;
 321             case 676979175:
 322                 return 676979175;
 323             case 676979179:
 324                 return 676979179;
 325             case 676979182:
 326                 return 676979182;
 327             case 676979194:
 328                 return 676979194;
 329             case 676979200:
 330                 return 676979200;
 331             case 676979205:
 332                 return 676979205;
 333             case 676979219:
 334                 return 676979219;
 335             default:
 336                 return -1;
 337         }
 338     }
 339 
 340     @Test
 341     public void run6() throws Throwable {
 342         runTest("test6", 0); // zero
 343         runTest("test6", 676979120); // bellow
 344         runTest("test6", 676979121); // first
 345         runTest("test6", 676979169); // middle
 346         runTest("test6", 676979219); // last
 347         runTest("test6", 676979220); // above
 348         runTest("test6", 676979122); // miss
 349     }
 350 
 351     // Hasher[function=(val >> min) ^ val, effort=3, cardinality=16]
 352     public static int test7(int arg) {
 353         switch (arg) {
 354             case 634218696:
 355                 return 634218696;
 356             case 634218710:
 357                 return 634218710;
 358             case 634218715:
 359                 return 634218715;
 360             case 634218720:
 361                 return 634218720;
 362             case 634218724:
 363                 return 634218724;
 364             case 634218732:
 365                 return 634218732;
 366             case 634218737:
 367                 return 634218737;
 368             case 634218749:
 369                 return 634218749;
 370             case 634218751:
 371                 return 634218751;
 372             case 634218758:
 373                 return 634218758;
 374             case 634218767:
 375                 return 634218767;
 376             case 634218772:
 377                 return 634218772;
 378             case 634218786:
 379                 return 634218786;
 380             case 634218792:
 381                 return 634218792;
 382             case 634218795:
 383                 return 634218795;
 384             default:
 385                 return -1;
 386         }
 387     }
 388 
 389     @Test
 390     public void run7() throws Throwable {
 391         runTest("test7", 0); // zero
 392         runTest("test7", 634218695); // bellow
 393         runTest("test7", 634218696); // first
 394         runTest("test7", 634218749); // middle
 395         runTest("test7", 634218795); // last
 396         runTest("test7", 634218796); // above
 397         runTest("test7", 634218697); // miss
 398     }
 399 
 400     // Hasher[function=val >> min, effort=2, cardinality=16]
 401     public static int test8(int arg) {
 402         switch (arg) {
 403             case 473982403:
 404                 return 473982403;
 405             case 473982413:
 406                 return 473982413;
 407             case 473982416:
 408                 return 473982416;
 409             case 473982425:
 410                 return 473982425;
 411             case 473982439:
 412                 return 473982439;
 413             case 473982445:
 414                 return 473982445;
 415             case 473982459:
 416                 return 473982459;
 417             case 473982468:
 418                 return 473982468;
 419             case 473982479:
 420                 return 473982479;
 421             case 473982482:
 422                 return 473982482;
 423             case 473982494:
 424                 return 473982494;
 425             case 473982501:
 426                 return 473982501;
 427             case 473982505:
 428                 return 473982505;
 429             case 473982519:
 430                 return 473982519;
 431             case 473982523:
 432                 return 473982523;
 433             default:
 434                 return -1;
 435         }
 436     }
 437 
 438     @Test
 439     public void run8() throws Throwable {
 440         runTest("test8", 0); // zero
 441         runTest("test8", 473982402); // bellow
 442         runTest("test8", 473982403); // first
 443         runTest("test8", 473982468); // middle
 444         runTest("test8", 473982523); // last
 445         runTest("test8", 473982524); // above
 446         runTest("test8", 473982404); // miss
 447     }
 448 
 449     // Hasher[function=val >> (val & min), effort=3, cardinality=16]
 450     public static int test9(int arg) {
 451         switch (arg) {
 452             case 15745090:
 453                 return 15745090;
 454             case 15745093:
 455                 return 15745093;
 456             case 15745102:
 457                 return 15745102;
 458             case 15745108:
 459                 return 15745108;
 460             case 15745122:
 461                 return 15745122;
 462             case 15745131:
 463                 return 15745131;
 464             case 15745132:
 465                 return 15745132;
 466             case 15745146:
 467                 return 15745146;
 468             case 15745151:
 469                 return 15745151;
 470             case 15745163:
 471                 return 15745163;
 472             case 15745169:
 473                 return 15745169;
 474             case 15745182:
 475                 return 15745182;
 476             case 15745191:
 477                 return 15745191;
 478             case 15745198:
 479                 return 15745198;
 480             case 15745207:
 481                 return 15745207;
 482             default:
 483                 return -1;
 484         }
 485     }
 486 
 487     @Test
 488     public void run9() throws Throwable {
 489         runTest("test9", 0); // zero
 490         runTest("test9", 15745089); // bellow
 491         runTest("test9", 15745090); // first
 492         runTest("test9", 15745146); // middle
 493         runTest("test9", 15745207); // last
 494         runTest("test9", 15745208); // above
 495         runTest("test9", 15745091); // miss
 496     }
 497 
 498     // Hasher[function=(val >> min) * val, effort=4, cardinality=28]
 499     public static int test10(int arg) {
 500         switch (arg) {
 501             case 989358996:
 502                 return 989358996;
 503             case 989359010:
 504                 return 989359010;
 505             case 989359022:
 506                 return 989359022;
 507             case 989359030:
 508                 return 989359030;
 509             case 989359038:
 510                 return 989359038;
 511             case 989359047:
 512                 return 989359047;
 513             case 989359053:
 514                 return 989359053;
 515             case 989359059:
 516                 return 989359059;
 517             case 989359061:
 518                 return 989359061;
 519             case 989359072:
 520                 return 989359072;
 521             case 989359073:
 522                 return 989359073;
 523             case 989359087:
 524                 return 989359087;
 525             case 989359097:
 526                 return 989359097;
 527             case 989359099:
 528                 return 989359099;
 529             case 989359108:
 530                 return 989359108;
 531             default:
 532                 return -1;
 533         }
 534     }
 535 
 536     @Test
 537     public void run10() throws Throwable {
 538         runTest("test10", 0); // zero
 539         runTest("test10", 989358995); // bellow
 540         runTest("test10", 989358996); // first
 541         runTest("test10", 989359059); // middle
 542         runTest("test10", 989359108); // last
 543         runTest("test10", 989359109); // above
 544         runTest("test10", 989358997); // miss
 545     }
 546 
 547     public static class TestGenerator {
 548 
 549         private static int nextId = 0;
 550         private static final int size = 15;
 551         private static double minDensity = 0.5;
 552 
 553         // test code generator
 554         public static void main(String[] args) {
 555 
 556             Random r = new Random(0);
 557             Set<String> seen = new HashSet<>();
 558             Set<String> all = HashFunction.instances().stream().map(Object::toString).collect(Collectors.toSet());
 559 
 560             println("@Test");
 561             println("public void checkHashFunctionInstances() {");
 562             println("    List<String> coveredByTestCases = Arrays.asList(" + String.join(", ", all.stream().map(s -> "\"" + s + "\"").collect(Collectors.toSet())) + ");");
 563             println("    Set<String> functions = HashFunction.instances().stream().map(Object::toString).collect(Collectors.toSet());");
 564             println("    functions.removeAll(coveredByTestCases);");
 565             println("    assertTrue(\"The following hash functions are not covered by the `Switch03` test: \" + functions +");
 566             println("            \". Re-run the `Switch03.TestGenerator.main` and update the test class.\", functions.isEmpty());");
 567             println("}");
 568 
 569             while (seen.size() < all.size()) {
 570                 int v = r.nextInt(Integer.MAX_VALUE / 2);
 571                 List<Integer> keys = new ArrayList<>();
 572                 while (keys.size() < 15) {
 573                     keys.add(v);
 574                     v += r.nextInt(15);
 575                 }
 576                 keys.sort(Integer::compare);
 577                 double density = ((double) keys.size() + 1) / (keys.get(keys.size() - 1) - keys.get(0));
 578                 if (density < minDensity) {
 579                     Hasher.forKeys(toConstants(keys), minDensity).ifPresent(h -> {
 580                         String f = h.function().toString();
 581                         if (!seen.contains(f)) {
 582                             gen(keys, h);
 583                             seen.add(f);
 584                         }
 585                     });
 586                 }
 587             }
 588         }
 589 
 590         private static void gen(List<Integer> keys, Hasher hasher) {
 591             int id = ++nextId;
 592 
 593             println("// " + hasher + "");
 594             println("public static int test" + id + "(int arg) {");
 595             println("    switch (arg) {");
 596 
 597             for (Integer key : keys) {
 598                 println("        case " + key + ": return " + key + ";");
 599             }
 600 
 601             println("        default: return -1;");
 602             println("    }");
 603             println("}");
 604 
 605             int miss = keys.get(0) + 1;
 606             while (keys.contains(miss)) {
 607                 miss++;
 608             }
 609 
 610             println("@Test");
 611             println("public void run" + id + "() throws Throwable {");
 612             println("    runTest(\"test" + id + "\", 0); // zero ");
 613             println("    runTest(\"test" + id + "\", " + (keys.get(0) - 1) + "); // bellow ");
 614             println("    runTest(\"test" + id + "\", " + keys.get(0) + "); // first ");
 615             println("    runTest(\"test" + id + "\", " + keys.get(size / 2) + "); // middle ");
 616             println("    runTest(\"test" + id + "\", " + keys.get(size - 1) + "); // last ");
 617             println("    runTest(\"test" + id + "\", " + (keys.get(size - 1) + 1) + "); // above ");
 618             println("    runTest(\"test" + id + "\", " + miss + "); // miss ");
 619             println("}");
 620         }
 621 
 622         private static void println(String s) {
 623             System.out.println(s);
 624         }
 625 
 626         private static JavaConstant[] toConstants(List<Integer> keys) {
 627             JavaConstant[] ckeys = new JavaConstant[keys.size()];
 628 
 629             for (int i = 0; i < keys.size(); i++) {
 630                 ckeys[i] = JavaConstant.forInt(keys.get(i));
 631             }
 632             return ckeys;
 633         }
 634     }
 635 }