1 /*
   2  * Copyright (c) 2021, 2022, 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 package ir_framework.tests;
  25 
  26 import compiler.lib.ir_framework.*;
  27 import compiler.lib.ir_framework.driver.irmatching.IRViolationException;
  28 import jdk.test.lib.Asserts;
  29 import jdk.test.lib.Platform;
  30 import sun.hotspot.WhiteBox;
  31 
  32 import java.io.ByteArrayOutputStream;
  33 import java.io.PrintStream;
  34 import java.util.*;
  35 import java.util.regex.Matcher;
  36 import java.util.regex.Pattern;
  37 
  38 /*
  39  * @test
  40  * @requires vm.debug == true & vm.compMode != "Xint" & vm.compiler1.enabled & vm.compiler2.enabled & vm.flagless
  41  * @summary Test IR matcher with different default IR node regexes. Use -DPrintIREncoding.
  42  *          Normally, the framework should be called with driver.
  43  * @library /test/lib /testlibrary_tests /
  44  * @build sun.hotspot.WhiteBox
  45  * @run driver jdk.test.lib.helpers.ClassFileInstaller sun.hotspot.WhiteBox
  46  * @run main/othervm/timeout=240 -Xbootclasspath/a:. -DSkipWhiteBoxInstall=true -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions
  47  *                               -XX:+WhiteBoxAPI -DPrintIREncoding=true  ir_framework.tests.TestIRMatching
  48  */
  49 
  50 public class TestIRMatching {
  51 
  52     private static final Map<Exception, String> exceptions = new LinkedHashMap<>();
  53     private static final ByteArrayOutputStream baos = new ByteArrayOutputStream();
  54     private static final ByteArrayOutputStream baosErr = new ByteArrayOutputStream();
  55     private static final PrintStream ps = new PrintStream(baos);
  56     private static final PrintStream psErr = new PrintStream(baosErr);
  57     private static final PrintStream oldOut = System.out;
  58     private static final PrintStream oldErr = System.err;
  59 
  60     private static void addException(Exception e) {
  61         System.out.flush();
  62         System.err.flush();
  63         exceptions.put(e, baos + System.lineSeparator() + baosErr);
  64     }
  65 
  66     public static void main(String[] args) {
  67         // Redirect System.out and System.err to reduce noise.
  68         System.setOut(ps);
  69         System.setErr(psErr);
  70         runWithArguments(AndOr1.class, "-XX:TLABRefillWasteFraction=52", "-XX:+UsePerfData", "-XX:+UseTLAB");
  71         runWithArguments(CountComparisons.class, "-XX:TLABRefillWasteFraction=50");
  72         runWithArguments(GoodCount.class, "-XX:TLABRefillWasteFraction=50");
  73         runWithArguments(MultipleFailOnGood.class, "-XX:TLABRefillWasteFraction=50");
  74 
  75         runCheck(new String[] {"-XX:TLABRefillWasteFraction=50", "-XX:+UsePerfData", "-XX:+UseTLAB"}, BadFailOnConstraint.create(AndOr1.class, "test1(int)", 1, "CallStaticJava"));
  76         runCheck(new String[] {"-XX:TLABRefillWasteFraction=50", "-XX:-UsePerfData", "-XX:+UseTLAB"}, BadFailOnConstraint.create(AndOr1.class, "test2()", 1, "CallStaticJava"));
  77 
  78         String[] allocMatches = { "MyClass", "wrapper for: _new_instance_Java" };
  79         runCheck(BadFailOnConstraint.create(MultipleFailOnBad.class, "fail1()", 1, 1, "Store"),
  80                  BadFailOnConstraint.create(MultipleFailOnBad.class, "fail1()", 1,  3, "Store"),
  81                  GoodFailOnRegexConstraint.create(MultipleFailOnBad.class, "fail1()", 1,  2, 4),
  82                  GoodFailOnRegexConstraint.create(MultipleFailOnBad.class, "fail2()", 1,  1),
  83                  BadFailOnConstraint.create(MultipleFailOnBad.class, "fail2()", 1,  2, "CallStaticJava"),
  84                  BadFailOnConstraint.create(MultipleFailOnBad.class, "fail3()", 1,  2, "Store"),
  85                  GoodFailOnRegexConstraint.create(MultipleFailOnBad.class, "fail3()", 1,  1, 3),
  86                  BadFailOnConstraint.create(MultipleFailOnBad.class, "fail4()", 1,  1, "Store"),
  87                  GoodFailOnRegexConstraint.create(MultipleFailOnBad.class, "fail4()", 1,  2, 3),
  88                  BadFailOnConstraint.create(MultipleFailOnBad.class, "fail5()", 1,  1, "Store"),
  89                  GoodFailOnRegexConstraint.create(MultipleFailOnBad.class, "fail5()", 1,  2, 3),
  90                  GoodFailOnRegexConstraint.create(MultipleFailOnBad.class, "fail6()", 1,  1),
  91                  BadFailOnConstraint.create(MultipleFailOnBad.class, "fail6()", 1,  2, allocMatches),
  92                  BadFailOnConstraint.create(MultipleFailOnBad.class, "fail6()", 1,  3, "CallStaticJava"),
  93                  GoodFailOnRegexConstraint.create(MultipleFailOnBad.class, "fail7()", 1,  1),
  94                  BadFailOnConstraint.create(MultipleFailOnBad.class, "fail7()", 1,  2, allocMatches),
  95                  GoodFailOnRegexConstraint.create(MultipleFailOnBad.class, "fail8()", 1,  1),
  96                  BadFailOnConstraint.create(MultipleFailOnBad.class, "fail8()", 1,  2, allocMatches),
  97                  BadFailOnConstraint.create(MultipleFailOnBad.class, "fail9()", 1,  1, "Store"),
  98                  BadFailOnConstraint.create(MultipleFailOnBad.class, "fail9()", 1,  2, "CallStaticJava"),
  99                  BadFailOnConstraint.create(MultipleFailOnBad.class, "fail10()", 1,  1, "Store", "iFld"),
 100                  GoodFailOnRegexConstraint.create(MultipleFailOnBad.class, "fail10()", 1,  2, 3)
 101         );
 102 
 103         runCheck(BadCountsConstraint.create(BadCount.class, "bad1()", 1, 1, "Load"),
 104                  GoodCountsConstraint.create(BadCount.class, "bad1()", 2),
 105                  GoodCountsConstraint.create(BadCount.class, "bad2()", 1),
 106                  BadCountsConstraint.create(BadCount.class, "bad2()", 2,  1, "Store"),
 107                  BadCountsConstraint.create(BadCount.class, "bad3()", 1,  1, "Load"),
 108                  BadCountsConstraint.create(BadCount.class, "bad3()", 2,  1, "Store")
 109         );
 110 
 111         runCheck(GoodRuleConstraint.create(Calls.class, "calls()", 1),
 112                  BadFailOnConstraint.create(Calls.class, "calls()", 2, 1, "CallStaticJava", "dontInline"),
 113                  BadFailOnConstraint.create(Calls.class, "calls()", 2, 2, "CallStaticJava", "dontInline"),
 114                  GoodRuleConstraint.create(Calls.class, "calls()", 3)
 115         );
 116 
 117         String[] allocArrayMatches = { "MyClass", "wrapper for: _new_array_Java"};
 118         runCheck(BadFailOnConstraint.create(AllocArray.class, "allocArray()", 1, allocArrayMatches),
 119                  BadFailOnConstraint.create(AllocArray.class, "allocArray()", 2,  allocArrayMatches),
 120                  GoodFailOnConstraint.create(AllocArray.class, "allocArray()", 3),
 121                  GoodFailOnConstraint.create(AllocArray.class, "allocArray()", 4),
 122                  BadFailOnConstraint.create(AllocArray.class, "allocArray()", 5,  allocArrayMatches)
 123         );
 124 
 125         runCheck(GoodRuleConstraint.create(RunTests.class, "good1()", 1),
 126                  GoodRuleConstraint.create(RunTests.class, "good1()", 2),
 127                  GoodRuleConstraint.create(RunTests.class, "good2()", 1),
 128                  GoodRuleConstraint.create(RunTests.class, "good2()", 2),
 129                  GoodRuleConstraint.create(RunTests.class, "good3(int)", 1),
 130                  BadCountsConstraint.create(RunTests.class, "bad1(int)", 1, 0),
 131                  BadFailOnConstraint.create(RunTests.class, "bad1(int)", 2, "Load")
 132         );
 133 
 134         runCheck(BadFailOnConstraint.create(Loads.class, "load()", 1, 1, "Load"),
 135                  BadFailOnConstraint.create(Loads.class, "load()", 1, 3, "LoadI"),
 136                  BadCountsConstraint.create(Loads.class, "load()", 1, 1, 0),
 137                  BadCountsConstraint.create(Loads.class, "load()", 1, 2, 1,"Load"),
 138                  GoodRuleConstraint.create(Loads.class, "load()", 2),
 139                  GoodFailOnConstraint.create(Loads.class, "load()", 3),
 140                  BadCountsConstraint.create(Loads.class, "load()", 3, 2, 2,"Store"),
 141                  BadFailOnConstraint.create(Loads.class, "load()", 4, 2, "Store"),
 142                  BadFailOnConstraint.create(Loads.class, "load()", 5, "Load"),
 143                  BadFailOnConstraint.create(Loads.class, "load()", 6, "Load"),
 144                  BadFailOnConstraint.create(Loads.class, "load()", 7, "Load"),
 145                  GoodRuleConstraint.create(Loads.class, "load()", 8),
 146                  GoodRuleConstraint.create(Loads.class, "load()", 9),
 147                  GoodRuleConstraint.create(Loads.class, "load()", 10),
 148                  BadFailOnConstraint.create(Loads.class, "loadKlass()", 1),
 149                  BadCountsConstraint.create(Loads.class, "loadKlass()", 2, 2,"Field")
 150                  );
 151 
 152         // Loops
 153         runCheck(BadFailOnConstraint.create(Loops.class, "loop()", 1, "Loop"),
 154                  GoodRuleConstraint.create(Loops.class, "loop()", 2),
 155                  GoodRuleConstraint.create(Loops.class, "loop()", 3),
 156                  GoodRuleConstraint.create(Loops.class, "countedLoop()", 1),
 157                  BadFailOnConstraint.create(Loops.class, "countedLoop()", 2, "CountedLoop"),
 158                  GoodRuleConstraint.create(Loops.class, "countedLoop()", 3),
 159                  BadFailOnConstraint.create(Loops.class, "loopAndCountedLoop()", 1, "Loop"),
 160                  BadFailOnConstraint.create(Loops.class, "loopAndCountedLoop()", 2, "CountedLoop"),
 161                  GoodRuleConstraint.create(Loops.class, "loopAndCountedLoop()", 3),
 162                  GoodRuleConstraint.create(Loops.class, "countedLoopMain()", 1),
 163                  BadFailOnConstraint.create(Loops.class, "countedLoopMain()", 2, "CountedLoop"),
 164                  BadFailOnConstraint.create(Loops.class, "countedLoopMain()", 3, "CountedLoop", "main"),
 165                  GoodRuleConstraint.create(Loops.class, "countedLoopUnrolled()", 1),
 166                  GoodRuleConstraint.create(Loops.class, "countedLoopUnrolled()", 2),
 167                  GoodRuleConstraint.create(Loops.class, "countedLoopUnrolled()", 3)
 168         );
 169 
 170         // Traps
 171         runCheck(GoodRuleConstraint.create(Traps.class, "noTraps()", 1),
 172                  BadFailOnConstraint.create(Traps.class, "noTraps()", 2, "Store", "iFld"),
 173                  GoodRuleConstraint.create(Traps.class, "noTraps()", 3),
 174                  BadFailOnConstraint.create(Traps.class, "predicateTrap()", 1, "CallStaticJava", "uncommon_trap"),
 175                  BadFailOnConstraint.create(Traps.class, "predicateTrap()", 2, "CallStaticJava", "uncommon_trap", "predicate"),
 176                  GoodRuleConstraint.create(Traps.class, "predicateTrap()", 3),
 177                  GoodRuleConstraint.create(Traps.class, "predicateTrap()", 4),
 178                  BadFailOnConstraint.create(Traps.class, "nullCheck()", 1, "CallStaticJava", "uncommon_trap"),
 179                  BadFailOnConstraint.create(Traps.class, "nullCheck()", 2, "CallStaticJava", "uncommon_trap", "null_check"),
 180                  BadFailOnConstraint.create(Traps.class, "nullCheck()", 3, "uncommon_trap", "unstable_if"),
 181                  GoodRuleConstraint.create(Traps.class, "nullCheck()", 4),
 182                  BadFailOnConstraint.create(Traps.class, "nullAssert()", 1, "CallStaticJava", "uncommon_trap"),
 183                  BadFailOnConstraint.create(Traps.class, "nullAssert()", 2, "CallStaticJava", "uncommon_trap", "null_assert"),
 184                  BadFailOnConstraint.create(Traps.class, "nullAssert()", 3, "CallStaticJava", "uncommon_trap", "null_check"),
 185                  GoodRuleConstraint.create(Traps.class, "nullAssert()", 4),
 186                  BadFailOnConstraint.create(Traps.class, "unstableIf(boolean)", 1, "CallStaticJava", "uncommon_trap"),
 187                  BadFailOnConstraint.create(Traps.class, "unstableIf(boolean)",  2, "CallStaticJava", "uncommon_trap", "unstable_if"),
 188                  GoodRuleConstraint.create(Traps.class, "unstableIf(boolean)", 3),
 189                  BadFailOnConstraint.create(Traps.class, "classCheck()", 1, "CallStaticJava", "uncommon_trap"),
 190                  BadFailOnConstraint.create(Traps.class, "classCheck()", 2, "CallStaticJava", "uncommon_trap", "class_check"),
 191                  BadFailOnConstraint.create(Traps.class, "classCheck()", 3, "CallStaticJava", "uncommon_trap", "null_check"),
 192                  GoodRuleConstraint.create(Traps.class, "classCheck()", 4),
 193                  BadFailOnConstraint.create(Traps.class, "rangeCheck()", 1, "CallStaticJava", "uncommon_trap"),
 194                  BadFailOnConstraint.create(Traps.class, "rangeCheck()", 2, "CallStaticJava", "uncommon_trap", "range_check"),
 195                  BadFailOnConstraint.create(Traps.class, "rangeCheck()", 3, "CallStaticJava", "uncommon_trap", "null_check"),
 196                  GoodRuleConstraint.create(Traps.class, "rangeCheck()", 4),
 197                  BadFailOnConstraint.create(Traps.class, "instrinsicOrTypeCheckedInlining()", 1, "CallStaticJava", "uncommon_trap"),
 198                  WhiteBox.getWhiteBox().isJVMCISupportedByGC() ?
 199                     BadFailOnConstraint.create(Traps.class, "instrinsicOrTypeCheckedInlining()", 2, "CallStaticJava", "uncommon_trap", "intrinsic_or_type_checked_inlining")
 200                     : GoodRuleConstraint.create(Traps.class, "instrinsicOrTypeCheckedInlining()", 2),
 201                  BadFailOnConstraint.create(Traps.class, "instrinsicOrTypeCheckedInlining()", 3, "CallStaticJava", "uncommon_trap", "intrinsic"),
 202                  BadFailOnConstraint.create(Traps.class, "instrinsicOrTypeCheckedInlining()", 4, "CallStaticJava", "uncommon_trap", "null_check"),
 203                  GoodRuleConstraint.create(Traps.class, "instrinsicOrTypeCheckedInlining()", 5)
 204         );
 205 
 206 
 207         runCheck(new String[] {"-XX:+BailoutToInterpreterForThrows"},
 208                  BadFailOnConstraint.create(UnhandledTrap.class, "unhandled()", 1, "CallStaticJava", "uncommon_trap"),
 209                  BadFailOnConstraint.create(UnhandledTrap.class, "unhandled()", 2, "CallStaticJava", "uncommon_trap", "unhandled"),
 210                  GoodRuleConstraint.create(UnhandledTrap.class, "unhandled()", 3)
 211         );
 212 
 213         runCheck(BadFailOnConstraint.create(ScopeObj.class, "scopeObject()", 1, "ScObj"));
 214         runCheck(BadFailOnConstraint.create(Membar.class, "membar()", 1, "MemBar"));
 215 
 216         String cmp;
 217         if (Platform.isPPC() || Platform.isX86()) {
 218             cmp = "CMP";
 219         } else if (Platform.isS390x()){
 220             cmp = "CLFI";
 221         } else {
 222             cmp = "cmp";
 223         }
 224         runCheck(BadFailOnConstraint.create(CheckCastArray.class, "array()", 1, cmp, "precise klass"),
 225                  BadFailOnConstraint.create(CheckCastArray.class, "array()", 2, 1,cmp, "precise klass", "MyClass"),
 226                  BadFailOnConstraint.create(CheckCastArray.class, "array()", 2, 2,cmp, "precise klass", "ir_framework/tests/MyClass"),
 227                  GoodFailOnConstraint.create(CheckCastArray.class, "array()", 3),
 228                  Platform.isS390x() ? // There is no checkcast_arraycopy stub for C2 on s390
 229                      GoodFailOnConstraint.create(CheckCastArray.class, "arrayCopy(java.lang.Object[],java.lang.Class)", 1)
 230                      : BadFailOnConstraint.create(CheckCastArray.class, "arrayCopy(java.lang.Object[],java.lang.Class)", 1, "checkcast_arraycopy")
 231         );
 232 
 233         try {
 234             runWithArgumentsFail(CompilationOutputOfFails.class);
 235             Utils.shouldHaveThrownException(baos.toString());
 236         } catch (IRViolationException e) {
 237             try {
 238                 StringBuilder failures = new StringBuilder();
 239                 System.out.flush();
 240                 String output = baos.toString();
 241                 baos.reset();
 242                 Pattern pattern = Pattern.compile(">>> Compilation.*both\\d.*\\RPrintIdeal:(?:(?!PrintOpto|>>> Compilation)[\\S\\s])+PrintOptoAssembly");
 243                 Matcher matcher = pattern.matcher(output);
 244                 long bothCount = matcher.results().count();
 245                 if (bothCount != 7L) {
 246                     failures.append("- Could not find all both() methods, expected 7 but found ").append(bothCount).append(System.lineSeparator());
 247                 }
 248                 pattern = Pattern.compile(">>> Compilation.*ideal\\d.*\\RPrintIdeal:(?:(?!>>> Compilation)[\\S\\s])+");
 249                 matcher = pattern.matcher(output);
 250                 int count = 0;
 251                 while (matcher.find()) {
 252                     String match = matcher.group();
 253                     if (match.contains("PrintOptoAssembly")) {
 254                         failures.append("Cannot contain opto assembly: ").append(System.lineSeparator()).append(match);
 255                     }
 256                     count++;
 257                 }
 258                 if (count != 7) {
 259                     failures.append("- Could not find all ideal() methods, expected 7 but found ").append(count).append(System.lineSeparator());
 260                 }
 261                 pattern = Pattern.compile(">>> Compilation.*opto\\d.*\\RPrintOptoAssembly:(?:(?!>>> Compilation)[\\S\\s])+");
 262                 matcher = pattern.matcher(output);
 263                 count = 0;
 264                 while (matcher.find()) {
 265                     String match = matcher.group();
 266                     if (match.contains("PrintIdeal")) {
 267                         failures.append("Cannot contain print assembly: ").append(System.lineSeparator()).append(match);
 268                     }
 269                     count++;
 270                 }
 271                 if (count != 7) {
 272                     failures.append("- Could not find all opto() methods, expected 7 but found ").append(count).append(System.lineSeparator());
 273                 }
 274                 if (!failures.isEmpty()) {
 275                     addException(new RuntimeException(failures.toString()));
 276                 }
 277             } catch (Exception e1) {
 278                 addException(e1);
 279             }
 280         } catch (Exception e) {
 281             addException(e);
 282         }
 283 
 284         runWithArguments(FlagComparisons.class, "-XX:TLABRefillWasteFraction=50");
 285         System.out.flush();
 286         String output = baos.toString();
 287         findIrIds(output, "testMatchAllIf50", 1, 22);
 288         findIrIds(output, "testMatchNoneIf50", -1, -1);
 289 
 290         runWithArguments(FlagComparisons.class, "-XX:TLABRefillWasteFraction=49");
 291         System.out.flush();
 292         output = baos.toString();
 293         findIrIds(output, "testMatchAllIf50", 5, 7, 14, 19);
 294         findIrIds(output, "testMatchNoneIf50", 1, 4, 9, 11, 18, 23);
 295 
 296         runWithArguments(FlagComparisons.class, "-XX:TLABRefillWasteFraction=51");
 297         System.out.flush();
 298         output = baos.toString();
 299         findIrIds(output, "testMatchAllIf50", 8, 13, 20, 22);
 300         findIrIds(output, "testMatchNoneIf50", 5, 8, 12, 17, 21, 23);
 301         System.setOut(oldOut);
 302         System.setErr(oldErr);
 303 
 304         if (!exceptions.isEmpty()) {
 305             System.err.println("TestIRMatching failed with " + exceptions.size() + " exception(s):");
 306             int i = 1;
 307             System.err.println("************************");
 308             for (Map.Entry<Exception, String> entry : exceptions.entrySet()) {
 309                 System.err.println("***** Exception " + String.format("%02d", i++) +" *****");
 310                 System.err.println("************************");
 311 
 312                 Exception e = entry.getKey();
 313                 e.printStackTrace(System.err);
 314                 System.err.println();
 315                 System.err.println("===== OUTPUT ======");
 316                 System.err.println(entry.getValue());
 317                 System.err.println("MESSAGE: " + e.getMessage());
 318                 System.err.println("************************");
 319             }
 320             i = 1;
 321             System.err.println("====================================");
 322             System.err.println("********************");
 323             System.err.println("***** OVERVIEW *****");
 324             System.err.println("********************");
 325             for (Map.Entry<Exception, String> entry : exceptions.entrySet()) {
 326                 Exception e = entry.getKey();
 327                 System.err.print((i++) + ") ");
 328                 entry.getKey().printStackTrace(System.err);
 329                 System.err.println("********************");
 330             }
 331             throw new RuntimeException("TestIRMatching failed with " + exceptions.size() + " exception(s) - check stderr and stdout");
 332         }
 333     }
 334 
 335     private static void runFramework(TestFramework framework) {
 336         baos.reset();
 337         baosErr.reset();
 338         framework.start();
 339     }
 340 
 341     private static void runWithArguments(Class<?> clazz, String... args) {
 342         try {
 343             runFramework(new TestFramework(clazz).addFlags(args));
 344         } catch (Exception e) {
 345             addException(e);
 346         }
 347     }
 348 
 349     private static void runWithArgumentsFail(Class<?> clazz, String... args) {
 350         runFramework(new TestFramework(clazz).addFlags(args));
 351     }
 352 
 353     private static void runCheck(String[] args , Constraint... constraints) {
 354         try {
 355             TestFramework framework = new TestFramework(constraints[0].getKlass()); // All constraints have the same class.
 356             if (args != null) {
 357                 framework.addFlags(args);
 358             }
 359             runFramework(framework);
 360             Utils.shouldHaveThrownException(baos.toString());
 361         } catch (IRViolationException e) {
 362             checkConstraints(e, constraints);
 363         } catch (Exception e) {
 364             addException(e);
 365         }
 366     }
 367 
 368     private static void runCheck(Constraint... constraints) {
 369         runCheck(null, constraints);
 370     }
 371 
 372     private static void checkConstraints(IRViolationException e, Constraint[] constraints) {
 373         String message = e.getExceptionInfo();
 374         try {
 375             for (Constraint constraint : constraints) {
 376                 constraint.checkConstraint(e);
 377             }
 378         } catch (Exception e1) {
 379             System.out.println(e.getCompilations());
 380             System.out.println(message);
 381             addException(e1);
 382         }
 383     }
 384 
 385     public static void findIrIds(String output, String method, int... numbers) {
 386         StringBuilder builder = new StringBuilder();
 387         builder.append(method);
 388         for (int i = 0; i < numbers.length; i+=2) {
 389             int start = numbers[i];
 390             int endIncluded = numbers[i + 1];
 391             for (int j = start; j <= endIncluded; j++) {
 392                 builder.append(",");
 393                 builder.append(j);
 394             }
 395         }
 396         if (!output.contains(builder.toString())) {
 397             addException(new RuntimeException("Could not find encoding: \"" + builder + System.lineSeparator()));
 398         }
 399     }
 400 }
 401 
 402 class AndOr1 {
 403     @Test
 404     @Arguments(Argument.DEFAULT)
 405     @IR(applyIfAnd = {"UsePerfData", "true", "TLABRefillWasteFraction", "50", "UseTLAB", "true"}, failOn = {IRNode.CALL})
 406     public void test1(int i) {
 407         dontInline();
 408     }
 409 
 410     @Test
 411     @IR(applyIfOr = {"UsePerfData", "false", "TLABRefillWasteFraction", "51", "UseTLAB", "false"}, failOn = {IRNode.CALL})
 412     public void test2() {
 413         dontInline();
 414     }
 415 
 416     @DontInline
 417     private void dontInline() {
 418     }
 419 }
 420 
 421 class MultipleFailOnGood {
 422     private int iFld;
 423     private MyClassSub myClassSub = new MyClassSub();
 424 
 425     @Test
 426     @IR(applyIf = {"TLABRefillWasteFraction", "50"}, failOn = {IRNode.STORE, IRNode.CALL})
 427     @IR(failOn = {IRNode.STORE, IRNode.CALL})
 428     @IR(applyIfOr = {"TLABRefillWasteFraction", "99", "TLABRefillWasteFraction", "100"}, failOn = {IRNode.LOOP, IRNode.CALL}) // Not applied
 429     public void good1() {
 430         forceInline();
 431     }
 432 
 433     @Test
 434     @IR(failOn = {IRNode.STORE, IRNode.CALL})
 435     @IR(applyIfNot = {"TLABRefillWasteFraction", "20"}, failOn = {IRNode.ALLOC})
 436     @IR(applyIfNot = {"TLABRefillWasteFraction", "< 100"}, failOn = {IRNode.ALLOC_OF, "Test"})
 437     public void good2() {
 438         forceInline();
 439     }
 440 
 441     @Test
 442     @IR(failOn = {IRNode.STORE_OF_CLASS, "Test", IRNode.CALL})
 443     @IR(applyIfNot = {"TLABRefillWasteFraction", "20"}, failOn = {IRNode.ALLOC})
 444     @IR(applyIfNot = {"TLABRefillWasteFraction", "< 100"}, failOn = {IRNode.ALLOC_OF, "Test"})
 445     public void good3() {
 446         forceInline();
 447     }
 448 
 449     @Test
 450     @IR(failOn = {IRNode.CALL, IRNode.STORE_OF_CLASS, "UnknownClass"})
 451     public void good4() {
 452         iFld = 42;
 453     }
 454 
 455     @Test
 456     @IR(failOn = {IRNode.STORE_OF_FIELD, "xFld", IRNode.CALL})
 457     public void good5() {
 458         iFld = 42;
 459     }
 460 
 461     @Test
 462     @IR(failOn = {IRNode.STORE_OF_CLASS, "MyClass"}) // Needs exact match to fail
 463     public void good6() {
 464         myClassSub.iFld = 42;
 465     }
 466 
 467     @Test
 468     @IR(failOn = {IRNode.STORE_OF_CLASS, "MyClassSub"}) // Static write is with Class and not MySubClass
 469     public void good7() {
 470         MyClassSub.iFldStatic = 42;
 471     }
 472 
 473     @ForceInline
 474     private void forceInline() {}
 475 }
 476 
 477 class MultipleFailOnBad {
 478     private int iFld;
 479     private int myInt;
 480     private MyClassEmpty myClass;
 481 
 482     @Test
 483     @IR(failOn = {IRNode.STORE, IRNode.CALL, IRNode.STORE_I, IRNode.LOOP})
 484     public void fail1() {
 485         iFld = 42;
 486     }
 487 
 488     @Test
 489     @IR(failOn = {IRNode.STORE, IRNode.CALL})
 490     public void fail2() {
 491         dontInline();
 492     }
 493 
 494     @Test
 495     @IR(failOn = {IRNode.CALL, IRNode.STORE_OF_CLASS, "MultipleFailOnBad", IRNode.ALLOC})
 496     public void fail3() {
 497         iFld = 42;
 498     }
 499 
 500     @Test
 501     @IR(failOn = {IRNode.STORE_OF_CLASS, "ir_framework/tests/MultipleFailOnBad", IRNode.CALL, IRNode.ALLOC})
 502     public void fail4() {
 503         iFld = 42;
 504     }
 505 
 506     @Test
 507     @IR(failOn = {IRNode.STORE_OF_FIELD, "iFld", IRNode.CALL, IRNode.ALLOC})
 508     public void fail5() {
 509         iFld = 42;
 510     }
 511 
 512     @Test
 513     @IR(failOn = {IRNode.STORE_OF_CLASS, "MyClassEmpty", IRNode.ALLOC, IRNode.CALL})
 514     public void fail6() {
 515         myClass = new MyClassEmpty();
 516     }
 517 
 518     @Test
 519     @IR(failOn = {IRNode.STORE_OF_CLASS, "UnknownClass", IRNode.ALLOC_OF, "MyClassEmpty"})
 520     public void fail7() {
 521         myClass = new MyClassEmpty();
 522     }
 523 
 524     @Test
 525     @IR(failOn = {IRNode.STORE_OF_CLASS, "UnknownClass", IRNode.ALLOC_OF, "ir_framework/tests/MyClassEmptySub"})
 526     public void fail8() {
 527         myClass = new MyClassEmptySub();
 528     }
 529 
 530     @Test
 531     @IR(failOn = {IRNode.STORE, IRNode.CALL})
 532     public void fail9() {
 533         iFld = 42;
 534         dontInline();
 535     }
 536 
 537     @Test
 538     @IR(failOn = {IRNode.STORE_OF_FIELD, "iFld", IRNode.CALL, IRNode.ALLOC})
 539     public void fail10() {
 540         myInt = 34;
 541         iFld = 42;
 542     }
 543 
 544     @DontInline
 545     private void dontInline() {}
 546 }
 547 
 548 // Called with -XX:TLABRefillWasteFraction=X.
 549 class FlagComparisons {
 550     // Applies all IR rules if TLABRefillWasteFraction=50
 551     @Test
 552     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", "50"}) // Index 1
 553     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", "=50"})
 554     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", "= 50"})
 555     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", " =   50"})
 556     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", "<=50"}) // Index 5
 557     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", "<= 50"})
 558     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", " <=  50"})
 559     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", ">=50"}) // Index 8
 560     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", ">= 50"})
 561     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", " >=  50"})
 562     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", ">49"})
 563     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", "> 49"})
 564     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", " >  49"})
 565     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", "<51"}) // Index 14
 566     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", "< 51"})
 567     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", " <  51"})
 568     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", "!=51"})
 569     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", "!= 51"})
 570     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", " !=  51"})
 571     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", "!=49"})
 572     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", "!= 49"})
 573     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", " !=  49"}) // Index 22
 574     public void testMatchAllIf50() {}
 575 
 576     // Applies no IR rules if TLABRefillWasteFraction=50
 577     @Test
 578     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", "49"}) // Index 1
 579     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", "=49"})
 580     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", "= 49"})
 581     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", " =  49"})
 582     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", "51"}) // Index 5
 583     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", "=51"})
 584     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", "= 51"})
 585     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", " =  51"})
 586     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", "<=49"}) // Index 9
 587     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", "<= 49"})
 588     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", " <=  49"})
 589     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", ">=51"}) // Index 12
 590     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", ">= 51"})
 591     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", " >=  51"})
 592     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", ">50"})
 593     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", "> 50"})
 594     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", " >  50"})
 595     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", "<50"}) // Index 18
 596     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", "< 50"})
 597     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", " <  50"})
 598     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", "!=50"})
 599     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", "!= 50"})
 600     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", " !=  50"}) // Index 23
 601     public void testMatchNoneIf50() {}
 602 }
 603 
 604 class CountComparisons {
 605     int iFld;
 606 
 607     @Test
 608     @IR(counts = {IRNode.STORE, "= 1",
 609                   IRNode.STORE, "=1",
 610                   IRNode.STORE, " = 1",
 611                   IRNode.STORE, "  =  1",
 612                   IRNode.STORE, ">= 1",
 613                   IRNode.STORE, ">=1",
 614                   IRNode.STORE, " >= 1",
 615                   IRNode.STORE, "  >=  1",
 616                   IRNode.STORE, "<= 1",
 617                   IRNode.STORE, "<=1",
 618                   IRNode.STORE, " <= 1",
 619                   IRNode.STORE, "  <=  1",
 620                   IRNode.STORE, "!= 0",
 621                   IRNode.STORE, "!=0",
 622                   IRNode.STORE, " != 0",
 623                   IRNode.STORE, "  !=  0",
 624                   IRNode.STORE, "> 0",
 625                   IRNode.STORE, ">0",
 626                   IRNode.STORE, " > 0",
 627                   IRNode.STORE, "  >  0",
 628                   IRNode.STORE, "< 2",
 629                   IRNode.STORE, "<2",
 630                   IRNode.STORE, " < 2",
 631                   IRNode.STORE, "  <  2",
 632     })
 633     public void countComparison() {
 634         iFld = 3;
 635     }
 636 }
 637 
 638 class GoodCount {
 639     boolean flag;
 640     char cFld;
 641     byte bFld;
 642     short sFld;
 643     int iFld;
 644     long lFld;
 645     float fFld;
 646     double dFld;
 647     long x;
 648 
 649     long result;
 650     MyClass myClass = new MyClass();
 651     MyClassEmpty myClassEmpty = new MyClassEmpty();
 652     MyClass myClassSubPoly = new MyClassSub();
 653     MyClassSub myClassSub = new MyClassSub();
 654 
 655     @Test
 656     @IR(counts = {IRNode.STORE, "1", IRNode.STORE_I, "1"},
 657         failOn = {IRNode.STORE_B, IRNode.STORE_C, IRNode.STORE_D,
 658                   IRNode.STORE_F, IRNode.STORE_L})
 659     public void good1() {
 660         iFld = 3;
 661     }
 662 
 663     @Test
 664     @IR(counts = {IRNode.STORE, "8",
 665                   IRNode.STORE_B, "2", // bFld + flag
 666                   IRNode.STORE_C, "2", // cFld + sFld
 667                   IRNode.STORE_I, "1",
 668                   IRNode.STORE_L, "1",
 669                   IRNode.STORE_F, "1",
 670                   IRNode.STORE_D, "1"})
 671     public void good2() {
 672         flag = true;
 673         cFld = 'a';
 674         bFld = 1;
 675         sFld = 2;
 676         iFld = 3;
 677         lFld = 4L;
 678         fFld = 5.0f;
 679         dFld = 6.0;
 680     }
 681 
 682     @Test
 683     @IR(counts = {IRNode.STORE, "8", IRNode.STORE_OF_CLASS, "GoodCount", "8",
 684                   IRNode.STORE_B, "2", IRNode.STORE_B_OF_CLASS, "GoodCount", "2",
 685                   IRNode.STORE_C, "2", IRNode.STORE_C_OF_CLASS, "GoodCount", "2",
 686                   IRNode.STORE_I, "1", IRNode.STORE_I_OF_CLASS, "GoodCount", "1",
 687                   IRNode.STORE_L, "1", IRNode.STORE_L_OF_CLASS, "GoodCount", "1",
 688                   IRNode.STORE_F, "1", IRNode.STORE_F_OF_CLASS, "GoodCount", "1",
 689                   IRNode.STORE_D, "1", IRNode.STORE_D_OF_CLASS, "GoodCount", "1"})
 690     public void good3() {
 691         flag = true;
 692         cFld = 'a';
 693         bFld = 1;
 694         sFld = 2;
 695         iFld = 3;
 696         lFld = 4L;
 697         fFld = 5.0f;
 698         dFld = 6.0;
 699     }
 700 
 701     @Test
 702     @IR(counts = {IRNode.STORE, "8", IRNode.STORE_OF_CLASS, "GoodCount", "8",
 703                   IRNode.STORE_B, "2", IRNode.STORE_B_OF_CLASS, "GoodCount", "2",
 704                   IRNode.STORE_C, "2", IRNode.STORE_C_OF_CLASS, "GoodCount", "2",
 705                   IRNode.STORE_I, "1", IRNode.STORE_I_OF_CLASS, "GoodCount", "1",
 706                   IRNode.STORE_L, "1", IRNode.STORE_L_OF_CLASS, "GoodCount", "1",
 707                   IRNode.STORE_F, "1", IRNode.STORE_F_OF_CLASS, "GoodCount", "1",
 708                   IRNode.STORE_D, "1", IRNode.STORE_D_OF_CLASS, "GoodCount", "1",
 709                   IRNode.STORE_OF_FIELD, "lFld", "1"})
 710     public void good4() {
 711         flag = true;
 712         cFld = 'a';
 713         bFld = 1;
 714         sFld = 2;
 715         iFld = 3;
 716         lFld = 4L;
 717         fFld = 5.0f;
 718         dFld = 6.0;
 719     }
 720 
 721     @Test
 722     @IR(counts = {IRNode.STORE, "2", IRNode.STORE_I, "1", IRNode.STORE_L, "1",
 723                   IRNode.STORE_OF_CLASS, "GoodCount", "1", IRNode.STORE_L_OF_CLASS, "GoodCount", "1",
 724                   IRNode.STORE_OF_CLASS, "ir_framework/tests/MyClass", "1",
 725                   IRNode.STORE_I_OF_CLASS, "ir_framework/tests/MyClass", "1",
 726                   IRNode.STORE_OF_CLASS, "ir_framework/tests/GoodCount", "1",
 727                   IRNode.STORE_L_OF_CLASS, "ir_framework/tests/GoodCount", "1",
 728                   IRNode.STORE_OF_FIELD, "x", "2"})
 729     public void good5() {
 730         x = 3; // long
 731         myClass.x = 4; // int
 732     }
 733 
 734     @Test
 735     @IR(counts = {IRNode.STORE_OF_FIELD, "myClassEmpty", "1", IRNode.STORE_OF_CLASS, "GoodCount", "1",
 736                   IRNode.STORE_OF_CLASS, "/GoodCount", "1", IRNode.STORE_OF_CLASS, "MyClassEmpty", "0"},
 737         failOn = {IRNode.STORE_OF_CLASS, "MyClassEmpty"})
 738     public void good6() {
 739         myClassEmpty = new MyClassEmpty();
 740     }
 741 
 742     @Test
 743     @IR(counts = {IRNode.STORE_OF_FIELD, "iFld", "3", IRNode.STORE_OF_CLASS, "GoodCount", "0",
 744                   IRNode.STORE_OF_CLASS, "MyClass", "2", IRNode.STORE_OF_CLASS, "MyClassSub", "1",
 745                   IRNode.STORE, "3"},
 746         failOn = {IRNode.STORE_OF_CLASS, "GoodCount"})
 747     public void good7() {
 748         myClass.iFld = 1;
 749         myClassSubPoly.iFld = 2;
 750         myClassSub.iFld = 3;
 751     }
 752 
 753     @Test
 754     @IR(counts = {IRNode.LOAD, "1", IRNode.STORE, "1"})
 755     public void good8() {
 756         result = iFld;
 757     }
 758 
 759 
 760     @Test
 761     @IR(counts = {IRNode.LOAD, "4", IRNode.STORE, "1", IRNode.LOAD_OF_FIELD, "iFld", "2", IRNode.LOAD_OF_FIELD, "iFld2", "0",
 762                   IRNode.LOAD_OF_FIELD, "lFldStatic", "1", IRNode.LOAD_OF_CLASS, "GoodCount", "2", IRNode.LOAD_OF_CLASS, "MyClass", "1",
 763                   IRNode.STORE_OF_CLASS, "GoodCount", "1", IRNode.STORE_OF_FIELD, "result", "1",
 764                   IRNode.LOAD_OF_FIELD, "myClass", "1"})
 765     public void good9() {
 766         result = iFld + MyClass.lFldStatic + myClass.iFld; // 1 + 1 + 2 loads (myClass is LoadN of GoodCount and myClass.iFld a LoadI of MyClass)
 767     }
 768 
 769     @Test
 770     @IR(counts = {IRNode.LOAD, "8",
 771                   IRNode.LOAD_B, "1",
 772                   IRNode.LOAD_UB, "1",
 773                   IRNode.LOAD_S, "1",
 774                   IRNode.LOAD_US, "1",
 775                   IRNode.LOAD_I, "1",
 776                   IRNode.LOAD_L, "1",
 777                   IRNode.LOAD_F, "1",
 778                   IRNode.LOAD_D, "1"})
 779     public void good10() {
 780         bFld++;
 781         cFld++;
 782         sFld++;
 783         iFld++;
 784         lFld++;
 785         fFld++;
 786         dFld++;
 787         flag = !flag;
 788     }
 789 
 790     @Test
 791     @IR(counts = {IRNode.LOAD, "8", IRNode.LOAD_OF_CLASS, "GoodCount", "8",
 792                   IRNode.LOAD_B, "1", IRNode.LOAD_B_OF_CLASS, "GoodCount", "1",
 793                   IRNode.LOAD_UB, "1", IRNode.LOAD_UB_OF_CLASS, "GoodCount", "1",
 794                   IRNode.LOAD_S, "1", IRNode.LOAD_S_OF_CLASS, "GoodCount", "1",
 795                   IRNode.LOAD_US, "1", IRNode.LOAD_US_OF_CLASS, "GoodCount", "1",
 796                   IRNode.LOAD_I, "1", IRNode.LOAD_I_OF_CLASS, "GoodCount", "1",
 797                   IRNode.LOAD_L, "1", IRNode.LOAD_L_OF_CLASS, "GoodCount", "1",
 798                   IRNode.LOAD_F, "1", IRNode.LOAD_F_OF_CLASS, "GoodCount", "1",
 799                   IRNode.LOAD_D, "1", IRNode.LOAD_D_OF_CLASS, "GoodCount", "1"})
 800     public void good11() {
 801         bFld++;
 802         cFld++;
 803         sFld++;
 804         iFld++;
 805         lFld++;
 806         fFld++;
 807         dFld++;
 808         flag = !flag;
 809     }
 810 }
 811 
 812 class BadCount {
 813     int iFld;
 814     int result;
 815     @Test
 816     @IR(counts = {IRNode.LOAD, "!= 1"}) // fail
 817     @IR(counts = {IRNode.STORE, "> 0"})
 818     public void bad1() {
 819         result = iFld;
 820     }
 821 
 822     @Test
 823     @IR(counts = {IRNode.LOAD, "1"}) // fail
 824     @IR(counts = {IRNode.STORE, "< 1"})
 825     public void bad2() {
 826         result = iFld;
 827     }
 828 
 829 
 830     @Test
 831     @IR(counts = {IRNode.LOAD, "0"}) // fail
 832     @IR(counts = {IRNode.STORE, " <= 0"}) // fail
 833     public void bad3() {
 834         result = iFld;
 835     }
 836 }
 837 
 838 
 839 class RunTests {
 840     public int iFld;
 841 
 842     @Test
 843     @IR(counts = {IRNode.STORE, "1"})
 844     @IR(failOn = IRNode.LOAD)
 845     public void good1() {
 846         iFld = 42;
 847     }
 848 
 849     @Test
 850     @IR(counts = {IRNode.LOAD, "1"})
 851     @IR(failOn = IRNode.STORE)
 852     public int good2() {
 853         return iFld;
 854     }
 855 
 856     @Run(test = {"good1", "good2"})
 857     public void runGood1() {
 858         good1();
 859         good2();
 860     }
 861 
 862 
 863     @Test
 864     @IR(counts = {IRNode.STORE, "1"})
 865     @IR(failOn = IRNode.LOAD)
 866     public void good3(int x) {
 867         iFld = x;
 868     }
 869 
 870     @Test
 871     @IR(counts = {IRNode.STORE, "1"})
 872     @IR(failOn = IRNode.LOAD)
 873     public int bad1(int x) {
 874         return iFld + x;
 875     }
 876 
 877     @Run(test = {"bad1", "good3"})
 878     public void run() {
 879         bad1(2);
 880         good3(4);
 881     }
 882 }
 883 
 884 class Calls {
 885 
 886     @Test
 887     @IR(counts = {IRNode.CALL, "1"})
 888     @IR(failOn = {IRNode.CALL_OF_METHOD, "dontInline",  // Fails
 889                   IRNode.STATIC_CALL_OF_METHOD, "dontInline"}) // Fails
 890     @IR(failOn = {IRNode.CALL_OF_METHOD, "forceInline",
 891                   IRNode.STATIC_CALL_OF_METHOD, "forceInline",
 892                   IRNode.CALL_OF_METHOD, "dontInlines",
 893                   IRNode.STATIC_CALL_OF_METHOD, "dontInlines",
 894                   IRNode.CALL_OF_METHOD, "dont",
 895                   IRNode.STATIC_CALL_OF_METHOD, "dont"})
 896     public void calls() {
 897         dontInline();
 898         forceInline();
 899     }
 900 
 901     @DontInline
 902     public void dontInline() {}
 903 
 904     @ForceInline
 905     public void forceInline() {}
 906 }
 907 
 908 class AllocArray {
 909     MyClass[] myClassArray;
 910 
 911     @Test
 912     @IR(failOn = {IRNode.ALLOC_ARRAY})
 913     @IR(failOn = {IRNode.ALLOC_ARRAY_OF, "MyClass"})
 914     @IR(failOn = {IRNode.ALLOC_ARRAY_OF, "MyClasss"}) // Does not fail
 915     @IR(failOn = {IRNode.ALLOC_ARRAY_OF, "ir_framework/tests/MySubClass"}) // Does not fail
 916     @IR(failOn = {IRNode.ALLOC_ARRAY_OF, "ir_framework/tests/MyClass"})
 917     public void allocArray() {
 918         myClassArray = new MyClass[2];
 919     }
 920 }
 921 
 922 class Loads {
 923     int iFld = 34;
 924     int result = 0;
 925     Object myClass = new MyClass();
 926 
 927     @Test
 928     @IR(failOn = {IRNode.LOAD, IRNode.LOOP, IRNode.LOAD_I}, counts = {IRNode.LOOP, "2", IRNode.LOAD, "2", IRNode.STORE, "2"})
 929     @IR(failOn = {IRNode.LOOP, IRNode.LOOP}, counts = {IRNode.LOOP, "0", IRNode.LOAD, "1"}) // Does not fail
 930     @IR(failOn = {IRNode.LOOP, IRNode.LOOP}, counts = {IRNode.LOOP, "0", IRNode.STORE, "1"})
 931     @IR(failOn = {IRNode.LOOP, IRNode.STORE}, counts = {IRNode.LOOP, "0", IRNode.LOAD, "1"})
 932     @IR(failOn = {IRNode.LOAD_OF_CLASS, "ir_framework/tests/Loads"})
 933     @IR(failOn = {IRNode.LOAD_OF_CLASS, "Loads"})
 934     @IR(failOn = {IRNode.LOAD_OF_FIELD, "iFld"})
 935     @IR(failOn = {IRNode.LOAD_OF_FIELD, "iFld2", IRNode.LOAD_OF_CLASS, "Load"}) // Does not fail
 936     @IR(failOn = {IRNode.LOAD_KLASS}) // Does not fail
 937     @IR(counts = {IRNode.FIELD_ACCESS, "3"}) // Does not fail
 938     public void load() {
 939         result = iFld;
 940         iFld = 3;
 941     }
 942 
 943     @Test
 944     @IR(failOn = {IRNode.LOAD_KLASS})
 945     @IR(counts = {IRNode.FIELD_ACCESS, "3"})
 946     public void loadKlass() {
 947         if (myClass instanceof MyClass) {
 948             result = 3;
 949         }
 950     }
 951 }
 952 
 953 class Loops {
 954     int limit = 1024;
 955     int[] iArr = new int[100];
 956 
 957     @DontInline
 958     public void dontInline() {}
 959 
 960     @Test
 961     @IR(failOn = IRNode.LOOP) // fails
 962     @IR(failOn = IRNode.COUNTEDLOOP)
 963     @IR(failOn = IRNode.COUNTEDLOOP_MAIN)
 964     public void loop() {
 965         for (int i = 0; i < limit; i++) {
 966             dontInline();
 967         }
 968     }
 969 
 970     @Test
 971     @IR(failOn = IRNode.LOOP)
 972     @IR(failOn = IRNode.COUNTEDLOOP) // fails
 973     @IR(failOn = IRNode.COUNTEDLOOP_MAIN)
 974     public void countedLoop() {
 975         for (int i = 0; i < 2000; i++) {
 976             dontInline();
 977         }
 978     }
 979 
 980     @Test
 981     @IR(failOn = IRNode.LOOP) // fails
 982     @IR(failOn = IRNode.COUNTEDLOOP) // fails
 983     @IR(failOn = IRNode.COUNTEDLOOP_MAIN)
 984     public void loopAndCountedLoop() {
 985         for (int i = 0; i < 2000; i++) {
 986             for (int j = 0; j < limit; j++) {
 987                 dontInline();
 988             }
 989         }
 990     }
 991 
 992     @Test
 993     @IR(failOn = IRNode.LOOP)
 994     @IR(failOn = IRNode.COUNTEDLOOP) // fails
 995     @IR(failOn = IRNode.COUNTEDLOOP_MAIN) // fails
 996     public void countedLoopMain() {
 997         // Cannot unroll completely -> create pre/main/post
 998         for (int i = 0; i < 100; i++) {
 999             iArr[i] = i;
1000         }
1001     }
1002 
1003     @Test
1004     @IR(failOn = IRNode.LOOP)
1005     @IR(failOn = IRNode.COUNTEDLOOP)
1006     @IR(failOn = IRNode.COUNTEDLOOP_MAIN)
1007     public void countedLoopUnrolled() {
1008         // Completely unrolled -> no pre/main/post
1009         for (int i = 0; i < 8; i++) {
1010             iArr[i] = i;
1011         }
1012     }
1013 }
1014 
1015 class Traps {
1016     int number42 = 42;
1017     int iFld = 10;
1018     int[] iArr = new int[2];
1019     MyClass myClass = new MyClass();
1020     MyClassSub myClassSub = new MyClassSub();
1021     NotLoaded notLoaded = new NotLoaded();
1022     Object[] oArr = new Object[10];
1023     MyClass[] mArr = new MyClass[10];
1024 
1025     @Test
1026     @IR(failOn = IRNode.TRAP)
1027     @IR(failOn = {IRNode.STORE_OF_FIELD, "iFld"}) // fails
1028     @IR(failOn = {IRNode.PREDICATE_TRAP,
1029                   IRNode.UNSTABLE_IF_TRAP,
1030                   IRNode.NULL_CHECK_TRAP,
1031                   IRNode.NULL_ASSERT_TRAP,
1032                   IRNode.RANGE_CHECK_TRAP,
1033                   IRNode.CLASS_CHECK_TRAP,
1034                   IRNode.INTRINSIC_TRAP,
1035                   IRNode.INTRINSIC_OR_TYPE_CHECKED_INLINING_TRAP,
1036                   IRNode.UNHANDLED_TRAP})
1037     public void noTraps() {
1038         for (int i = 0; i < 100; i++) {
1039             if (i < 42) {
1040                 // Reached, no uncommon trap
1041                 iFld = i;
1042             }
1043         }
1044     }
1045 
1046     @Test
1047     @IR(failOn = IRNode.TRAP) // fails
1048     @IR(failOn = IRNode.PREDICATE_TRAP) // fails
1049     @IR(failOn = {IRNode.STORE_OF_FIELD, "iFld"})
1050     @IR(failOn = {IRNode.UNSTABLE_IF_TRAP,
1051                   IRNode.NULL_CHECK_TRAP,
1052                   IRNode.NULL_ASSERT_TRAP,
1053                   IRNode.RANGE_CHECK_TRAP,
1054                   IRNode.CLASS_CHECK_TRAP,
1055                   IRNode.INTRINSIC_TRAP,
1056                   IRNode.INTRINSIC_OR_TYPE_CHECKED_INLINING_TRAP,
1057                   IRNode.UNHANDLED_TRAP})
1058     public void predicateTrap() {
1059         for (int i = 0; i < 100; i++) {
1060             if (number42 != 42) {
1061                 // Never reached
1062                 iFld = i;
1063             }
1064         }
1065     }
1066 
1067     @Test
1068     @IR(failOn = IRNode.TRAP) // fails
1069     @IR(failOn = IRNode.NULL_CHECK_TRAP) // fails
1070     @IR(failOn = IRNode.UNSTABLE_IF_TRAP) // fails
1071     @IR(failOn = {IRNode.PREDICATE_TRAP,
1072                   IRNode.NULL_ASSERT_TRAP,
1073                   IRNode.RANGE_CHECK_TRAP,
1074                   IRNode.CLASS_CHECK_TRAP,
1075                   IRNode.INTRINSIC_TRAP,
1076                   IRNode.INTRINSIC_OR_TYPE_CHECKED_INLINING_TRAP,
1077                   IRNode.UNHANDLED_TRAP})
1078     public void nullCheck() {
1079         if (myClass instanceof MyClassSub) {
1080             iFld = 4;
1081         }
1082     }
1083 
1084     @Test
1085     @IR(failOn = IRNode.TRAP) // fails
1086     @IR(failOn = IRNode.NULL_ASSERT_TRAP) // fails
1087     @IR(failOn = IRNode.NULL_CHECK_TRAP) // fails
1088     @IR(failOn = {IRNode.PREDICATE_TRAP,
1089                   IRNode.UNSTABLE_IF_TRAP,
1090                   IRNode.RANGE_CHECK_TRAP,
1091                   IRNode.CLASS_CHECK_TRAP,
1092                   IRNode.INTRINSIC_TRAP,
1093                   IRNode.INTRINSIC_OR_TYPE_CHECKED_INLINING_TRAP,
1094                   IRNode.UNHANDLED_TRAP})
1095     public Object nullAssert() {
1096         return notLoaded.notLoadedFld;
1097     }
1098 
1099     @Test
1100     @Arguments(Argument.TRUE)
1101     @IR(failOn = IRNode.TRAP) // fails
1102     @IR(failOn = IRNode.UNSTABLE_IF_TRAP) // fails
1103     @IR(failOn = {IRNode.PREDICATE_TRAP,
1104                   IRNode.NULL_CHECK_TRAP,
1105                   IRNode.NULL_ASSERT_TRAP,
1106                   IRNode.RANGE_CHECK_TRAP,
1107                   IRNode.CLASS_CHECK_TRAP,
1108                   IRNode.INTRINSIC_TRAP,
1109                   IRNode.INTRINSIC_OR_TYPE_CHECKED_INLINING_TRAP,
1110                   IRNode.UNHANDLED_TRAP})
1111     public void unstableIf(boolean flag) {
1112         if (flag) {
1113             iFld++;
1114         } else {
1115             iFld--;
1116         }
1117     }
1118 
1119     @Test
1120     @IR(failOn = IRNode.TRAP) // fails
1121     @IR(failOn = IRNode.CLASS_CHECK_TRAP) // fails
1122     @IR(failOn = IRNode.NULL_CHECK_TRAP) // fails
1123     @IR(failOn = {IRNode.PREDICATE_TRAP,
1124                   IRNode.UNSTABLE_IF_TRAP,
1125                   IRNode.NULL_ASSERT_TRAP,
1126                   IRNode.RANGE_CHECK_TRAP,
1127                   IRNode.INTRINSIC_TRAP,
1128                   IRNode.INTRINSIC_OR_TYPE_CHECKED_INLINING_TRAP,
1129                   IRNode.UNHANDLED_TRAP})
1130     public void classCheck() {
1131         try {
1132             myClassSub = (MyClassSub) myClass;
1133         } catch (ClassCastException e) {
1134             // Expected
1135         }
1136     }
1137 
1138     @Test
1139     @IR(failOn = IRNode.TRAP) // fails
1140     @IR(failOn = IRNode.RANGE_CHECK_TRAP) // fails
1141     @IR(failOn = IRNode.NULL_CHECK_TRAP) // fails
1142     @IR(failOn = {IRNode.PREDICATE_TRAP,
1143                   IRNode.UNSTABLE_IF_TRAP,
1144                   IRNode.NULL_ASSERT_TRAP,
1145                   IRNode.CLASS_CHECK_TRAP,
1146                   IRNode.INTRINSIC_TRAP,
1147                   IRNode.INTRINSIC_OR_TYPE_CHECKED_INLINING_TRAP,
1148                   IRNode.UNHANDLED_TRAP})
1149     public void rangeCheck() {
1150         iArr[1] = 3;
1151     }
1152 
1153 
1154     @Test
1155     @IR(failOn = IRNode.TRAP) // fails
1156     @IR(failOn = IRNode.INTRINSIC_OR_TYPE_CHECKED_INLINING_TRAP) // fails
1157     @IR(failOn = IRNode.INTRINSIC_TRAP) // fails
1158     @IR(failOn = IRNode.NULL_CHECK_TRAP) // fails
1159     @IR(failOn = {IRNode.PREDICATE_TRAP,
1160                   IRNode.UNSTABLE_IF_TRAP,
1161                   IRNode.NULL_ASSERT_TRAP,
1162                   IRNode.CLASS_CHECK_TRAP,
1163                   IRNode.RANGE_CHECK_TRAP,
1164                   IRNode.UNHANDLED_TRAP})
1165     public void instrinsicOrTypeCheckedInlining() {
1166         System.arraycopy(oArr, 0, mArr, 0, 8);
1167     }
1168 }
1169 
1170 class UnhandledTrap {
1171     int iFld = 34;
1172 
1173     @Test
1174     @IR(failOn = IRNode.TRAP) // fails
1175     @IR(failOn = IRNode.UNHANDLED_TRAP) // fails
1176     @IR(failOn = {IRNode.PREDICATE_TRAP,
1177                   IRNode.UNSTABLE_IF_TRAP,
1178                   IRNode.NULL_CHECK_TRAP,
1179                   IRNode.NULL_ASSERT_TRAP,
1180                   IRNode.RANGE_CHECK_TRAP,
1181                   IRNode.CLASS_CHECK_TRAP})
1182     public void unhandled() {
1183         try {
1184             throw new RuntimeException();
1185         } catch (RuntimeException e) {
1186             // Expected
1187         }
1188     }
1189 }
1190 
1191 class ScopeObj {
1192 
1193     @DontInline
1194     public void dontInline(int i) {}
1195 
1196     @Test
1197     @IR(failOn = IRNode.SCOPE_OBJECT) // fails
1198     public int scopeObject() {
1199         MyClass myClass = new MyClass();
1200         for (int i = 0; i < 100; i++) {
1201             dontInline(myClass.iFld);
1202         }
1203         return 3;
1204     }
1205 }
1206 
1207 class Membar {
1208     volatile MyClass myClass;
1209 
1210     @Test
1211     @IR(failOn = IRNode.MEMBAR) // fails
1212     public int membar() {
1213         myClass = new MyClass();
1214         return myClass.x;
1215     }
1216 }
1217 
1218 class CheckCastArray {
1219     Object[] oArr = new Object[10];
1220     MyClass[] mArr = new MyClass[10];
1221 
1222     @Test
1223     @IR(failOn = IRNode.CHECKCAST_ARRAY) // fails
1224     @IR(failOn = {IRNode.CHECKCAST_ARRAY_OF, "MyClass", // fails
1225                   IRNode.CHECKCAST_ARRAY_OF, "ir_framework/tests/MyClass"}) // fails
1226     @IR(failOn = {IRNode.CHECKCAST_ARRAY_OF, "MyClasss", IRNode.CHECKCAST_ARRAY_OF, "Object"})
1227     public boolean array() {
1228         return oArr instanceof MyClass[];
1229     }
1230 
1231     @Test
1232     @IR(failOn = IRNode.CHECKCAST_ARRAYCOPY) // fails
1233     public Object[] arrayCopy(Object[] src, Class klass) {
1234         return Arrays.copyOf(src, 8, klass);
1235     }
1236 
1237     @Run(test = "arrayCopy")
1238     public void testArrayCopy() {
1239         arrayCopy(mArr, MyClass[].class);
1240         arrayCopy(mArr, Object[].class);
1241         arrayCopy(mArr, MyClassEmpty[].class);
1242     }
1243 }
1244 
1245 class CompilationOutputOfFails {
1246 
1247     @Test
1248     @IR(failOn = IRNode.COUNTEDLOOP + "[\\s\\S]*" + "call")
1249     public void both1() {
1250         for (int i = 0; i < 100; i++) {
1251             dontInline();
1252         }
1253     }
1254 
1255     @Test
1256     @IR(failOn = IRNode.COUNTEDLOOP + "|" + "call")
1257     public void both2() {
1258         for (int i = 0; i < 100; i++) {
1259             dontInline();
1260         }
1261     }
1262 
1263     @Test
1264     @IR(failOn = IRNode.COUNTEDLOOP)
1265     @IR(failOn = "call")
1266     public void both3() {
1267         for (int i = 0; i < 100; i++) {
1268             dontInline();
1269         }
1270     }
1271 
1272     @Test
1273     @IR(counts = {IRNode.COUNTEDLOOP + "[\\s\\S]*" + "call", "0"})
1274     public void both4() {
1275         for (int i = 0; i < 100; i++) {
1276             dontInline();
1277         }
1278     }
1279 
1280     @Test
1281     @IR(counts = {IRNode.COUNTEDLOOP + "|" + "call", "1"})
1282     public void both5() {
1283         for (int i = 0; i < 100; i++) {
1284             dontInline();
1285         }
1286     }
1287 
1288     @Test
1289     @IR(counts = {IRNode.COUNTEDLOOP, "0"})
1290     @IR(counts = {"call", "0"})
1291     public void both6() {
1292         for (int i = 0; i < 100; i++) {
1293             dontInline();
1294         }
1295     }
1296 
1297     @Test
1298     @IR(failOn = IRNode.COUNTEDLOOP)
1299     @IR(counts = {"call", "0"})
1300     public void both7() {
1301         for (int i = 0; i < 100; i++) {
1302             dontInline();
1303         }
1304     }
1305 
1306     @Test
1307     @IR(failOn = IRNode.COUNTEDLOOP)
1308     public void ideal1() {
1309         for (int i = 0; i < 100; i++) {
1310             dontInline();
1311         }
1312     }
1313 
1314     @Test
1315     @IR(failOn = IRNode.COUNTEDLOOP)
1316     @IR(failOn = IRNode.ALLOC) // not fail
1317     public void ideal2() {
1318         for (int i = 0; i < 100; i++) {
1319             dontInline();
1320         }
1321     }
1322 
1323     @Test
1324     @IR(failOn = IRNode.COUNTEDLOOP)
1325     @IR(counts = {IRNode.ALLOC, "0"}) // not fail
1326     public void ideal3() {
1327         for (int i = 0; i < 100; i++) {
1328             dontInline();
1329         }
1330     }
1331 
1332     @Test
1333     @IR(counts = {IRNode.COUNTEDLOOP, "2"})
1334     public void ideal4() {
1335         for (int i = 0; i < 100; i++) {
1336             dontInline();
1337         }
1338     }
1339 
1340     @Test
1341     @IR(failOn = IRNode.ALLOC) // not fail
1342     @IR(counts = {IRNode.COUNTEDLOOP, "2"})
1343     public void ideal5() {
1344         for (int i = 0; i < 100; i++) {
1345             dontInline();
1346         }
1347     }
1348 
1349     @Test
1350     @IR(counts = {IRNode.ALLOC, "0"}) // not fail
1351     @IR(counts = {IRNode.COUNTEDLOOP, "2"})
1352     public void ideal6() {
1353         for (int i = 0; i < 100; i++) {
1354             dontInline();
1355         }
1356     }
1357 
1358     @Test
1359     @IR(counts = {IRNode.COUNTEDLOOP, "5"})
1360     @IR(counts = {IRNode.COUNTEDLOOP, "2"})
1361     public void ideal7() {
1362         for (int i = 0; i < 100; i++) {
1363             dontInline();
1364         }
1365     }
1366 
1367     @Test
1368     @IR(failOn = "call")
1369     public void opto1() {
1370         for (int i = 0; i < 100; i++) {
1371             dontInline();
1372         }
1373     }
1374 
1375     @Test
1376     @IR(failOn = "call")
1377     @IR(failOn = IRNode.STORE) // not fail
1378     public void opto2() {
1379         for (int i = 0; i < 100; i++) {
1380             dontInline();
1381         }
1382     }
1383 
1384     @Test
1385     @IR(failOn = "call")
1386     @IR(counts = {IRNode.COUNTEDLOOP, "1"}) // not fail
1387     public void opto3() {
1388         for (int i = 0; i < 100; i++) {
1389             dontInline();
1390         }
1391     }
1392 
1393     @Test
1394     @IR(counts = {"call", "0"})
1395     public void opto4() {
1396         for (int i = 0; i < 100; i++) {
1397             dontInline();
1398         }
1399     }
1400 
1401     @Test
1402     @IR(failOn = IRNode.STORE) // not fail
1403     @IR(counts = {"call", "0"})
1404     public void opto5() {
1405         for (int i = 0; i < 100; i++) {
1406             dontInline();
1407         }
1408     }
1409 
1410     @Test
1411     @IR(counts = {IRNode.STORE, "0"}) // not fail
1412     @IR(counts = {"call", "0"})
1413     public void opto6() {
1414         for (int i = 0; i < 100; i++) {
1415             dontInline();
1416         }
1417     }
1418 
1419     @Test
1420     @IR(counts = {"call", "10"})
1421     @IR(counts = {"call", "0"})
1422     public void opto7() {
1423         for (int i = 0; i < 100; i++) {
1424             dontInline();
1425         }
1426     }
1427 
1428     @DontInline
1429     private void dontInline() {}
1430 }
1431 
1432 
1433 // Used only by class Traps
1434 class NotLoaded {
1435     NotLoadedHelper notLoadedFld;
1436 }
1437 
1438 // Used only by class Traps
1439 class NotLoadedHelper {}
1440 
1441 class MyClass {
1442     int iFld = 3;
1443     int x = 5;
1444     static long lFldStatic;
1445 }
1446 
1447 class MyClassEmpty {}
1448 
1449 class MyClassEmptySub extends MyClassEmpty {}
1450 
1451 class MyClassSub extends MyClass {
1452     int iFld;
1453     static int iFldStatic;
1454 }
1455 
1456 
1457 // Base class for any kind of constraint that is used to verify if the framework reports the correct IR failures.
1458 abstract class Constraint {
1459     private final Class<?> klass;
1460     protected final int ruleIdx;
1461     private final Pattern methodPattern;
1462     private final String classAndMethod;
1463     protected final Pattern irPattern;
1464     private final String methodName;
1465     protected boolean matched;
1466 
1467     Constraint(Class<?> klass, String methodName, int ruleIdx, Pattern irPattern) {
1468         this.klass = klass;
1469         classAndMethod = klass.getSimpleName() + "." + methodName;
1470         this.ruleIdx = ruleIdx;
1471         this.methodPattern = Pattern.compile(Pattern.quote(classAndMethod));
1472         this.irPattern = irPattern;
1473         this.methodName = methodName;
1474         this.matched = false;
1475     }
1476 
1477     // For good constraints only
1478     Constraint(Class<?> klass, String methodName, int ruleIdx) {
1479         this.klass = klass;
1480         classAndMethod = klass.getSimpleName() + "." + methodName;
1481         this.ruleIdx = ruleIdx;
1482         this.methodPattern = Pattern.compile(Pattern.quote(classAndMethod));
1483         this.irPattern = null;
1484         this.methodName = methodName;
1485         this.matched = false;
1486     }
1487 
1488     @Override
1489     public String toString() {
1490         return "Constraint " + getClass().getSimpleName() + ", " + errorPrefix();
1491     }
1492 
1493     public Class<?> getKlass() {
1494         return klass;
1495     }
1496 
1497     protected String errorPrefix() {
1498         return "Class " + klass.getSimpleName() + ", Method " + methodName + ", Rule " + ruleIdx;
1499     }
1500 
1501     public void checkConstraint(IRViolationException e) {
1502         String message = e.getExceptionInfo();
1503         String[] splitMethods = message.split("Method");
1504         for (int i = 1; i < splitMethods.length; i++) {
1505             String method = splitMethods[i];
1506             if (methodPattern.matcher(method).find()) {
1507                 String[] splitIrRules = method.split("@IR ");
1508                 for (int j = 1; j < splitIrRules.length; j++) {
1509                     String irRule = splitIrRules[j];
1510                     if (irRule.startsWith("rule " + ruleIdx)) {
1511                         checkIRRule(irRule);
1512                     }
1513                 }
1514             }
1515         }
1516         Asserts.assertTrue(matched, this + " should have been matched");
1517     }
1518 
1519     abstract protected void checkIRRule(String irRule);
1520 }
1521 
1522 // Constraint for rule that does not fail.
1523 class GoodRuleConstraint extends Constraint {
1524 
1525     GoodRuleConstraint(Class<?> klass, String methodName, int ruleIdx) {
1526         super(klass, methodName, ruleIdx);
1527         matched = true;
1528     }
1529 
1530     public static GoodRuleConstraint create(Class<?> klass, String methodName, int ruleIdx) {
1531         return new GoodRuleConstraint(klass, methodName, ruleIdx);
1532     }
1533 
1534     @Override
1535     protected void checkIRRule(String irRule) {
1536         Asserts.fail(errorPrefix() + " should not fail:" + System.lineSeparator() + irRule);
1537     }
1538 }
1539 
1540 // Constraint for rule that might fail but not with "failOn".
1541 class GoodFailOnConstraint extends GoodRuleConstraint {
1542 
1543     private GoodFailOnConstraint(Class<?> klass, String methodName, int ruleIdx) {
1544         super(klass, methodName, ruleIdx);
1545     }
1546 
1547     public static GoodFailOnConstraint create(Class<?> klass, String methodName, int ruleIdx) {
1548         return new GoodFailOnConstraint(klass, methodName, ruleIdx);
1549     }
1550 
1551     @Override
1552     protected void checkIRRule(String irRule) {
1553         Asserts.assertFalse(irRule.contains("- failOn"), errorPrefix() + " should not have failed:" + System.lineSeparator() + irRule);
1554     }
1555 }
1556 
1557 // Constraint for rule that might fail but not with "counts".
1558 class GoodCountsConstraint extends GoodRuleConstraint {
1559 
1560     private GoodCountsConstraint(Class<?> klass, String methodName, int ruleIdx) {
1561         super(klass, methodName, ruleIdx);
1562     }
1563 
1564     public static GoodCountsConstraint create(Class<?> klass, String methodName, int ruleIdx) {
1565         return new GoodCountsConstraint(klass, methodName, ruleIdx);
1566     }
1567 
1568     @Override
1569     protected void checkIRRule(String irRule) {
1570         Asserts.assertFalse(irRule.contains("- counts"), errorPrefix() + " should not have failed with counts:"
1571                                                          + System.lineSeparator() + irRule);
1572     }
1573 }
1574 
1575 // Base class for all Regex based constraint.
1576 abstract class RegexConstraint extends Constraint {
1577     final String category;
1578     final String otherCategory;
1579     final int[] regexIndexes;
1580     final boolean isGood;
1581     final List<String> matches;
1582 
1583     RegexConstraint(Class<?> klass, String methodName, String category, boolean isGood, List<String> matches, int ruleIdx, int... regexIndexes) {
1584         super(klass, methodName, ruleIdx, initIRPattern(category, ruleIdx));
1585         this.category = category;
1586         this.regexIndexes = regexIndexes;
1587         if (category.equals("failOn")) {
1588             this.otherCategory = "counts";
1589         } else {
1590             Asserts.assertTrue(category.equals("counts"));
1591             this.otherCategory = "failOn";
1592         }
1593         this.isGood = isGood;
1594         this.matches = matches;
1595     }
1596 
1597     @Override
1598     public String toString() {
1599         String msg = super.toString() + ", ";
1600         if (regexIndexes.length > 1) {
1601             msg += "regexes: [" + String.join(", ", Arrays.stream(regexIndexes).mapToObj(String::valueOf).toArray(String[]::new)) + "]";
1602         } else {
1603             msg += "regex: " + regexIndexes[0];
1604         }
1605         return msg;
1606     }
1607 
1608     @Override
1609     protected String errorPrefix() {
1610         return super.errorPrefix() + " with \"" + category + "\"";
1611     }
1612 
1613     private static Pattern initIRPattern(String category, int ruleIdx) {
1614         if (category.equals("failOn")) {
1615             return Pattern.compile("rule " + ruleIdx + ":.*\\R.*- failOn: Graph contains forbidden nodes.*\\R" +
1616                                    ".*Regex \\d+:.*\\R.*Matched forbidden node.*");
1617         } else {
1618             return Pattern.compile("rule " + ruleIdx + ":.*\\R.*- counts: Graph contains wrong number of nodes:\\R" +
1619                                    ".*Regex \\d+:.*\\R.*Expected.*");
1620         }
1621     }
1622 
1623     @Override
1624     protected void checkIRRule(String irRule) {
1625         int categoryIndex = irRule.indexOf("- " + category);
1626         Asserts.assertTrue(categoryIndex != -1, errorPrefix() + " should have failed");
1627 
1628         int endIndex;
1629         int otherCategoryIndex = irRule.indexOf("- " + otherCategory);
1630         if (otherCategoryIndex == -1 || categoryIndex > otherCategoryIndex) {
1631             endIndex = irRule.length();
1632         } else {
1633             endIndex = otherCategoryIndex;
1634         }
1635         String categoryString = irRule.substring(irRule.indexOf("- " + category), endIndex);
1636         Pattern pattern;
1637         Matcher matcher;
1638         for (int regexIndex : this.regexIndexes) {
1639             pattern = Pattern.compile("Regex " + regexIndex + ":.*");
1640             matcher = pattern.matcher(categoryString);
1641             if (isGood) {
1642                 Asserts.assertFalse(matcher.find(), errorPrefix() + " failed with Regex " + regexIndex);
1643                 matched = true;
1644             } else {
1645                 Asserts.assertTrue(matcher.find(), errorPrefix() + " should have failed at Regex " + regexIndex);
1646                 String[] splitRegex = categoryString.split("Regex ");
1647                 if (matches != null) {
1648                     for (int i = 1; i < splitRegex.length; i++) {
1649                         String regexString = splitRegex[i];
1650                         if (regexString.startsWith(String.valueOf(regexIndex))) {
1651                             // Do matching on actual match and not on regex string
1652                             String actualMatch = regexString.split("\\R", 2)[1];
1653                             Asserts.assertTrue(matches.stream().allMatch(actualMatch::contains),
1654                                                errorPrefix() + " could not find all matches at Regex " + regexIndex);
1655                             matched = true;
1656                         }
1657                     }
1658                 }
1659             }
1660         }
1661     }
1662 }
1663 
1664 // Base class for all good regex based constraints.
1665 abstract class GoodRegexConstraint extends RegexConstraint {
1666 
1667     GoodRegexConstraint(Class<?> klass, String methodName, String category, int ruleIdx, int... regexIndexes) {
1668         super(klass, methodName, category, true, null, ruleIdx, regexIndexes);
1669     }
1670 }
1671 
1672 // Constraint for rule that might fail with "counts" or "failOn", but the specified regex in "failOn" does not fail.
1673 class GoodFailOnRegexConstraint extends GoodRegexConstraint {
1674 
1675     private GoodFailOnRegexConstraint(Class<?> klass, String methodName, int ruleIdx, int... regexIndexes) {
1676         super(klass, methodName, "failOn", ruleIdx, regexIndexes);
1677     }
1678 
1679 
1680     public static GoodFailOnRegexConstraint create(Class<?> klass, String methodName, int ruleIdx, int... regexIndexes) {
1681         return new GoodFailOnRegexConstraint(klass, methodName, ruleIdx, regexIndexes);
1682     }
1683 }
1684 
1685 
1686 // Constraint for rule that might fail with "counts" or "failOn", but the specified regex in "counts" does not fail.
1687 class GoodCountsRegexConstraint extends GoodRegexConstraint {
1688 
1689     private GoodCountsRegexConstraint(Class<?> klass, String methodName, int ruleIdx, int... regexIndexes) {
1690         super(klass, methodName, "counts", ruleIdx, regexIndexes);
1691     }
1692 
1693 
1694     public static GoodCountsRegexConstraint create(Class<?> klass, String methodName, int ruleIdx, int... regexIndexes) {
1695         return new GoodCountsRegexConstraint(klass, methodName, ruleIdx, regexIndexes);
1696     }
1697 }
1698 
1699 // Constraint for rule that fails with "failOn" and the specified regex must also fail.
1700 class BadFailOnConstraint extends RegexConstraint {
1701 
1702     BadFailOnConstraint(Class<?> klass, String methodName, int ruleIdx, List<String> matches, int... regexIndexes) {
1703         super(klass, methodName, "failOn", false, matches, ruleIdx, regexIndexes);
1704     }
1705 
1706     public static BadFailOnConstraint create(Class<?> klass, String methodName, int ruleIdx, int regexId, String... matches) {
1707         return new BadFailOnConstraint(klass, methodName, ruleIdx, new ArrayList<>(Arrays.asList(matches)), regexId);
1708     }
1709 
1710     public static BadFailOnConstraint create(Class<?> klass, String methodName, int ruleIdx, String... matches) {
1711         return new BadFailOnConstraint(klass, methodName, ruleIdx, new ArrayList<>(Arrays.asList(matches)), 1);
1712     }
1713 }
1714 
1715 // Constraint for rule that fails with "counts" and the specified regex must also fail.
1716 class BadCountsConstraint extends RegexConstraint {
1717 
1718     BadCountsConstraint(Class<?> klass, String methodName, int ruleIdx, List<String> matches, int... regexIndexes) {
1719         super(klass, methodName, "counts", false, matches, ruleIdx, regexIndexes);
1720     }
1721 
1722     public static BadCountsConstraint create(Class<?> klass, String methodName, int ruleIdx, int regexId, int foundCount, String... matches) {
1723         List<String> matchesList = getMatchesList(foundCount, matches, Arrays.asList(matches));
1724         return new BadCountsConstraint(klass, methodName, ruleIdx, matchesList, regexId);
1725     }
1726 
1727     public static BadCountsConstraint create(Class<?> klass, String methodName, int ruleIdx, int foundCount, String... matches) {
1728         List<String> matchesList = getMatchesList(foundCount, matches, Arrays.asList(matches));
1729         return new BadCountsConstraint(klass, methodName, ruleIdx, matchesList, 1);
1730     }
1731 
1732     private static List<String> getMatchesList(int foundCount, String[] matches, List<String> strings) {
1733         List<String> matchesList = new ArrayList<>();
1734         matchesList.add("Failed comparison: [found] " + foundCount);
1735         if (matches != null) {
1736             matchesList.addAll(strings);
1737         }
1738         return matchesList;
1739     }
1740 }