1 /*
2  * Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 
25 package org.graalvm.compiler.nodes.calc;
26 
27 import org.graalvm.compiler.core.common.calc.CanonicalCondition;
28 import org.graalvm.compiler.core.common.type.AbstractPointerStamp;
29 import org.graalvm.compiler.core.common.type.ObjectStamp;
30 import org.graalvm.compiler.core.common.type.Stamp;
31 import org.graalvm.compiler.graph.Node;
32 import org.graalvm.compiler.graph.NodeClass;
33 import org.graalvm.compiler.graph.spi.Canonicalizable.BinaryCommutative;
34 import org.graalvm.compiler.graph.spi.CanonicalizerTool;
35 import org.graalvm.compiler.nodeinfo.NodeInfo;
36 import org.graalvm.compiler.nodes.LogicConstantNode;
37 import org.graalvm.compiler.nodes.LogicNode;
38 import org.graalvm.compiler.nodes.NodeView;
39 import org.graalvm.compiler.nodes.ValueNode;
40 import org.graalvm.compiler.nodes.extended.LoadHubNode;
41 import org.graalvm.compiler.nodes.extended.LoadMethodNode;
42 import org.graalvm.compiler.nodes.type.StampTool;
43 import org.graalvm.compiler.nodes.util.GraphUtil;
44 import org.graalvm.compiler.options.OptionValues;
45 
46 import jdk.vm.ci.meta.ConstantReflectionProvider;
47 import jdk.vm.ci.meta.MetaAccessProvider;
48 import jdk.vm.ci.meta.ResolvedJavaMethod;
49 import jdk.vm.ci.meta.ResolvedJavaType;
50 import jdk.vm.ci.meta.TriState;
51 
52 @NodeInfo(shortName = "==")
53 public class PointerEqualsNode extends CompareNode implements BinaryCommutative<ValueNode> {
54 
55     public static final NodeClass<PointerEqualsNode> TYPE = NodeClass.create(PointerEqualsNode.class);
56     private static final PointerEqualsOp OP = new PointerEqualsOp();
57 
PointerEqualsNode(ValueNode x, ValueNode y)58     public PointerEqualsNode(ValueNode x, ValueNode y) {
59         this(TYPE, x, y);
60     }
61 
create(ValueNode x, ValueNode y, NodeView view)62     public static LogicNode create(ValueNode x, ValueNode y, NodeView view) {
63         LogicNode result = findSynonym(x, y, view);
64         if (result != null) {
65             return result;
66         }
67         return new PointerEqualsNode(x, y);
68     }
69 
PointerEqualsNode(NodeClass<? extends PointerEqualsNode> c, ValueNode x, ValueNode y)70     protected PointerEqualsNode(NodeClass<? extends PointerEqualsNode> c, ValueNode x, ValueNode y) {
71         super(c, CanonicalCondition.EQ, false, x, y);
72         assert x.stamp(NodeView.DEFAULT).isPointerStamp();
73         assert y.stamp(NodeView.DEFAULT).isPointerStamp();
74     }
75 
76     @Override
canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY)77     public Node canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) {
78         NodeView view = NodeView.from(tool);
79         ValueNode value = OP.canonical(tool.getConstantReflection(), tool.getMetaAccess(), tool.getOptions(), tool.smallestCompareWidth(), CanonicalCondition.EQ, false, forX, forY, view);
80         if (value != null) {
81             return value;
82         }
83         return this;
84     }
85 
86     public static class PointerEqualsOp extends CompareOp {
87 
88         /**
89          * Determines if this is a comparison used to determine whether dispatching on a receiver
90          * could select a certain method and if so, returns {@code true} if the answer is guaranteed
91          * to be false. Otherwise, returns {@code false}.
92          */
isAlwaysFailingVirtualDispatchTest(CanonicalCondition condition, ValueNode forX, ValueNode forY)93         private static boolean isAlwaysFailingVirtualDispatchTest(CanonicalCondition condition, ValueNode forX, ValueNode forY) {
94             if (forY.isConstant()) {
95                 if (forX instanceof LoadMethodNode && condition == CanonicalCondition.EQ) {
96                     LoadMethodNode lm = ((LoadMethodNode) forX);
97                     if (lm.getMethod().getEncoding().equals(forY.asConstant())) {
98                         if (lm.getHub() instanceof LoadHubNode) {
99                             ValueNode object = ((LoadHubNode) lm.getHub()).getValue();
100                             ResolvedJavaType type = StampTool.typeOrNull(object);
101                             ResolvedJavaType declaringClass = lm.getMethod().getDeclaringClass();
102                             if (type != null && !type.equals(declaringClass) && declaringClass.isAssignableFrom(type)) {
103                                 ResolvedJavaMethod override = type.resolveMethod(lm.getMethod(), lm.getCallerType());
104                                 if (override != null && !override.equals(lm.getMethod())) {
105                                     assert declaringClass.isAssignableFrom(override.getDeclaringClass());
106                                     return true;
107                                 }
108                             }
109                         }
110                     }
111                 }
112             }
113             return false;
114         }
115 
116         @Override
canonical(ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, OptionValues options, Integer smallestCompareWidth, CanonicalCondition condition, boolean unorderedIsTrue, ValueNode forX, ValueNode forY, NodeView view)117         public LogicNode canonical(ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, OptionValues options, Integer smallestCompareWidth, CanonicalCondition condition,
118                         boolean unorderedIsTrue, ValueNode forX, ValueNode forY, NodeView view) {
119             LogicNode result = findSynonym(forX, forY, view);
120             if (result != null) {
121                 return result;
122             }
123             if (isAlwaysFailingVirtualDispatchTest(condition, forX, forY)) {
124                 return LogicConstantNode.contradiction();
125             }
126             return super.canonical(constantReflection, metaAccess, options, smallestCompareWidth, condition, unorderedIsTrue, forX, forY, view);
127         }
128 
129         @Override
duplicateModified(ValueNode newX, ValueNode newY, boolean unorderedIsTrue, NodeView view)130         protected CompareNode duplicateModified(ValueNode newX, ValueNode newY, boolean unorderedIsTrue, NodeView view) {
131             return new PointerEqualsNode(newX, newY);
132         }
133     }
134 
findSynonym(ValueNode forX, ValueNode forY, NodeView view)135     public static LogicNode findSynonym(ValueNode forX, ValueNode forY, NodeView view) {
136         if (GraphUtil.unproxify(forX) == GraphUtil.unproxify(forY)) {
137             return LogicConstantNode.tautology();
138         } else if (forX.stamp(view).alwaysDistinct(forY.stamp(view))) {
139             return LogicConstantNode.contradiction();
140         } else if (forX.stamp(view) instanceof AbstractPointerStamp && ((AbstractPointerStamp) forX.stamp(view)).alwaysNull()) {
141             return nullSynonym(forY, forX);
142         } else if (forY.stamp(view) instanceof AbstractPointerStamp && ((AbstractPointerStamp) forY.stamp(view)).alwaysNull()) {
143             return nullSynonym(forX, forY);
144         } else {
145             return null;
146         }
147     }
148 
nullSynonym(ValueNode nonNullValue, ValueNode nullValue)149     private static LogicNode nullSynonym(ValueNode nonNullValue, ValueNode nullValue) {
150         if (nullValue.isConstant()) {
151             return IsNullNode.create(nonNullValue, nullValue.asJavaConstant());
152         } else {
153             return IsNullNode.create(nonNullValue);
154         }
155     }
156 
157     @Override
getSucceedingStampForX(boolean negated, Stamp xStamp, Stamp yStamp)158     public Stamp getSucceedingStampForX(boolean negated, Stamp xStamp, Stamp yStamp) {
159         if (!negated) {
160             Stamp newStamp = xStamp.join(yStamp);
161             if (!newStamp.equals(xStamp)) {
162                 return newStamp;
163             }
164         }
165         return null;
166     }
167 
168     @Override
getSucceedingStampForY(boolean negated, Stamp xStamp, Stamp yStamp)169     public Stamp getSucceedingStampForY(boolean negated, Stamp xStamp, Stamp yStamp) {
170         if (!negated) {
171             Stamp newStamp = yStamp.join(xStamp);
172             if (!newStamp.equals(yStamp)) {
173                 return newStamp;
174             }
175         }
176         return null;
177     }
178 
179     @Override
tryFold(Stamp xStampGeneric, Stamp yStampGeneric)180     public TriState tryFold(Stamp xStampGeneric, Stamp yStampGeneric) {
181         if (xStampGeneric instanceof ObjectStamp && yStampGeneric instanceof ObjectStamp) {
182             ObjectStamp xStamp = (ObjectStamp) xStampGeneric;
183             ObjectStamp yStamp = (ObjectStamp) yStampGeneric;
184             if (xStamp.alwaysDistinct(yStamp)) {
185                 return TriState.FALSE;
186             } else if (xStamp.neverDistinct(yStamp)) {
187                 return TriState.TRUE;
188             }
189         }
190         return TriState.UNKNOWN;
191     }
192 }
193