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, 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"),
 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, "> 0",
 636                   IRNode.STORE, ">0",
 637                   IRNode.STORE, " > 0",
 638                   IRNode.STORE, "  >  0",
 639                   IRNode.STORE, "< 2",
 640                   IRNode.STORE, "<2",
 641                   IRNode.STORE, " < 2",
 642                   IRNode.STORE, "  <  2",
 643     })
 644     public void countComparison() {
 645         iFld = 3;
 646     }
 647 }
 648 
 649 class GoodCount {
 650     boolean flag;
 651     char cFld;
 652     byte bFld;
 653     short sFld;
 654     int iFld;
 655     long lFld;
 656     float fFld;
 657     double dFld;
 658     long x;
 659 
 660     long result;
 661     MyClass myClass = new MyClass();
 662     MyClassEmpty myClassEmpty = new MyClassEmpty();
 663     MyClass myClassSubPoly = new MyClassSub();
 664     MyClassSub myClassSub = new MyClassSub();
 665 
 666     @Test
 667     @IR(counts = {IRNode.STORE, "1", IRNode.STORE_I, "1"},
 668         failOn = {IRNode.STORE_B, IRNode.STORE_C, IRNode.STORE_D,
 669                   IRNode.STORE_F, IRNode.STORE_L})
 670     public void good1() {
 671         iFld = 3;
 672     }
 673 
 674     @Test
 675     @IR(counts = {IRNode.STORE, "8",
 676                   IRNode.STORE_B, "2", // bFld + flag
 677                   IRNode.STORE_C, "2", // cFld + sFld
 678                   IRNode.STORE_I, "1",
 679                   IRNode.STORE_L, "1",
 680                   IRNode.STORE_F, "1",
 681                   IRNode.STORE_D, "1"})
 682     public void good2() {
 683         flag = true;
 684         cFld = 'a';
 685         bFld = 1;
 686         sFld = 2;
 687         iFld = 3;
 688         lFld = 4L;
 689         fFld = 5.0f;
 690         dFld = 6.0;
 691     }
 692 
 693     @Test
 694     @IR(counts = {IRNode.STORE, "8", IRNode.STORE_OF_CLASS, "GoodCount", "8",
 695                   IRNode.STORE_B, "2", IRNode.STORE_B_OF_CLASS, "GoodCount", "2",
 696                   IRNode.STORE_C, "2", IRNode.STORE_C_OF_CLASS, "GoodCount", "2",
 697                   IRNode.STORE_I, "1", IRNode.STORE_I_OF_CLASS, "GoodCount", "1",
 698                   IRNode.STORE_L, "1", IRNode.STORE_L_OF_CLASS, "GoodCount", "1",
 699                   IRNode.STORE_F, "1", IRNode.STORE_F_OF_CLASS, "GoodCount", "1",
 700                   IRNode.STORE_D, "1", IRNode.STORE_D_OF_CLASS, "GoodCount", "1"})
 701     public void good3() {
 702         flag = true;
 703         cFld = 'a';
 704         bFld = 1;
 705         sFld = 2;
 706         iFld = 3;
 707         lFld = 4L;
 708         fFld = 5.0f;
 709         dFld = 6.0;
 710     }
 711 
 712     @Test
 713     @IR(counts = {IRNode.STORE, "8", IRNode.STORE_OF_CLASS, "GoodCount", "8",
 714                   IRNode.STORE_B, "2", IRNode.STORE_B_OF_CLASS, "GoodCount", "2",
 715                   IRNode.STORE_C, "2", IRNode.STORE_C_OF_CLASS, "GoodCount", "2",
 716                   IRNode.STORE_I, "1", IRNode.STORE_I_OF_CLASS, "GoodCount", "1",
 717                   IRNode.STORE_L, "1", IRNode.STORE_L_OF_CLASS, "GoodCount", "1",
 718                   IRNode.STORE_F, "1", IRNode.STORE_F_OF_CLASS, "GoodCount", "1",
 719                   IRNode.STORE_D, "1", IRNode.STORE_D_OF_CLASS, "GoodCount", "1",
 720                   IRNode.STORE_OF_FIELD, "lFld", "1"})
 721     public void good4() {
 722         flag = true;
 723         cFld = 'a';
 724         bFld = 1;
 725         sFld = 2;
 726         iFld = 3;
 727         lFld = 4L;
 728         fFld = 5.0f;
 729         dFld = 6.0;
 730     }
 731 
 732     @Test
 733     @IR(counts = {IRNode.STORE, "2", IRNode.STORE_I, "1", IRNode.STORE_L, "1",
 734                   IRNode.STORE_OF_CLASS, "GoodCount", "1", IRNode.STORE_L_OF_CLASS, "GoodCount", "1",
 735                   IRNode.STORE_OF_CLASS, "ir_framework/tests/MyClass", "1",
 736                   IRNode.STORE_I_OF_CLASS, "ir_framework/tests/MyClass", "1",
 737                   IRNode.STORE_OF_CLASS, "ir_framework/tests/GoodCount", "1",
 738                   IRNode.STORE_L_OF_CLASS, "ir_framework/tests/GoodCount", "1",
 739                   IRNode.STORE_OF_FIELD, "x", "2"})
 740     public void good5() {
 741         x = 3; // long
 742         myClass.x = 4; // int
 743     }
 744 
 745     @Test
 746     @IR(counts = {IRNode.STORE_OF_FIELD, "myClassEmpty", "1", IRNode.STORE_OF_CLASS, "GoodCount", "1",
 747                   IRNode.STORE_OF_CLASS, "/GoodCount", "1", IRNode.STORE_OF_CLASS, "MyClassEmpty", "0"},
 748         failOn = {IRNode.STORE_OF_CLASS, "MyClassEmpty"})
 749     public void good6() {
 750         myClassEmpty = new MyClassEmpty();
 751     }
 752 
 753     @Test
 754     @IR(counts = {IRNode.STORE_OF_FIELD, "iFld", "3", IRNode.STORE_OF_CLASS, "GoodCount", "0",
 755                   IRNode.STORE_OF_CLASS, "MyClass", "2", IRNode.STORE_OF_CLASS, "MyClassSub", "1",
 756                   IRNode.STORE, "3"},
 757         failOn = {IRNode.STORE_OF_CLASS, "GoodCount"})
 758     public void good7() {
 759         myClass.iFld = 1;
 760         myClassSubPoly.iFld = 2;
 761         myClassSub.iFld = 3;
 762     }
 763 
 764     @Test
 765     @IR(counts = {IRNode.LOAD, "1", IRNode.STORE, "1"})
 766     public void good8() {
 767         result = iFld;
 768     }
 769 
 770 
 771     @Test
 772     @IR(counts = {IRNode.LOAD, "4", IRNode.STORE, "1", IRNode.LOAD_OF_FIELD, "iFld", "2", IRNode.LOAD_OF_FIELD, "iFld2", "0",
 773                   IRNode.LOAD_OF_FIELD, "lFldStatic", "1", IRNode.LOAD_OF_CLASS, "GoodCount", "2", IRNode.LOAD_OF_CLASS, "MyClass", "1",
 774                   IRNode.STORE_OF_CLASS, "GoodCount", "1", IRNode.STORE_OF_FIELD, "result", "1",
 775                   IRNode.LOAD_OF_FIELD, "myClass", "1"})
 776     public void good9() {
 777         result = iFld + MyClass.lFldStatic + myClass.iFld; // 1 + 1 + 2 loads (myClass is LoadN of GoodCount and myClass.iFld a LoadI of MyClass)
 778     }
 779 
 780     @Test
 781     @IR(counts = {IRNode.LOAD, "8",
 782                   IRNode.LOAD_B, "1",
 783                   IRNode.LOAD_UB, "1",
 784                   IRNode.LOAD_S, "1",
 785                   IRNode.LOAD_US, "1",
 786                   IRNode.LOAD_I, "1",
 787                   IRNode.LOAD_L, "1",
 788                   IRNode.LOAD_F, "1",
 789                   IRNode.LOAD_D, "1"})
 790     public void good10() {
 791         bFld++;
 792         cFld++;
 793         sFld++;
 794         iFld++;
 795         lFld++;
 796         fFld++;
 797         dFld++;
 798         flag = !flag;
 799     }
 800 
 801     @Test
 802     @IR(counts = {IRNode.LOAD, "8", IRNode.LOAD_OF_CLASS, "GoodCount", "8",
 803                   IRNode.LOAD_B, "1", IRNode.LOAD_B_OF_CLASS, "GoodCount", "1",
 804                   IRNode.LOAD_UB, "1", IRNode.LOAD_UB_OF_CLASS, "GoodCount", "1",
 805                   IRNode.LOAD_S, "1", IRNode.LOAD_S_OF_CLASS, "GoodCount", "1",
 806                   IRNode.LOAD_US, "1", IRNode.LOAD_US_OF_CLASS, "GoodCount", "1",
 807                   IRNode.LOAD_I, "1", IRNode.LOAD_I_OF_CLASS, "GoodCount", "1",
 808                   IRNode.LOAD_L, "1", IRNode.LOAD_L_OF_CLASS, "GoodCount", "1",
 809                   IRNode.LOAD_F, "1", IRNode.LOAD_F_OF_CLASS, "GoodCount", "1",
 810                   IRNode.LOAD_D, "1", IRNode.LOAD_D_OF_CLASS, "GoodCount", "1"})
 811     public void good11() {
 812         bFld++;
 813         cFld++;
 814         sFld++;
 815         iFld++;
 816         lFld++;
 817         fFld++;
 818         dFld++;
 819         flag = !flag;
 820     }
 821 }
 822 
 823 class BadCount {
 824     int iFld;
 825     int result;
 826     @Test
 827     @IR(counts = {IRNode.LOAD, "!= 1"}) // fail
 828     @IR(counts = {IRNode.STORE, "> 0"})
 829     public void bad1() {
 830         result = iFld;
 831     }
 832 
 833     @Test
 834     @IR(counts = {IRNode.LOAD, "1"}) // fail
 835     @IR(counts = {IRNode.STORE, "< 1"})
 836     public void bad2() {
 837         result = iFld;
 838     }
 839 
 840 
 841     @Test
 842     @IR(counts = {IRNode.LOAD, "0"}) // fail
 843     @IR(counts = {IRNode.STORE, " <= 0"}) // fail
 844     public void bad3() {
 845         result = iFld;
 846     }
 847 }
 848 
 849 
 850 class RunTests {
 851     public int iFld;
 852 
 853     @Test
 854     @IR(counts = {IRNode.STORE, "1"})
 855     @IR(failOn = IRNode.LOAD)
 856     public void good1() {
 857         iFld = 42;
 858     }
 859 
 860     @Test
 861     @IR(counts = {IRNode.LOAD, "1"})
 862     @IR(failOn = IRNode.STORE)
 863     public int good2() {
 864         return iFld;
 865     }
 866 
 867     @Run(test = {"good1", "good2"})
 868     public void runGood1() {
 869         good1();
 870         good2();
 871     }
 872 
 873 
 874     @Test
 875     @IR(counts = {IRNode.STORE, "1"})
 876     @IR(failOn = IRNode.LOAD)
 877     public void good3(int x) {
 878         iFld = x;
 879     }
 880 
 881     @Test
 882     @IR(counts = {IRNode.STORE, "1"})
 883     @IR(failOn = IRNode.LOAD)
 884     public int bad1(int x) {
 885         return iFld + x;
 886     }
 887 
 888     @Run(test = {"bad1", "good3"})
 889     public void run() {
 890         bad1(2);
 891         good3(4);
 892     }
 893 }
 894 
 895 class Calls {
 896 
 897     @Test
 898     @IR(counts = {IRNode.CALL, "1"})
 899     @IR(failOn = {IRNode.CALL_OF_METHOD, "dontInline",  // Fails
 900                   IRNode.STATIC_CALL_OF_METHOD, "dontInline"}) // Fails
 901     @IR(failOn = {IRNode.CALL_OF_METHOD, "forceInline",
 902                   IRNode.STATIC_CALL_OF_METHOD, "forceInline",
 903                   IRNode.CALL_OF_METHOD, "dontInlines",
 904                   IRNode.STATIC_CALL_OF_METHOD, "dontInlines",
 905                   IRNode.CALL_OF_METHOD, "dont",
 906                   IRNode.STATIC_CALL_OF_METHOD, "dont"})
 907     public void calls() {
 908         dontInline();
 909         forceInline();
 910     }
 911 
 912     @DontInline
 913     public void dontInline() {}
 914 
 915     @ForceInline
 916     public void forceInline() {}
 917 }
 918 
 919 class AllocArray {
 920     MyClass[] myClassArray;
 921 
 922     @Test
 923     @IR(failOn = {IRNode.ALLOC_ARRAY})
 924     @IR(failOn = {IRNode.ALLOC_ARRAY_OF, "MyClass"})
 925     @IR(failOn = {IRNode.ALLOC_ARRAY_OF, "MyClasss"}) // Does not fail
 926     @IR(failOn = {IRNode.ALLOC_ARRAY_OF, "ir_framework/tests/MySubClass"}) // Does not fail
 927     @IR(failOn = {IRNode.ALLOC_ARRAY_OF, "ir_framework/tests/MyClass"})
 928     public void allocArray() {
 929         myClassArray = new MyClass[2];
 930     }
 931 }
 932 
 933 class Loads {
 934     int iFld = 34;
 935     int result = 0;
 936     Object myClass = new MyClass();
 937 
 938     @Test
 939     @IR(failOn = {IRNode.LOAD, IRNode.LOOP, IRNode.LOAD_I}, counts = {IRNode.LOOP, "2", IRNode.LOAD, "2", IRNode.STORE, "2"})
 940     @IR(failOn = {IRNode.LOOP, IRNode.LOOP}, counts = {IRNode.LOOP, "0", IRNode.LOAD, "1"}) // Does not fail
 941     @IR(failOn = {IRNode.LOOP, IRNode.LOOP}, counts = {IRNode.LOOP, "0", IRNode.STORE, "1"})
 942     @IR(failOn = {IRNode.LOOP, IRNode.STORE}, counts = {IRNode.LOOP, "0", IRNode.LOAD, "1"})
 943     @IR(failOn = {IRNode.LOAD_OF_CLASS, "ir_framework/tests/Loads"})
 944     @IR(failOn = {IRNode.LOAD_OF_CLASS, "Loads"})
 945     @IR(failOn = {IRNode.LOAD_OF_FIELD, "iFld"})
 946     @IR(failOn = {IRNode.LOAD_OF_FIELD, "iFld2", IRNode.LOAD_OF_CLASS, "Load"}) // Does not fail
 947     @IR(failOn = {IRNode.LOAD_KLASS}) // Does not fail
 948     @IR(counts = {IRNode.FIELD_ACCESS, "3"}) // Does not fail
 949     public void load() {
 950         result = iFld;
 951         iFld = 3;
 952     }
 953 
 954     @Test
 955     @IR(failOn = {IRNode.LOAD_KLASS})
 956     @IR(counts = {IRNode.FIELD_ACCESS, "3"})
 957     public void loadKlass() {
 958         if (myClass instanceof MyClass) {
 959             result = 3;
 960         }
 961     }
 962 }
 963 
 964 class Loops {
 965     int limit = 1024;
 966     int[] iArr = new int[100];
 967 
 968     @DontInline
 969     public void dontInline() {}
 970 
 971     @Test
 972     @IR(failOn = IRNode.LOOP) // fails
 973     @IR(failOn = IRNode.COUNTED_LOOP)
 974     @IR(failOn = IRNode.COUNTED_LOOP_MAIN)
 975     public void loop() {
 976         for (int i = 0; i < limit; i++) {
 977             dontInline();
 978         }
 979     }
 980 
 981     @Test
 982     @IR(failOn = IRNode.LOOP)
 983     @IR(failOn = IRNode.COUNTED_LOOP) // fails
 984     @IR(failOn = IRNode.COUNTED_LOOP_MAIN)
 985     public void countedLoop() {
 986         for (int i = 0; i < 2000; i++) {
 987             dontInline();
 988         }
 989     }
 990 
 991     @Test
 992     @IR(failOn = IRNode.LOOP) // fails
 993     @IR(failOn = IRNode.COUNTED_LOOP) // fails
 994     @IR(failOn = IRNode.COUNTED_LOOP_MAIN)
 995     public void loopAndCountedLoop() {
 996         for (int i = 0; i < 2000; i++) {
 997             for (int j = 0; j < limit; j++) {
 998                 dontInline();
 999             }
1000         }
1001     }
1002 
1003     @Test
1004     @IR(failOn = IRNode.LOOP)
1005     @IR(failOn = IRNode.COUNTED_LOOP) // fails
1006     @IR(failOn = IRNode.COUNTED_LOOP_MAIN) // fails
1007     public void countedLoopMain() {
1008         // Cannot unroll completely -> create pre/main/post
1009         for (int i = 0; i < 100; i++) {
1010             iArr[i] = i;
1011         }
1012     }
1013 
1014     @Test
1015     @IR(failOn = IRNode.LOOP)
1016     @IR(failOn = IRNode.COUNTED_LOOP)
1017     @IR(failOn = IRNode.COUNTED_LOOP_MAIN)
1018     public void countedLoopUnrolled() {
1019         // Completely unrolled -> no pre/main/post
1020         for (int i = 0; i < 8; i++) {
1021             iArr[i] = i;
1022         }
1023     }
1024 }
1025 
1026 class Traps {
1027     int number42 = 42;
1028     int iFld = 10;
1029     int[] iArr = new int[2];
1030     MyClass myClass = new MyClass();
1031     MyClassSub myClassSub = new MyClassSub();
1032     NotLoaded notLoaded = new NotLoaded();
1033     Object[] oArr = new Object[10];
1034     MyClass[] mArr = new MyClass[10];
1035 
1036     @Test
1037     @IR(failOn = IRNode.TRAP)
1038     @IR(failOn = {IRNode.STORE_OF_FIELD, "iFld"}) // fails
1039     @IR(failOn = {IRNode.PREDICATE_TRAP,
1040                   IRNode.UNSTABLE_IF_TRAP,
1041                   IRNode.NULL_CHECK_TRAP,
1042                   IRNode.NULL_ASSERT_TRAP,
1043                   IRNode.RANGE_CHECK_TRAP,
1044                   IRNode.CLASS_CHECK_TRAP,
1045                   IRNode.INTRINSIC_TRAP,
1046                   IRNode.INTRINSIC_OR_TYPE_CHECKED_INLINING_TRAP,
1047                   IRNode.UNHANDLED_TRAP})
1048     public void noTraps() {
1049         for (int i = 0; i < 100; i++) {
1050             if (i < 42) {
1051                 // Reached, no uncommon trap
1052                 iFld = i;
1053             }
1054         }
1055     }
1056 
1057     @Test
1058     @IR(failOn = IRNode.TRAP) // fails
1059     @IR(failOn = IRNode.PREDICATE_TRAP) // fails
1060     @IR(failOn = {IRNode.STORE_OF_FIELD, "iFld"})
1061     @IR(failOn = {IRNode.UNSTABLE_IF_TRAP,
1062                   IRNode.NULL_CHECK_TRAP,
1063                   IRNode.NULL_ASSERT_TRAP,
1064                   IRNode.RANGE_CHECK_TRAP,
1065                   IRNode.CLASS_CHECK_TRAP,
1066                   IRNode.INTRINSIC_TRAP,
1067                   IRNode.INTRINSIC_OR_TYPE_CHECKED_INLINING_TRAP,
1068                   IRNode.UNHANDLED_TRAP})
1069     public void predicateTrap() {
1070         for (int i = 0; i < 100; i++) {
1071             if (number42 != 42) {
1072                 // Never reached
1073                 iFld = i;
1074             }
1075         }
1076     }
1077 
1078     @Test
1079     @IR(failOn = IRNode.TRAP) // fails
1080     @IR(failOn = IRNode.NULL_CHECK_TRAP) // fails
1081     @IR(failOn = IRNode.UNSTABLE_IF_TRAP) // fails
1082     @IR(failOn = {IRNode.PREDICATE_TRAP,
1083                   IRNode.NULL_ASSERT_TRAP,
1084                   IRNode.RANGE_CHECK_TRAP,
1085                   IRNode.CLASS_CHECK_TRAP,
1086                   IRNode.INTRINSIC_TRAP,
1087                   IRNode.INTRINSIC_OR_TYPE_CHECKED_INLINING_TRAP,
1088                   IRNode.UNHANDLED_TRAP})
1089     public void nullCheck() {
1090         if (myClass instanceof MyClassSub) {
1091             iFld = 4;
1092         }
1093     }
1094 
1095     @Test
1096     @IR(failOn = IRNode.TRAP) // fails
1097     @IR(failOn = IRNode.NULL_ASSERT_TRAP) // fails
1098     @IR(failOn = IRNode.NULL_CHECK_TRAP) // fails
1099     @IR(failOn = {IRNode.PREDICATE_TRAP,
1100                   IRNode.UNSTABLE_IF_TRAP,
1101                   IRNode.RANGE_CHECK_TRAP,
1102                   IRNode.CLASS_CHECK_TRAP,
1103                   IRNode.INTRINSIC_TRAP,
1104                   IRNode.INTRINSIC_OR_TYPE_CHECKED_INLINING_TRAP,
1105                   IRNode.UNHANDLED_TRAP})
1106     public Object nullAssert() {
1107         return notLoaded.notLoadedFld;
1108     }
1109 
1110     @Test
1111     @Arguments(Argument.TRUE)
1112     @IR(failOn = IRNode.TRAP) // fails
1113     @IR(failOn = IRNode.UNSTABLE_IF_TRAP) // fails
1114     @IR(failOn = {IRNode.PREDICATE_TRAP,
1115                   IRNode.NULL_CHECK_TRAP,
1116                   IRNode.NULL_ASSERT_TRAP,
1117                   IRNode.RANGE_CHECK_TRAP,
1118                   IRNode.CLASS_CHECK_TRAP,
1119                   IRNode.INTRINSIC_TRAP,
1120                   IRNode.INTRINSIC_OR_TYPE_CHECKED_INLINING_TRAP,
1121                   IRNode.UNHANDLED_TRAP})
1122     public void unstableIf(boolean flag) {
1123         if (flag) {
1124             iFld++;
1125         } else {
1126             iFld--;
1127         }
1128     }
1129 
1130     @Test
1131     @IR(failOn = IRNode.TRAP) // fails
1132     @IR(failOn = IRNode.CLASS_CHECK_TRAP) // fails
1133     @IR(failOn = IRNode.NULL_CHECK_TRAP) // fails
1134     @IR(failOn = {IRNode.PREDICATE_TRAP,
1135                   IRNode.UNSTABLE_IF_TRAP,
1136                   IRNode.NULL_ASSERT_TRAP,
1137                   IRNode.RANGE_CHECK_TRAP,
1138                   IRNode.INTRINSIC_TRAP,
1139                   IRNode.INTRINSIC_OR_TYPE_CHECKED_INLINING_TRAP,
1140                   IRNode.UNHANDLED_TRAP})
1141     public void classCheck() {
1142         try {
1143             myClassSub = (MyClassSub) myClass;
1144         } catch (ClassCastException e) {
1145             // Expected
1146         }
1147     }
1148 
1149     @Test
1150     @IR(failOn = IRNode.TRAP) // fails
1151     @IR(failOn = IRNode.RANGE_CHECK_TRAP) // fails
1152     @IR(failOn = IRNode.NULL_CHECK_TRAP) // fails
1153     @IR(failOn = {IRNode.PREDICATE_TRAP,
1154                   IRNode.UNSTABLE_IF_TRAP,
1155                   IRNode.NULL_ASSERT_TRAP,
1156                   IRNode.CLASS_CHECK_TRAP,
1157                   IRNode.INTRINSIC_TRAP,
1158                   IRNode.INTRINSIC_OR_TYPE_CHECKED_INLINING_TRAP,
1159                   IRNode.UNHANDLED_TRAP})
1160     public void rangeCheck() {
1161         iArr[1] = 3;
1162     }
1163 
1164 
1165     @Test
1166     @IR(failOn = IRNode.TRAP) // fails
1167     @IR(failOn = IRNode.INTRINSIC_OR_TYPE_CHECKED_INLINING_TRAP) // fails
1168     @IR(failOn = IRNode.INTRINSIC_TRAP) // fails
1169     @IR(failOn = IRNode.NULL_CHECK_TRAP) // fails
1170     @IR(failOn = {IRNode.PREDICATE_TRAP,
1171                   IRNode.UNSTABLE_IF_TRAP,
1172                   IRNode.NULL_ASSERT_TRAP,
1173                   IRNode.CLASS_CHECK_TRAP,
1174                   IRNode.RANGE_CHECK_TRAP,
1175                   IRNode.UNHANDLED_TRAP})
1176     public void instrinsicOrTypeCheckedInlining() {
1177         System.arraycopy(oArr, 0, mArr, 0, 8);
1178     }
1179 }
1180 
1181 class UnhandledTrap {
1182     int iFld = 34;
1183 
1184     @Test
1185     @IR(failOn = IRNode.TRAP) // fails
1186     @IR(failOn = IRNode.UNHANDLED_TRAP) // fails
1187     @IR(failOn = {IRNode.PREDICATE_TRAP,
1188                   IRNode.UNSTABLE_IF_TRAP,
1189                   IRNode.NULL_CHECK_TRAP,
1190                   IRNode.NULL_ASSERT_TRAP,
1191                   IRNode.RANGE_CHECK_TRAP,
1192                   IRNode.CLASS_CHECK_TRAP})
1193     public void unhandled() {
1194         try {
1195             throw new RuntimeException();
1196         } catch (RuntimeException e) {
1197             // Expected
1198         }
1199     }
1200 }
1201 
1202 class ScopeObj {
1203 
1204     @DontInline
1205     public void dontInline(int i) {}
1206 
1207     @Test
1208     @IR(failOn = IRNode.SCOPE_OBJECT) // fails
1209     public int scopeObject() {
1210         MyClass myClass = new MyClass();
1211         for (int i = 0; i < 100; i++) {
1212             dontInline(myClass.iFld);
1213         }
1214         return 3;
1215     }
1216 }
1217 
1218 class Membar {
1219     volatile MyClass myClass;
1220 
1221     @Test
1222     @IR(failOn = IRNode.MEMBAR) // fails
1223     public int membar() {
1224         myClass = new MyClass();
1225         return myClass.x;
1226     }
1227 }
1228 
1229 class CheckCastArray {
1230     Object[] oArr = new Object[10];
1231     MyClass[] mArr = new MyClass[10];
1232 
1233     @Test
1234     @IR(failOn = IRNode.CHECKCAST_ARRAY) // fails
1235     @IR(failOn = {IRNode.CHECKCAST_ARRAY_OF, "MyClass", // fails
1236                   IRNode.CHECKCAST_ARRAY_OF, "ir_framework/tests/MyClass"}) // fails
1237     @IR(failOn = {IRNode.CHECKCAST_ARRAY_OF, "MyClasss", IRNode.CHECKCAST_ARRAY_OF, "Object"})
1238     public boolean array() {
1239         return oArr instanceof MyClass[];
1240     }
1241 
1242     @Test
1243     @IR(failOn = IRNode.CHECKCAST_ARRAYCOPY) // fails
1244     public Object[] arrayCopy(Object[] src, Class klass) {
1245         return Arrays.copyOf(src, 8, klass);
1246     }
1247 
1248     @Run(test = "arrayCopy")
1249     public void testArrayCopy() {
1250         arrayCopy(mArr, MyClass[].class);
1251         arrayCopy(mArr, Object[].class);
1252         arrayCopy(mArr, MyClassEmpty[].class);
1253     }
1254 }
1255 
1256 class CompilationOutputOfFails {
1257     private Object obj;
1258 
1259     @Test
1260     @IR(failOn = IRNode.COUNTED_LOOP)
1261     @IR(failOn = {"call"},
1262         phase = CompilePhase.PRINT_OPTO_ASSEMBLY)
1263     public void both1() {
1264         for (int i = 0; i < 100; i++) {
1265             dontInline();
1266         }
1267     }
1268 
1269     @Test
1270     @IR(failOn = "CountedLoop|call",
1271         phase = {CompilePhase.PRINT_IDEAL, CompilePhase.PRINT_OPTO_ASSEMBLY})
1272     public void both2() {
1273         for (int i = 0; i < 100; i++) {
1274             dontInline();
1275         }
1276     }
1277 
1278     @Test
1279     @IR(failOn = IRNode.COUNTED_LOOP)
1280     @IR(failOn = "call", phase = CompilePhase.PRINT_OPTO_ASSEMBLY)
1281     public void both3() {
1282         for (int i = 0; i < 100; i++) {
1283             dontInline();
1284         }
1285     }
1286 
1287     @Test
1288     @IR(counts = {IRNode.COUNTED_LOOP, "0"})
1289     @IR(counts = {"call", "0"},
1290         phase = {CompilePhase.PRINT_OPTO_ASSEMBLY})
1291     public void both4() {
1292         for (int i = 0; i < 100; i++) {
1293             dontInline();
1294         }
1295     }
1296 
1297     @Test
1298     @IR(counts = {"CountedLoop|call", "10"},
1299         phase = {CompilePhase.PRINT_IDEAL, CompilePhase.PRINT_OPTO_ASSEMBLY})
1300     public void both5() {
1301         for (int i = 0; i < 100; i++) {
1302             dontInline();
1303         }
1304     }
1305 
1306     @Test
1307     @IR(counts = {IRNode.COUNTED_LOOP, "0"})
1308     @IR(counts = {"call", "0"}, phase = CompilePhase.PRINT_OPTO_ASSEMBLY)
1309     public void both6() {
1310         for (int i = 0; i < 100; i++) {
1311             dontInline();
1312         }
1313     }
1314 
1315     @Test
1316     @IR(failOn = IRNode.COUNTED_LOOP)
1317     @IR(counts = {"call", "0"}, phase = CompilePhase.PRINT_OPTO_ASSEMBLY)
1318     public void both7() {
1319         for (int i = 0; i < 100; i++) {
1320             dontInline();
1321         }
1322     }
1323 
1324     @Test
1325     @IR(failOn = IRNode.COUNTED_LOOP)
1326     public void ideal1() {
1327         for (int i = 0; i < 100; i++) {
1328             dontInline();
1329         }
1330     }
1331 
1332     @Test
1333     @IR(failOn = IRNode.COUNTED_LOOP)
1334     @IR(failOn = IRNode.ALLOC) // not fail
1335     public void ideal2() {
1336         for (int i = 0; i < 100; i++) {
1337             dontInline();
1338         }
1339     }
1340 
1341     @Test
1342     @IR(failOn = IRNode.COUNTED_LOOP)
1343     @IR(counts = {IRNode.ALLOC, "0"}) // not fail
1344     public void ideal3() {
1345         for (int i = 0; i < 100; i++) {
1346             dontInline();
1347         }
1348     }
1349 
1350     @Test
1351     @IR(counts = {IRNode.COUNTED_LOOP, "2"})
1352     public void ideal4() {
1353         for (int i = 0; i < 100; i++) {
1354             dontInline();
1355         }
1356     }
1357 
1358     @Test
1359     @IR(failOn = IRNode.ALLOC) // not fail
1360     @IR(counts = {IRNode.COUNTED_LOOP, "2"})
1361     public void ideal5() {
1362         for (int i = 0; i < 100; i++) {
1363             dontInline();
1364         }
1365     }
1366 
1367     @Test
1368     @IR(counts = {IRNode.ALLOC, "0"}) // not fail
1369     @IR(counts = {IRNode.COUNTED_LOOP, "2"})
1370     public void ideal6() {
1371         for (int i = 0; i < 100; i++) {
1372             dontInline();
1373         }
1374     }
1375 
1376     @Test
1377     @IR(counts = {IRNode.COUNTED_LOOP, "5"})
1378     @IR(counts = {IRNode.COUNTED_LOOP, "2"})
1379     public void ideal7() {
1380         for (int i = 0; i < 100; i++) {
1381             dontInline();
1382         }
1383     }
1384 
1385     @Test
1386     @IR(failOn = IRNode.ALLOC)
1387     public void opto1() {
1388         obj = new Object();
1389     }
1390 
1391     @Test
1392     @IR(failOn = IRNode.ALLOC)
1393     @IR(failOn = IRNode.STORE_F) // not fail
1394     public void opto2() {
1395         obj = new Object();
1396     }
1397 
1398     @Test
1399     @IR(failOn = IRNode.ALLOC)
1400     @IR(counts = {IRNode.COUNTED_LOOP, "1"}) // not fail
1401     public void opto3() {
1402         for (int i = 0; i < 100; i++) {
1403             obj = new Object();
1404         }
1405     }
1406 
1407     @Test
1408     @IR(counts = {IRNode.ALLOC, "0"})
1409     public void opto4() {
1410         obj = new Object();
1411     }
1412 
1413     @Test
1414     @IR(failOn = IRNode.STORE_F) // not fail
1415     @IR(counts = {IRNode.ALLOC, "0"})
1416     public void opto5() {
1417         obj = new Object();
1418     }
1419 
1420     @Test
1421     @IR(counts = {IRNode.STORE_F, "0"}) // not fail
1422     @IR(counts = {IRNode.ALLOC, "0"})
1423     public void opto6() {
1424         obj = new Object();
1425     }
1426 
1427     @Test
1428     @IR(counts = {IRNode.ALLOC, "10"})
1429     @IR(counts = {IRNode.ALLOC, "0"})
1430     public void opto7() {
1431         obj = new Object();
1432     }
1433 
1434     @DontInline
1435     private void dontInline() {}
1436 }
1437 
1438 
1439 // Used only by class Traps
1440 class NotLoaded {
1441     NotLoadedHelper notLoadedFld;
1442 }
1443 
1444 // Used only by class Traps
1445 class NotLoadedHelper {}
1446 
1447 class MyClass {
1448     int iFld = 3;
1449     int x = 5;
1450     static long lFldStatic;
1451 }
1452 
1453 class MyClassEmpty {}
1454 
1455 class MyClassEmptySub extends MyClassEmpty {}
1456 
1457 class MyClassSub extends MyClass {
1458     int iFld;
1459     static int iFldStatic;
1460 }
1461 
1462 
1463 // Base class for any kind of constraint that is used to verify if the framework reports the correct IR failures.
1464 abstract class Constraint {
1465     private final Class<?> klass;
1466     protected final int ruleIdx;
1467     private final Pattern methodPattern;
1468     private final String classAndMethod;
1469     protected final Pattern irPattern;
1470     private final String methodName;
1471     protected boolean matched;
1472 
1473     Constraint(Class<?> klass, String methodName, int ruleIdx, Pattern irPattern) {
1474         this.klass = klass;
1475         classAndMethod = klass.getSimpleName() + "." + methodName;
1476         this.ruleIdx = ruleIdx;
1477         this.methodPattern = Pattern.compile(Pattern.quote(classAndMethod));
1478         this.irPattern = irPattern;
1479         this.methodName = methodName;
1480         this.matched = false;
1481     }
1482 
1483     // For good constraints only
1484     Constraint(Class<?> klass, String methodName, int ruleIdx) {
1485         this.klass = klass;
1486         classAndMethod = klass.getSimpleName() + "." + methodName;
1487         this.ruleIdx = ruleIdx;
1488         this.methodPattern = Pattern.compile(Pattern.quote(classAndMethod));
1489         this.irPattern = null;
1490         this.methodName = methodName;
1491         this.matched = false;
1492     }
1493 
1494     @Override
1495     public String toString() {
1496         return "Constraint " + getClass().getSimpleName() + ", " + errorPrefix();
1497     }
1498 
1499     public Class<?> getKlass() {
1500         return klass;
1501     }
1502 
1503     protected String errorPrefix() {
1504         return "Class " + klass.getSimpleName() + ", Method " + methodName + ", Rule " + ruleIdx;
1505     }
1506 
1507     public void checkConstraint(IRViolationException e) {
1508         String message = e.getExceptionInfo();
1509         String[] splitMethods = message.split("Method");
1510         for (int i = 1; i < splitMethods.length; i++) {
1511             String method = splitMethods[i];
1512             if (methodPattern.matcher(method).find()) {
1513                 String[] splitIrRules = method.split("@IR ");
1514                 for (int j = 1; j < splitIrRules.length; j++) {
1515                     String irRule = splitIrRules[j];
1516                     if (irRule.startsWith("rule " + ruleIdx)) {
1517                         checkIRRule(irRule);
1518                     }
1519                 }
1520             }
1521         }
1522         Asserts.assertTrue(matched, this + " should have been matched");
1523     }
1524 
1525     abstract protected void checkIRRule(String irRule);
1526 }
1527 
1528 // Constraint for rule that does not fail.
1529 class GoodRuleConstraint extends Constraint {
1530 
1531     GoodRuleConstraint(Class<?> klass, String methodName, int ruleIdx) {
1532         super(klass, methodName, ruleIdx);
1533         matched = true;
1534     }
1535 
1536     public static GoodRuleConstraint create(Class<?> klass, String methodName, int ruleIdx) {
1537         return new GoodRuleConstraint(klass, methodName, ruleIdx);
1538     }
1539 
1540     @Override
1541     protected void checkIRRule(String irRule) {
1542         Asserts.fail(errorPrefix() + " should not fail:" + System.lineSeparator() + irRule);
1543     }
1544 }
1545 
1546 // Constraint for rule that might fail but not with "failOn".
1547 class GoodFailOnConstraint extends GoodRuleConstraint {
1548 
1549     private GoodFailOnConstraint(Class<?> klass, String methodName, int ruleIdx) {
1550         super(klass, methodName, ruleIdx);
1551     }
1552 
1553     public static GoodFailOnConstraint create(Class<?> klass, String methodName, int ruleIdx) {
1554         return new GoodFailOnConstraint(klass, methodName, ruleIdx);
1555     }
1556 
1557     @Override
1558     protected void checkIRRule(String irRule) {
1559         Asserts.assertFalse(irRule.contains("- failOn"), errorPrefix() + " should not have failed:" + System.lineSeparator() + irRule);
1560     }
1561 }
1562 
1563 // Constraint for rule that might fail but not with "counts".
1564 class GoodCountsConstraint extends GoodRuleConstraint {
1565 
1566     private GoodCountsConstraint(Class<?> klass, String methodName, int ruleIdx) {
1567         super(klass, methodName, ruleIdx);
1568     }
1569 
1570     public static GoodCountsConstraint create(Class<?> klass, String methodName, int ruleIdx) {
1571         return new GoodCountsConstraint(klass, methodName, ruleIdx);
1572     }
1573 
1574     @Override
1575     protected void checkIRRule(String irRule) {
1576         Asserts.assertFalse(irRule.contains("- counts"), errorPrefix() + " should not have failed with counts:"
1577                                                          + System.lineSeparator() + irRule);
1578     }
1579 }
1580 
1581 // Base class for all Regex based constraint.
1582 abstract class RegexConstraint extends Constraint {
1583     final String category;
1584     final String otherCategory;
1585     final int[] regexIndexes;
1586     final boolean isGood;
1587     final List<String> matches;
1588 
1589     RegexConstraint(Class<?> klass, String methodName, String category, boolean isGood, List<String> matches, int ruleIdx, int... regexIndexes) {
1590         super(klass, methodName, ruleIdx, initIRPattern(category, ruleIdx));
1591         this.category = category;
1592         this.regexIndexes = regexIndexes;
1593         if (category.equals("failOn")) {
1594             this.otherCategory = "counts";
1595         } else {
1596             Asserts.assertTrue(category.equals("counts"));
1597             this.otherCategory = "failOn";
1598         }
1599         this.isGood = isGood;
1600         this.matches = matches;
1601     }
1602 
1603     @Override
1604     public String toString() {
1605         String msg = super.toString() + ", ";
1606         if (regexIndexes.length > 1) {
1607             msg += "regexes: [" + String.join(", ", Arrays.stream(regexIndexes).mapToObj(String::valueOf).toArray(String[]::new)) + "]";
1608         } else {
1609             msg += "regex: " + regexIndexes[0];
1610         }
1611         return msg;
1612     }
1613 
1614     @Override
1615     protected String errorPrefix() {
1616         return super.errorPrefix() + " with \"" + category + "\"";
1617     }
1618 
1619     private static Pattern initIRPattern(String category, int ruleIdx) {
1620         if (category.equals("failOn")) {
1621             return Pattern.compile("rule " + ruleIdx + ":.*\\R.*- failOn: Graph contains forbidden nodes.*\\R" +
1622                                    ".*Constraint \\d+:.*\\R.*Matched forbidden node.*");
1623         } else {
1624             return Pattern.compile("rule " + ruleIdx + ":.*\\R.*- counts: Graph contains wrong number of nodes:\\R" +
1625                                    ".*Constraint \\d+:.*\\R.*Expected.*");
1626         }
1627     }
1628 
1629     @Override
1630     protected void checkIRRule(String irRule) {
1631         int categoryIndex = irRule.indexOf("- " + category);
1632         Asserts.assertTrue(categoryIndex != -1, errorPrefix() + " should have failed");
1633 
1634         int endIndex;
1635         int otherCategoryIndex = irRule.indexOf("- " + otherCategory);
1636         if (otherCategoryIndex == -1 || categoryIndex > otherCategoryIndex) {
1637             endIndex = irRule.length();
1638         } else {
1639             endIndex = otherCategoryIndex;
1640         }
1641         String categoryString = irRule.substring(irRule.indexOf("- " + category), endIndex);
1642         Pattern pattern;
1643         Matcher matcher;
1644         for (int regexIndex : this.regexIndexes) {
1645             pattern = Pattern.compile("Constraint " + regexIndex + ":.*");
1646             matcher = pattern.matcher(categoryString);
1647             if (isGood) {
1648                 Asserts.assertFalse(matcher.find(), errorPrefix() + " failed with Constraint " + regexIndex);
1649                 matched = true;
1650             } else {
1651                 Asserts.assertTrue(matcher.find(), errorPrefix() + " should have failed at Constraint " + regexIndex);
1652                 String[] splitRegex = categoryString.split("Constraint ");
1653                 if (matches != null) {
1654                     for (int i = 1; i < splitRegex.length; i++) {
1655                         String regexString = splitRegex[i];
1656                         if (regexString.startsWith(String.valueOf(regexIndex))) {
1657                             // Do matching on actual match and not on regex string
1658                             String actualMatch = regexString.split("\\R", 2)[1];
1659                             Asserts.assertTrue(matches.stream().allMatch(actualMatch::contains),
1660                                                errorPrefix() + " could not find all matches at Constraint " + regexIndex);
1661                             matched = true;
1662                         }
1663                     }
1664                 }
1665             }
1666         }
1667     }
1668 }
1669 
1670 // Base class for all good regex based constraints.
1671 abstract class GoodRegexConstraint extends RegexConstraint {
1672 
1673     GoodRegexConstraint(Class<?> klass, String methodName, String category, int ruleIdx, int... regexIndexes) {
1674         super(klass, methodName, category, true, null, ruleIdx, regexIndexes);
1675     }
1676 }
1677 
1678 // Constraint for rule that might fail with "counts" or "failOn", but the specified regex in "failOn" does not fail.
1679 class GoodFailOnRegexConstraint extends GoodRegexConstraint {
1680 
1681     private GoodFailOnRegexConstraint(Class<?> klass, String methodName, int ruleIdx, int... regexIndexes) {
1682         super(klass, methodName, "failOn", ruleIdx, regexIndexes);
1683     }
1684 
1685 
1686     public static GoodFailOnRegexConstraint create(Class<?> klass, String methodName, int ruleIdx, int... regexIndexes) {
1687         return new GoodFailOnRegexConstraint(klass, methodName, ruleIdx, regexIndexes);
1688     }
1689 }
1690 
1691 
1692 // Constraint for rule that might fail with "counts" or "failOn", but the specified regex in "counts" does not fail.
1693 class GoodCountsRegexConstraint extends GoodRegexConstraint {
1694 
1695     private GoodCountsRegexConstraint(Class<?> klass, String methodName, int ruleIdx, int... regexIndexes) {
1696         super(klass, methodName, "counts", ruleIdx, regexIndexes);
1697     }
1698 
1699 
1700     public static GoodCountsRegexConstraint create(Class<?> klass, String methodName, int ruleIdx, int... regexIndexes) {
1701         return new GoodCountsRegexConstraint(klass, methodName, ruleIdx, regexIndexes);
1702     }
1703 }
1704 
1705 // Constraint for rule that fails with "failOn" and the specified regex must also fail.
1706 class BadFailOnConstraint extends RegexConstraint {
1707 
1708     BadFailOnConstraint(Class<?> klass, String methodName, int ruleIdx, List<String> matches, int... regexIndexes) {
1709         super(klass, methodName, "failOn", false, matches, ruleIdx, regexIndexes);
1710     }
1711 
1712     public static BadFailOnConstraint create(Class<?> klass, String methodName, int ruleIdx, int regexId, String... matches) {
1713         return new BadFailOnConstraint(klass, methodName, ruleIdx, new ArrayList<>(Arrays.asList(matches)), regexId);
1714     }
1715 
1716     public static BadFailOnConstraint create(Class<?> klass, String methodName, int ruleIdx, String... matches) {
1717         return new BadFailOnConstraint(klass, methodName, ruleIdx, new ArrayList<>(Arrays.asList(matches)), 1);
1718     }
1719 }
1720 
1721 // Constraint for rule that fails with "counts" and the specified regex must also fail.
1722 class BadCountsConstraint extends RegexConstraint {
1723 
1724     BadCountsConstraint(Class<?> klass, String methodName, int ruleIdx, List<String> matches, int... regexIndexes) {
1725         super(klass, methodName, "counts", false, matches, ruleIdx, regexIndexes);
1726     }
1727 
1728     public static BadCountsConstraint create(Class<?> klass, String methodName, int ruleIdx, int regexId, int foundCount, String... matches) {
1729         List<String> matchesList = getMatchesList(foundCount, matches, Arrays.asList(matches));
1730         return new BadCountsConstraint(klass, methodName, ruleIdx, matchesList, regexId);
1731     }
1732 
1733     public static BadCountsConstraint create(Class<?> klass, String methodName, int ruleIdx, int foundCount, String... matches) {
1734         List<String> matchesList = getMatchesList(foundCount, matches, Arrays.asList(matches));
1735         return new BadCountsConstraint(klass, methodName, ruleIdx, matchesList, 1);
1736     }
1737 
1738     private static List<String> getMatchesList(int foundCount, String[] matches, List<String> strings) {
1739         List<String> matchesList = new ArrayList<>();
1740         matchesList.add("Failed comparison: [found] " + foundCount);
1741         if (matches != null) {
1742             matchesList.addAll(strings);
1743         }
1744         return matchesList;
1745     }
1746 }