001/*
002 * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.
008 *
009 * This code is distributed in the hope that it will be useful, but WITHOUT
010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
011 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
012 * version 2 for more details (a copy is included in the LICENSE file that
013 * accompanied this code).
014 *
015 * You should have received a copy of the GNU General Public License version
016 * 2 along with this work; if not, write to the Free Software Foundation,
017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
018 *
019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
020 * or visit www.oracle.com if you need additional information or have any
021 * questions.
022 */
023package com.oracle.graal.hotspot.replacements;
024
025import jdk.internal.jvmci.code.*;
026import jdk.internal.jvmci.common.*;
027import jdk.internal.jvmci.hotspot.*;
028import jdk.internal.jvmci.meta.*;
029import jdk.internal.jvmci.options.*;
030import static com.oracle.graal.hotspot.replacements.HotSpotReplacementsUtil.*;
031import static com.oracle.graal.hotspot.replacements.InstanceOfSnippets.Options.*;
032import static com.oracle.graal.hotspot.replacements.TypeCheckSnippetUtils.*;
033import static com.oracle.graal.nodes.extended.BranchProbabilityNode.*;
034import static jdk.internal.jvmci.meta.DeoptimizationAction.*;
035import static jdk.internal.jvmci.meta.DeoptimizationReason.*;
036
037import com.oracle.graal.compiler.common.type.*;
038import com.oracle.graal.hotspot.meta.*;
039import com.oracle.graal.hotspot.nodes.*;
040import com.oracle.graal.hotspot.replacements.TypeCheckSnippetUtils.Hints;
041import com.oracle.graal.hotspot.word.*;
042import com.oracle.graal.nodes.*;
043import com.oracle.graal.nodes.extended.*;
044import com.oracle.graal.nodes.java.*;
045import com.oracle.graal.nodes.spi.*;
046import com.oracle.graal.replacements.*;
047import com.oracle.graal.replacements.Snippet.ConstantParameter;
048import com.oracle.graal.replacements.Snippet.VarargsParameter;
049import com.oracle.graal.replacements.SnippetTemplate.Arguments;
050import com.oracle.graal.replacements.SnippetTemplate.SnippetInfo;
051import com.oracle.graal.replacements.nodes.*;
052
053/**
054 * Snippets used for implementing the type test of an instanceof instruction. Since instanceof is a
055 * floating node, it is lowered separately for each of its usages.
056 *
057 * The type tests implemented are described in the paper <a
058 * href="http://dl.acm.org/citation.cfm?id=583821"> Fast subtype checking in the HotSpot JVM</a> by
059 * Cliff Click and John Rose.
060 */
061public class InstanceOfSnippets implements Snippets {
062
063    /**
064     * A test against a set of hints derived from a profile with 100% precise coverage of seen
065     * types. This snippet deoptimizes on hint miss paths.
066     */
067    @Snippet
068    public static Object instanceofWithProfile(Object object, @VarargsParameter KlassPointer[] hints, @VarargsParameter boolean[] hintIsPositive, Object trueValue, Object falseValue,
069                    @ConstantParameter boolean nullSeen) {
070        if (probability(NOT_FREQUENT_PROBABILITY, object == null)) {
071            isNull.inc();
072            if (!nullSeen) {
073                // See comment below for other deoptimization path; the
074                // same reasoning applies here.
075                DeoptimizeNode.deopt(InvalidateReprofile, OptimizedTypeCheckViolated);
076            }
077            return falseValue;
078        }
079        GuardingNode anchorNode = SnippetAnchorNode.anchor();
080        KlassPointer objectHub = loadHubIntrinsic(object, anchorNode);
081        // if we get an exact match: succeed immediately
082        ExplodeLoopNode.explodeLoop();
083        for (int i = 0; i < hints.length; i++) {
084            KlassPointer hintHub = hints[i];
085            boolean positive = hintIsPositive[i];
086            if (probability(LIKELY_PROBABILITY, hintHub.equal(objectHub))) {
087                hintsHit.inc();
088                return positive ? trueValue : falseValue;
089            }
090            hintsMiss.inc();
091        }
092        // This maybe just be a rare event but it might also indicate a phase change
093        // in the application. Ideally we want to use DeoptimizationAction.None for
094        // the former but the cost is too high if indeed it is the latter. As such,
095        // we defensively opt for InvalidateReprofile.
096        DeoptimizeNode.deopt(DeoptimizationAction.InvalidateReprofile, OptimizedTypeCheckViolated);
097        return falseValue;
098    }
099
100    /**
101     * A test against a final type.
102     */
103    @Snippet
104    public static Object instanceofExact(Object object, KlassPointer exactHub, Object trueValue, Object falseValue) {
105        if (probability(NOT_FREQUENT_PROBABILITY, object == null)) {
106            isNull.inc();
107            return falseValue;
108        }
109        GuardingNode anchorNode = SnippetAnchorNode.anchor();
110        KlassPointer objectHub = loadHubIntrinsic(object, anchorNode);
111        if (probability(LIKELY_PROBABILITY, objectHub.notEqual(exactHub))) {
112            exactMiss.inc();
113            return falseValue;
114        }
115        exactHit.inc();
116        return trueValue;
117    }
118
119    /**
120     * A test against a primary type.
121     */
122    @Snippet
123    public static Object instanceofPrimary(KlassPointer hub, Object object, @ConstantParameter int superCheckOffset, Object trueValue, Object falseValue) {
124        if (probability(NOT_FREQUENT_PROBABILITY, object == null)) {
125            isNull.inc();
126            return falseValue;
127        }
128        GuardingNode anchorNode = SnippetAnchorNode.anchor();
129        KlassPointer objectHub = loadHubIntrinsic(object, anchorNode);
130        if (probability(NOT_LIKELY_PROBABILITY, objectHub.readKlassPointer(superCheckOffset, PRIMARY_SUPERS_LOCATION).notEqual(hub))) {
131            displayMiss.inc();
132            return falseValue;
133        }
134        displayHit.inc();
135        return trueValue;
136    }
137
138    /**
139     * A test against a restricted secondary type type.
140     */
141    @Snippet
142    public static Object instanceofSecondary(KlassPointer hub, Object object, @VarargsParameter KlassPointer[] hints, @VarargsParameter boolean[] hintIsPositive, Object trueValue, Object falseValue) {
143        if (probability(NOT_FREQUENT_PROBABILITY, object == null)) {
144            isNull.inc();
145            return falseValue;
146        }
147        GuardingNode anchorNode = SnippetAnchorNode.anchor();
148        KlassPointer objectHub = loadHubIntrinsic(object, anchorNode);
149        // if we get an exact match: succeed immediately
150        ExplodeLoopNode.explodeLoop();
151        for (int i = 0; i < hints.length; i++) {
152            KlassPointer hintHub = hints[i];
153            boolean positive = hintIsPositive[i];
154            if (probability(NOT_FREQUENT_PROBABILITY, hintHub.equal(objectHub))) {
155                hintsHit.inc();
156                return positive ? trueValue : falseValue;
157            }
158        }
159        hintsMiss.inc();
160        if (!checkSecondarySubType(hub, objectHub)) {
161            return falseValue;
162        }
163        return trueValue;
164    }
165
166    /**
167     * Type test used when the type being tested against is not known at compile time.
168     */
169    @Snippet
170    public static Object instanceofDynamic(Class<?> mirror, Object object, Object trueValue, Object falseValue) {
171        if (probability(NOT_FREQUENT_PROBABILITY, object == null)) {
172            isNull.inc();
173            return falseValue;
174        }
175        GuardingNode anchorNode = SnippetAnchorNode.anchor();
176        KlassPointer hub = ClassGetHubNode.readClass(mirror, anchorNode);
177        KlassPointer objectHub = loadHubIntrinsic(object, anchorNode);
178        if (hub.isNull() || !checkUnknownSubType(hub, objectHub)) {
179            return falseValue;
180        }
181        return trueValue;
182    }
183
184    @Snippet
185    public static Object isAssignableFrom(Class<?> thisClass, Class<?> otherClass, Object trueValue, Object falseValue) {
186        if (BranchProbabilityNode.probability(BranchProbabilityNode.NOT_FREQUENT_PROBABILITY, otherClass == null)) {
187            DeoptimizeNode.deopt(DeoptimizationAction.InvalidateReprofile, DeoptimizationReason.NullCheckException);
188            return false;
189        }
190        GuardingNode anchorNode = SnippetAnchorNode.anchor();
191        KlassPointer thisHub = ClassGetHubNode.readClass(thisClass, anchorNode);
192        KlassPointer otherHub = ClassGetHubNode.readClass(otherClass, anchorNode);
193        if (thisHub.isNull() || otherHub.isNull()) {
194            // primitive types, only true if equal.
195            return thisClass == otherClass ? trueValue : falseValue;
196        }
197        if (!TypeCheckSnippetUtils.checkUnknownSubType(thisHub, otherHub)) {
198            return falseValue;
199        }
200        return trueValue;
201    }
202
203    static class Options {
204
205        // @formatter:off
206        @Option(help = "If the probability that a type check will hit one the profiled types (up to " +
207                       "TypeCheckMaxHints) is below this value, the type check will be compiled without profiling info", type = OptionType.Expert)
208        static final OptionValue<Double> TypeCheckMinProfileHitProbability = new OptionValue<>(0.5);
209
210        @Option(help = "The maximum number of profiled types that will be used when compiling a profiled type check. " +
211                        "Note that TypeCheckMinProfileHitProbability also influences whether profiling info is used in compiled type checks.", type = OptionType.Expert)
212        static final OptionValue<Integer> TypeCheckMaxHints = new OptionValue<>(2);
213        // @formatter:on
214    }
215
216    public static class Templates extends InstanceOfSnippetsTemplates {
217
218        private final SnippetInfo instanceofWithProfile = snippet(InstanceOfSnippets.class, "instanceofWithProfile");
219        private final SnippetInfo instanceofExact = snippet(InstanceOfSnippets.class, "instanceofExact");
220        private final SnippetInfo instanceofPrimary = snippet(InstanceOfSnippets.class, "instanceofPrimary");
221        private final SnippetInfo instanceofSecondary = snippet(InstanceOfSnippets.class, "instanceofSecondary", SECONDARY_SUPER_CACHE_LOCATION);
222        private final SnippetInfo instanceofDynamic = snippet(InstanceOfSnippets.class, "instanceofDynamic", SECONDARY_SUPER_CACHE_LOCATION);
223        private final SnippetInfo isAssignableFrom = snippet(InstanceOfSnippets.class, "isAssignableFrom", SECONDARY_SUPER_CACHE_LOCATION);
224
225        public Templates(HotSpotProviders providers, TargetDescription target) {
226            super(providers, providers.getSnippetReflection(), target);
227        }
228
229        @Override
230        protected Arguments makeArguments(InstanceOfUsageReplacer replacer, LoweringTool tool) {
231            Stamp hubStamp = tool.getStampProvider().createHubStamp(true);
232            if (replacer.instanceOf instanceof InstanceOfNode) {
233                InstanceOfNode instanceOf = (InstanceOfNode) replacer.instanceOf;
234                ValueNode object = instanceOf.getValue();
235                Assumptions assumptions = instanceOf.graph().getAssumptions();
236                TypeCheckHints hintInfo = new TypeCheckHints(instanceOf.type(), instanceOf.profile(), assumptions, TypeCheckMinProfileHitProbability.getValue(), TypeCheckMaxHints.getValue());
237                final HotSpotResolvedObjectType type = (HotSpotResolvedObjectType) instanceOf.type();
238                ConstantNode hub = ConstantNode.forConstant(hubStamp, type.klass(), providers.getMetaAccess(), instanceOf.graph());
239
240                Arguments args;
241
242                StructuredGraph graph = instanceOf.graph();
243                if (hintInfo.hintHitProbability >= 1.0 && hintInfo.exact == null) {
244                    Hints hints = createHints(hintInfo, providers.getMetaAccess(), false, graph, tool);
245                    args = new Arguments(instanceofWithProfile, graph.getGuardsStage(), tool.getLoweringStage());
246                    args.add("object", object);
247                    args.addVarargs("hints", KlassPointer.class, hubStamp, hints.hubs);
248                    args.addVarargs("hintIsPositive", boolean.class, StampFactory.forKind(Kind.Boolean), hints.isPositive);
249                } else if (hintInfo.exact != null) {
250                    args = new Arguments(instanceofExact, graph.getGuardsStage(), tool.getLoweringStage());
251                    args.add("object", object);
252                    args.add("exactHub", ConstantNode.forConstant(hubStamp, ((HotSpotResolvedObjectType) hintInfo.exact).klass(), providers.getMetaAccess(), graph));
253                } else if (type.isPrimaryType()) {
254                    args = new Arguments(instanceofPrimary, graph.getGuardsStage(), tool.getLoweringStage());
255                    args.add("hub", hub);
256                    args.add("object", object);
257                    args.addConst("superCheckOffset", type.superCheckOffset());
258                } else {
259                    Hints hints = createHints(hintInfo, providers.getMetaAccess(), false, graph, tool);
260                    args = new Arguments(instanceofSecondary, graph.getGuardsStage(), tool.getLoweringStage());
261                    args.add("hub", hub);
262                    args.add("object", object);
263                    args.addVarargs("hints", KlassPointer.class, hubStamp, hints.hubs);
264                    args.addVarargs("hintIsPositive", boolean.class, StampFactory.forKind(Kind.Boolean), hints.isPositive);
265                }
266                args.add("trueValue", replacer.trueValue);
267                args.add("falseValue", replacer.falseValue);
268                if (hintInfo.hintHitProbability >= 1.0 && hintInfo.exact == null) {
269                    args.addConst("nullSeen", hintInfo.profile.getNullSeen() != TriState.FALSE);
270                }
271                return args;
272
273            } else if (replacer.instanceOf instanceof TypeCheckNode) {
274                TypeCheckNode typeCheck = (TypeCheckNode) replacer.instanceOf;
275                ValueNode object = typeCheck.getValue();
276                Arguments args = new Arguments(instanceofExact, typeCheck.graph().getGuardsStage(), tool.getLoweringStage());
277                args.add("object", object);
278                args.add("exactHub", ConstantNode.forConstant(hubStamp, ((HotSpotResolvedObjectType) typeCheck.type()).klass(), providers.getMetaAccess(), typeCheck.graph()));
279                args.add("trueValue", replacer.trueValue);
280                args.add("falseValue", replacer.falseValue);
281                return args;
282            } else if (replacer.instanceOf instanceof InstanceOfDynamicNode) {
283                InstanceOfDynamicNode instanceOf = (InstanceOfDynamicNode) replacer.instanceOf;
284                ValueNode object = instanceOf.object();
285
286                Arguments args = new Arguments(instanceofDynamic, instanceOf.graph().getGuardsStage(), tool.getLoweringStage());
287                args.add("mirror", instanceOf.mirror());
288                args.add("object", object);
289                args.add("trueValue", replacer.trueValue);
290                args.add("falseValue", replacer.falseValue);
291                return args;
292            } else if (replacer.instanceOf instanceof ClassIsAssignableFromNode) {
293                ClassIsAssignableFromNode isAssignable = (ClassIsAssignableFromNode) replacer.instanceOf;
294                Arguments args = new Arguments(isAssignableFrom, isAssignable.graph().getGuardsStage(), tool.getLoweringStage());
295                args.add("thisClass", isAssignable.getThisClass());
296                args.add("otherClass", isAssignable.getOtherClass());
297                args.add("trueValue", replacer.trueValue);
298                args.add("falseValue", replacer.falseValue);
299                return args;
300            } else {
301                throw JVMCIError.shouldNotReachHere();
302            }
303        }
304    }
305}