# HG changeset patch # User Gilles Duboscq # Date 1416404161 -3600 # Node ID 1d2e382d825990b1b522c109dd132f673658f018 # Parent 2d2fcdbae37b8a8245e1f4fda3f527bcb2f156ff 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. diff -r 2d2fcdbae37b -r 1d2e382d8259 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/Invoke.java --- 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; + } } diff -r 2d2fcdbae37b -r 1d2e382d8259 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/ParameterNode.java --- 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; + } } diff -r 2d2fcdbae37b -r 1d2e382d8259 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/LoadFieldNode.java --- 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, VirtualizableRoot { +public class LoadFieldNode extends AccessFieldNode implements Canonicalizable.Unary, VirtualizableRoot, UncheckedInterfaceProvider { /** * Creates a new LoadFieldNode instance. @@ -134,4 +134,8 @@ } } } + + public Stamp uncheckedStamp() { + return UncheckedInterfaceProvider.uncheckedOrNull(field().getType(), stamp()); + } } diff -r 2d2fcdbae37b -r 1d2e382d8259 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/MethodCallTargetNode.java --- 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) { diff -r 2d2fcdbae37b -r 1d2e382d8259 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/UncheckedInterfaceProvider.java --- /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; + } +}