changeset 18467:1d2e382d8259

Add UncheckedInterfaceProvider interface for nodes that can give an unchecked hint about the interface type they are likely to return. Use it in MethodCallTargetNode to attempt single-implementor checkcast based devirtualization.
author Gilles Duboscq <duboscq@ssw.jku.at>
date Wed, 19 Nov 2014 14:36:01 +0100
parents 2d2fcdbae37b
children 6014e40b07f8
files graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/Invoke.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/ParameterNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/LoadFieldNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/MethodCallTargetNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/UncheckedInterfaceProvider.java
diffstat 5 files changed, 119 insertions(+), 28 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/Invoke.java	Wed Nov 19 17:11:19 2014 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/Invoke.java	Wed Nov 19 14:36:01 2014 +0100
@@ -23,6 +23,7 @@
 package com.oracle.graal.nodes;
 
 import com.oracle.graal.api.meta.*;
+import com.oracle.graal.compiler.common.type.*;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.CallTargetNode.InvokeKind;
 import com.oracle.graal.nodes.extended.*;
@@ -30,7 +31,7 @@
 import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.nodes.type.*;
 
-public interface Invoke extends StateSplit, Lowerable, DeoptimizingNode.DeoptDuring, GuardedNode {
+public interface Invoke extends StateSplit, Lowerable, DeoptimizingNode.DeoptDuring, GuardedNode, UncheckedInterfaceProvider {
 
     FixedNode next();
 
@@ -104,4 +105,13 @@
     default InvokeKind getInvokeKind() {
         return callTarget().invokeKind();
     }
+
+    default Stamp uncheckedStamp() {
+        if (callTarget() instanceof MethodCallTargetNode) {
+            MethodCallTargetNode methodCallTargetNode = (MethodCallTargetNode) callTarget();
+            JavaType returnType = methodCallTargetNode.targetMethod().getSignature().getReturnType(getContextType());
+            return UncheckedInterfaceProvider.uncheckedOrNull(returnType, asNode().stamp());
+        }
+        return null;
+    }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/ParameterNode.java	Wed Nov 19 17:11:19 2014 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/ParameterNode.java	Wed Nov 19 14:36:01 2014 +0100
@@ -22,15 +22,17 @@
  */
 package com.oracle.graal.nodes;
 
+import com.oracle.graal.api.meta.*;
 import com.oracle.graal.compiler.common.type.*;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.nodeinfo.*;
+import com.oracle.graal.nodes.spi.*;
 
 /**
  * The {@code Parameter} instruction is a placeholder for an incoming argument to a function call.
  */
 @NodeInfo(nameTemplate = "Param({p#index})")
-public class ParameterNode extends AbstractLocalNode implements IterableNodeType {
+public class ParameterNode extends AbstractLocalNode implements IterableNodeType, UncheckedInterfaceProvider {
 
     public static ParameterNode create(int index, Stamp stamp) {
         return new ParameterNode(index, stamp);
@@ -39,4 +41,19 @@
     protected ParameterNode(int index, Stamp stamp) {
         super(index, stamp);
     }
+
+    public Stamp uncheckedStamp() {
+        ResolvedJavaMethod method = graph().method();
+        if (method != null) {
+            JavaType parameterType;
+            if (method.isStatic() || index() > 0) {
+                int signatureIndex = method.isStatic() ? index() : index() - 1;
+                parameterType = method.getSignature().getParameterType(signatureIndex, method.getDeclaringClass());
+            } else {
+                parameterType = method.getDeclaringClass();
+            }
+            return UncheckedInterfaceProvider.uncheckedOrNull(parameterType, stamp());
+        }
+        return null;
+    }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/LoadFieldNode.java	Wed Nov 19 17:11:19 2014 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/LoadFieldNode.java	Wed Nov 19 14:36:01 2014 +0100
@@ -37,7 +37,7 @@
  * The {@code LoadFieldNode} represents a read of a static or instance field.
  */
 @NodeInfo(nameTemplate = "LoadField#{p#field/s}")
-public class LoadFieldNode extends AccessFieldNode implements Canonicalizable.Unary<ValueNode>, VirtualizableRoot {
+public class LoadFieldNode extends AccessFieldNode implements Canonicalizable.Unary<ValueNode>, VirtualizableRoot, UncheckedInterfaceProvider {
 
     /**
      * Creates a new LoadFieldNode instance.
@@ -134,4 +134,8 @@
             }
         }
     }
+
+    public Stamp uncheckedStamp() {
+        return UncheckedInterfaceProvider.uncheckedOrNull(field().getType(), stamp());
+    }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/MethodCallTargetNode.java	Wed Nov 19 17:11:19 2014 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/MethodCallTargetNode.java	Wed Nov 19 14:36:01 2014 +0100
@@ -28,6 +28,7 @@
 import com.oracle.graal.graph.spi.*;
 import com.oracle.graal.nodeinfo.*;
 import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.nodes.type.*;
 
 @NodeInfo
@@ -156,37 +157,51 @@
              * interface methods calls.
              */
             if (declaredReceiverType.isInterface() && !invokeKind().equals(InvokeKind.Virtual)) {
-                ResolvedJavaType singleImplementor = declaredReceiverType.getSingleImplementor();
-                if (singleImplementor != null && !singleImplementor.equals(declaredReceiverType)) {
-                    ResolvedJavaMethod singleImplementorMethod = singleImplementor.resolveMethod(targetMethod(), invoke().getContextType(), true);
-                    if (singleImplementorMethod != null) {
-                        assert graph().getGuardsStage().ordinal() < StructuredGraph.GuardsStage.FIXED_DEOPTS.ordinal() : "Graph already fixed!";
-                        /**
-                         * We have an invoke on an interface with a single implementor. We can
-                         * replace this with an invoke virtual.
-                         *
-                         * To do so we need to ensure two properties: 1) the receiver must implement
-                         * the interface (declaredReceiverType). The verifier does not prove this so
-                         * we need a dynamic check. 2) we need to ensure that there is still only
-                         * one implementor of this interface, i.e. that we are calling the right
-                         * method. We could do this with an assumption but as we need an instanceof
-                         * check anyway we can verify both properties by checking of the receiver is
-                         * an instance of the single implementor.
-                         */
-                        LogicNode condition = graph().unique(InstanceOfNode.create(singleImplementor, receiver, getProfile()));
-                        GuardNode guard = graph().unique(
-                                        GuardNode.create(condition, BeginNode.prevBegin(invoke().asNode()), DeoptimizationReason.OptimizedTypeCheckViolated, DeoptimizationAction.InvalidateRecompile,
-                                                        false, JavaConstant.NULL_OBJECT));
-                        PiNode piNode = graph().unique(PiNode.create(receiver, StampFactory.declaredNonNull(singleImplementor), guard));
-                        arguments().set(0, piNode);
-                        setInvokeKind(InvokeKind.Virtual);
-                        setTargetMethod(singleImplementorMethod);
+                tryCheckCastSingleImplementor(receiver, declaredReceiverType);
+            }
+
+            if (invokeKind().equals(InvokeKind.Interface) && receiver instanceof UncheckedInterfaceProvider) {
+                UncheckedInterfaceProvider uncheckedInterfaceProvider = (UncheckedInterfaceProvider) receiver;
+                Stamp uncheckedStamp = uncheckedInterfaceProvider.uncheckedStamp();
+                if (uncheckedStamp != null) {
+                    ResolvedJavaType uncheckedReceiverType = StampTool.typeOrNull(uncheckedStamp);
+                    if (uncheckedReceiverType.isInterface()) {
+                        tryCheckCastSingleImplementor(receiver, uncheckedReceiverType);
                     }
                 }
             }
         }
     }
 
+    private void tryCheckCastSingleImplementor(ValueNode receiver, ResolvedJavaType declaredReceiverType) {
+        ResolvedJavaType singleImplementor = declaredReceiverType.getSingleImplementor();
+        if (singleImplementor != null && !singleImplementor.equals(declaredReceiverType)) {
+            ResolvedJavaMethod singleImplementorMethod = singleImplementor.resolveMethod(targetMethod(), invoke().getContextType(), true);
+            if (singleImplementorMethod != null) {
+                assert graph().getGuardsStage().ordinal() < StructuredGraph.GuardsStage.FIXED_DEOPTS.ordinal() : "Graph already fixed!";
+                /**
+                 * We have an invoke on an interface with a single implementor. We can replace this
+                 * with an invoke virtual.
+                 *
+                 * To do so we need to ensure two properties: 1) the receiver must implement the
+                 * interface (declaredReceiverType). The verifier does not prove this so we need a
+                 * dynamic check. 2) we need to ensure that there is still only one implementor of
+                 * this interface, i.e. that we are calling the right method. We could do this with
+                 * an assumption but as we need an instanceof check anyway we can verify both
+                 * properties by checking of the receiver is an instance of the single implementor.
+                 */
+                LogicNode condition = graph().unique(InstanceOfNode.create(singleImplementor, receiver, getProfile()));
+                GuardNode guard = graph().unique(
+                                GuardNode.create(condition, BeginNode.prevBegin(invoke().asNode()), DeoptimizationReason.OptimizedTypeCheckViolated, DeoptimizationAction.InvalidateRecompile, false,
+                                                JavaConstant.NULL_OBJECT));
+                PiNode piNode = graph().unique(PiNode.create(receiver, StampFactory.declaredNonNull(singleImplementor), guard));
+                arguments().set(0, piNode);
+                setInvokeKind(InvokeKind.Virtual);
+                setTargetMethod(singleImplementorMethod);
+            }
+        }
+    }
+
     private JavaTypeProfile getProfile() {
         assert !isStatic();
         if (receiver() instanceof TypeProfileProxyNode) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/UncheckedInterfaceProvider.java	Wed Nov 19 14:36:01 2014 +0100
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.nodes.spi;
+
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.compiler.common.type.*;
+
+public interface UncheckedInterfaceProvider {
+    /**
+     * Returns a stamp containing information about interface types that has not been verified or
+     * null if no such stamp is available. A type check is needed before using informations from
+     * this stamp.
+     */
+    Stamp uncheckedStamp();
+
+    static Stamp uncheckedOrNull(JavaType type, Stamp originalStamp) {
+        if (type instanceof ResolvedJavaType) {
+            Stamp unchecked = StampFactory.declaredTrusted((ResolvedJavaType) type);
+            if (!unchecked.equals(originalStamp)) {
+                return unchecked;
+            }
+        }
+        return null;
+    }
+}