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