1 /* 2 * Copyright (c) 2012, 2014, 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 package org.graalvm.compiler.replacements; 24 25 import static org.graalvm.compiler.nodes.calc.CompareNode.createCompareNode; 26 27 import java.util.List; 28 29 import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; 30 import org.graalvm.compiler.core.common.calc.Condition; 31 import org.graalvm.compiler.graph.Node; 32 import org.graalvm.compiler.nodes.ConditionAnchorNode; 33 import org.graalvm.compiler.nodes.ConstantNode; 34 import org.graalvm.compiler.nodes.FixedGuardNode; 35 import org.graalvm.compiler.nodes.IfNode; 36 import org.graalvm.compiler.nodes.LogicConstantNode; 37 import org.graalvm.compiler.nodes.LogicNode; 38 import org.graalvm.compiler.nodes.PhiNode; 39 import org.graalvm.compiler.nodes.ShortCircuitOrNode; 40 import org.graalvm.compiler.nodes.StructuredGraph; 41 import org.graalvm.compiler.nodes.ValueNode; 42 import org.graalvm.compiler.nodes.calc.CompareNode; 43 import org.graalvm.compiler.nodes.calc.ConditionalNode; 44 import org.graalvm.compiler.nodes.calc.FloatingNode; 45 import org.graalvm.compiler.nodes.java.ClassIsAssignableFromNode; 46 import org.graalvm.compiler.nodes.java.InstanceOfDynamicNode; 47 import org.graalvm.compiler.nodes.java.InstanceOfNode; 48 import org.graalvm.compiler.nodes.spi.LoweringTool; 49 import org.graalvm.compiler.nodes.util.GraphUtil; 50 import org.graalvm.compiler.phases.util.Providers; 51 import org.graalvm.compiler.replacements.SnippetTemplate.AbstractTemplates; 52 import org.graalvm.compiler.replacements.SnippetTemplate.Arguments; 53 import org.graalvm.compiler.replacements.SnippetTemplate.UsageReplacer; 54 55 import jdk.vm.ci.code.TargetDescription; 56 57 /** 58 * Helper class for lowering {@link InstanceOfNode}s with snippets. The majority of the complexity 59 * in such a lowering derives from the fact that {@link InstanceOfNode} is a floating node. A 60 * snippet used to lower an {@link InstanceOfNode} will almost always incorporate control flow and 61 * replacing a floating node with control flow is not trivial. 62 * <p> 63 * The mechanism implemented in this class ensures that the graph for an instanceof snippet is 64 * instantiated once per {@link InstanceOfNode} being lowered. The result produced is then re-used 65 * by all usages of the node. Additionally, if there is a single usage that is an {@link IfNode}, 66 * the control flow in the snippet is connected directly to the true and false successors of the 67 * {@link IfNode}. This avoids materializing the instanceof test as a boolean which is then retested 68 * by the {@link IfNode}. 69 */ 70 public abstract class InstanceOfSnippetsTemplates extends AbstractTemplates { 71 72 public InstanceOfSnippetsTemplates(Providers providers, SnippetReflectionProvider snippetReflection, TargetDescription target) { 73 super(providers, snippetReflection, target); 74 } 75 76 /** 77 * Gets the arguments used to retrieve and instantiate an instanceof snippet template. 78 */ 79 protected abstract Arguments makeArguments(InstanceOfUsageReplacer replacer, LoweringTool tool); 80 81 public void lower(FloatingNode instanceOf, LoweringTool tool) { 82 assert instanceOf instanceof InstanceOfNode || instanceOf instanceof InstanceOfDynamicNode || instanceOf instanceof ClassIsAssignableFromNode; 83 List<Node> usages = instanceOf.usages().snapshot(); 84 85 Instantiation instantiation = new Instantiation(); 86 for (Node usage : usages) { 87 final StructuredGraph graph = (StructuredGraph) usage.graph(); 88 89 InstanceOfUsageReplacer replacer = createReplacer(instanceOf, instantiation, usage, graph); 90 91 if (instantiation.isInitialized()) { 92 // No need to re-instantiate the snippet - just re-use its result 93 replacer.replaceUsingInstantiation(); 94 } else { 95 Arguments args = makeArguments(replacer, tool); 96 template(args).instantiate(providers.getMetaAccess(), instanceOf, replacer, tool, args); 97 } 98 } 99 100 assert instanceOf.hasNoUsages(); 101 if (!instanceOf.isDeleted()) { 102 GraphUtil.killWithUnusedFloatingInputs(instanceOf); 103 } 104 } 105 106 /** 107 * Gets the specific replacer object used to replace the usage of an instanceof node with the 108 * result of an instantiated instanceof snippet. 109 */ 110 protected InstanceOfUsageReplacer createReplacer(FloatingNode instanceOf, Instantiation instantiation, Node usage, final StructuredGraph graph) { 111 InstanceOfUsageReplacer replacer; 112 if (!canMaterialize(usage)) { 113 ValueNode trueValue = ConstantNode.forInt(1, graph); 114 ValueNode falseValue = ConstantNode.forInt(0, graph); 115 if (instantiation.isInitialized() && (trueValue != instantiation.trueValue || falseValue != instantiation.falseValue)) { 116 /* 117 * This code doesn't really care what values are used so adopt the values from the 118 * previous instantiation. 119 */ 120 trueValue = instantiation.trueValue; 121 falseValue = instantiation.falseValue; 122 } 123 replacer = new NonMaterializationUsageReplacer(instantiation, trueValue, falseValue, instanceOf, usage); 124 } else { 125 assert usage instanceof ConditionalNode : "unexpected usage of " + instanceOf + ": " + usage; 126 ConditionalNode c = (ConditionalNode) usage; 127 replacer = new MaterializationUsageReplacer(instantiation, c.trueValue(), c.falseValue(), instanceOf, c); 128 } 129 return replacer; 130 } 131 132 /** 133 * Determines if an {@code instanceof} usage can be materialized. 134 */ 135 protected boolean canMaterialize(Node usage) { 136 if (usage instanceof ConditionalNode) { 137 ConditionalNode cn = (ConditionalNode) usage; 138 return cn.trueValue().isConstant() && cn.falseValue().isConstant(); 139 } 140 if (usage instanceof IfNode || usage instanceof FixedGuardNode || usage instanceof ShortCircuitOrNode || usage instanceof ConditionAnchorNode) { 141 return false; 142 } 143 return true; 144 } 145 146 /** 147 * The result of instantiating an instanceof snippet. This enables a snippet instantiation to be 148 * re-used which reduces compile time and produces better code. 149 */ 150 public static final class Instantiation { 151 152 private ValueNode result; 153 private LogicNode condition; 154 private ValueNode trueValue; 155 private ValueNode falseValue; 156 157 /** 158 * Determines if the instantiation has occurred. 159 */ 160 boolean isInitialized() { 161 return result != null; 162 } 163 164 void initialize(ValueNode r, ValueNode t, ValueNode f) { 165 assert !isInitialized(); 166 this.result = r; 167 this.trueValue = t; 168 this.falseValue = f; 169 } 170 171 /** 172 * Gets the result of this instantiation as a condition. 173 * 174 * @param testValue the returned condition is true if the result is equal to this value 175 */ 176 LogicNode asCondition(ValueNode testValue) { 177 assert isInitialized(); 178 if (result.isConstant()) { 179 assert testValue.isConstant(); 180 return LogicConstantNode.forBoolean(result.asConstant().equals(testValue.asConstant()), result.graph()); 181 } 182 if (condition == null || (!(condition instanceof CompareNode)) || ((CompareNode) condition).getY() != testValue) { 183 // Re-use previously generated condition if the trueValue for the test is the same 184 condition = createCompareNode(result.graph(), Condition.EQ, result, testValue, null); 185 } 186 return condition; 187 } 188 189 /** 190 * Gets the result of the instantiation as a materialized value. 191 * 192 * @param t the true value for the materialization 193 * @param f the false value for the materialization 194 */ 195 ValueNode asMaterialization(StructuredGraph graph, ValueNode t, ValueNode f) { 196 assert isInitialized(); 197 if (t == this.trueValue && f == this.falseValue) { 198 // Can simply use the phi result if the same materialized values are expected. 199 return result; 200 } else { 201 return graph.unique(new ConditionalNode(asCondition(trueValue), t, f)); 202 } 203 } 204 } 205 206 /** 207 * Replaces a usage of an {@link InstanceOfNode} or {@link InstanceOfDynamicNode}. 208 */ 209 public abstract static class InstanceOfUsageReplacer implements UsageReplacer { 210 211 public final Instantiation instantiation; 212 public final FloatingNode instanceOf; 213 public final ValueNode trueValue; 214 public final ValueNode falseValue; 215 216 public InstanceOfUsageReplacer(Instantiation instantiation, FloatingNode instanceOf, ValueNode trueValue, ValueNode falseValue) { 217 assert instanceOf instanceof InstanceOfNode || instanceOf instanceof InstanceOfDynamicNode || instanceOf instanceof ClassIsAssignableFromNode; 218 this.instantiation = instantiation; 219 this.instanceOf = instanceOf; 220 this.trueValue = trueValue; 221 this.falseValue = falseValue; 222 } 223 224 /** 225 * Does the replacement based on a previously snippet instantiation. 226 */ 227 public abstract void replaceUsingInstantiation(); 228 } 229 230 /** 231 * Replaces the usage of an {@link InstanceOfNode} or {@link InstanceOfDynamicNode} that does 232 * not materialize the result of the type test. 233 */ 234 public static class NonMaterializationUsageReplacer extends InstanceOfUsageReplacer { 235 236 private final Node usage; 237 238 public NonMaterializationUsageReplacer(Instantiation instantiation, ValueNode trueValue, ValueNode falseValue, FloatingNode instanceOf, Node usage) { 239 super(instantiation, instanceOf, trueValue, falseValue); 240 this.usage = usage; 241 } 242 243 @Override 244 public void replaceUsingInstantiation() { 245 usage.replaceFirstInput(instanceOf, instantiation.asCondition(trueValue)); 246 } 247 248 @Override 249 public void replace(ValueNode oldNode, ValueNode newNode) { 250 assert newNode instanceof PhiNode; 251 assert oldNode == instanceOf; 252 newNode.inferStamp(); 253 instantiation.initialize(newNode, trueValue, falseValue); 254 usage.replaceFirstInput(oldNode, instantiation.asCondition(trueValue)); 255 } 256 } 257 258 /** 259 * Replaces the usage of an {@link InstanceOfNode} or {@link InstanceOfDynamicNode} that does 260 * materializes the result of the type test. 261 */ 262 public static class MaterializationUsageReplacer extends InstanceOfUsageReplacer { 263 264 public final ConditionalNode usage; 265 266 public MaterializationUsageReplacer(Instantiation instantiation, ValueNode trueValue, ValueNode falseValue, FloatingNode instanceOf, ConditionalNode usage) { 267 super(instantiation, instanceOf, trueValue, falseValue); 268 this.usage = usage; 269 } 270 271 @Override 272 public void replaceUsingInstantiation() { 273 ValueNode newValue = instantiation.asMaterialization(usage.graph(), trueValue, falseValue); 274 usage.replaceAtUsages(newValue); 275 assert usage.hasNoUsages(); 276 GraphUtil.killWithUnusedFloatingInputs(usage); 277 } 278 279 @Override 280 public void replace(ValueNode oldNode, ValueNode newNode) { 281 assert newNode instanceof PhiNode; 282 assert oldNode == instanceOf; 283 newNode.inferStamp(); 284 instantiation.initialize(newNode, trueValue, falseValue); 285 usage.replaceAtUsages(newNode); 286 assert usage.hasNoUsages(); 287 GraphUtil.killWithUnusedFloatingInputs(usage); 288 } 289 } 290 }