changeset 23160:c01c5942b880

lazily initialize invocation plugins per class the first time an invocation to a method for which a plugin exists is compiled
author Doug Simon <doug.simon@oracle.com>
date Sat, 12 Dec 2015 00:29:57 +0100
parents 9248b33b07be
children 59bb4f5ec80e
files graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotGraphBuilderPlugins.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotInvocationPlugins.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/graphbuilderconf/InvocationPlugins.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/graphbuilderconf/MethodSubstitutionPlugin.java graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/StandardGraphBuilderPlugins.java
diffstat 5 files changed, 332 insertions(+), 196 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotGraphBuilderPlugins.java	Fri Dec 11 23:33:50 2015 +0100
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotGraphBuilderPlugins.java	Sat Dec 12 00:29:57 2015 +0100
@@ -68,7 +68,6 @@
 import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugin.Receiver;
 import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugins;
 import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugins.Registration;
-import com.oracle.graal.nodes.graphbuilderconf.MethodSubstitutionPlugin;
 import com.oracle.graal.nodes.graphbuilderconf.NodeIntrinsicPluginFactory;
 import com.oracle.graal.nodes.memory.HeapAccess.BarrierType;
 import com.oracle.graal.nodes.memory.address.AddressNode;
@@ -77,8 +76,8 @@
 import com.oracle.graal.nodes.util.GraphUtil;
 import com.oracle.graal.replacements.InlineDuringParsingPlugin;
 import com.oracle.graal.replacements.MethodHandlePlugin;
+import com.oracle.graal.replacements.NodeIntrinsificationPlugin;
 import com.oracle.graal.replacements.NodeIntrinsificationProvider;
-import com.oracle.graal.replacements.NodeIntrinsificationPlugin;
 import com.oracle.graal.replacements.ReplacementsImpl;
 import com.oracle.graal.replacements.StandardGraphBuilderPlugins;
 import com.oracle.graal.replacements.WordOperationPlugin;
@@ -306,19 +305,13 @@
             assert config.cipherBlockChainingDecryptAESCryptStub != 0L;
             String arch = HotSpotVMConfig.config().getHostArchitectureName();
             String decryptSuffix = arch.equals("sparc") ? "WithOriginalKey" : "";
-            Class<?> c = MethodSubstitutionPlugin.resolveClass("com.sun.crypto.provider.CipherBlockChaining", true);
-            if (c != null) {
-                Registration r = new Registration(plugins, c);
-                r.registerMethodSubstitution(CipherBlockChainingSubstitutions.class, cbcEncryptName, Receiver.class, byte[].class, int.class, int.class, byte[].class, int.class);
-                r.registerMethodSubstitution(CipherBlockChainingSubstitutions.class, cbcDecryptName, cbcDecryptName + decryptSuffix, Receiver.class, byte[].class, int.class, int.class, byte[].class,
-                                int.class);
-            }
-            c = MethodSubstitutionPlugin.resolveClass("com.sun.crypto.provider.AESCrypt", true);
-            if (c != null) {
-                Registration r = new Registration(plugins, c);
-                r.registerMethodSubstitution(AESCryptSubstitutions.class, aesEncryptName, Receiver.class, byte[].class, int.class, byte[].class, int.class);
-                r.registerMethodSubstitution(AESCryptSubstitutions.class, aesDecryptName, aesDecryptName + decryptSuffix, Receiver.class, byte[].class, int.class, byte[].class, int.class);
-            }
+            Registration r = new Registration(plugins, "com.sun.crypto.provider.CipherBlockChaining");
+            r.registerMethodSubstitution(CipherBlockChainingSubstitutions.class, cbcEncryptName, Receiver.class, byte[].class, int.class, int.class, byte[].class, int.class);
+            r.registerMethodSubstitution(CipherBlockChainingSubstitutions.class, cbcDecryptName, cbcDecryptName + decryptSuffix, Receiver.class, byte[].class, int.class, int.class, byte[].class,
+                            int.class);
+            r = new Registration(plugins, "com.sun.crypto.provider.AESCrypt");
+            r.registerMethodSubstitution(AESCryptSubstitutions.class, aesEncryptName, Receiver.class, byte[].class, int.class, byte[].class, int.class);
+            r.registerMethodSubstitution(AESCryptSubstitutions.class, aesDecryptName, aesDecryptName + decryptSuffix, Receiver.class, byte[].class, int.class, byte[].class, int.class);
         }
     }
 
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotInvocationPlugins.java	Fri Dec 11 23:33:50 2015 +0100
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotInvocationPlugins.java	Sat Dec 12 00:29:57 2015 +0100
@@ -22,6 +22,8 @@
  */
 package com.oracle.graal.hotspot.meta;
 
+import java.lang.reflect.Type;
+
 import jdk.vm.ci.hotspot.HotSpotVMConfig;
 import jdk.vm.ci.meta.JavaKind;
 import jdk.vm.ci.meta.MetaAccessProvider;
@@ -51,7 +53,7 @@
     }
 
     @Override
-    public void register(InvocationPlugin plugin, Class<?> declaringClass, String name, Class<?>... argumentTypes) {
+    public void register(InvocationPlugin plugin, Type declaringClass, String name, Type... argumentTypes) {
         if (!config.usePopCountInstruction) {
             if (name.equals("bitCount")) {
                 assert declaringClass.equals(Integer.class) || declaringClass.equals(Long.class);
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/graphbuilderconf/InvocationPlugins.java	Fri Dec 11 23:33:50 2015 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/graphbuilderconf/InvocationPlugins.java	Sat Dec 12 00:29:57 2015 +0100
@@ -27,17 +27,19 @@
 import java.lang.reflect.Executable;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
+import java.lang.reflect.Type;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.stream.Collectors;
 
 import jdk.vm.ci.common.JVMCIError;
 import jdk.vm.ci.meta.MetaAccessProvider;
+import jdk.vm.ci.meta.MetaUtil;
 import jdk.vm.ci.meta.ResolvedJavaMethod;
+import sun.misc.Launcher;
 
 import com.oracle.graal.graph.Node;
 import com.oracle.graal.graph.iterators.NodeIterable;
@@ -85,6 +87,40 @@
     }
 
     /**
+     * A symbol that is lazily {@linkplain OptionalLazySymbol#resolve() resolved} to a {@link Type}.
+     */
+    static class OptionalLazySymbol implements Type {
+        private static final Class<?> MASK_NULL = OptionalLazySymbol.class;
+        private final String name;
+        private Class<?> resolved;
+
+        public OptionalLazySymbol(String name) {
+            this.name = name;
+        }
+
+        public String getTypeName() {
+            return name;
+        }
+
+        /**
+         * Gets the resolved {@link Class} corresponding to this symbol or {@code null} if
+         * resolution fails.
+         */
+        public Class<?> resolve() {
+            if (resolved == null) {
+                Class<?> resolvedOrNull = resolveClass(name, true);
+                resolved = resolvedOrNull == null ? MASK_NULL : resolvedOrNull;
+            }
+            return resolved == MASK_NULL ? null : resolved;
+        }
+
+        @Override
+        public String toString() {
+            return name;
+        }
+    }
+
+    /**
      * Utility for
      * {@linkplain InvocationPlugins#register(InvocationPlugin, Class, String, Class...)
      * registration} of invocation plugins.
@@ -92,7 +128,7 @@
     public static class Registration {
 
         private final InvocationPlugins plugins;
-        private final Class<?> declaringClass;
+        private final Type declaringType;
         private boolean allowOverwrite;
 
         /**
@@ -100,12 +136,12 @@
          * given class.
          *
          * @param plugins where to register the plugins
-         * @param declaringClass the class declaring the methods for which plugins will be
-         *            registered via this object
+         * @param declaringType the class declaring the methods for which plugins will be registered
+         *            via this object
          */
-        public Registration(InvocationPlugins plugins, Class<?> declaringClass) {
+        public Registration(InvocationPlugins plugins, Type declaringType) {
             this.plugins = plugins;
-            this.declaringClass = declaringClass;
+            this.declaringType = declaringType;
         }
 
         /**
@@ -118,11 +154,7 @@
          */
         public Registration(InvocationPlugins plugins, String declaringClassName) {
             this.plugins = plugins;
-            try {
-                this.declaringClass = Class.forName(declaringClassName);
-            } catch (ClassNotFoundException ex) {
-                throw JVMCIError.shouldNotReachHere(ex);
-            }
+            this.declaringType = new OptionalLazySymbol(declaringClassName);
         }
 
         /**
@@ -140,7 +172,7 @@
          * @param plugin the plugin to be registered
          */
         public void register0(String name, InvocationPlugin plugin) {
-            plugins.register(plugin, false, allowOverwrite, declaringClass, name);
+            plugins.register(plugin, false, allowOverwrite, declaringType, name);
         }
 
         /**
@@ -149,8 +181,8 @@
          * @param name the name of the method
          * @param plugin the plugin to be registered
          */
-        public void register1(String name, Class<?> arg, InvocationPlugin plugin) {
-            plugins.register(plugin, false, allowOverwrite, declaringClass, name, arg);
+        public void register1(String name, Type arg, InvocationPlugin plugin) {
+            plugins.register(plugin, false, allowOverwrite, declaringType, name, arg);
         }
 
         /**
@@ -159,8 +191,8 @@
          * @param name the name of the method
          * @param plugin the plugin to be registered
          */
-        public void register2(String name, Class<?> arg1, Class<?> arg2, InvocationPlugin plugin) {
-            plugins.register(plugin, false, allowOverwrite, declaringClass, name, arg1, arg2);
+        public void register2(String name, Type arg1, Type arg2, InvocationPlugin plugin) {
+            plugins.register(plugin, false, allowOverwrite, declaringType, name, arg1, arg2);
         }
 
         /**
@@ -169,8 +201,8 @@
          * @param name the name of the method
          * @param plugin the plugin to be registered
          */
-        public void register3(String name, Class<?> arg1, Class<?> arg2, Class<?> arg3, InvocationPlugin plugin) {
-            plugins.register(plugin, false, allowOverwrite, declaringClass, name, arg1, arg2, arg3);
+        public void register3(String name, Type arg1, Type arg2, Type arg3, InvocationPlugin plugin) {
+            plugins.register(plugin, false, allowOverwrite, declaringType, name, arg1, arg2, arg3);
         }
 
         /**
@@ -179,8 +211,8 @@
          * @param name the name of the method
          * @param plugin the plugin to be registered
          */
-        public void register4(String name, Class<?> arg1, Class<?> arg2, Class<?> arg3, Class<?> arg4, InvocationPlugin plugin) {
-            plugins.register(plugin, false, allowOverwrite, declaringClass, name, arg1, arg2, arg3, arg4);
+        public void register4(String name, Type arg1, Type arg2, Type arg3, Type arg4, InvocationPlugin plugin) {
+            plugins.register(plugin, false, allowOverwrite, declaringType, name, arg1, arg2, arg3, arg4);
         }
 
         /**
@@ -189,8 +221,8 @@
          * @param name the name of the method
          * @param plugin the plugin to be registered
          */
-        public void register5(String name, Class<?> arg1, Class<?> arg2, Class<?> arg3, Class<?> arg4, Class<?> arg5, InvocationPlugin plugin) {
-            plugins.register(plugin, false, allowOverwrite, declaringClass, name, arg1, arg2, arg3, arg4, arg5);
+        public void register5(String name, Type arg1, Type arg2, Type arg3, Type arg4, Type arg5, InvocationPlugin plugin) {
+            plugins.register(plugin, false, allowOverwrite, declaringType, name, arg1, arg2, arg3, arg4, arg5);
         }
 
         /**
@@ -200,7 +232,7 @@
          * @param plugin the plugin to be registered
          */
         public void registerOptional0(String name, InvocationPlugin plugin) {
-            plugins.register(plugin, true, allowOverwrite, declaringClass, name);
+            plugins.register(plugin, true, allowOverwrite, declaringType, name);
         }
 
         /**
@@ -209,8 +241,8 @@
          * @param name the name of the method
          * @param plugin the plugin to be registered
          */
-        public void registerOptional1(String name, Class<?> arg, InvocationPlugin plugin) {
-            plugins.register(plugin, true, allowOverwrite, declaringClass, name, arg);
+        public void registerOptional1(String name, Type arg, InvocationPlugin plugin) {
+            plugins.register(plugin, true, allowOverwrite, declaringType, name, arg);
         }
 
         /**
@@ -219,8 +251,8 @@
          * @param name the name of the method
          * @param plugin the plugin to be registered
          */
-        public void registerOptional2(String name, Class<?> arg1, Class<?> arg2, InvocationPlugin plugin) {
-            plugins.register(plugin, true, allowOverwrite, declaringClass, name, arg1, arg2);
+        public void registerOptional2(String name, Type arg1, Type arg2, InvocationPlugin plugin) {
+            plugins.register(plugin, true, allowOverwrite, declaringType, name, arg1, arg2);
         }
 
         /**
@@ -229,8 +261,8 @@
          * @param name the name of the method
          * @param plugin the plugin to be registered
          */
-        public void registerOptional3(String name, Class<?> arg1, Class<?> arg2, Class<?> arg3, InvocationPlugin plugin) {
-            plugins.register(plugin, true, allowOverwrite, declaringClass, name, arg1, arg2, arg3);
+        public void registerOptional3(String name, Type arg1, Type arg2, Type arg3, InvocationPlugin plugin) {
+            plugins.register(plugin, true, allowOverwrite, declaringType, name, arg1, arg2, arg3);
         }
 
         /**
@@ -239,8 +271,8 @@
          * @param name the name of the method
          * @param plugin the plugin to be registered
          */
-        public void registerOptional4(String name, Class<?> arg1, Class<?> arg2, Class<?> arg3, Class<?> arg4, InvocationPlugin plugin) {
-            plugins.register(plugin, true, allowOverwrite, declaringClass, name, arg1, arg2, arg3, arg4);
+        public void registerOptional4(String name, Type arg1, Type arg2, Type arg3, Type arg4, InvocationPlugin plugin) {
+            plugins.register(plugin, true, allowOverwrite, declaringType, name, arg1, arg2, arg3, arg4);
         }
 
         /**
@@ -253,7 +285,7 @@
          *            is non-static. Upon returning, element 0 will have been rewritten to
          *            {@code declaringClass}
          */
-        public void registerMethodSubstitution(Class<?> substituteDeclaringClass, String name, Class<?>... argumentTypes) {
+        public void registerMethodSubstitution(Class<?> substituteDeclaringClass, String name, Type... argumentTypes) {
             registerMethodSubstitution(substituteDeclaringClass, name, name, argumentTypes);
         }
 
@@ -268,9 +300,9 @@
          *            is non-static. Upon returning, element 0 will have been rewritten to
          *            {@code declaringClass}
          */
-        public void registerMethodSubstitution(Class<?> substituteDeclaringClass, String name, String substituteName, Class<?>... argumentTypes) {
+        public void registerMethodSubstitution(Class<?> substituteDeclaringClass, String name, String substituteName, Type... argumentTypes) {
             MethodSubstitutionPlugin plugin = new MethodSubstitutionPlugin(substituteDeclaringClass, substituteName, argumentTypes);
-            plugins.register(plugin, false, allowOverwrite, declaringClass, name, argumentTypes);
+            plugins.register(plugin, false, allowOverwrite, declaringType, name, argumentTypes);
         }
     }
 
@@ -286,55 +318,73 @@
          */
         final boolean isOptional;
 
-        final Class<?> declaringClass;
         final String name;
-        final Class<?>[] argumentTypes;
+        final Type[] argumentTypes;
         final InvocationPlugin value;
 
-        MethodKey(InvocationPlugin data, boolean isStatic, boolean isOptional, Class<?> declaringClass, String name, Class<?>... argumentTypes) {
-            assert isStatic || argumentTypes[0] == declaringClass;
+        /**
+         * Used to lazily initialize {@link #resolved}.
+         */
+        private final MetaAccessProvider metaAccess;
+
+        private volatile ResolvedJavaMethod resolved;
+
+        MethodKey(MetaAccessProvider metaAccess, InvocationPlugin data, boolean isStatic, boolean isOptional, String name, Type... argumentTypes) {
+            this.metaAccess = metaAccess;
             this.value = data;
             this.isStatic = isStatic;
             this.isOptional = isOptional;
-            this.declaringClass = declaringClass;
             this.name = name;
             this.argumentTypes = argumentTypes;
-            assert isOptional || resolveJava() != null;
         }
 
         @Override
         public boolean equals(Object obj) {
             if (obj instanceof MethodKey) {
                 MethodKey that = (MethodKey) obj;
-                boolean res = this.name.equals(that.name) && this.declaringClass.equals(that.declaringClass) && Arrays.equals(this.argumentTypes, that.argumentTypes);
+                boolean res = this.name.equals(that.name) && areEqual(this.argumentTypes, that.argumentTypes);
                 assert !res || this.isStatic == that.isStatic;
                 return res;
             }
             return false;
         }
 
+        private static boolean areEqual(Type[] args1, Type[] args2) {
+            if (args1.length == args2.length) {
+                for (int i = 0; i < args1.length; i++) {
+                    if (!args1[i].getTypeName().equals(args2[i].getTypeName())) {
+                        return false;
+                    }
+                }
+                return true;
+            }
+            return false;
+        }
+
         public int getDeclaredParameterCount() {
             return isStatic ? argumentTypes.length : argumentTypes.length - 1;
         }
 
         @Override
         public int hashCode() {
-            // Replay compilation mandates use of stable hash codes
-            return declaringClass.getName().hashCode() ^ name.hashCode();
+            return name.hashCode();
         }
 
-        private ResolvedJavaMethod resolve(MetaAccessProvider metaAccess) {
-            Executable method = resolveJava();
-            if (method == null) {
-                return null;
+        private ResolvedJavaMethod resolve(Class<?> declaringClass) {
+            if (resolved == null) {
+                Executable method = resolveJava(declaringClass);
+                if (method == null) {
+                    return null;
+                }
+                resolved = metaAccess.lookupJavaMethod(method);
             }
-            return metaAccess.lookupJavaMethod(method);
+            return resolved;
         }
 
-        private Executable resolveJava() {
+        private Executable resolveJava(Class<?> declaringClass) {
             try {
                 Executable res;
-                Class<?>[] parameterTypes = isStatic ? argumentTypes : Arrays.copyOfRange(argumentTypes, 1, argumentTypes.length);
+                Class<?>[] parameterTypes = resolveTypes(argumentTypes, isStatic ? 0 : 1, argumentTypes.length);
                 if (name.equals("<init>")) {
                     res = declaringClass.getDeclaredConstructor(parameterTypes);
                 } else {
@@ -352,12 +402,12 @@
 
         @Override
         public String toString() {
-            StringBuilder sb = new StringBuilder(declaringClass.getName()).append('.').append(name).append('(');
-            for (Class<?> p : argumentTypes) {
+            StringBuilder sb = new StringBuilder(name).append('(');
+            for (Type p : argumentTypes) {
                 if (sb.charAt(sb.length() - 1) != '(') {
                     sb.append(", ");
                 }
-                sb.append(p.getSimpleName());
+                sb.append(p.getTypeName());
             }
             return sb.append(')').toString();
         }
@@ -365,14 +415,11 @@
 
     private final MetaAccessProvider metaAccess;
 
-    /**
-     * Initial list of entries.
-     */
-    private final List<MethodKey> registrations = new ArrayList<>(INITIAL_CAPACITY);
+    private final Map<String, ClassPlugins> registrations = new HashMap<>();
 
     /**
-     * Deferred registrations as well as guard for initialization of {@link #entries}. The guard
-     * uses double-checked locking which is why this field is {@code volatile}.
+     * Deferred registrations as well as guard for initialization. The guard uses double-checked
+     * locking which is why this field is {@code volatile}.
      */
     private volatile List<Runnable> deferredRegistrations = new ArrayList<>();
 
@@ -381,16 +428,75 @@
      * {@link #get(ResolvedJavaMethod)} or {@link #closeRegistration()} is called on this object.
      */
     public void defer(Runnable deferrable) {
-        assert entries == null && deferredRegistrations != null : "registration is closed";
+        assert deferredRegistrations != null : "registration is closed";
         deferredRegistrations.add(deferrable);
     }
 
     /**
-     * Entry map that is initialized by {@link #initializeMap()}.
+     * Per-class invocation plugins.
      */
-    private Map<ResolvedJavaMethod, InvocationPlugin> entries;
+    protected static class ClassPlugins {
+        private final Type declaringType;
+
+        private final List<MethodKey> registrations = new ArrayList<>();
+
+        public ClassPlugins(Type declaringClass) {
+            this.declaringType = declaringClass;
+        }
+
+        /**
+         * Entry map that is initialized upon first call to {@link #get(ResolvedJavaMethod)}.
+         *
+         * Note: this must be volatile as threads may race to initialize it.
+         */
+        private volatile Map<ResolvedJavaMethod, InvocationPlugin> entries;
 
-    private static final int INITIAL_CAPACITY = 64;
+        void initializeMap() {
+            if (entries == null) {
+                if (registrations.isEmpty()) {
+                    entries = Collections.emptyMap();
+                } else {
+                    Class<?> declaringClass = resolveType(declaringType, true);
+                    if (declaringClass == null) {
+                        // An optional type that could not be resolved
+                        entries = Collections.emptyMap();
+                    } else {
+                        Map<ResolvedJavaMethod, InvocationPlugin> newEntries = new HashMap<>();
+                        for (MethodKey methodKey : registrations) {
+                            ResolvedJavaMethod m = methodKey.resolve(declaringClass);
+                            newEntries.put(m, methodKey.value);
+                            if (entries != null) {
+                                // Another thread finished initializing entries first
+                                return;
+                            }
+                        }
+                        entries = newEntries;
+                    }
+                }
+            }
+        }
+
+        public InvocationPlugin get(ResolvedJavaMethod method) {
+            if (entries == null) {
+                initializeMap();
+            }
+            return entries.get(method);
+        }
+
+        public void register(MethodKey methodKey, boolean allowOverwrite) {
+            assert entries == null : "registration is closed";
+            if (allowOverwrite) {
+                int index = registrations.indexOf(methodKey);
+                if (index >= 0) {
+                    registrations.set(index, methodKey);
+                    return;
+                }
+            } else {
+                assert !registrations.contains(methodKey) : "a value is already registered for " + declaringType + "." + methodKey;
+            }
+            registrations.add(methodKey);
+        }
+    }
 
     /**
      * Adds an entry to this map for a specified method.
@@ -404,27 +510,54 @@
      *            {@code declaringClass} iff the method is non-static.
      * @return an object representing the method
      */
-    MethodKey put(InvocationPlugin value, boolean isStatic, boolean isOptional, boolean allowOverwrite, Class<?> declaringClass, String name, Class<?>... argumentTypes) {
+    MethodKey put(InvocationPlugin value, boolean isStatic, boolean isOptional, boolean allowOverwrite, Type declaringClass, String name, Type... argumentTypes) {
         assert isStatic || argumentTypes[0] == declaringClass;
-        MethodKey methodKey = new MethodKey(value, isStatic, isOptional, declaringClass, name, argumentTypes);
-        assert entries == null : "registration is closed";
-        assert allowOverwrite || !registrations.contains(methodKey) : "a value is already registered for " + methodKey;
-        registrations.add(methodKey);
+
+        String internalName = MetaUtil.toInternalName(declaringClass.getTypeName());
+        ClassPlugins classPlugins = registrations.get(internalName);
+        if (classPlugins == null) {
+            classPlugins = new ClassPlugins(declaringClass);
+            registrations.put(internalName, classPlugins);
+        }
+        assert isStatic || argumentTypes[0] == declaringClass;
+        MethodKey methodKey = new MethodKey(metaAccess, value, isStatic, isOptional, name, argumentTypes);
+        classPlugins.register(methodKey, allowOverwrite);
         return methodKey;
     }
 
     /**
      * Determines if a method denoted by a given {@link MethodKey} is in this map.
      */
-    boolean containsKey(MethodKey key) {
-        return registrations.contains(key);
+    boolean containsKey(Type declaringType, MethodKey key) {
+        String internalName = MetaUtil.toInternalName(declaringType.getTypeName());
+        ClassPlugins classPlugins = registrations.get(internalName);
+        return classPlugins != null && classPlugins.registrations.contains(key);
     }
 
     InvocationPlugin get(ResolvedJavaMethod method) {
+        flushDeferrables();
+        String internalName = method.getDeclaringClass().getName();
+        ClassPlugins classPlugins = registrations.get(internalName);
+        if (classPlugins != null) {
+            return classPlugins.get(method);
+        }
+        return null;
+    }
+
+    private void flushDeferrables() {
         if (deferredRegistrations != null) {
-            initializeMap();
+            synchronized (this) {
+                if (deferredRegistrations != null) {
+                    for (Runnable deferrable : deferredRegistrations) {
+                        deferrable.run();
+                    }
+                    deferredRegistrations = null;
+                }
+            }
+            for (Map.Entry<String, ClassPlugins> e : registrations.entrySet()) {
+                e.getValue().initializeMap();
+            }
         }
-        return entries.get(method);
     }
 
     /**
@@ -432,32 +565,9 @@
      * lookup.
      */
     public void closeRegistration() {
-        if (deferredRegistrations != null) {
-            initializeMap();
-        }
-    }
-
-    void initializeMap() {
-        if (deferredRegistrations != null) {
-            synchronized (this) {
-                if (deferredRegistrations != null) {
-                    List<Runnable> localDeferredRegistrations = deferredRegistrations;
-                    for (Runnable deferrable : localDeferredRegistrations) {
-                        deferrable.run();
-                    }
-                    if (registrations.isEmpty()) {
-                        entries = Collections.emptyMap();
-                    } else {
-                        Map<ResolvedJavaMethod, InvocationPlugin> newEntries = new HashMap<>();
-                        for (MethodKey methodKey : registrations) {
-                            ResolvedJavaMethod m = methodKey.resolve(metaAccess);
-                            newEntries.put(m, methodKey.value);
-                        }
-                        entries = newEntries;
-                    }
-                    deferredRegistrations = null;
-                }
-            }
+        flushDeferrables();
+        for (Map.Entry<String, ClassPlugins> e : registrations.entrySet()) {
+            e.getValue().initializeMap();
         }
     }
 
@@ -492,13 +602,13 @@
         this(null, metaAccess);
     }
 
-    private void register(InvocationPlugin plugin, boolean isOptional, boolean allowOverwrite, Class<?> declaringClass, String name, Class<?>... argumentTypes) {
+    private void register(InvocationPlugin plugin, boolean isOptional, boolean allowOverwrite, Type declaringClass, String name, Type... argumentTypes) {
         boolean isStatic = argumentTypes.length == 0 || argumentTypes[0] != InvocationPlugin.Receiver.class;
         if (!isStatic) {
             argumentTypes[0] = declaringClass;
         }
-        MethodKey methodInfo = put(plugin, isStatic, isOptional, allowOverwrite, declaringClass, name, argumentTypes);
-        assert Checker.check(this, methodInfo, plugin);
+        MethodKey methodKey = put(plugin, isStatic, isOptional, allowOverwrite, declaringClass, name, argumentTypes);
+        assert Checker.check(this, declaringClass, methodKey, plugin);
     }
 
     /**
@@ -510,10 +620,14 @@
      *            non-static. Upon returning, element 0 will have been rewritten to
      *            {@code declaringClass}
      */
-    public void register(InvocationPlugin plugin, Class<?> declaringClass, String name, Class<?>... argumentTypes) {
+    public void register(InvocationPlugin plugin, Type declaringClass, String name, Type... argumentTypes) {
         register(plugin, false, false, declaringClass, name, argumentTypes);
     }
 
+    public void register(InvocationPlugin plugin, String declaringClass, String name, Type... argumentTypes) {
+        register(plugin, false, false, new OptionalLazySymbol(declaringClass), name, argumentTypes);
+    }
+
     /**
      * Registers an invocation plugin for a given, optional method. There must be no plugin
      * currently registered for {@code method}.
@@ -523,7 +637,7 @@
      *            non-static. Upon returning, element 0 will have been rewritten to
      *            {@code declaringClass}
      */
-    public void registerOptional(InvocationPlugin plugin, Class<?> declaringClass, String name, Class<?>... argumentTypes) {
+    public void registerOptional(InvocationPlugin plugin, Type declaringClass, String name, Type... argumentTypes) {
         register(plugin, true, false, declaringClass, name, argumentTypes);
     }
 
@@ -553,7 +667,13 @@
 
     @Override
     public String toString() {
-        return registrations.stream().map(MethodKey::toString).collect(Collectors.joining(", ")) + " / parent: " + this.parent;
+        StringBuilder buf = new StringBuilder();
+        registrations.forEach((name, cp) -> buf.append(name).append('.').append(cp).append(", "));
+        String s = buf.toString();
+        if (buf.length() != 0) {
+            s = s.substring(buf.length() - ", ".length());
+        }
+        return s + " / parent: " + this.parent;
     }
 
     private static class Checker {
@@ -583,10 +703,10 @@
             SIGS = sigs.toArray(new Class<?>[sigs.size()][]);
         }
 
-        public static boolean check(InvocationPlugins plugins, MethodKey method, InvocationPlugin plugin) {
+        public static boolean check(InvocationPlugins plugins, Type declaringType, MethodKey method, InvocationPlugin plugin) {
             InvocationPlugins p = plugins.parent;
             while (p != null) {
-                assert !p.containsKey(method) : "a plugin is already registered for " + method;
+                assert !p.containsKey(declaringType, method) : "a plugin is already registered for " + method;
                 p = p.parent;
             }
             if (plugin instanceof ForeignCallPlugin || plugin instanceof GeneratedInvocationPlugin) {
@@ -624,4 +744,64 @@
             parent.checkNewNodes(b, plugin, newNodes);
         }
     }
+
+    /**
+     * Resolves a name to a class.
+     *
+     * @param className the name of the class to resolve
+     * @param optional if true, resolution failure returns null
+     * @return the resolved class or null if resolution fails and {@code optional} is true
+     */
+    public static Class<?> resolveClass(String className, boolean optional) {
+        try {
+            // Need to use launcher class path to handle classes
+            // that are not on the boot class path
+            ClassLoader cl = Launcher.getLauncher().getClassLoader();
+            return Class.forName(className, false, cl);
+        } catch (ClassNotFoundException e) {
+            if (optional) {
+                return null;
+            }
+            throw new JVMCIError("Could not resolve type " + className);
+        }
+    }
+
+    /**
+     * Resolves a {@link Type} to a {@link Class}.
+     *
+     * @param type the type to resolve
+     * @param optional if true, resolution failure returns null
+     * @return the resolved class or null if resolution fails and {@code optional} is true
+     */
+    public static Class<?> resolveType(Type type, boolean optional) {
+        if (type instanceof Class) {
+            return (Class<?>) type;
+        }
+        if (optional && type instanceof OptionalLazySymbol) {
+            return ((OptionalLazySymbol) type).resolve();
+        }
+        return resolveClass(type.getTypeName(), optional);
+    }
+
+    private static final Class<?>[] NO_CLASSES = {};
+
+    /**
+     * Resolves an array of {@link Type}s to an array of {@link Class}es.
+     *
+     * @param types the types to resolve
+     * @param from the initial index of the range to be resolved, inclusive
+     * @param to the final index of the range to be resolved, exclusive
+     * @return the resolved class or null if resolution fails and {@code optional} is true
+     */
+    public static Class<?>[] resolveTypes(Type[] types, int from, int to) {
+        int length = to - from;
+        if (length <= 0) {
+            return NO_CLASSES;
+        }
+        Class<?>[] classes = new Class<?>[length];
+        for (int i = 0; i < length; i++) {
+            classes[i] = resolveType(types[i + from], false);
+        }
+        return classes;
+    }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/graphbuilderconf/MethodSubstitutionPlugin.java	Fri Dec 11 23:33:50 2015 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/graphbuilderconf/MethodSubstitutionPlugin.java	Sat Dec 12 00:29:57 2015 +0100
@@ -22,15 +22,17 @@
  */
 package com.oracle.graal.nodes.graphbuilderconf;
 
+import static com.oracle.graal.nodes.graphbuilderconf.InvocationPlugins.resolveType;
+
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
+import java.lang.reflect.Type;
 import java.util.Arrays;
 import java.util.stream.Collectors;
 
 import jdk.vm.ci.common.JVMCIError;
 import jdk.vm.ci.meta.MetaAccessProvider;
 import jdk.vm.ci.meta.ResolvedJavaMethod;
-import sun.misc.Launcher;
 
 import com.oracle.graal.nodes.ValueNode;
 
@@ -56,7 +58,7 @@
     /**
      * The parameter types of the substitute method.
      */
-    private final Class<?>[] parameters;
+    private final Type[] parameters;
 
     private final boolean originalIsStatic;
 
@@ -69,7 +71,7 @@
      *            static, then {@code parameters[0]} must be the {@link Class} value denoting
      *            {@link InvocationPlugin.Receiver}
      */
-    public MethodSubstitutionPlugin(Class<?> declaringClass, String name, Class<?>... parameters) {
+    public MethodSubstitutionPlugin(Class<?> declaringClass, String name, Type... parameters) {
         this.declaringClass = declaringClass;
         this.name = name;
         this.parameters = parameters;
@@ -83,7 +85,7 @@
      * @param name the name of the substitute method
      * @param parameters the parameter types of the substitute method
      */
-    public MethodSubstitutionPlugin(boolean originalIsStatic, Class<?> declaringClass, String name, Class<?>... parameters) {
+    public MethodSubstitutionPlugin(boolean originalIsStatic, Class<?> declaringClass, String name, Type... parameters) {
         this.declaringClass = declaringClass;
         this.name = name;
         this.parameters = parameters;
@@ -130,12 +132,12 @@
                 int start = 0;
                 if (!originalIsStatic) {
                     start = 1;
-                    if (!mparams[0].isAssignableFrom(parameters[0])) {
+                    if (!mparams[0].isAssignableFrom(resolveType(parameters[0], false))) {
                         return false;
                     }
                 }
                 for (int i = start; i < mparams.length; i++) {
-                    if (mparams[i] != parameters[i]) {
+                    if (mparams[i] != resolveType(parameters[i], false)) {
                         return false;
                     }
                 }
@@ -157,27 +159,6 @@
         throw new JVMCIError("No method found specified by %s", this);
     }
 
-    /**
-     * Resolves a name to a class.
-     *
-     * @param className the name of the class to resolve
-     * @param optional if true, resolution failure returns null
-     * @return the resolved class or null if resolution fails and {@code optional} is true
-     */
-    public static Class<?> resolveClass(String className, boolean optional) {
-        try {
-            // Need to use launcher class path to handle classes
-            // that are not on the boot class path
-            ClassLoader cl = Launcher.getLauncher().getClassLoader();
-            return Class.forName(className, false, cl);
-        } catch (ClassNotFoundException e) {
-            if (optional) {
-                return null;
-            }
-            throw new JVMCIError("Could not resolve type " + className);
-        }
-    }
-
     @Override
     public boolean execute(GraphBuilderContext b, ResolvedJavaMethod targetMethod, InvocationPlugin.Receiver receiver, ValueNode[] argsIncludingReceiver) {
         ResolvedJavaMethod subst = getSubstitute(b.getMetaAccess());
@@ -201,6 +182,6 @@
     @Override
     public String toString() {
         return String.format("%s[%s.%s(%s)]", getClass().getSimpleName(), declaringClass.getName(), name,
-                        Arrays.asList(parameters).stream().map(c -> c.getSimpleName()).collect(Collectors.joining(", ")));
+                        Arrays.asList(parameters).stream().map(c -> c.getTypeName()).collect(Collectors.joining(", ")));
     }
 }
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/StandardGraphBuilderPlugins.java	Fri Dec 11 23:33:50 2015 +0100
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/StandardGraphBuilderPlugins.java	Sat Dec 12 00:29:57 2015 +0100
@@ -32,6 +32,17 @@
 import java.lang.reflect.Field;
 import java.util.Arrays;
 
+import jdk.vm.ci.common.JVMCIError;
+import jdk.vm.ci.meta.DeoptimizationAction;
+import jdk.vm.ci.meta.DeoptimizationReason;
+import jdk.vm.ci.meta.JavaKind;
+import jdk.vm.ci.meta.LocationIdentity;
+import jdk.vm.ci.meta.MetaAccessProvider;
+import jdk.vm.ci.meta.ResolvedJavaField;
+import jdk.vm.ci.meta.ResolvedJavaMethod;
+import jdk.vm.ci.meta.ResolvedJavaType;
+import sun.misc.Unsafe;
+
 import com.oracle.graal.api.directives.GraalDirectives;
 import com.oracle.graal.compiler.common.calc.Condition;
 import com.oracle.graal.compiler.common.calc.UnsignedMath;
@@ -74,9 +85,8 @@
 import com.oracle.graal.nodes.extended.UnsafeStoreNode;
 import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderContext;
 import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugin;
+import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugin.Receiver;
 import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugins;
-import com.oracle.graal.nodes.graphbuilderconf.MethodSubstitutionPlugin;
-import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugin.Receiver;
 import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugins.Registration;
 import com.oracle.graal.nodes.java.ClassIsAssignableFromNode;
 import com.oracle.graal.nodes.java.CompareAndSwapNode;
@@ -101,31 +111,11 @@
 import com.oracle.graal.replacements.nodes.arithmetic.IntegerMulExactNode;
 import com.oracle.graal.replacements.nodes.arithmetic.IntegerSubExactNode;
 
-import jdk.vm.ci.common.JVMCIError;
-import jdk.vm.ci.meta.DeoptimizationAction;
-import jdk.vm.ci.meta.DeoptimizationReason;
-import jdk.vm.ci.meta.JavaKind;
-import jdk.vm.ci.meta.LocationIdentity;
-import jdk.vm.ci.meta.MetaAccessProvider;
-import jdk.vm.ci.meta.ResolvedJavaField;
-import jdk.vm.ci.meta.ResolvedJavaMethod;
-import jdk.vm.ci.meta.ResolvedJavaType;
-import jdk.vm.ci.options.Option;
-import jdk.vm.ci.options.OptionValue;
-import sun.misc.Unsafe;
-
 /**
  * Provides non-runtime specific {@link InvocationPlugin}s.
  */
 public class StandardGraphBuilderPlugins {
 
-    // @formatter:off
-    static class Options {
-        @Option(help = "Enable use of intrinsics for the JMH Blackhole class")
-        public static final OptionValue<Boolean> UseBlackholeSubstitution = new OptionValue<>(true);
-    }
-    // @formatter:on
-
     public static void registerInvocationPlugins(MetaAccessProvider metaAccess, InvocationPlugins plugins, boolean allowDeoptimization) {
         registerObjectPlugins(plugins);
         registerClassPlugins(plugins);
@@ -146,9 +136,7 @@
         registerEdgesPlugins(metaAccess, plugins);
         registerGraalDirectivesPlugins(plugins);
         registerBoxingPlugins(plugins);
-        if (Options.UseBlackholeSubstitution.getValue()) {
-            registerJMHBlackholePlugins(plugins);
-        }
+        registerJMHBlackholePlugins(plugins);
         registerJFRThrowablePlugins(plugins);
     }
 
@@ -778,32 +766,24 @@
         };
         String[] names = {"org.openjdk.jmh.infra.Blackhole", "org.openjdk.jmh.logic.BlackHole"};
         for (String name : names) {
-            Class<?> blackholeClass;
-            blackholeClass = MethodSubstitutionPlugin.resolveClass(name, true);
-            if (blackholeClass != null) {
-                Registration r = new Registration(plugins, blackholeClass);
-                for (JavaKind kind : JavaKind.values()) {
-                    if ((kind.isPrimitive() && kind != JavaKind.Void) || kind == JavaKind.Object) {
-                        Class<?> javaClass = kind == JavaKind.Object ? Object.class : kind.toJavaClass();
-                        r.register2("consume", Receiver.class, javaClass, blackholePlugin);
-                    }
+            Registration r = new Registration(plugins, name);
+            for (JavaKind kind : JavaKind.values()) {
+                if ((kind.isPrimitive() && kind != JavaKind.Void) || kind == JavaKind.Object) {
+                    Class<?> javaClass = kind == JavaKind.Object ? Object.class : kind.toJavaClass();
+                    r.register2("consume", Receiver.class, javaClass, blackholePlugin);
                 }
-                r.register2("consume", Receiver.class, Object[].class, blackholePlugin);
             }
+            r.register2("consume", Receiver.class, Object[].class, blackholePlugin);
         }
     }
 
     private static void registerJFRThrowablePlugins(InvocationPlugins plugins) {
-        String name = "oracle.jrockit.jfr.jdkevents.ThrowableTracer";
-        Class<?> tracerClass = MethodSubstitutionPlugin.resolveClass(name, true);
-        if (tracerClass != null) {
-            Registration r = new Registration(plugins, tracerClass);
-            r.register2("traceThrowable", Throwable.class, String.class, new InvocationPlugin() {
-                public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode throwable, ValueNode message) {
-                    b.add(new VirtualizableInvokeMacroNode(b.getInvokeKind(), targetMethod, b.bci(), b.getInvokeReturnType(), throwable, message));
-                    return true;
-                }
-            });
-        }
+        Registration r = new Registration(plugins, "oracle.jrockit.jfr.jdkevents.ThrowableTracer");
+        r.register2("traceThrowable", Throwable.class, String.class, new InvocationPlugin() {
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode throwable, ValueNode message) {
+                b.add(new VirtualizableInvokeMacroNode(b.getInvokeKind(), targetMethod, b.bci(), b.getInvokeReturnType(), throwable, message));
+                return true;
+            }
+        });
     }
 }