changeset 7383:36474f315a8a

added support for substitute methods to call the original/substituted methods
author Doug Simon <doug.simon@oracle.com>
date Tue, 15 Jan 2013 21:08:13 +0100
parents 31d1cc9219d8
children d0fbdf2f7a0e
files graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/ClassSubstitution.java graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/SnippetInstaller.java
diffstat 2 files changed, 51 insertions(+), 21 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/ClassSubstitution.java	Tue Jan 15 20:43:44 2013 +0100
+++ b/graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/ClassSubstitution.java	Tue Jan 15 21:08:13 2013 +0100
@@ -27,7 +27,7 @@
 import com.oracle.graal.api.meta.*;
 
 /**
- * Denotes a class that substitutes methods of another specified class with snippets.
+ * Denotes a class that substitutes methods of another specified class.
  * The substitute methods are exactly those annotated by {@link MethodSubstitution}.
  */
 @Retention(RetentionPolicy.RUNTIME)
@@ -35,7 +35,7 @@
 public @interface ClassSubstitution {
 
     /**
-     * Specifies the substituted class.
+     * Specifies the original class.
      * <p>
      * If the default value is specified for this element, then a non-default
      * value must be given for the {@link #className()} element.
@@ -43,9 +43,9 @@
     Class<?> value() default ClassSubstitution.class;
 
     /**
-     * Specifies the substituted class.
+     * Specifies the original class.
      * <p>
-     * This method is provided for cases where the substituted class
+     * This method is provided for cases where the original class
      * is not accessible (according to Java language access control rules).
      * <p>
      * If the default value is specified for this element, then a non-default
@@ -54,29 +54,30 @@
     String className() default "";
 
     /**
-     * Denotes a substitute method.
+     * Denotes a substitute method. A substitute method can call the original/substituted
+     * method by making a recursive call to itself.
      */
     @Retention(RetentionPolicy.RUNTIME)
     @Target(ElementType.METHOD)
     public @interface MethodSubstitution {
         /**
-         * Gets the name of the substituted method.
+         * Gets the name of the original method.
          * <p>
          * If the default value is specified for this element, then the
-         * name of the substituted method is same as the substitute method.
+         * name of the original method is same as the substitute method.
          */
         String value() default "";
 
         /**
-         * Determines if the substituted method is static.
+         * Determines if the original method is static.
          */
         boolean isStatic() default true;
 
         /**
-         * Gets the {@linkplain Signature#getMethodDescriptor() signature} of the substituted method.
+         * Gets the {@linkplain Signature#getMethodDescriptor() signature} of the original method.
          * <p>
          * If the default value is specified for this element, then the
-         * signature of the substituted method is the same as the substitute method.
+         * signature of the original method is the same as the substitute method.
          */
         String signature() default "";
     }
--- a/graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/SnippetInstaller.java	Tue Jan 15 20:43:44 2013 +0100
+++ b/graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/SnippetInstaller.java	Tue Jan 15 21:08:13 2013 +0100
@@ -56,6 +56,7 @@
     private final TargetDescription target;
     private final Assumptions assumptions;
     private final BoxingMethodPool pool;
+    private final Thread owner;
 
     /**
      * A graph cache used by this installer to avoid using the compiler
@@ -71,6 +72,7 @@
         this.assumptions = assumptions;
         this.pool = new BoxingMethodPool(runtime);
         this.graphCache = new HashMap<>();
+        this.owner = Thread.currentThread();
     }
 
     /**
@@ -101,6 +103,7 @@
      * {@linkplain ResolvedJavaMethod#getCompilerStorage() compiler storage} of each original method.
      */
     public void installSubstitutions(Class<?> substitutions) {
+        assert owner == Thread.currentThread() : "substitution installation must be single threaded";
         ClassSubstitution classSubstitution = substitutions.getAnnotation(ClassSubstitution.class);
         for (Method substituteMethod : substitutions.getDeclaredMethods()) {
             MethodSubstitution methodSubstitution = substituteMethod.getAnnotation(MethodSubstitution.class);
@@ -122,6 +125,11 @@
         }
     }
 
+    // These fields are used to detect calls from the substitute method to the original method.
+    ResolvedJavaMethod substitute;
+    ResolvedJavaMethod original;
+    boolean substituteCallsOriginal;
+
     /**
      * Installs a method substitution.
      *
@@ -129,12 +137,18 @@
      * @param substituteMethod the substitute method
      */
     protected void installSubstitution(Method originalMethod, Method substituteMethod) {
-        ResolvedJavaMethod substitute = runtime.lookupJavaMethod(substituteMethod);
-        ResolvedJavaMethod original = runtime.lookupJavaMethod(originalMethod);
-        //System.out.println("substitution: " + MetaUtil.format("%H.%n(%p)", original) + " --> " + MetaUtil.format("%H.%n(%p)", substitute));
-        StructuredGraph graph = makeGraph(substitute, inliningPolicy(substitute), true);
-        Object oldValue = original.getCompilerStorage().put(Graph.class, graph);
-        assert oldValue == null;
+        substitute = runtime.lookupJavaMethod(substituteMethod);
+        original = runtime.lookupJavaMethod(originalMethod);
+        try {
+            //System.out.println("substitution: " + MetaUtil.format("%H.%n(%p)", original) + " --> " + MetaUtil.format("%H.%n(%p)", substitute));
+            StructuredGraph graph = makeGraph(substitute, inliningPolicy(substitute), true);
+            Object oldValue = original.getCompilerStorage().put(Graph.class, graph);
+            assert oldValue == null;
+        } finally {
+            substitute = null;
+            original = null;
+            substituteCallsOriginal = false;
+        }
     }
 
     private SnippetInliningPolicy inliningPolicy(ResolvedJavaMethod method) {
@@ -161,7 +175,7 @@
 
                 new SnippetIntrinsificationPhase(runtime, pool, SnippetTemplate.hasConstantParameter(method)).apply(graph);
 
-                if (isSubstitution) {
+                if (isSubstitution && !substituteCallsOriginal) {
                     // TODO (ds) remove the constraint of only processing substitutions
                     // once issues with the arraycopy snippets have been resolved
                     new SnippetFrameStateCleanupPhase().apply(graph);
@@ -203,14 +217,29 @@
         for (Invoke invoke : graph.getInvokes()) {
             MethodCallTargetNode callTarget = invoke.methodCallTarget();
             ResolvedJavaMethod callee = callTarget.targetMethod();
-            if ((callTarget.invokeKind() == InvokeKind.Static || callTarget.invokeKind() == InvokeKind.Special) && policy.shouldInline(callee, method)) {
-                StructuredGraph targetGraph = parseGraph(callee, policy);
-                InliningUtil.inline(invoke, targetGraph, true);
+            if (callee == substitute) {
+                final StructuredGraph originalGraph = new StructuredGraph(original);
+                new GraphBuilderPhase(runtime, GraphBuilderConfiguration.getSnippetDefault(), OptimisticOptimizations.NONE).apply(originalGraph);
+                InliningUtil.inline(invoke, originalGraph, true);
+
+                // TODO the inlined frame states still show the call from the substitute to the original.
+                // If this poses a problem, a phase should added to fix up these frame states.
+
                 Debug.dump(graph, "after inlining %s", callee);
                 if (GraalOptions.OptCanonicalizer) {
-                    new WordTypeRewriterPhase(runtime, target.wordKind).apply(graph);
                     new CanonicalizerPhase(target, runtime, assumptions).apply(graph);
                 }
+                substituteCallsOriginal = true;
+            } else {
+                if ((callTarget.invokeKind() == InvokeKind.Static || callTarget.invokeKind() == InvokeKind.Special) && policy.shouldInline(callee, method)) {
+                    StructuredGraph targetGraph = parseGraph(callee, policy);
+                    InliningUtil.inline(invoke, targetGraph, true);
+                    Debug.dump(graph, "after inlining %s", callee);
+                    if (GraalOptions.OptCanonicalizer) {
+                        new WordTypeRewriterPhase(runtime, target.wordKind).apply(graph);
+                        new CanonicalizerPhase(target, runtime, assumptions).apply(graph);
+                    }
+                }
             }
         }