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