--- /dev/null 2016-05-31 09:42:47.975716356 -0700 +++ new/src/jdk.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/InstanceOfSnippetsTemplates.java 2016-12-09 00:57:00.166186387 -0800 @@ -0,0 +1,290 @@ +/* + * Copyright (c) 2012, 2014, 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 org.graalvm.compiler.replacements; + +import static org.graalvm.compiler.nodes.calc.CompareNode.createCompareNode; + +import java.util.List; + +import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; +import org.graalvm.compiler.core.common.calc.Condition; +import org.graalvm.compiler.graph.Node; +import org.graalvm.compiler.nodes.ConditionAnchorNode; +import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.FixedGuardNode; +import org.graalvm.compiler.nodes.IfNode; +import org.graalvm.compiler.nodes.LogicConstantNode; +import org.graalvm.compiler.nodes.LogicNode; +import org.graalvm.compiler.nodes.PhiNode; +import org.graalvm.compiler.nodes.ShortCircuitOrNode; +import org.graalvm.compiler.nodes.StructuredGraph; +import org.graalvm.compiler.nodes.ValueNode; +import org.graalvm.compiler.nodes.calc.CompareNode; +import org.graalvm.compiler.nodes.calc.ConditionalNode; +import org.graalvm.compiler.nodes.calc.FloatingNode; +import org.graalvm.compiler.nodes.java.ClassIsAssignableFromNode; +import org.graalvm.compiler.nodes.java.InstanceOfDynamicNode; +import org.graalvm.compiler.nodes.java.InstanceOfNode; +import org.graalvm.compiler.nodes.spi.LoweringTool; +import org.graalvm.compiler.nodes.util.GraphUtil; +import org.graalvm.compiler.phases.util.Providers; +import org.graalvm.compiler.replacements.SnippetTemplate.AbstractTemplates; +import org.graalvm.compiler.replacements.SnippetTemplate.Arguments; +import org.graalvm.compiler.replacements.SnippetTemplate.UsageReplacer; + +import jdk.vm.ci.code.TargetDescription; + +/** + * Helper class for lowering {@link InstanceOfNode}s with snippets. The majority of the complexity + * in such a lowering derives from the fact that {@link InstanceOfNode} is a floating node. A + * snippet used to lower an {@link InstanceOfNode} will almost always incorporate control flow and + * replacing a floating node with control flow is not trivial. + *

+ * The mechanism implemented in this class ensures that the graph for an instanceof snippet is + * instantiated once per {@link InstanceOfNode} being lowered. The result produced is then re-used + * by all usages of the node. Additionally, if there is a single usage that is an {@link IfNode}, + * the control flow in the snippet is connected directly to the true and false successors of the + * {@link IfNode}. This avoids materializing the instanceof test as a boolean which is then retested + * by the {@link IfNode}. + */ +public abstract class InstanceOfSnippetsTemplates extends AbstractTemplates { + + public InstanceOfSnippetsTemplates(Providers providers, SnippetReflectionProvider snippetReflection, TargetDescription target) { + super(providers, snippetReflection, target); + } + + /** + * Gets the arguments used to retrieve and instantiate an instanceof snippet template. + */ + protected abstract Arguments makeArguments(InstanceOfUsageReplacer replacer, LoweringTool tool); + + public void lower(FloatingNode instanceOf, LoweringTool tool) { + assert instanceOf instanceof InstanceOfNode || instanceOf instanceof InstanceOfDynamicNode || instanceOf instanceof ClassIsAssignableFromNode; + List usages = instanceOf.usages().snapshot(); + + Instantiation instantiation = new Instantiation(); + for (Node usage : usages) { + final StructuredGraph graph = (StructuredGraph) usage.graph(); + + InstanceOfUsageReplacer replacer = createReplacer(instanceOf, instantiation, usage, graph); + + if (instantiation.isInitialized()) { + // No need to re-instantiate the snippet - just re-use its result + replacer.replaceUsingInstantiation(); + } else { + Arguments args = makeArguments(replacer, tool); + template(args).instantiate(providers.getMetaAccess(), instanceOf, replacer, tool, args); + } + } + + assert instanceOf.hasNoUsages(); + if (!instanceOf.isDeleted()) { + GraphUtil.killWithUnusedFloatingInputs(instanceOf); + } + } + + /** + * Gets the specific replacer object used to replace the usage of an instanceof node with the + * result of an instantiated instanceof snippet. + */ + protected InstanceOfUsageReplacer createReplacer(FloatingNode instanceOf, Instantiation instantiation, Node usage, final StructuredGraph graph) { + InstanceOfUsageReplacer replacer; + if (!canMaterialize(usage)) { + ValueNode trueValue = ConstantNode.forInt(1, graph); + ValueNode falseValue = ConstantNode.forInt(0, graph); + if (instantiation.isInitialized() && (trueValue != instantiation.trueValue || falseValue != instantiation.falseValue)) { + /* + * This code doesn't really care what values are used so adopt the values from the + * previous instantiation. + */ + trueValue = instantiation.trueValue; + falseValue = instantiation.falseValue; + } + replacer = new NonMaterializationUsageReplacer(instantiation, trueValue, falseValue, instanceOf, usage); + } else { + assert usage instanceof ConditionalNode : "unexpected usage of " + instanceOf + ": " + usage; + ConditionalNode c = (ConditionalNode) usage; + replacer = new MaterializationUsageReplacer(instantiation, c.trueValue(), c.falseValue(), instanceOf, c); + } + return replacer; + } + + /** + * Determines if an {@code instanceof} usage can be materialized. + */ + protected boolean canMaterialize(Node usage) { + if (usage instanceof ConditionalNode) { + ConditionalNode cn = (ConditionalNode) usage; + return cn.trueValue().isConstant() && cn.falseValue().isConstant(); + } + if (usage instanceof IfNode || usage instanceof FixedGuardNode || usage instanceof ShortCircuitOrNode || usage instanceof ConditionAnchorNode) { + return false; + } + return true; + } + + /** + * The result of instantiating an instanceof snippet. This enables a snippet instantiation to be + * re-used which reduces compile time and produces better code. + */ + public static final class Instantiation { + + private ValueNode result; + private LogicNode condition; + private ValueNode trueValue; + private ValueNode falseValue; + + /** + * Determines if the instantiation has occurred. + */ + boolean isInitialized() { + return result != null; + } + + void initialize(ValueNode r, ValueNode t, ValueNode f) { + assert !isInitialized(); + this.result = r; + this.trueValue = t; + this.falseValue = f; + } + + /** + * Gets the result of this instantiation as a condition. + * + * @param testValue the returned condition is true if the result is equal to this value + */ + LogicNode asCondition(ValueNode testValue) { + assert isInitialized(); + if (result.isConstant()) { + assert testValue.isConstant(); + return LogicConstantNode.forBoolean(result.asConstant().equals(testValue.asConstant()), result.graph()); + } + if (condition == null || (!(condition instanceof CompareNode)) || ((CompareNode) condition).getY() != testValue) { + // Re-use previously generated condition if the trueValue for the test is the same + condition = createCompareNode(result.graph(), Condition.EQ, result, testValue, null); + } + return condition; + } + + /** + * Gets the result of the instantiation as a materialized value. + * + * @param t the true value for the materialization + * @param f the false value for the materialization + */ + ValueNode asMaterialization(StructuredGraph graph, ValueNode t, ValueNode f) { + assert isInitialized(); + if (t == this.trueValue && f == this.falseValue) { + // Can simply use the phi result if the same materialized values are expected. + return result; + } else { + return graph.unique(new ConditionalNode(asCondition(trueValue), t, f)); + } + } + } + + /** + * Replaces a usage of an {@link InstanceOfNode} or {@link InstanceOfDynamicNode}. + */ + public abstract static class InstanceOfUsageReplacer implements UsageReplacer { + + public final Instantiation instantiation; + public final FloatingNode instanceOf; + public final ValueNode trueValue; + public final ValueNode falseValue; + + public InstanceOfUsageReplacer(Instantiation instantiation, FloatingNode instanceOf, ValueNode trueValue, ValueNode falseValue) { + assert instanceOf instanceof InstanceOfNode || instanceOf instanceof InstanceOfDynamicNode || instanceOf instanceof ClassIsAssignableFromNode; + this.instantiation = instantiation; + this.instanceOf = instanceOf; + this.trueValue = trueValue; + this.falseValue = falseValue; + } + + /** + * Does the replacement based on a previously snippet instantiation. + */ + public abstract void replaceUsingInstantiation(); + } + + /** + * Replaces the usage of an {@link InstanceOfNode} or {@link InstanceOfDynamicNode} that does + * not materialize the result of the type test. + */ + public static class NonMaterializationUsageReplacer extends InstanceOfUsageReplacer { + + private final Node usage; + + public NonMaterializationUsageReplacer(Instantiation instantiation, ValueNode trueValue, ValueNode falseValue, FloatingNode instanceOf, Node usage) { + super(instantiation, instanceOf, trueValue, falseValue); + this.usage = usage; + } + + @Override + public void replaceUsingInstantiation() { + usage.replaceFirstInput(instanceOf, instantiation.asCondition(trueValue)); + } + + @Override + public void replace(ValueNode oldNode, ValueNode newNode) { + assert newNode instanceof PhiNode; + assert oldNode == instanceOf; + newNode.inferStamp(); + instantiation.initialize(newNode, trueValue, falseValue); + usage.replaceFirstInput(oldNode, instantiation.asCondition(trueValue)); + } + } + + /** + * Replaces the usage of an {@link InstanceOfNode} or {@link InstanceOfDynamicNode} that does + * materializes the result of the type test. + */ + public static class MaterializationUsageReplacer extends InstanceOfUsageReplacer { + + public final ConditionalNode usage; + + public MaterializationUsageReplacer(Instantiation instantiation, ValueNode trueValue, ValueNode falseValue, FloatingNode instanceOf, ConditionalNode usage) { + super(instantiation, instanceOf, trueValue, falseValue); + this.usage = usage; + } + + @Override + public void replaceUsingInstantiation() { + ValueNode newValue = instantiation.asMaterialization(usage.graph(), trueValue, falseValue); + usage.replaceAtUsages(newValue); + assert usage.hasNoUsages(); + GraphUtil.killWithUnusedFloatingInputs(usage); + } + + @Override + public void replace(ValueNode oldNode, ValueNode newNode) { + assert newNode instanceof PhiNode; + assert oldNode == instanceOf; + newNode.inferStamp(); + instantiation.initialize(newNode, trueValue, falseValue); + usage.replaceAtUsages(newNode); + assert usage.hasNoUsages(); + GraphUtil.killWithUnusedFloatingInputs(usage); + } + } +}