# HG changeset patch # User Doug Simon # Date 1358280493 -3600 # Node ID 36474f315a8a7cb425a5b0fdacc72010632ad75c # Parent 31d1cc9219d84509cd34e213a2fdbeb95d722852 added support for substitute methods to call the original/substituted methods diff -r 31d1cc9219d8 -r 36474f315a8a graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/ClassSubstitution.java --- 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. *

* 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. *

- * 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). *

* 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. *

* 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. *

* 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 ""; } diff -r 31d1cc9219d8 -r 36474f315a8a graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/SnippetInstaller.java --- 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); + } + } } }