1 /*
   2  * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 package ir_framework.tests;
  25 
  26 import compiler.lib.ir_framework.*;
  27 import compiler.lib.ir_framework.driver.irmatching.IRViolationException;
  28 import jdk.test.lib.Asserts;
  29 import jdk.test.lib.Platform;
  30 import 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(new String[] {"-XX:+IgnoreUnrecognizedVMOptions", "-XX:-UseCompressedClassPointers"},
 135                  BadFailOnConstraint.create(Loads.class, "load()", 1, 1, "Load"),
 136                  BadFailOnConstraint.create(Loads.class, "load()", 1, 3, "LoadI"),
 137                  BadCountsConstraint.create(Loads.class, "load()", 1, 1, 0),
 138                  BadCountsConstraint.create(Loads.class, "load()", 1, 2, 1,"Load"),
 139                  GoodRuleConstraint.create(Loads.class, "load()", 2),
 140                  GoodFailOnConstraint.create(Loads.class, "load()", 3),
 141                  BadCountsConstraint.create(Loads.class, "load()", 3, 2, 2,"Store"),
 142                  BadFailOnConstraint.create(Loads.class, "load()", 4, 2, "Store"),
 143                  BadFailOnConstraint.create(Loads.class, "load()", 5, "Load"),
 144                  BadFailOnConstraint.create(Loads.class, "load()", 6, "Load"),
 145                  BadFailOnConstraint.create(Loads.class, "load()", 7, "Load"),
 146                  GoodRuleConstraint.create(Loads.class, "load()", 8),
 147                  GoodRuleConstraint.create(Loads.class, "load()", 9),
 148                  GoodRuleConstraint.create(Loads.class, "load()", 10),
 149                  BadFailOnConstraint.create(Loads.class, "loadKlass()", 1),
 150                  BadCountsConstraint.create(Loads.class, "loadKlass()", 2, 2,"Field")
 151                  );
 152 
 153         // Loops
 154         runCheck(BadFailOnConstraint.create(Loops.class, "loop()", 1, "Loop"),
 155                  GoodRuleConstraint.create(Loops.class, "loop()", 2),
 156                  GoodRuleConstraint.create(Loops.class, "loop()", 3),
 157                  GoodRuleConstraint.create(Loops.class, "countedLoop()", 1),
 158                  BadFailOnConstraint.create(Loops.class, "countedLoop()", 2, "CountedLoop"),
 159                  GoodRuleConstraint.create(Loops.class, "countedLoop()", 3),
 160                  BadFailOnConstraint.create(Loops.class, "loopAndCountedLoop()", 1, "Loop"),
 161                  BadFailOnConstraint.create(Loops.class, "loopAndCountedLoop()", 2, "CountedLoop"),
 162                  GoodRuleConstraint.create(Loops.class, "loopAndCountedLoop()", 3),
 163                  GoodRuleConstraint.create(Loops.class, "countedLoopMain()", 1),
 164                  BadFailOnConstraint.create(Loops.class, "countedLoopMain()", 2, "CountedLoop"),
 165                  BadFailOnConstraint.create(Loops.class, "countedLoopMain()", 3, "CountedLoop", "main"),
 166                  GoodRuleConstraint.create(Loops.class, "countedLoopUnrolled()", 1),
 167                  GoodRuleConstraint.create(Loops.class, "countedLoopUnrolled()", 2),
 168                  GoodRuleConstraint.create(Loops.class, "countedLoopUnrolled()", 3)
 169         );
 170 
 171         // Traps
 172         runCheck(GoodRuleConstraint.create(Traps.class, "noTraps()", 1),
 173                  BadFailOnConstraint.create(Traps.class, "noTraps()", 2, "Store", "iFld"),
 174                  GoodRuleConstraint.create(Traps.class, "noTraps()", 3),
 175                  BadFailOnConstraint.create(Traps.class, "predicateTrap()", 1, "CallStaticJava", "uncommon_trap"),
 176                  BadFailOnConstraint.create(Traps.class, "predicateTrap()", 2, "CallStaticJava", "uncommon_trap", "predicate"),
 177                  GoodRuleConstraint.create(Traps.class, "predicateTrap()", 3),
 178                  GoodRuleConstraint.create(Traps.class, "predicateTrap()", 4),
 179                  BadFailOnConstraint.create(Traps.class, "nullCheck()", 1, "CallStaticJava", "uncommon_trap"),
 180                  BadFailOnConstraint.create(Traps.class, "nullCheck()", 2, "CallStaticJava", "uncommon_trap", "null_check"),
 181                  BadFailOnConstraint.create(Traps.class, "nullCheck()", 3, "uncommon_trap", "unstable_if"),
 182                  GoodRuleConstraint.create(Traps.class, "nullCheck()", 4),
 183                  BadFailOnConstraint.create(Traps.class, "nullAssert()", 1, "CallStaticJava", "uncommon_trap"),
 184                  BadFailOnConstraint.create(Traps.class, "nullAssert()", 2, "CallStaticJava", "uncommon_trap", "null_assert"),
 185                  BadFailOnConstraint.create(Traps.class, "nullAssert()", 3, "CallStaticJava", "uncommon_trap", "null_check"),
 186                  GoodRuleConstraint.create(Traps.class, "nullAssert()", 4),
 187                  BadFailOnConstraint.create(Traps.class, "unstableIf(boolean)", 1, "CallStaticJava", "uncommon_trap"),
 188                  BadFailOnConstraint.create(Traps.class, "unstableIf(boolean)",  2, "CallStaticJava", "uncommon_trap", "unstable_if"),
 189                  GoodRuleConstraint.create(Traps.class, "unstableIf(boolean)", 3),
 190                  BadFailOnConstraint.create(Traps.class, "classCheck()", 1, "CallStaticJava", "uncommon_trap"),
 191                  BadFailOnConstraint.create(Traps.class, "classCheck()", 2, "CallStaticJava", "uncommon_trap", "class_check"),
 192                  BadFailOnConstraint.create(Traps.class, "classCheck()", 3, "CallStaticJava", "uncommon_trap", "null_check"),
 193                  GoodRuleConstraint.create(Traps.class, "classCheck()", 4),
 194                  BadFailOnConstraint.create(Traps.class, "rangeCheck()", 1, "CallStaticJava", "uncommon_trap"),
 195                  BadFailOnConstraint.create(Traps.class, "rangeCheck()", 2, "CallStaticJava", "uncommon_trap", "range_check"),
 196                  BadFailOnConstraint.create(Traps.class, "rangeCheck()", 3, "CallStaticJava", "uncommon_trap", "null_check"),
 197                  GoodRuleConstraint.create(Traps.class, "rangeCheck()", 4),
 198                  BadFailOnConstraint.create(Traps.class, "instrinsicOrTypeCheckedInlining()", 1, "CallStaticJava", "uncommon_trap"),
 199                  WhiteBox.getWhiteBox().isJVMCISupportedByGC() ?
 200                     BadFailOnConstraint.create(Traps.class, "instrinsicOrTypeCheckedInlining()", 2, "CallStaticJava", "uncommon_trap", "intrinsic_or_type_checked_inlining")
 201                     : GoodRuleConstraint.create(Traps.class, "instrinsicOrTypeCheckedInlining()", 2),
 202                  BadFailOnConstraint.create(Traps.class, "instrinsicOrTypeCheckedInlining()", 3, "CallStaticJava", "uncommon_trap", "intrinsic"),
 203                  BadFailOnConstraint.create(Traps.class, "instrinsicOrTypeCheckedInlining()", 4, "CallStaticJava", "uncommon_trap", "null_check"),
 204                  GoodRuleConstraint.create(Traps.class, "instrinsicOrTypeCheckedInlining()", 5)
 205         );
 206 
 207 
 208         runCheck(new String[] {"-XX:+BailoutToInterpreterForThrows"},
 209                  BadFailOnConstraint.create(UnhandledTrap.class, "unhandled()", 1, "CallStaticJava", "uncommon_trap"),
 210                  BadFailOnConstraint.create(UnhandledTrap.class, "unhandled()", 2, "CallStaticJava", "uncommon_trap", "unhandled"),
 211                  GoodRuleConstraint.create(UnhandledTrap.class, "unhandled()", 3)
 212         );
 213 
 214         runCheck(BadFailOnConstraint.create(ScopeObj.class, "scopeObject()", 1, "ScObj"));
 215         runCheck(BadFailOnConstraint.create(Membar.class, "membar()", 1, "MemBar"));
 216 
 217         String cmp;
 218         if (Platform.isPPC() || Platform.isX86()) {
 219             cmp = "CMP";
 220         } else if (Platform.isS390x()){
 221             cmp = "CLFI";
 222         } else {
 223             cmp = "cmp";
 224         }
 225         runCheck(BadFailOnConstraint.create(CheckCastArray.class, "array()", 1, cmp, "precise"),
 226                  BadFailOnConstraint.create(CheckCastArray.class, "array()", 2, 1,cmp, "precise", "MyClass"),
 227                  BadFailOnConstraint.create(CheckCastArray.class, "array()", 2, 2,cmp, "precise", "ir_framework/tests/MyClass"),
 228                  GoodFailOnConstraint.create(CheckCastArray.class, "array()", 3),
 229                  Platform.isS390x() ? // There is no checkcast_arraycopy stub for C2 on s390
 230                      GoodFailOnConstraint.create(CheckCastArray.class, "arrayCopy(java.lang.Object[],java.lang.Class)", 1)
 231                      : BadFailOnConstraint.create(CheckCastArray.class, "arrayCopy(java.lang.Object[],java.lang.Class)", 1, "checkcast_arraycopy")
 232         );
 233 
 234         try {
 235             runWithArgumentsFail(CompilationOutputOfFails.class);
 236             Utils.shouldHaveThrownException(baos.toString());
 237         } catch (IRViolationException e) {
 238             try {
 239                 StringBuilder failures = new StringBuilder();
 240                 System.out.flush();
 241                 String output = baos.toString();
 242                 baos.reset();
 243                 Pattern pattern = Pattern.compile(compilationPrefix() + ".*both\\d.*\\R> Phase \""
 244                                                   + CompilePhase.PRINT_IDEAL.getName()
 245                                                   + "\":(?:(?!PrintOpto|" + compilationPrefix()
 246                                                   + ")[\\S\\s])+PrintOptoAssembly");
 247                 Matcher matcher = pattern.matcher(output);
 248                 long bothCount = matcher.results().count();
 249                 if (bothCount != 7L) {
 250                     failures.append("- Could not find all both() methods, expected 7 but found ").append(bothCount)
 251                             .append(System.lineSeparator());
 252                 }
 253                 pattern = Pattern.compile(compilationPrefix() + ".*ideal\\d.*\\R> Phase \""
 254                                           + CompilePhase.PRINT_IDEAL.getName()
 255                                           + "\":(?:(?!" + compilationPrefix() + ")[\\S\\s])+");
 256                 matcher = pattern.matcher(output);
 257                 int count = 0;
 258                 while (matcher.find()) {
 259                     String match = matcher.group();
 260                     if (match.contains("PrintOptoAssembly")) {
 261                         failures.append("Cannot contain opto assembly: ").append(System.lineSeparator()).append(match);
 262                     }
 263                     count++;
 264                 }
 265                 if (count != 7) {
 266                     failures.append("- Could not find all ideal() methods, expected 7 but found ").append(count)
 267                             .append(System.lineSeparator());
 268                 }
 269                 pattern = Pattern.compile(compilationPrefix() + ".*opto\\d.*\\R> Phase \"PrintOptoAssembly\":(?:(?!"
 270                                           + compilationPrefix()  + ")[\\S\\s])+");
 271                 matcher = pattern.matcher(output);
 272                 count = 0;
 273                 while (matcher.find()) {
 274                     String match = matcher.group();
 275                     if (match.contains("PrintIdeal")) {
 276                         failures.append("Cannot contain print assembly: ").append(System.lineSeparator()).append(match);
 277                     }
 278                     count++;
 279                 }
 280                 if (count != 7) {
 281                     failures.append("- Could not find all opto() methods, expected 7 but found ").append(count).append(System.lineSeparator());
 282                 }
 283                 if (!failures.isEmpty()) {
 284                     addException(new RuntimeException(failures.toString()));
 285                 }
 286             } catch (Exception e1) {
 287                 addException(e1);
 288             }
 289         } catch (Exception e) {
 290             addException(e);
 291         }
 292 
 293         runWithArguments(FlagComparisons.class, "-XX:TLABRefillWasteFraction=50");
 294         System.out.flush();
 295         String output = baos.toString();
 296         findIrIds(output, "testMatchAllIf50", 1, 22);
 297         findIrIds(output, "testMatchNoneIf50", -1, -1);
 298 
 299         runWithArguments(FlagComparisons.class, "-XX:TLABRefillWasteFraction=49");
 300         System.out.flush();
 301         output = baos.toString();
 302         findIrIds(output, "testMatchAllIf50", 5, 7, 14, 19);
 303         findIrIds(output, "testMatchNoneIf50", 1, 4, 9, 11, 18, 23);
 304 
 305         runWithArguments(FlagComparisons.class, "-XX:TLABRefillWasteFraction=51");
 306         System.out.flush();
 307         output = baos.toString();
 308         findIrIds(output, "testMatchAllIf50", 8, 13, 20, 22);
 309         findIrIds(output, "testMatchNoneIf50", 5, 8, 12, 17, 21, 23);
 310         System.setOut(oldOut);
 311         System.setErr(oldErr);
 312 
 313         if (!exceptions.isEmpty()) {
 314             System.err.println("TestIRMatching failed with " + exceptions.size() + " exception(s):");
 315             int i = 1;
 316             System.err.println("************************");
 317             for (Map.Entry<Exception, String> entry : exceptions.entrySet()) {
 318                 System.err.println("***** Exception " + String.format("%02d", i++) +" *****");
 319                 System.err.println("************************");
 320 
 321                 Exception e = entry.getKey();
 322                 e.printStackTrace(System.err);
 323                 System.err.println();
 324                 System.err.println("===== OUTPUT ======");
 325                 System.err.println(entry.getValue());
 326                 System.err.println("MESSAGE: " + e.getMessage());
 327                 System.err.println("************************");
 328             }
 329             i = 1;
 330             System.err.println("====================================");
 331             System.err.println("********************");
 332             System.err.println("***** OVERVIEW *****");
 333             System.err.println("********************");
 334             for (Map.Entry<Exception, String> entry : exceptions.entrySet()) {
 335                 System.err.print((i++) + ") ");
 336                 entry.getKey().printStackTrace(System.err);
 337                 System.err.println("********************");
 338             }
 339             throw new RuntimeException("TestIRMatching failed with " + exceptions.size() + " exception(s) - check stderr and stdout");
 340         }
 341     }
 342 
 343     private static void runFramework(TestFramework framework) {
 344         baos.reset();
 345         baosErr.reset();
 346         framework.start();
 347     }
 348 
 349     private static void runWithArguments(Class<?> clazz, String... args) {
 350         try {
 351             runFramework(new TestFramework(clazz).addFlags(args));
 352         } catch (Exception e) {
 353             addException(e);
 354         }
 355     }
 356 
 357     private static void runWithArgumentsFail(Class<?> clazz, String... args) {
 358         runFramework(new TestFramework(clazz).addFlags(args));
 359     }
 360 
 361     private static void runCheck(String[] args , Constraint... constraints) {
 362         try {
 363             TestFramework framework = new TestFramework(constraints[0].getKlass()); // All constraints have the same class.
 364             if (args != null) {
 365                 framework.addFlags(args);
 366             }
 367             runFramework(framework);
 368             Utils.shouldHaveThrownException(baos.toString());
 369         } catch (IRViolationException e) {
 370             checkConstraints(e, constraints);
 371         } catch (Exception e) {
 372             addException(e);
 373         }
 374     }
 375 
 376     private static void runCheck(Constraint... constraints) {
 377         runCheck(null, constraints);
 378     }
 379 
 380     private static void checkConstraints(IRViolationException e, Constraint[] constraints) {
 381         String message = e.getExceptionInfo();
 382         try {
 383             for (Constraint constraint : constraints) {
 384                 constraint.checkConstraint(e);
 385             }
 386         } catch (Exception e1) {
 387             System.out.println(e.getCompilations());
 388             System.out.println(message);
 389             addException(e1);
 390         }
 391     }
 392 
 393     private static String compilationPrefix() {
 394         return "\\d\\) Compilation";
 395     }
 396 
 397     private static void findIrIds(String output, String method, int... numbers) {
 398         StringBuilder builder = new StringBuilder();
 399         builder.append(method);
 400         for (int i = 0; i < numbers.length; i+=2) {
 401             int start = numbers[i];
 402             int endIncluded = numbers[i + 1];
 403             for (int j = start; j <= endIncluded; j++) {
 404                 builder.append(",");
 405                 builder.append(j);
 406             }
 407         }
 408         if (!output.contains(builder.toString())) {
 409             addException(new RuntimeException("Could not find encoding: \"" + builder + System.lineSeparator()));
 410         }
 411     }
 412 }
 413 
 414 class AndOr1 {
 415     @Test
 416     @Arguments(Argument.DEFAULT)
 417     @IR(applyIfAnd = {"UsePerfData", "true", "TLABRefillWasteFraction", "50", "UseTLAB", "true"}, failOn = {IRNode.CALL})
 418     public void test1(int i) {
 419         dontInline();
 420     }
 421 
 422     @Test
 423     @IR(applyIfOr = {"UsePerfData", "false", "TLABRefillWasteFraction", "51", "UseTLAB", "false"}, failOn = {IRNode.CALL})
 424     public void test2() {
 425         dontInline();
 426     }
 427 
 428     @DontInline
 429     private void dontInline() {
 430     }
 431 }
 432 
 433 class MultipleFailOnGood {
 434     private int iFld;
 435     private MyClassSub myClassSub = new MyClassSub();
 436 
 437     @Test
 438     @IR(applyIf = {"TLABRefillWasteFraction", "50"}, failOn = {IRNode.STORE, IRNode.CALL})
 439     @IR(failOn = {IRNode.STORE, IRNode.CALL})
 440     @IR(applyIfOr = {"TLABRefillWasteFraction", "99", "TLABRefillWasteFraction", "100"}, failOn = {IRNode.LOOP, IRNode.CALL}) // Not applied
 441     public void good1() {
 442         forceInline();
 443     }
 444 
 445     @Test
 446     @IR(failOn = {IRNode.STORE, IRNode.CALL})
 447     @IR(applyIfNot = {"TLABRefillWasteFraction", "20"}, failOn = {IRNode.ALLOC})
 448     @IR(applyIfNot = {"TLABRefillWasteFraction", "< 100"}, failOn = {IRNode.ALLOC_OF, "Test"})
 449     public void good2() {
 450         forceInline();
 451     }
 452 
 453     @Test
 454     @IR(failOn = {IRNode.STORE_OF_CLASS, "Test", IRNode.CALL})
 455     @IR(applyIfNot = {"TLABRefillWasteFraction", "20"}, failOn = {IRNode.ALLOC})
 456     @IR(applyIfNot = {"TLABRefillWasteFraction", "< 100"}, failOn = {IRNode.ALLOC_OF, "Test"})
 457     public void good3() {
 458         forceInline();
 459     }
 460 
 461     @Test
 462     @IR(failOn = {IRNode.CALL, IRNode.STORE_OF_CLASS, "UnknownClass"})
 463     public void good4() {
 464         iFld = 42;
 465     }
 466 
 467     @Test
 468     @IR(failOn = {IRNode.STORE_OF_FIELD, "xFld", IRNode.CALL})
 469     public void good5() {
 470         iFld = 42;
 471     }
 472 
 473     @Test
 474     @IR(failOn = {IRNode.STORE_OF_CLASS, "MyClass"}) // Needs exact match to fail
 475     public void good6() {
 476         myClassSub.iFld = 42;
 477     }
 478 
 479     @Test
 480     @IR(failOn = {IRNode.STORE_OF_CLASS, "MyClassSub"}) // Static write is with Class and not MySubClass
 481     public void good7() {
 482         MyClassSub.iFldStatic = 42;
 483     }
 484 
 485     @ForceInline
 486     private void forceInline() {}
 487 }
 488 
 489 class MultipleFailOnBad {
 490     private int iFld;
 491     private int myInt;
 492     private MyClassEmpty myClass;
 493 
 494     @Test
 495     @IR(failOn = {IRNode.STORE, IRNode.CALL, IRNode.STORE_I, IRNode.LOOP})
 496     public void fail1() {
 497         iFld = 42;
 498     }
 499 
 500     @Test
 501     @IR(failOn = {IRNode.STORE, IRNode.CALL})
 502     public void fail2() {
 503         dontInline();
 504     }
 505 
 506     @Test
 507     @IR(failOn = {IRNode.CALL, IRNode.STORE_OF_CLASS, "MultipleFailOnBad", IRNode.ALLOC})
 508     public void fail3() {
 509         iFld = 42;
 510     }
 511 
 512     @Test
 513     @IR(failOn = {IRNode.STORE_OF_CLASS, "ir_framework/tests/MultipleFailOnBad", IRNode.CALL, IRNode.ALLOC})
 514     public void fail4() {
 515         iFld = 42;
 516     }
 517 
 518     @Test
 519     @IR(failOn = {IRNode.STORE_OF_FIELD, "iFld", IRNode.CALL, IRNode.ALLOC})
 520     public void fail5() {
 521         iFld = 42;
 522     }
 523 
 524     @Test
 525     @IR(failOn = {IRNode.STORE_OF_CLASS, "MyClassEmpty", IRNode.ALLOC, IRNode.CALL})
 526     public void fail6() {
 527         myClass = new MyClassEmpty();
 528     }
 529 
 530     @Test
 531     @IR(failOn = {IRNode.STORE_OF_CLASS, "UnknownClass", IRNode.ALLOC_OF, "MyClassEmpty"})
 532     public void fail7() {
 533         myClass = new MyClassEmpty();
 534     }
 535 
 536     @Test
 537     @IR(failOn = {IRNode.STORE_OF_CLASS, "UnknownClass", IRNode.ALLOC_OF, "ir_framework/tests/MyClassEmptySub"})
 538     public void fail8() {
 539         myClass = new MyClassEmptySub();
 540     }
 541 
 542     @Test
 543     @IR(failOn = {IRNode.STORE, IRNode.CALL})
 544     public void fail9() {
 545         iFld = 42;
 546         dontInline();
 547     }
 548 
 549     @Test
 550     @IR(failOn = {IRNode.STORE_OF_FIELD, "iFld", IRNode.CALL, IRNode.ALLOC})
 551     public void fail10() {
 552         myInt = 34;
 553         iFld = 42;
 554     }
 555 
 556     @DontInline
 557     private void dontInline() {}
 558 }
 559 
 560 // Called with -XX:TLABRefillWasteFraction=X.
 561 class FlagComparisons {
 562     // Applies all IR rules if TLABRefillWasteFraction=50
 563     @Test
 564     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", "50"}) // Index 1
 565     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", "=50"})
 566     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", "= 50"})
 567     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", " =   50"})
 568     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", "<=50"}) // Index 5
 569     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", "<= 50"})
 570     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", " <=  50"})
 571     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", ">=50"}) // Index 8
 572     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", ">= 50"})
 573     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", " >=  50"})
 574     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", ">49"})
 575     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", "> 49"})
 576     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", " >  49"})
 577     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", "<51"}) // Index 14
 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", " !=  51"})
 583     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", "!=49"})
 584     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", "!= 49"})
 585     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", " !=  49"}) // Index 22
 586     public void testMatchAllIf50() {}
 587 
 588     // Applies no IR rules if TLABRefillWasteFraction=50
 589     @Test
 590     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", "49"}) // Index 1
 591     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", "=49"})
 592     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", "= 49"})
 593     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", " =  49"})
 594     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", "51"}) // Index 5
 595     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", "=51"})
 596     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", "= 51"})
 597     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", " =  51"})
 598     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", "<=49"}) // Index 9
 599     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", "<= 49"})
 600     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", " <=  49"})
 601     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", ">=51"}) // Index 12
 602     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", ">= 51"})
 603     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", " >=  51"})
 604     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", ">50"})
 605     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", "> 50"})
 606     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", " >  50"})
 607     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", "<50"}) // Index 18
 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"})
 612     @IR(failOn = IRNode.CALL, applyIf = {"TLABRefillWasteFraction", " !=  50"}) // Index 23
 613     public void testMatchNoneIf50() {}
 614 }
 615 
 616 class CountComparisons {
 617     int iFld;
 618 
 619     @Test
 620     @IR(counts = {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, "  <=  1",
 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, "  >  0",
 640                   IRNode.STORE, "< 2",
 641                   IRNode.STORE, "<2",
 642                   IRNode.STORE, " < 2",
 643                   IRNode.STORE, "  <  2",
 644     })
 645     public void countComparison() {
 646         iFld = 3;
 647     }
 648 }
 649 
 650 class GoodCount {
 651     boolean flag;
 652     char cFld;
 653     byte bFld;
 654     short sFld;
 655     int iFld;
 656     long lFld;
 657     float fFld;
 658     double dFld;
 659     long x;
 660 
 661     long result;
 662     MyClass myClass = new MyClass();
 663     MyClassEmpty myClassEmpty = new MyClassEmpty();
 664     MyClass myClassSubPoly = new MyClassSub();
 665     MyClassSub myClassSub = new MyClassSub();
 666 
 667     @Test
 668     @IR(counts = {IRNode.STORE, "1", IRNode.STORE_I, "1"},
 669         failOn = {IRNode.STORE_B, IRNode.STORE_C, IRNode.STORE_D,
 670                   IRNode.STORE_F, IRNode.STORE_L})
 671     public void good1() {
 672         iFld = 3;
 673     }
 674 
 675     @Test
 676     @IR(counts = {IRNode.STORE, "8",
 677                   IRNode.STORE_B, "2", // bFld + flag
 678                   IRNode.STORE_C, "2", // cFld + sFld
 679                   IRNode.STORE_I, "1",
 680                   IRNode.STORE_L, "1",
 681                   IRNode.STORE_F, "1",
 682                   IRNode.STORE_D, "1"})
 683     public void good2() {
 684         flag = true;
 685         cFld = 'a';
 686         bFld = 1;
 687         sFld = 2;
 688         iFld = 3;
 689         lFld = 4L;
 690         fFld = 5.0f;
 691         dFld = 6.0;
 692     }
 693 
 694     @Test
 695     @IR(counts = {IRNode.STORE, "8", IRNode.STORE_OF_CLASS, "GoodCount", "8",
 696                   IRNode.STORE_B, "2", IRNode.STORE_B_OF_CLASS, "GoodCount", "2",
 697                   IRNode.STORE_C, "2", IRNode.STORE_C_OF_CLASS, "GoodCount", "2",
 698                   IRNode.STORE_I, "1", IRNode.STORE_I_OF_CLASS, "GoodCount", "1",
 699                   IRNode.STORE_L, "1", IRNode.STORE_L_OF_CLASS, "GoodCount", "1",
 700                   IRNode.STORE_F, "1", IRNode.STORE_F_OF_CLASS, "GoodCount", "1",
 701                   IRNode.STORE_D, "1", IRNode.STORE_D_OF_CLASS, "GoodCount", "1"})
 702     public void good3() {
 703         flag = true;
 704         cFld = 'a';
 705         bFld = 1;
 706         sFld = 2;
 707         iFld = 3;
 708         lFld = 4L;
 709         fFld = 5.0f;
 710         dFld = 6.0;
 711     }
 712 
 713     @Test
 714     @IR(counts = {IRNode.STORE, "8", IRNode.STORE_OF_CLASS, "GoodCount", "8",
 715                   IRNode.STORE_B, "2", IRNode.STORE_B_OF_CLASS, "GoodCount", "2",
 716                   IRNode.STORE_C, "2", IRNode.STORE_C_OF_CLASS, "GoodCount", "2",
 717                   IRNode.STORE_I, "1", IRNode.STORE_I_OF_CLASS, "GoodCount", "1",
 718                   IRNode.STORE_L, "1", IRNode.STORE_L_OF_CLASS, "GoodCount", "1",
 719                   IRNode.STORE_F, "1", IRNode.STORE_F_OF_CLASS, "GoodCount", "1",
 720                   IRNode.STORE_D, "1", IRNode.STORE_D_OF_CLASS, "GoodCount", "1",
 721                   IRNode.STORE_OF_FIELD, "lFld", "1"})
 722     public void good4() {
 723         flag = true;
 724         cFld = 'a';
 725         bFld = 1;
 726         sFld = 2;
 727         iFld = 3;
 728         lFld = 4L;
 729         fFld = 5.0f;
 730         dFld = 6.0;
 731     }
 732 
 733     @Test
 734     @IR(counts = {IRNode.STORE, "2", IRNode.STORE_I, "1", IRNode.STORE_L, "1",
 735                   IRNode.STORE_OF_CLASS, "GoodCount", "1", IRNode.STORE_L_OF_CLASS, "GoodCount", "1",
 736                   IRNode.STORE_OF_CLASS, "ir_framework/tests/MyClass", "1",
 737                   IRNode.STORE_I_OF_CLASS, "ir_framework/tests/MyClass", "1",
 738                   IRNode.STORE_OF_CLASS, "ir_framework/tests/GoodCount", "1",
 739                   IRNode.STORE_L_OF_CLASS, "ir_framework/tests/GoodCount", "1",
 740                   IRNode.STORE_OF_FIELD, "x", "2"})
 741     public void good5() {
 742         x = 3; // long
 743         myClass.x = 4; // int
 744     }
 745 
 746     @Test
 747     @IR(counts = {IRNode.STORE_OF_FIELD, "myClassEmpty", "1", IRNode.STORE_OF_CLASS, "GoodCount", "1",
 748                   IRNode.STORE_OF_CLASS, "/GoodCount", "1", IRNode.STORE_OF_CLASS, "MyClassEmpty", "0"},
 749         failOn = {IRNode.STORE_OF_CLASS, "MyClassEmpty"})
 750     public void good6() {
 751         myClassEmpty = new MyClassEmpty();
 752     }
 753 
 754     @Test
 755     @IR(counts = {IRNode.STORE_OF_FIELD, "iFld", "3", IRNode.STORE_OF_CLASS, "GoodCount", "0",
 756                   IRNode.STORE_OF_CLASS, "MyClass", "2", IRNode.STORE_OF_CLASS, "MyClassSub", "1",
 757                   IRNode.STORE, "3"},
 758         failOn = {IRNode.STORE_OF_CLASS, "GoodCount"})
 759     public void good7() {
 760         myClass.iFld = 1;
 761         myClassSubPoly.iFld = 2;
 762         myClassSub.iFld = 3;
 763     }
 764 
 765     @Test
 766     @IR(counts = {IRNode.LOAD, "1", IRNode.STORE, "1"})
 767     public void good8() {
 768         result = iFld;
 769     }
 770 
 771 
 772     @Test
 773     @IR(counts = {IRNode.LOAD, "4", IRNode.STORE, "1", IRNode.LOAD_OF_FIELD, "iFld", "2", IRNode.LOAD_OF_FIELD, "iFld2", "0",
 774                   IRNode.LOAD_OF_FIELD, "lFldStatic", "1", IRNode.LOAD_OF_CLASS, "GoodCount", "2", IRNode.LOAD_OF_CLASS, "MyClass", "1",
 775                   IRNode.STORE_OF_CLASS, "GoodCount", "1", IRNode.STORE_OF_FIELD, "result", "1",
 776                   IRNode.LOAD_OF_FIELD, "myClass", "1"})
 777     public void good9() {
 778         result = iFld + MyClass.lFldStatic + myClass.iFld; // 1 + 1 + 2 loads (myClass is LoadN of GoodCount and myClass.iFld a LoadI of MyClass)
 779     }
 780 
 781     @Test
 782     @IR(counts = {IRNode.LOAD, "8",
 783                   IRNode.LOAD_B, "1",
 784                   IRNode.LOAD_UB, "1",
 785                   IRNode.LOAD_S, "1",
 786                   IRNode.LOAD_US, "1",
 787                   IRNode.LOAD_I, "1",
 788                   IRNode.LOAD_L, "1",
 789                   IRNode.LOAD_F, "1",
 790                   IRNode.LOAD_D, "1"})
 791     public void good10() {
 792         bFld++;
 793         cFld++;
 794         sFld++;
 795         iFld++;
 796         lFld++;
 797         fFld++;
 798         dFld++;
 799         flag = !flag;
 800     }
 801 
 802     @Test
 803     @IR(counts = {IRNode.LOAD, "8", IRNode.LOAD_OF_CLASS, "GoodCount", "8",
 804                   IRNode.LOAD_B, "1", IRNode.LOAD_B_OF_CLASS, "GoodCount", "1",
 805                   IRNode.LOAD_UB, "1", IRNode.LOAD_UB_OF_CLASS, "GoodCount", "1",
 806                   IRNode.LOAD_S, "1", IRNode.LOAD_S_OF_CLASS, "GoodCount", "1",
 807                   IRNode.LOAD_US, "1", IRNode.LOAD_US_OF_CLASS, "GoodCount", "1",
 808                   IRNode.LOAD_I, "1", IRNode.LOAD_I_OF_CLASS, "GoodCount", "1",
 809                   IRNode.LOAD_L, "1", IRNode.LOAD_L_OF_CLASS, "GoodCount", "1",
 810                   IRNode.LOAD_F, "1", IRNode.LOAD_F_OF_CLASS, "GoodCount", "1",
 811                   IRNode.LOAD_D, "1", IRNode.LOAD_D_OF_CLASS, "GoodCount", "1"})
 812     public void good11() {
 813         bFld++;
 814         cFld++;
 815         sFld++;
 816         iFld++;
 817         lFld++;
 818         fFld++;
 819         dFld++;
 820         flag = !flag;
 821     }
 822 }
 823 
 824 class BadCount {
 825     int iFld;
 826     int result;
 827     @Test
 828     @IR(counts = {IRNode.LOAD, "!= 1"}) // fail
 829     @IR(counts = {IRNode.STORE, "> 0"})
 830     public void bad1() {
 831         result = iFld;
 832     }
 833 
 834     @Test
 835     @IR(counts = {IRNode.LOAD, "1"}) // fail
 836     @IR(counts = {IRNode.STORE, "< 1"})
 837     public void bad2() {
 838         result = iFld;
 839     }
 840 
 841 
 842     @Test
 843     @IR(counts = {IRNode.LOAD, "0"}) // fail
 844     @IR(counts = {IRNode.STORE, " <= 0"}) // fail
 845     public void bad3() {
 846         result = iFld;
 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 }