1 /*
   2  * Copyright (c) 2014, 2018, 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 
  25 package org.graalvm.compiler.core.test;
  26 
  27 import java.io.File;
  28 import java.io.IOException;
  29 import java.io.PrintWriter;
  30 import java.io.StringWriter;
  31 import java.lang.annotation.Annotation;
  32 import java.lang.reflect.Method;
  33 import java.lang.reflect.Modifier;
  34 import java.util.ArrayList;
  35 import java.util.Collections;
  36 import java.util.Enumeration;
  37 import java.util.List;
  38 import java.util.concurrent.LinkedBlockingQueue;
  39 import java.util.concurrent.ThreadPoolExecutor;
  40 import java.util.concurrent.TimeUnit;
  41 import java.util.zip.ZipEntry;
  42 import java.util.zip.ZipFile;
  43 
  44 import org.graalvm.compiler.api.replacements.MethodSubstitution;
  45 import org.graalvm.compiler.api.replacements.Snippet;
  46 import org.graalvm.compiler.api.replacements.Snippet.ConstantParameter;
  47 import org.graalvm.compiler.api.replacements.Snippet.NonNullParameter;
  48 import org.graalvm.compiler.api.replacements.Snippet.VarargsParameter;
  49 import org.graalvm.compiler.api.test.Graal;
  50 import org.graalvm.compiler.bytecode.BridgeMethodUtils;
  51 import org.graalvm.compiler.core.CompilerThreadFactory;
  52 import org.graalvm.compiler.core.common.LIRKind;
  53 import org.graalvm.compiler.core.common.type.ArithmeticOpTable;
  54 import org.graalvm.compiler.debug.DebugCloseable;
  55 import org.graalvm.compiler.debug.DebugContext;
  56 import org.graalvm.compiler.debug.DebugHandlersFactory;
  57 import org.graalvm.compiler.debug.GraalError;
  58 import org.graalvm.compiler.graph.Node;
  59 import org.graalvm.compiler.graph.NodeClass;
  60 import org.graalvm.compiler.java.GraphBuilderPhase;
  61 import org.graalvm.compiler.nodeinfo.NodeInfo;
  62 import org.graalvm.compiler.nodes.PhiNode;
  63 import org.graalvm.compiler.nodes.StructuredGraph;
  64 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
  65 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration;
  66 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins;
  67 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins;
  68 import org.graalvm.compiler.options.OptionValues;
  69 import org.graalvm.compiler.phases.OptimisticOptimizations;
  70 import org.graalvm.compiler.phases.PhaseSuite;
  71 import org.graalvm.compiler.phases.VerifyPhase;
  72 import org.graalvm.compiler.phases.VerifyPhase.VerificationError;
  73 import org.graalvm.compiler.phases.contract.VerifyNodeCosts;
  74 import org.graalvm.compiler.phases.tiers.HighTierContext;
  75 import org.graalvm.compiler.phases.tiers.PhaseContext;
  76 import org.graalvm.compiler.phases.util.Providers;
  77 import org.graalvm.compiler.runtime.RuntimeProvider;
  78 import jdk.internal.vm.compiler.word.LocationIdentity;
  79 import org.junit.Assert;
  80 import org.junit.Assume;
  81 import org.junit.Test;
  82 
  83 import jdk.vm.ci.code.BailoutException;
  84 import jdk.vm.ci.code.Register;
  85 import jdk.vm.ci.code.Register.RegisterCategory;
  86 import jdk.vm.ci.meta.JavaField;
  87 import jdk.vm.ci.meta.JavaMethod;
  88 import jdk.vm.ci.meta.JavaType;
  89 import jdk.vm.ci.meta.MetaAccessProvider;
  90 import jdk.vm.ci.meta.ResolvedJavaMethod;
  91 import jdk.vm.ci.meta.ResolvedJavaType;
  92 import jdk.vm.ci.meta.Value;
  93 
  94 /**
  95  * Checks that all classes in *graal*.jar and *jvmci*.jar entries on the boot class path comply with
  96  * global invariants such as using {@link Object#equals(Object)} to compare certain types instead of
  97  * identity comparisons.
  98  */
  99 public class CheckGraalInvariants extends GraalCompilerTest {
 100 
 101     private static boolean shouldVerifyEquals(ResolvedJavaMethod m) {
 102         if (m.getName().equals("identityEquals")) {
 103             ResolvedJavaType c = m.getDeclaringClass();
 104             if (c.getName().equals("Ljdk/vm/ci/meta/AbstractValue;") || c.getName().equals("jdk/vm/ci/meta/Value")) {
 105                 return false;
 106             }
 107         }
 108 
 109         return true;
 110     }
 111 
 112     public static String relativeFileName(String absolutePath) {
 113         int lastFileSeparatorIndex = absolutePath.lastIndexOf(File.separator);
 114         return absolutePath.substring(lastFileSeparatorIndex >= 0 ? lastFileSeparatorIndex : 0);
 115     }
 116 
 117     public static class InvariantsTool {
 118 
 119         protected boolean shouldProcess(String classpathEntry) {
 120             if (classpathEntry.endsWith(".jar")) {
 121                 String name = new File(classpathEntry).getName();
 122                 return name.contains("jvmci") || name.contains("graal") || name.contains("jdk.internal.vm.compiler");
 123             }
 124             return false;
 125         }
 126 
 127         protected String getClassPath() {
 128             String bootclasspath;
 129             if (Java8OrEarlier) {
 130                 bootclasspath = System.getProperty("sun.boot.class.path");
 131             } else {
 132                 bootclasspath = System.getProperty("jdk.module.path") + File.pathSeparatorChar + System.getProperty("jdk.module.upgrade.path");
 133             }
 134             return bootclasspath;
 135         }
 136 
 137         protected boolean shouldLoadClass(String className) {
 138             if (className.equals("module-info") || className.startsWith("META-INF.versions.")) {
 139                 return false;
 140             }
 141             if (!Java8OrEarlier) {
 142                 // @formatter:off
 143                 /*
 144                  * Work around to prevent:
 145                  *
 146                  * org.graalvm.compiler.debug.GraalError: java.lang.IllegalAccessError: class org.graalvm.compiler.serviceprovider.GraalServices$Lazy (in module
 147                  * jdk.internal.vm.compiler) cannot access class java.lang.management.ManagementFactory (in module java.management) because module
 148                  * jdk.internal.vm.compiler does not read module java.management
 149                  *     at jdk.internal.vm.compiler/org.graalvm.compiler.debug.GraalError.shouldNotReachHere(GraalError.java:55)
 150                  *     at org.graalvm.compiler.core.test.CheckGraalInvariants$InvariantsTool.handleClassLoadingException(CheckGraalInvariants.java:149)
 151                  *     at org.graalvm.compiler.core.test.CheckGraalInvariants.initializeClasses(CheckGraalInvariants.java:321)
 152                  *     at org.graalvm.compiler.core.test.CheckGraalInvariants.runTest(CheckGraalInvariants.java:239)
 153                  *
 154                  * which occurs because JDK8 overlays are in modular jars. They are never used normally.
 155                  */
 156                 // @formatter:on
 157                 if (className.equals("org.graalvm.compiler.serviceprovider.GraalServices$Lazy")) {
 158                     return false;
 159                 }
 160             }
 161             return true;
 162         }
 163 
 164         protected void handleClassLoadingException(Throwable t) {
 165             GraalError.shouldNotReachHere(t);
 166         }
 167 
 168         protected void handleParsingException(Throwable t) {
 169             GraalError.shouldNotReachHere(t);
 170         }
 171 
 172         public boolean shouldVerifyFoldableMethods() {
 173             return true;
 174         }
 175     }
 176 
 177     @Test
 178     @SuppressWarnings("try")
 179     public void test() {
 180         assumeManagementLibraryIsLoadable();
 181         runTest(new InvariantsTool());
 182     }
 183 
 184     @SuppressWarnings("try")
 185     public static void runTest(InvariantsTool tool) {
 186         RuntimeProvider rt = Graal.getRequiredCapability(RuntimeProvider.class);
 187         Providers providers = rt.getHostBackend().getProviders();
 188         MetaAccessProvider metaAccess = providers.getMetaAccess();
 189 
 190         PhaseSuite<HighTierContext> graphBuilderSuite = new PhaseSuite<>();
 191         Plugins plugins = new Plugins(new InvocationPlugins());
 192         GraphBuilderConfiguration config = GraphBuilderConfiguration.getDefault(plugins).withEagerResolving(true).withUnresolvedIsError(true);
 193         graphBuilderSuite.appendPhase(new GraphBuilderPhase(config));
 194         HighTierContext context = new HighTierContext(providers, graphBuilderSuite, OptimisticOptimizations.NONE);
 195 
 196         Assume.assumeTrue(VerifyPhase.class.desiredAssertionStatus());
 197 
 198         String bootclasspath = tool.getClassPath();
 199         Assert.assertNotNull("Cannot find boot class path", bootclasspath);
 200 
 201         final List<String> classNames = new ArrayList<>();
 202         for (String path : bootclasspath.split(File.pathSeparator)) {
 203             if (tool.shouldProcess(path)) {
 204                 try {
 205                     final ZipFile zipFile = new ZipFile(new File(path));
 206                     for (final Enumeration<? extends ZipEntry> entry = zipFile.entries(); entry.hasMoreElements();) {
 207                         final ZipEntry zipEntry = entry.nextElement();
 208                         String name = zipEntry.getName();
 209                         if (name.endsWith(".class") && !name.startsWith("META-INF/versions/")) {
 210                             String className = name.substring(0, name.length() - ".class".length()).replace('/', '.');
 211                             if (isInNativeImage(className)) {
 212                                 /*
 213                                  * Native Image is an external tool and does not need to follow the
 214                                  * Graal invariants.
 215                                  */
 216                                 continue;
 217                             }
 218                             if (isGSON(className)) {
 219                                 /*
 220                                  * GSON classes are compiled with old JDK
 221                                  */
 222                                 continue;
 223                             }
 224                             classNames.add(className);
 225                         }
 226                     }
 227                 } catch (IOException ex) {
 228                     Assert.fail(ex.toString());
 229                 }
 230             }
 231         }
 232         Assert.assertFalse("Could not find graal jars on boot class path: " + bootclasspath, classNames.isEmpty());
 233 
 234         // Allows a subset of methods to be checked through use of a system property
 235         String property = System.getProperty(CheckGraalInvariants.class.getName() + ".filters");
 236         String[] filters = property == null ? null : property.split(",");
 237 
 238         OptionValues options = getInitialOptions();
 239         CompilerThreadFactory factory = new CompilerThreadFactory("CheckInvariantsThread");
 240         int availableProcessors = Runtime.getRuntime().availableProcessors();
 241         ThreadPoolExecutor executor = new ThreadPoolExecutor(availableProcessors, availableProcessors, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), factory);
 242 
 243         List<String> errors = Collections.synchronizedList(new ArrayList<>());
 244 
 245         List<VerifyPhase<PhaseContext>> verifiers = new ArrayList<>();
 246 
 247         // If you add a new type to test here, be sure to add appropriate
 248         // methods to the BadUsageWithEquals class below
 249         verifiers.add(new VerifyUsageWithEquals(Value.class));
 250         verifiers.add(new VerifyUsageWithEquals(Register.class));
 251         verifiers.add(new VerifyUsageWithEquals(RegisterCategory.class));
 252         verifiers.add(new VerifyUsageWithEquals(JavaType.class));
 253         verifiers.add(new VerifyUsageWithEquals(JavaMethod.class));
 254         verifiers.add(new VerifyUsageWithEquals(JavaField.class));
 255         verifiers.add(new VerifyUsageWithEquals(LocationIdentity.class));
 256         verifiers.add(new VerifyUsageWithEquals(LIRKind.class));
 257         verifiers.add(new VerifyUsageWithEquals(ArithmeticOpTable.class));
 258         verifiers.add(new VerifyUsageWithEquals(ArithmeticOpTable.Op.class));
 259 
 260         verifiers.add(new VerifyDebugUsage());
 261         verifiers.add(new VerifyCallerSensitiveMethods());
 262         verifiers.add(new VerifyVirtualizableUsage());
 263         verifiers.add(new VerifyUpdateUsages());
 264         verifiers.add(new VerifyBailoutUsage());
 265         verifiers.add(new VerifyInstanceOfUsage());
 266         verifiers.add(new VerifyGraphAddUsage());
 267         verifiers.add(new VerifyGetOptionsUsage());
 268 
 269         VerifyFoldableMethods foldableMethodsVerifier = new VerifyFoldableMethods();
 270         if (tool.shouldVerifyFoldableMethods()) {
 271             verifiers.add(foldableMethodsVerifier);
 272         }
 273 
 274         for (Method m : BadUsageWithEquals.class.getDeclaredMethods()) {
 275             ResolvedJavaMethod method = metaAccess.lookupJavaMethod(m);
 276             try (DebugContext debug = DebugContext.create(options, DebugHandlersFactory.LOADER)) {
 277                 StructuredGraph graph = new StructuredGraph.Builder(options, debug, AllowAssumptions.YES).method(method).build();
 278                 try (DebugCloseable s = debug.disableIntercept(); DebugContext.Scope ds = debug.scope("CheckingGraph", graph, method)) {
 279                     graphBuilderSuite.apply(graph, context);
 280                     // update phi stamps
 281                     graph.getNodes().filter(PhiNode.class).forEach(PhiNode::inferStamp);
 282                     checkGraph(verifiers, context, graph);
 283                     errors.add(String.format("Expected error while checking %s", m));
 284                 } catch (VerificationError e) {
 285                     // expected!
 286                 } catch (Throwable e) {
 287                     errors.add(String.format("Error while checking %s:%n%s", m, printStackTraceToString(e)));
 288                 }
 289             }
 290         }
 291         if (errors.isEmpty()) {
 292             // Order outer classes before the inner classes
 293             classNames.sort((String a, String b) -> a.compareTo(b));
 294             // Initialize classes in single thread to avoid deadlocking issues during initialization
 295             List<Class<?>> classes = initializeClasses(tool, classNames);
 296             for (Class<?> c : classes) {
 297                 String className = c.getName();
 298                 executor.execute(() -> {
 299                     try {
 300                         checkClass(c, metaAccess);
 301                     } catch (Throwable e) {
 302                         errors.add(String.format("Error while checking %s:%n%s", className, printStackTraceToString(e)));
 303                     }
 304                 });
 305 
 306                 for (Method m : c.getDeclaredMethods()) {
 307                     if (Modifier.isNative(m.getModifiers()) || Modifier.isAbstract(m.getModifiers())) {
 308                         // ignore
 309                     } else {
 310                         String methodName = className + "." + m.getName();
 311                         if (matches(filters, methodName)) {
 312                             executor.execute(() -> {
 313                                 try (DebugContext debug = DebugContext.create(options, DebugHandlersFactory.LOADER)) {
 314                                     ResolvedJavaMethod method = metaAccess.lookupJavaMethod(m);
 315                                     boolean isSubstitution = method.getAnnotation(Snippet.class) != null || method.getAnnotation(MethodSubstitution.class) != null;
 316                                     StructuredGraph graph = new StructuredGraph.Builder(options, debug).method(method).setIsSubstitution(isSubstitution).build();
 317                                     try (DebugCloseable s = debug.disableIntercept(); DebugContext.Scope ds = debug.scope("CheckingGraph", graph, method)) {
 318                                         checkMethod(method);
 319                                         graphBuilderSuite.apply(graph, context);
 320                                         // update phi stamps
 321                                         graph.getNodes().filter(PhiNode.class).forEach(PhiNode::inferStamp);
 322                                         checkGraph(verifiers, context, graph);
 323                                     } catch (VerificationError e) {
 324                                         errors.add(e.getMessage());
 325                                     } catch (LinkageError e) {
 326                                         // suppress linkages errors resulting from eager resolution
 327                                     } catch (BailoutException e) {
 328                                         // Graal bail outs on certain patterns in Java bytecode
 329                                         // (e.g.,
 330                                         // unbalanced monitors introduced by jacoco).
 331                                     } catch (Throwable e) {
 332                                         try {
 333                                             tool.handleParsingException(e);
 334                                         } catch (Throwable t) {
 335                                             errors.add(String.format("Error while checking %s:%n%s", methodName, printStackTraceToString(e)));
 336                                         }
 337                                     }
 338                                 }
 339                             });
 340                         }
 341                     }
 342                 }
 343             }
 344 
 345             executor.shutdown();
 346             try {
 347                 executor.awaitTermination(1, TimeUnit.HOURS);
 348             } catch (InterruptedException e1) {
 349                 throw new RuntimeException(e1);
 350             }
 351 
 352             if (tool.shouldVerifyFoldableMethods()) {
 353                 try {
 354                     foldableMethodsVerifier.finish();
 355                 } catch (Throwable e) {
 356                     errors.add(e.getMessage());
 357                 }
 358             }
 359         }
 360         if (!errors.isEmpty()) {
 361             StringBuilder msg = new StringBuilder();
 362             String nl = String.format("%n");
 363             for (String e : errors) {
 364                 if (msg.length() != 0) {
 365                     msg.append(nl);
 366                 }
 367                 msg.append(e);
 368             }
 369             Assert.fail(msg.toString());
 370         }
 371     }
 372 
 373     private static boolean isInNativeImage(String className) {
 374         return className.startsWith("org.graalvm.nativeimage");
 375     }
 376 
 377     private static boolean isGSON(String className) {
 378         return className.contains("com.google.gson");
 379     }
 380 
 381     private static List<Class<?>> initializeClasses(InvariantsTool tool, List<String> classNames) {
 382         List<Class<?>> classes = new ArrayList<>(classNames.size());
 383         for (String className : classNames) {
 384             if (!tool.shouldLoadClass(className)) {
 385                 continue;
 386             }
 387             try {
 388                 Class<?> c = Class.forName(className, true, CheckGraalInvariants.class.getClassLoader());
 389                 classes.add(c);
 390             } catch (Throwable t) {
 391                 tool.handleClassLoadingException(t);
 392             }
 393         }
 394         return classes;
 395     }
 396 
 397     /**
 398      * @param metaAccess
 399      */
 400     private static void checkClass(Class<?> c, MetaAccessProvider metaAccess) {
 401         if (Node.class.isAssignableFrom(c)) {
 402             if (c.getAnnotation(NodeInfo.class) == null) {
 403                 throw new AssertionError(String.format("Node subclass %s requires %s annotation", c.getName(), NodeClass.class.getSimpleName()));
 404             }
 405             VerifyNodeCosts.verifyNodeClass(c);
 406         }
 407     }
 408 
 409     private static void checkMethod(ResolvedJavaMethod method) {
 410         if (method.getAnnotation(Snippet.class) == null) {
 411             Annotation[][] parameterAnnotations = method.getParameterAnnotations();
 412             for (int i = 0; i < parameterAnnotations.length; i++) {
 413                 for (Annotation a : parameterAnnotations[i]) {
 414                     Class<? extends Annotation> annotationType = a.annotationType();
 415                     if (annotationType == ConstantParameter.class || annotationType == VarargsParameter.class || annotationType == NonNullParameter.class) {
 416                         VerificationError verificationError = new VerificationError("Parameter %d of %s is annotated with %s but the method is not annotated with %s", i, method,
 417                                         annotationType.getSimpleName(),
 418                                         Snippet.class.getSimpleName());
 419                         throw verificationError;
 420                     }
 421                 }
 422             }
 423         }
 424     }
 425 
 426     /**
 427      * Checks the invariants for a single graph.
 428      */
 429     private static void checkGraph(List<VerifyPhase<PhaseContext>> verifiers, HighTierContext context, StructuredGraph graph) {
 430         for (VerifyPhase<PhaseContext> verifier : verifiers) {
 431             if (!(verifier instanceof VerifyUsageWithEquals) || shouldVerifyEquals(graph.method())) {
 432                 verifier.apply(graph, context);
 433             } else {
 434                 verifier.apply(graph, context);
 435             }
 436         }
 437         if (graph.method().isBridge()) {
 438             BridgeMethodUtils.getBridgedMethod(graph.method());
 439         }
 440     }
 441 
 442     private static boolean matches(String[] filters, String s) {
 443         if (filters == null || filters.length == 0) {
 444             return true;
 445         }
 446         for (String filter : filters) {
 447             if (s.contains(filter)) {
 448                 return true;
 449             }
 450         }
 451         return false;
 452     }
 453 
 454     private static String printStackTraceToString(Throwable t) {
 455         StringWriter sw = new StringWriter();
 456         t.printStackTrace(new PrintWriter(sw));
 457         return sw.toString();
 458     }
 459 
 460     static class BadUsageWithEquals {
 461         Value aValue;
 462         Register aRegister;
 463         RegisterCategory aRegisterCategory;
 464         JavaType aJavaType;
 465         JavaField aJavaField;
 466         JavaMethod aJavaMethod;
 467         LocationIdentity aLocationIdentity;
 468         LIRKind aLIRKind;
 469         ArithmeticOpTable anArithmeticOpTable;
 470         ArithmeticOpTable.Op anArithmeticOpTableOp;
 471 
 472         static Value aStaticValue;
 473         static Register aStaticRegister;
 474         static RegisterCategory aStaticRegisterCategory;
 475         static JavaType aStaticJavaType;
 476         static JavaField aStaticJavaField;
 477         static JavaMethod aStaticJavaMethod;
 478         static LocationIdentity aStaticLocationIdentity;
 479         static LIRKind aStaticLIRKind;
 480         static ArithmeticOpTable aStaticArithmeticOpTable;
 481         static ArithmeticOpTable.Op aStaticArithmeticOpTableOp;
 482 
 483         boolean test01(Value f) {
 484             return aValue == f;
 485         }
 486 
 487         boolean test02(Register f) {
 488             return aRegister == f;
 489         }
 490 
 491         boolean test03(RegisterCategory f) {
 492             return aRegisterCategory == f;
 493         }
 494 
 495         boolean test04(JavaType f) {
 496             return aJavaType == f;
 497         }
 498 
 499         boolean test05(JavaField f) {
 500             return aJavaField == f;
 501         }
 502 
 503         boolean test06(JavaMethod f) {
 504             return aJavaMethod == f;
 505         }
 506 
 507         boolean test07(LocationIdentity f) {
 508             return aLocationIdentity == f;
 509         }
 510 
 511         boolean test08(LIRKind f) {
 512             return aLIRKind == f;
 513         }
 514 
 515         boolean test09(ArithmeticOpTable f) {
 516             return anArithmeticOpTable == f;
 517         }
 518 
 519         boolean test10(ArithmeticOpTable.Op f) {
 520             return anArithmeticOpTableOp == f;
 521         }
 522 
 523         boolean test12(Value f) {
 524             return aStaticValue == f;
 525         }
 526 
 527         boolean test13(Register f) {
 528             return aStaticRegister == f;
 529         }
 530 
 531         boolean test14(RegisterCategory f) {
 532             return aStaticRegisterCategory == f;
 533         }
 534 
 535         boolean test15(JavaType f) {
 536             return aStaticJavaType == f;
 537         }
 538 
 539         boolean test16(JavaField f) {
 540             return aStaticJavaField == f;
 541         }
 542 
 543         boolean test17(JavaMethod f) {
 544             return aStaticJavaMethod == f;
 545         }
 546 
 547         boolean test18(LocationIdentity f) {
 548             return aStaticLocationIdentity == f;
 549         }
 550 
 551         boolean test19(LIRKind f) {
 552             return aStaticLIRKind == f;
 553         }
 554 
 555         boolean test20(ArithmeticOpTable f) {
 556             return aStaticArithmeticOpTable == f;
 557         }
 558 
 559         boolean test21(ArithmeticOpTable.Op f) {
 560             return aStaticArithmeticOpTableOp == f;
 561         }
 562     }
 563 }