changeset 15942:5c73b162eec2

reduced execution time of ReplacementsImple.registerSubstitutions() by deferring parsing of substitution classes until the first request for a substitute method is received
author Doug Simon <doug.simon@oracle.com>
date Wed, 28 May 2014 00:50:11 +0200
parents b35b1dc75ec0
children 57303ce74a21
files graal/com.oracle.graal.compiler.hsail.test.infra/src/com/oracle/graal/compiler/hsail/test/infra/GraalKernelTester.java graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GraalCompilerTest.java graal/com.oracle.graal.hotspot.hsail/src/com/oracle/graal/hotspot/hsail/HSAILHotSpotReplacementsImpl.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotReplacementsImpl.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/CallSiteSubstitutions.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/HotSpotSubstitutions.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/Replacements.java graal/com.oracle.graal.replacements.amd64/src/com/oracle/graal/replacements/amd64/AMD64Substitutions.java graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/BoxingSubstitutions.java graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/GraalMethodSubstitutions.java graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ReplacementsImpl.java graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/ExactMathTest.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleReplacements.java
diffstat 13 files changed, 275 insertions(+), 173 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.compiler.hsail.test.infra/src/com/oracle/graal/compiler/hsail/test/infra/GraalKernelTester.java	Tue May 27 22:00:41 2014 +0200
+++ b/graal/com.oracle.graal.compiler.hsail.test.infra/src/com/oracle/graal/compiler/hsail/test/infra/GraalKernelTester.java	Wed May 28 00:50:11 2014 +0200
@@ -54,7 +54,7 @@
 
     private static synchronized void installSubstitutions() {
         if (!substitutionsInstalled) {
-            getHSAILBackend().getProviders().getReplacements().registerSubstitutions(ForceDeoptSubstitutions.class);
+            getHSAILBackend().getProviders().getReplacements().registerSubstitutions(GraalKernelTester.class, ForceDeoptSubstitutions.class);
             substitutionsInstalled = true;
         }
     }
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GraalCompilerTest.java	Tue May 27 22:00:41 2014 +0200
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GraalCompilerTest.java	Wed May 28 00:50:11 2014 +0200
@@ -89,7 +89,7 @@
 
     private void installSubstitutions() {
         if (!substitutionsInstalled) {
-            this.providers.getReplacements().registerSubstitutions(InjectProfileDataSubstitutions.class);
+            this.providers.getReplacements().registerSubstitutions(GraalCompilerTest.class, InjectProfileDataSubstitutions.class);
             substitutionsInstalled = true;
         }
     }
--- a/graal/com.oracle.graal.hotspot.hsail/src/com/oracle/graal/hotspot/hsail/HSAILHotSpotReplacementsImpl.java	Tue May 27 22:00:41 2014 +0200
+++ b/graal/com.oracle.graal.hotspot.hsail/src/com/oracle/graal/hotspot/hsail/HSAILHotSpotReplacementsImpl.java	Wed May 28 00:50:11 2014 +0200
@@ -59,16 +59,16 @@
 
     public void completeInitialization() {
         // Register the substitutions for java.lang.Math routines.
-        registerSubstitutions(HSAILMathSubstitutions.class);
+        registerSubstitutions(Math.class, HSAILMathSubstitutions.class);
 
         // Register the ignored substitutions
         addIgnoredResolvedMethod(String.class, "equals", Object.class);
     }
 
     @Override
-    protected ResolvedJavaMethod registerMethodSubstitution(Member originalMethod, Method substituteMethod) {
+    protected ResolvedJavaMethod registerMethodSubstitution(ClassReplacements cr, Member originalMethod, Method substituteMethod) {
         // TODO: decide if we want to override this in any way
-        return super.registerMethodSubstitution(originalMethod, substituteMethod);
+        return super.registerMethodSubstitution(cr, originalMethod, substituteMethod);
     }
 
     @Override
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotReplacementsImpl.java	Tue May 27 22:00:41 2014 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotReplacementsImpl.java	Wed May 28 00:50:11 2014 +0200
@@ -48,7 +48,7 @@
     }
 
     @Override
-    protected ResolvedJavaMethod registerMethodSubstitution(Member originalMethod, Method substituteMethod) {
+    protected ResolvedJavaMethod registerMethodSubstitution(ClassReplacements cr, Member originalMethod, Method substituteMethod) {
         final Class<?> substituteClass = substituteMethod.getDeclaringClass();
         if (substituteClass.getDeclaringClass() == BoxingSubstitutions.class) {
             if (config.useHeapProfiler) {
@@ -78,7 +78,7 @@
                 return null;
             }
         }
-        return super.registerMethodSubstitution(originalMethod, substituteMethod);
+        return super.registerMethodSubstitution(cr, originalMethod, substituteMethod);
     }
 
     @Override
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/CallSiteSubstitutions.java	Tue May 27 22:00:41 2014 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/CallSiteSubstitutions.java	Wed May 28 00:50:11 2014 +0200
@@ -35,9 +35,9 @@
 
     @Override
     public void registerReplacements(MetaAccessProvider metaAccess, LoweringProvider loweringProvider, SnippetReflectionProvider snippetReflection, Replacements replacements, TargetDescription target) {
-        replacements.registerSubstitutions(ConstantCallSiteSubstitutions.class);
-        replacements.registerSubstitutions(MutableCallSiteSubstitutions.class);
-        replacements.registerSubstitutions(VolatileCallSiteSubstitutions.class);
+        replacements.registerSubstitutions(ConstantCallSite.class, ConstantCallSiteSubstitutions.class);
+        replacements.registerSubstitutions(MutableCallSite.class, MutableCallSiteSubstitutions.class);
+        replacements.registerSubstitutions(VolatileCallSite.class, VolatileCallSiteSubstitutions.class);
     }
 
     @ClassSubstitution(ConstantCallSite.class)
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/HotSpotSubstitutions.java	Tue May 27 22:00:41 2014 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/HotSpotSubstitutions.java	Wed May 28 00:50:11 2014 +0200
@@ -22,29 +22,50 @@
  */
 package com.oracle.graal.hotspot.replacements;
 
+import java.lang.reflect.*;
+import java.util.zip.*;
+
+import sun.misc.*;
+import sun.reflect.*;
+
 import com.oracle.graal.api.code.*;
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.api.replacements.*;
 import com.oracle.graal.api.runtime.*;
+import com.oracle.graal.graph.*;
+import com.oracle.graal.lir.*;
 import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.replacements.*;
 
 @ServiceProvider(ReplacementsProvider.class)
 public class HotSpotSubstitutions implements ReplacementsProvider {
 
+    static class NamedType implements Type {
+        private final String name;
+
+        public NamedType(String name) {
+            this.name = name;
+        }
+
+        @Override
+        public String toString() {
+            return name;
+        }
+    }
+
     @Override
     public void registerReplacements(MetaAccessProvider metaAccess, LoweringProvider loweringProvider, SnippetReflectionProvider snippetReflection, Replacements replacements, TargetDescription target) {
-        replacements.registerSubstitutions(ObjectSubstitutions.class);
-        replacements.registerSubstitutions(SystemSubstitutions.class);
-        replacements.registerSubstitutions(ThreadSubstitutions.class);
-        replacements.registerSubstitutions(UnsafeSubstitutions.class);
-        replacements.registerSubstitutions(ClassSubstitutions.class);
-        replacements.registerSubstitutions(AESCryptSubstitutions.class);
-        replacements.registerSubstitutions(CipherBlockChainingSubstitutions.class);
-        replacements.registerSubstitutions(CRC32Substitutions.class);
-        replacements.registerSubstitutions(ReflectionSubstitutions.class);
-        replacements.registerSubstitutions(HotSpotNodeClassSubstitutions.class);
-        replacements.registerSubstitutions(HotSpotNodeSubstitutions.class);
-        replacements.registerSubstitutions(CompositeValueClassSubstitutions.class);
+        replacements.registerSubstitutions(Object.class, ObjectSubstitutions.class);
+        replacements.registerSubstitutions(System.class, SystemSubstitutions.class);
+        replacements.registerSubstitutions(Thread.class, ThreadSubstitutions.class);
+        replacements.registerSubstitutions(Unsafe.class, UnsafeSubstitutions.class);
+        replacements.registerSubstitutions(Class.class, ClassSubstitutions.class);
+        replacements.registerSubstitutions(CRC32.class, CRC32Substitutions.class);
+        replacements.registerSubstitutions(Reflection.class, ReflectionSubstitutions.class);
+        replacements.registerSubstitutions(NodeClass.class, HotSpotNodeClassSubstitutions.class);
+        replacements.registerSubstitutions(Node.class, HotSpotNodeSubstitutions.class);
+        replacements.registerSubstitutions(CompositeValueClass.class, CompositeValueClassSubstitutions.class);
+        replacements.registerSubstitutions(new NamedType("com.sun.crypto.provider.AESCrypt"), AESCryptSubstitutions.class);
+        replacements.registerSubstitutions(new NamedType("com.sun.crypto.provider.CipherBlockChaining"), CipherBlockChainingSubstitutions.class);
     }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/Replacements.java	Tue May 27 22:00:41 2014 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/Replacements.java	Wed May 28 00:50:11 2014 +0200
@@ -22,6 +22,7 @@
  */
 package com.oracle.graal.nodes.spi;
 
+import java.lang.reflect.*;
 import java.util.*;
 
 import com.oracle.graal.api.code.*;
@@ -36,14 +37,14 @@
 
     /**
      * Gets the snippet graph derived from a given method.
-     * 
+     *
      * @return the snippet graph, if any, that is derived from {@code method}
      */
     StructuredGraph getSnippet(ResolvedJavaMethod method);
 
     /**
      * Gets the snippet graph derived from a given method.
-     * 
+     *
      * @param recursiveEntry if the snippet contains a call to this method, it's considered as
      *            recursive call and won't be processed for {@linkplain MethodSubstitution
      *            substitutions} or {@linkplain MacroSubstitution macro nodes}.
@@ -59,7 +60,7 @@
     /**
      * Notifies this object during snippet specialization once the specialized snippet's constant
      * parameters have been replaced with constant values.
-     * 
+     *
      * @param specializedSnippet the snippet in the process of being specialized. This is a copy of
      *            the unspecialized snippet graph created during snippet preparation.
      */
@@ -67,14 +68,14 @@
 
     /**
      * Gets the graph that is a substitution for a given method.
-     * 
+     *
      * @return the graph, if any, that is a substitution for {@code method}
      */
     StructuredGraph getMethodSubstitution(ResolvedJavaMethod method);
 
     /**
      * Gets the node class with which a method invocation should be replaced.
-     * 
+     *
      * @param method target of an invocation
      * @return the {@linkplain MacroSubstitution#macro() macro node class} associated with
      *         {@code method} or null if there is no such association
@@ -89,8 +90,15 @@
     /**
      * Registers all the {@linkplain MethodSubstitution method} and {@linkplain MacroSubstitution
      * macro} substitutions defined by a given class.
+     *
+     * @param original the original class for which substitutions are being registered. This must be
+     *            the same type denoted by the {@link ClassSubstitution} annotation on
+     *            {@code substitutions}. It is required here so that an implementation is not forced
+     *            to read annotations during registration.
+     * @param substitutions the class defining substitutions for {@code original}. This class must
+     *            be annotated with {@link ClassSubstitution}.
      */
-    void registerSubstitutions(Class<?> substitutions);
+    void registerSubstitutions(Type original, Class<?> substitutions);
 
     /**
      * Returns all methods that are currently registered as method/macro substitution or as a
--- a/graal/com.oracle.graal.replacements.amd64/src/com/oracle/graal/replacements/amd64/AMD64Substitutions.java	Tue May 27 22:00:41 2014 +0200
+++ b/graal/com.oracle.graal.replacements.amd64/src/com/oracle/graal/replacements/amd64/AMD64Substitutions.java	Wed May 28 00:50:11 2014 +0200
@@ -25,6 +25,8 @@
 
 import static com.oracle.graal.compiler.common.GraalOptions.*;
 
+import java.util.*;
+
 import com.oracle.graal.api.code.*;
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.api.replacements.*;
@@ -40,8 +42,8 @@
 
     public void registerReplacements(MetaAccessProvider metaAccess, LoweringProvider lowerer, SnippetReflectionProvider snippetReflection, Replacements replacements, TargetDescription target) {
         if (Intrinsify.getValue()) {
-            replacements.registerSubstitutions(ArraysSubstitutions.class);
-            replacements.registerSubstitutions(StringSubstitutions.class);
+            replacements.registerSubstitutions(Arrays.class, ArraysSubstitutions.class);
+            replacements.registerSubstitutions(String.class, StringSubstitutions.class);
         }
     }
 
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/BoxingSubstitutions.java	Tue May 27 22:00:41 2014 +0200
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/BoxingSubstitutions.java	Wed May 28 00:50:11 2014 +0200
@@ -25,6 +25,7 @@
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.api.replacements.*;
 import com.oracle.graal.nodes.extended.*;
+import com.oracle.graal.nodes.spi.*;
 
 public class BoxingSubstitutions {
 
@@ -140,8 +141,14 @@
         }
     }
 
-    public static Class<?>[] getClasses() {
-        return new Class<?>[]{BooleanSubstitutions.class, ByteSubstitutions.class, CharacterSubstitutions.class, DoubleSubstitutions.class, FloatSubstitutions.class, IntegerSubstitutions.class,
-                        LongSubstitutions.class, ShortSubstitutions.class};
+    public static void registerReplacements(Replacements replacements) {
+        replacements.registerSubstitutions(Boolean.class, BooleanSubstitutions.class);
+        replacements.registerSubstitutions(Character.class, CharacterSubstitutions.class);
+        replacements.registerSubstitutions(Double.class, DoubleSubstitutions.class);
+        replacements.registerSubstitutions(Byte.class, ByteSubstitutions.class);
+        replacements.registerSubstitutions(Float.class, FloatSubstitutions.class);
+        replacements.registerSubstitutions(Integer.class, IntegerSubstitutions.class);
+        replacements.registerSubstitutions(Short.class, ShortSubstitutions.class);
+        replacements.registerSubstitutions(Long.class, LongSubstitutions.class);
     }
 }
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/GraalMethodSubstitutions.java	Tue May 27 22:00:41 2014 +0200
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/GraalMethodSubstitutions.java	Wed May 28 00:50:11 2014 +0200
@@ -24,6 +24,8 @@
 
 import static com.oracle.graal.compiler.common.GraalOptions.*;
 
+import java.lang.reflect.*;
+
 import com.oracle.graal.api.code.*;
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.api.replacements.*;
@@ -37,20 +39,17 @@
 public class GraalMethodSubstitutions implements ReplacementsProvider {
 
     public void registerReplacements(MetaAccessProvider metaAccess, LoweringProvider loweringProvider, SnippetReflectionProvider snippetReflection, Replacements replacements, TargetDescription target) {
-        for (Class<?> clazz : BoxingSubstitutions.getClasses()) {
-            replacements.registerSubstitutions(clazz);
-        }
-
+        BoxingSubstitutions.registerReplacements(replacements);
         if (Intrinsify.getValue()) {
-            replacements.registerSubstitutions(ArraySubstitutions.class);
-            replacements.registerSubstitutions(MathSubstitutionsX86.class);
-            replacements.registerSubstitutions(DoubleSubstitutions.class);
-            replacements.registerSubstitutions(FloatSubstitutions.class);
-            replacements.registerSubstitutions(LongSubstitutions.class);
-            replacements.registerSubstitutions(IntegerSubstitutions.class);
-            replacements.registerSubstitutions(CharacterSubstitutions.class);
-            replacements.registerSubstitutions(ShortSubstitutions.class);
-            replacements.registerSubstitutions(UnsignedMathSubstitutions.class);
+            replacements.registerSubstitutions(Array.class, ArraySubstitutions.class);
+            replacements.registerSubstitutions(Math.class, MathSubstitutionsX86.class);
+            replacements.registerSubstitutions(Double.class, DoubleSubstitutions.class);
+            replacements.registerSubstitutions(Float.class, FloatSubstitutions.class);
+            replacements.registerSubstitutions(Long.class, LongSubstitutions.class);
+            replacements.registerSubstitutions(Integer.class, IntegerSubstitutions.class);
+            replacements.registerSubstitutions(Character.class, CharacterSubstitutions.class);
+            replacements.registerSubstitutions(Short.class, ShortSubstitutions.class);
+            replacements.registerSubstitutions(UnsignedMath.class, UnsignedMathSubstitutions.class);
         }
     }
 }
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ReplacementsImpl.java	Tue May 27 22:00:41 2014 +0200
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ReplacementsImpl.java	Wed May 28 00:50:11 2014 +0200
@@ -29,6 +29,7 @@
 import java.lang.reflect.*;
 import java.util.*;
 import java.util.concurrent.*;
+import java.util.concurrent.atomic.*;
 
 import sun.misc.*;
 
@@ -68,28 +69,165 @@
      */
     protected final ConcurrentMap<ResolvedJavaMethod, StructuredGraph> graphs;
 
-    // These data structures are all fully initialized during single-threaded
-    // compiler startup and so do not need to be concurrent.
-    protected final Map<ResolvedJavaMethod, ResolvedJavaMethod> registeredMethodSubstitutions;
-    private final Map<ResolvedJavaMethod, Class<? extends FixedWithNextNode>> registeredMacroSubstitutions;
-    private final Set<ResolvedJavaMethod> forcedSubstitutions;
+    /**
+     * Encapsulates method and macro substitutions for a single class.
+     */
+    protected class ClassReplacements {
+        protected final Map<ResolvedJavaMethod, ResolvedJavaMethod> methodSubstitutions = new HashMap<>();
+        private final Map<ResolvedJavaMethod, Class<? extends FixedWithNextNode>> macroSubstitutions = new HashMap<>();
+        private final Set<ResolvedJavaMethod> forcedSubstitutions = new HashSet<>();
+
+        public ClassReplacements(Class<?>[] substitutionClasses, AtomicReference<ClassReplacements> ref) {
+            for (Class<?> substitutionClass : substitutionClasses) {
+                ClassSubstitution classSubstitution = substitutionClass.getAnnotation(ClassSubstitution.class);
+                assert !Snippets.class.isAssignableFrom(substitutionClass);
+                SubstitutionGuard defaultGuard = getGuard(classSubstitution.defaultGuard());
+                for (Method substituteMethod : substitutionClass.getDeclaredMethods()) {
+                    if (ref.get() != null) {
+                        // Bail if another thread beat us creating the substitutions
+                        return;
+                    }
+                    MethodSubstitution methodSubstitution = substituteMethod.getAnnotation(MethodSubstitution.class);
+                    MacroSubstitution macroSubstitution = substituteMethod.getAnnotation(MacroSubstitution.class);
+                    if (methodSubstitution == null && macroSubstitution == null) {
+                        continue;
+                    }
+
+                    int modifiers = substituteMethod.getModifiers();
+                    if (!Modifier.isStatic(modifiers)) {
+                        throw new GraalInternalError("Substitution methods must be static: " + substituteMethod);
+                    }
+
+                    if (methodSubstitution != null) {
+                        SubstitutionGuard guard = getGuard(methodSubstitution.guard());
+                        if (guard == null) {
+                            guard = defaultGuard;
+                        }
+
+                        if (macroSubstitution != null && macroSubstitution.isStatic() != methodSubstitution.isStatic()) {
+                            throw new GraalInternalError("Macro and method substitution must agree on isStatic attribute: " + substituteMethod);
+                        }
+                        if (Modifier.isAbstract(modifiers) || Modifier.isNative(modifiers)) {
+                            throw new GraalInternalError("Substitution method must not be abstract or native: " + substituteMethod);
+                        }
+                        String originalName = originalName(substituteMethod, methodSubstitution.value());
+                        JavaSignature originalSignature = originalSignature(substituteMethod, methodSubstitution.signature(), methodSubstitution.isStatic());
+                        Member originalMethod = originalMethod(classSubstitution, methodSubstitution.optional(), originalName, originalSignature);
+                        if (originalMethod != null && (guard == null || guard.execute())) {
+                            ResolvedJavaMethod original = registerMethodSubstitution(this, originalMethod, substituteMethod);
+                            if (original != null && methodSubstitution.forced() && shouldIntrinsify(original)) {
+                                forcedSubstitutions.add(original);
+                            }
+                        }
+                    }
+                    // We don't have per method guards for macro substitutions but at
+                    // least respect the defaultGuard if there is one.
+                    if (macroSubstitution != null && (defaultGuard == null || defaultGuard.execute())) {
+                        String originalName = originalName(substituteMethod, macroSubstitution.value());
+                        JavaSignature originalSignature = originalSignature(substituteMethod, macroSubstitution.signature(), macroSubstitution.isStatic());
+                        Member originalMethod = originalMethod(classSubstitution, macroSubstitution.optional(), originalName, originalSignature);
+                        if (originalMethod != null) {
+                            ResolvedJavaMethod original = registerMacroSubstitution(this, originalMethod, macroSubstitution.macro());
+                            if (original != null && macroSubstitution.forced() && shouldIntrinsify(original)) {
+                                forcedSubstitutions.add(original);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        private JavaSignature originalSignature(Method substituteMethod, String methodSubstitution, boolean isStatic) {
+            Class<?>[] parameters;
+            Class<?> returnType;
+            if (methodSubstitution.isEmpty()) {
+                parameters = substituteMethod.getParameterTypes();
+                if (!isStatic) {
+                    assert parameters.length > 0 : "must be a static method with the 'this' object as its first parameter";
+                    parameters = Arrays.copyOfRange(parameters, 1, parameters.length);
+                }
+                returnType = substituteMethod.getReturnType();
+            } else {
+                Signature signature = providers.getMetaAccess().parseMethodDescriptor(methodSubstitution);
+                parameters = new Class[signature.getParameterCount(false)];
+                for (int i = 0; i < parameters.length; i++) {
+                    parameters[i] = resolveClass(signature.getParameterType(i, null));
+                }
+                returnType = resolveClass(signature.getReturnType(null));
+            }
+            return new JavaSignature(returnType, parameters);
+        }
+
+        private Member originalMethod(ClassSubstitution classSubstitution, boolean optional, String name, JavaSignature signature) {
+            Class<?> originalClass = classSubstitution.value();
+            if (originalClass == ClassSubstitution.class) {
+                originalClass = resolveClass(classSubstitution.className(), classSubstitution.optional());
+                if (originalClass == null) {
+                    // optional class was not found
+                    return null;
+                }
+            }
+            try {
+                if (name.equals("<init>")) {
+                    assert signature.returnType.equals(void.class) : signature;
+                    Constructor<?> original = originalClass.getDeclaredConstructor(signature.parameters);
+                    return original;
+                } else {
+                    Method original = originalClass.getDeclaredMethod(name, signature.parameters);
+                    if (!original.getReturnType().equals(signature.returnType)) {
+                        throw new NoSuchMethodException(originalClass.getName() + "." + name + signature);
+                    }
+                    return original;
+                }
+            } catch (NoSuchMethodException | SecurityException e) {
+                if (optional) {
+                    return null;
+                }
+                throw new GraalInternalError(e);
+            }
+        }
+    }
+
+    /**
+     * Per-class replacements. The entries in these maps are all fully initialized during
+     * single-threaded compiler startup and so do not need to be concurrent.
+     */
+    private final Map<String, AtomicReference<ClassReplacements>> classReplacements;
+    private final Map<String, Class<?>[]> internalNameToSubstitutionClasses;
+
     private final Map<Class<? extends SnippetTemplateCache>, SnippetTemplateCache> snippetTemplateCache;
 
     public ReplacementsImpl(Providers providers, SnippetReflectionProvider snippetReflection, Assumptions assumptions, TargetDescription target) {
         this.providers = providers.copyWith(this);
+        this.classReplacements = new HashMap<>();
+        this.internalNameToSubstitutionClasses = new HashMap<>();
         this.snippetReflection = snippetReflection;
         this.target = target;
         this.assumptions = assumptions;
         this.graphs = new ConcurrentHashMap<>();
-        this.registeredMethodSubstitutions = new HashMap<>();
-        this.registeredMacroSubstitutions = new HashMap<>();
-        this.forcedSubstitutions = new HashSet<>();
         this.snippetTemplateCache = new HashMap<>();
     }
 
     private static final boolean UseSnippetGraphCache = Boolean.parseBoolean(System.getProperty("graal.useSnippetGraphCache", "true"));
     private static final DebugTimer SnippetPreparationTime = Debug.timer("SnippetPreparationTime");
 
+    /**
+     * Gets the method and macro replacements for a given class. This method will parse the
+     * replacements in the substitution classes associated with {@code internalName} the first time
+     * this method is called for {@code internalName}.
+     */
+    protected ClassReplacements getClassReplacements(String internalName) {
+        Class<?>[] substitutionClasses = internalNameToSubstitutionClasses.get(internalName);
+        if (substitutionClasses != null) {
+            AtomicReference<ClassReplacements> crRef = classReplacements.get(internalName);
+            if (crRef.get() == null) {
+                crRef.compareAndSet(null, new ClassReplacements(substitutionClasses, crRef));
+            }
+            return crRef.get();
+        }
+        return null;
+    }
+
     public StructuredGraph getSnippet(ResolvedJavaMethod method) {
         return getSnippet(method, null);
     }
@@ -133,7 +271,8 @@
 
     @Override
     public StructuredGraph getMethodSubstitution(ResolvedJavaMethod original) {
-        ResolvedJavaMethod substitute = registeredMethodSubstitutions.get(original);
+        ClassReplacements cr = getClassReplacements(original.getDeclaringClass().getName());
+        ResolvedJavaMethod substitute = cr == null ? null : cr.methodSubstitutions.get(original);
         if (substitute == null) {
             return null;
         }
@@ -150,7 +289,8 @@
     }
 
     public Class<? extends FixedWithNextNode> getMacroSubstitution(ResolvedJavaMethod method) {
-        return registeredMacroSubstitutions.get(method);
+        ClassReplacements cr = getClassReplacements(method.getDeclaringClass().getName());
+        return cr == null ? null : cr.macroSubstitutions.get(method);
     }
 
     public Assumptions getAssumptions() {
@@ -168,59 +308,29 @@
         return null;
     }
 
-    public void registerSubstitutions(Class<?> substitutions) {
-        ClassSubstitution classSubstitution = substitutions.getAnnotation(ClassSubstitution.class);
-        assert classSubstitution != null;
-        assert !Snippets.class.isAssignableFrom(substitutions);
-        SubstitutionGuard defaultGuard = getGuard(classSubstitution.defaultGuard());
-        for (Method substituteMethod : substitutions.getDeclaredMethods()) {
-            MethodSubstitution methodSubstitution = substituteMethod.getAnnotation(MethodSubstitution.class);
-            MacroSubstitution macroSubstitution = substituteMethod.getAnnotation(MacroSubstitution.class);
-            if (methodSubstitution == null && macroSubstitution == null) {
-                continue;
-            }
-
-            int modifiers = substituteMethod.getModifiers();
-            if (!Modifier.isStatic(modifiers)) {
-                throw new GraalInternalError("Substitution methods must be static: " + substituteMethod);
-            }
-
-            if (methodSubstitution != null) {
-                SubstitutionGuard guard = getGuard(methodSubstitution.guard());
-                if (guard == null) {
-                    guard = defaultGuard;
-                }
+    private static String getOriginalInternalName(Class<?> substitutions) {
+        ClassSubstitution cs = substitutions.getAnnotation(ClassSubstitution.class);
+        assert cs != null : substitutions + " must be annotated by " + ClassSubstitution.class.getSimpleName();
+        if (cs.value() == ClassSubstitution.class) {
+            return toInternalName(cs.className());
+        }
+        return toInternalName(cs.value().getName());
+    }
 
-                if (macroSubstitution != null && macroSubstitution.isStatic() != methodSubstitution.isStatic()) {
-                    throw new GraalInternalError("Macro and method substitution must agree on isStatic attribute: " + substituteMethod);
-                }
-                if (Modifier.isAbstract(modifiers) || Modifier.isNative(modifiers)) {
-                    throw new GraalInternalError("Substitution method must not be abstract or native: " + substituteMethod);
-                }
-                String originalName = originalName(substituteMethod, methodSubstitution.value());
-                JavaSignature originalSignature = originalSignature(substituteMethod, methodSubstitution.signature(), methodSubstitution.isStatic());
-                Member originalMethod = originalMethod(classSubstitution, methodSubstitution.optional(), originalName, originalSignature);
-                if (originalMethod != null && (guard == null || guard.execute())) {
-                    ResolvedJavaMethod original = registerMethodSubstitution(originalMethod, substituteMethod);
-                    if (original != null && methodSubstitution.forced() && shouldIntrinsify(original)) {
-                        forcedSubstitutions.add(original);
-                    }
-                }
-            }
-            // We don't have per method guards for macro substitutions but at
-            // least respect the defaultGuard if there is one.
-            if (macroSubstitution != null && (defaultGuard == null || defaultGuard.execute())) {
-                String originalName = originalName(substituteMethod, macroSubstitution.value());
-                JavaSignature originalSignature = originalSignature(substituteMethod, macroSubstitution.signature(), macroSubstitution.isStatic());
-                Member originalMethod = originalMethod(classSubstitution, macroSubstitution.optional(), originalName, originalSignature);
-                if (originalMethod != null) {
-                    ResolvedJavaMethod original = registerMacroSubstitution(originalMethod, macroSubstitution.macro());
-                    if (original != null && macroSubstitution.forced() && shouldIntrinsify(original)) {
-                        forcedSubstitutions.add(original);
-                    }
-                }
-            }
+    public void registerSubstitutions(Type original, Class<?> substitutionClass) {
+        String internalName = toInternalName(original.getTypeName());
+        assert getOriginalInternalName(substitutionClass).equals(internalName) : getOriginalInternalName(substitutionClass) + " != " + (internalName);
+        Class<?>[] classes = internalNameToSubstitutionClasses.get(internalName);
+        if (classes == null) {
+            classes = new Class<?>[]{substitutionClass};
+        } else {
+            assert !Arrays.asList(classes).contains(substitutionClass);
+            classes = Arrays.copyOf(classes, classes.length + 1);
+            classes[classes.length - 1] = substitutionClass;
         }
+        internalNameToSubstitutionClasses.put(internalName, classes);
+        AtomicReference<ClassReplacements> existing = classReplacements.put(internalName, new AtomicReference<>());
+        assert existing == null || existing.get() == null;
     }
 
     /**
@@ -230,7 +340,7 @@
      * @param substituteMethod the substitute method
      * @return the original method
      */
-    protected ResolvedJavaMethod registerMethodSubstitution(Member originalMember, Method substituteMethod) {
+    protected ResolvedJavaMethod registerMethodSubstitution(ClassReplacements cr, Member originalMember, Method substituteMethod) {
         MetaAccessProvider metaAccess = providers.getMetaAccess();
         ResolvedJavaMethod substitute = metaAccess.lookupJavaMethod(substituteMethod);
         ResolvedJavaMethod original;
@@ -243,7 +353,7 @@
             Debug.log("substitution: %s --> %s", MetaUtil.format("%H.%n(%p) %r", original), MetaUtil.format("%H.%n(%p) %r", substitute));
         }
 
-        registeredMethodSubstitutions.put(original, substitute);
+        cr.methodSubstitutions.put(original, substitute);
         return original;
     }
 
@@ -254,7 +364,7 @@
      * @param macro the substitute macro node class
      * @return the original method
      */
-    protected ResolvedJavaMethod registerMacroSubstitution(Member originalMethod, Class<? extends FixedWithNextNode> macro) {
+    protected ResolvedJavaMethod registerMacroSubstitution(ClassReplacements cr, Member originalMethod, Class<? extends FixedWithNextNode> macro) {
         ResolvedJavaMethod originalJavaMethod;
         MetaAccessProvider metaAccess = providers.getMetaAccess();
         if (originalMethod instanceof Method) {
@@ -262,7 +372,7 @@
         } else {
             originalJavaMethod = metaAccess.lookupJavaConstructor((Constructor<?>) originalMethod);
         }
-        registeredMacroSubstitutions.put(originalJavaMethod, macro);
+        cr.macroSubstitutions.put(originalJavaMethod, macro);
         return originalJavaMethod;
     }
 
@@ -551,7 +661,7 @@
      * @param optional if true, resolution failure returns null
      * @return the resolved class or null if resolution fails and {@code optional} is true
      */
-    static Class<?> resolveType(String className, boolean optional) {
+    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
@@ -565,7 +675,7 @@
         }
     }
 
-    private static Class<?> resolveType(JavaType type) {
+    private static Class<?> resolveClass(JavaType type) {
         JavaType base = type;
         int dimensions = 0;
         while (base.getComponentType() != null) {
@@ -573,7 +683,7 @@
             dimensions++;
         }
 
-        Class<?> baseClass = base.getKind() != Kind.Object ? base.getKind().toJavaClass() : resolveType(toJavaName(base), false);
+        Class<?> baseClass = base.getKind() != Kind.Object ? base.getKind().toJavaClass() : resolveClass(toJavaName(base), false);
         return dimensions == 0 ? baseClass : Array.newInstance(baseClass, new int[dimensions]).getClass();
     }
 
@@ -599,67 +709,21 @@
         }
     }
 
-    private JavaSignature originalSignature(Method substituteMethod, String methodSubstitution, boolean isStatic) {
-        Class<?>[] parameters;
-        Class<?> returnType;
-        if (methodSubstitution.isEmpty()) {
-            parameters = substituteMethod.getParameterTypes();
-            if (!isStatic) {
-                assert parameters.length > 0 : "must be a static method with the 'this' object as its first parameter";
-                parameters = Arrays.copyOfRange(parameters, 1, parameters.length);
-            }
-            returnType = substituteMethod.getReturnType();
-        } else {
-            Signature signature = providers.getMetaAccess().parseMethodDescriptor(methodSubstitution);
-            parameters = new Class[signature.getParameterCount(false)];
-            for (int i = 0; i < parameters.length; i++) {
-                parameters[i] = resolveType(signature.getParameterType(i, null));
-            }
-            returnType = resolveType(signature.getReturnType(null));
-        }
-        return new JavaSignature(returnType, parameters);
-    }
-
-    private static Member originalMethod(ClassSubstitution classSubstitution, boolean optional, String name, JavaSignature signature) {
-        Class<?> originalClass = classSubstitution.value();
-        if (originalClass == ClassSubstitution.class) {
-            originalClass = resolveType(classSubstitution.className(), classSubstitution.optional());
-            if (originalClass == null) {
-                // optional class was not found
-                return null;
-            }
-        }
-        try {
-            if (name.equals("<init>")) {
-                assert signature.returnType.equals(void.class) : signature;
-                Constructor<?> original = originalClass.getDeclaredConstructor(signature.parameters);
-                return original;
-            } else {
-                Method original = originalClass.getDeclaredMethod(name, signature.parameters);
-                if (!original.getReturnType().equals(signature.returnType)) {
-                    throw new NoSuchMethodException(originalClass.getName() + "." + name + signature);
-                }
-                return original;
-            }
-        } catch (NoSuchMethodException | SecurityException e) {
-            if (optional) {
-                return null;
-            }
-            throw new GraalInternalError(e);
-        }
-    }
-
     @Override
     public Collection<ResolvedJavaMethod> getAllReplacements() {
         HashSet<ResolvedJavaMethod> result = new HashSet<>();
-        result.addAll(registeredMethodSubstitutions.keySet());
-        result.addAll(registeredMacroSubstitutions.keySet());
+        for (String internalName : classReplacements.keySet()) {
+            ClassReplacements cr = getClassReplacements(internalName);
+            result.addAll(cr.methodSubstitutions.keySet());
+            result.addAll(cr.macroSubstitutions.keySet());
+        }
         return result;
     }
 
     @Override
     public boolean isForcedSubstitution(ResolvedJavaMethod method) {
-        return forcedSubstitutions.contains(method);
+        ClassReplacements cr = getClassReplacements(method.getDeclaringClass().getName());
+        return cr != null && cr.forcedSubstitutions.contains(method);
     }
 
     @Override
--- a/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/ExactMathTest.java	Tue May 27 22:00:41 2014 +0200
+++ b/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/ExactMathTest.java	Wed May 28 00:50:11 2014 +0200
@@ -38,7 +38,7 @@
     public ExactMathTest() {
         if (!substitutionsInstalled) {
             Replacements replacements = Graal.getRequiredCapability(RuntimeProvider.class).getHostBackend().getProviders().getReplacements();
-            replacements.registerSubstitutions(ExactMathSubstitutions.class);
+            replacements.registerSubstitutions(ExactMath.class, ExactMathSubstitutions.class);
             substitutionsInstalled = true;
         }
     }
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleReplacements.java	Tue May 27 22:00:41 2014 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleReplacements.java	Wed May 28 00:50:11 2014 +0200
@@ -32,6 +32,7 @@
 import com.oracle.graal.phases.util.*;
 import com.oracle.graal.replacements.*;
 import com.oracle.graal.truffle.substitutions.*;
+import com.oracle.truffle.api.*;
 
 /**
  * Custom {@link Replacements} for Truffle compilation.
@@ -48,11 +49,11 @@
     }
 
     protected void registerTruffleSubstitutions() {
-        registerSubstitutions(CompilerAssertsSubstitutions.class);
-        registerSubstitutions(CompilerDirectivesSubstitutions.class);
-        registerSubstitutions(ExactMathSubstitutions.class);
-        registerSubstitutions(OptimizedAssumptionSubstitutions.class);
-        registerSubstitutions(OptimizedCallTargetSubstitutions.class);
+        registerSubstitutions(CompilerAsserts.class, CompilerAssertsSubstitutions.class);
+        registerSubstitutions(CompilerDirectives.class, CompilerDirectivesSubstitutions.class);
+        registerSubstitutions(ExactMath.class, ExactMathSubstitutions.class);
+        registerSubstitutions(OptimizedAssumption.class, OptimizedAssumptionSubstitutions.class);
+        registerSubstitutions(OptimizedCallTarget.class, OptimizedCallTargetSubstitutions.class);
     }
 
     @Override