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