changeset 10001:38d7b55f87b0

added instanceof snippets that for a profile with 100% precise coverage of seen types. This snippet deoptimizes on any path that contradicts the profile.
author Doug Simon <doug.simon@oracle.com>
date Tue, 11 Jun 2013 22:02:23 +0200
parents d9c14b1828fc
children 3bc930dd9313
files graal/com.oracle.graal.api.code/src/com/oracle/graal/api/code/TypeCheckHints.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/CheckCastSnippets.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/InstanceOfSnippets.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/TypeProfileProxyNode.java graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/InstanceOfSnippetsTemplates.java graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/SnippetTemplate.java
diffstat 6 files changed, 162 insertions(+), 53 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.api.code/src/com/oracle/graal/api/code/TypeCheckHints.java	Tue Jun 11 17:03:30 2013 +0200
+++ b/graal/com.oracle.graal.api.code/src/com/oracle/graal/api/code/TypeCheckHints.java	Tue Jun 11 22:02:23 2013 +0200
@@ -47,7 +47,7 @@
         public final ResolvedJavaType type;
 
         /**
-         * Specifies if {@link #type} was a sub-type of the checked type.
+         * Specifies if {@link #type} is a sub-type of the checked type.
          */
         public final boolean positive;
 
@@ -60,11 +60,11 @@
     private static final Hint[] NO_HINTS = {};
 
     /**
-     * If true, then {@link #hints} contains the only possible type that could pass the type check
-     * because the target of the type check is a final class or has been speculated to be a final
-     * class.
+     * If non-null, then this is the only type that could pass the type check because the target of
+     * the type check is a final class or has been speculated to be a final class and this value is
+     * the only concrete subclass of the target type.
      */
-    public final boolean exact;
+    public final ResolvedJavaType exact;
 
     /**
      * The most likely types that the type check instruction will see.
@@ -72,6 +72,16 @@
     public final Hint[] hints;
 
     /**
+     * The profile from which this information was derived.
+     */
+    public final JavaTypeProfile profile;
+
+    /**
+     * The total probability that the type check will hit one of the types in {@link #hints}.
+     */
+    public final double hintHitProbability;
+
+    /**
      * Derives hint information for use when generating the code for a type check instruction.
      * 
      * @param targetType the target type of the type check
@@ -84,50 +94,58 @@
      * @param maxHints the maximum length of {@link #hints}
      */
     public TypeCheckHints(ResolvedJavaType targetType, JavaTypeProfile profile, Assumptions assumptions, double minHintHitProbability, int maxHints) {
+        this.profile = profile;
         if (targetType != null && !canHaveSubtype(targetType)) {
-            hints = new Hint[]{new Hint(targetType, true)};
-            exact = true;
+            exact = targetType;
         } else {
             ResolvedJavaType uniqueSubtype = targetType == null ? null : targetType.findUniqueConcreteSubtype();
             if (uniqueSubtype != null) {
-                hints = new Hint[]{new Hint(uniqueSubtype, true)};
                 if (assumptions.useOptimisticAssumptions()) {
                     assumptions.recordConcreteSubtype(targetType, uniqueSubtype);
-                    exact = true;
+                    exact = uniqueSubtype;
                 } else {
-                    exact = false;
+                    exact = null;
                 }
             } else {
-                exact = false;
-                Hint[] hintsBuf = NO_HINTS;
-                JavaTypeProfile typeProfile = profile;
-                if (typeProfile != null) {
-                    double notRecordedTypes = typeProfile.getNotRecordedProbability();
-                    ProfiledType[] ptypes = typeProfile.getTypes();
-                    if (notRecordedTypes < (1D - minHintHitProbability) && ptypes != null && ptypes.length > 0) {
-                        hintsBuf = new Hint[ptypes.length];
-                        int hintCount = 0;
-                        double totalHintProbability = 0.0d;
-                        for (ProfiledType ptype : ptypes) {
-                            if (targetType != null) {
-                                ResolvedJavaType hintType = ptype.getType();
-                                hintsBuf[hintCount++] = new Hint(hintType, targetType.isAssignableFrom(hintType));
-                                totalHintProbability += ptype.getProbability();
-                            }
-                        }
-                        if (totalHintProbability >= minHintHitProbability) {
-                            if (hintsBuf.length != hintCount || hintCount > maxHints) {
-                                hintsBuf = Arrays.copyOf(hintsBuf, Math.min(maxHints, hintCount));
-                            }
-                        } else {
-                            hintsBuf = NO_HINTS;
-                        }
+                exact = null;
+            }
+        }
+        Double[] hitProbability = {null};
+        this.hints = makeHints(targetType, profile, minHintHitProbability, maxHints, hitProbability);
+        this.hintHitProbability = hitProbability[0];
+    }
 
+    private static Hint[] makeHints(ResolvedJavaType targetType, JavaTypeProfile profile, double minHintHitProbability, int maxHints, Double[] hitProbability) {
+        double hitProb = 0.0d;
+        Hint[] hintsBuf = NO_HINTS;
+        if (profile != null) {
+            double notRecordedTypes = profile.getNotRecordedProbability();
+            ProfiledType[] ptypes = profile.getTypes();
+            if (notRecordedTypes < (1D - minHintHitProbability) && ptypes != null && ptypes.length > 0) {
+                hintsBuf = new Hint[ptypes.length];
+                int hintCount = 0;
+                for (ProfiledType ptype : ptypes) {
+                    if (targetType != null) {
+                        ResolvedJavaType hintType = ptype.getType();
+                        hintsBuf[hintCount++] = new Hint(hintType, targetType.isAssignableFrom(hintType));
+                        hitProb += ptype.getProbability();
+                    }
+                    if (hintCount == maxHints) {
+                        break;
                     }
                 }
-                this.hints = hintsBuf;
+                if (hitProb >= minHintHitProbability) {
+                    if (hintsBuf.length != hintCount || hintCount > maxHints) {
+                        hintsBuf = Arrays.copyOf(hintsBuf, Math.min(maxHints, hintCount));
+                    }
+                } else {
+                    hintsBuf = NO_HINTS;
+                    hitProb = 0.0d;
+                }
             }
         }
+        hitProbability[0] = hitProb;
+        return hintsBuf;
     }
 
     /**
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/CheckCastSnippets.java	Tue Jun 11 17:03:30 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/CheckCastSnippets.java	Tue Jun 11 22:02:23 2013 +0200
@@ -172,12 +172,10 @@
             ValueNode hub = ConstantNode.forConstant(type.klass(), runtime, checkcast.graph());
 
             Arguments args;
-            if (hintInfo.exact) {
-                ConstantNode[] hints = createHints(hintInfo, runtime, true, graph).hubs;
-                assert hints.length == 1;
+            if (hintInfo.exact != null) {
                 args = new Arguments(exact);
                 args.add("object", object);
-                args.add("exactHub", hints[0]);
+                args.add("exactHub", ConstantNode.forConstant(((HotSpotResolvedObjectType) hintInfo.exact).klass(), runtime, graph));
             } else if (type.isPrimaryType()) {
                 args = new Arguments(primary);
                 args.add("hub", hub);
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/InstanceOfSnippets.java	Tue Jun 11 17:03:30 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/InstanceOfSnippets.java	Tue Jun 11 22:02:23 2013 +0200
@@ -22,6 +22,9 @@
  */
 package com.oracle.graal.hotspot.replacements;
 
+import static com.oracle.graal.api.code.DeoptimizationAction.*;
+import static com.oracle.graal.api.meta.DeoptimizationReason.*;
+import static com.oracle.graal.hotspot.HotSpotGraalRuntime.*;
 import static com.oracle.graal.hotspot.replacements.HotSpotReplacementsUtil.*;
 import static com.oracle.graal.hotspot.replacements.TypeCheckSnippetUtils.*;
 import static com.oracle.graal.phases.GraalOptions.*;
@@ -29,6 +32,7 @@
 
 import com.oracle.graal.api.code.*;
 import com.oracle.graal.api.meta.*;
+import com.oracle.graal.api.meta.ProfilingInfo.TriState;
 import com.oracle.graal.hotspot.meta.*;
 import com.oracle.graal.hotspot.replacements.TypeCheckSnippetUtils.Hints;
 import com.oracle.graal.nodes.*;
@@ -54,6 +58,35 @@
 public class InstanceOfSnippets implements Snippets {
 
     /**
+     * A test against a set of hints derived from a profile with 100% precise coverage of seen
+     * types. This snippet deoptimizes on any path that contradicts the profile.
+     */
+    @Snippet
+    public static Object instanceofWithProfile(Object object, @VarargsParameter Word[] hints, @VarargsParameter boolean[] hintIsPositive, Object trueValue, Object falseValue,
+                    @ConstantParameter boolean checkNull, @ConstantParameter boolean nullSeen) {
+        if (probability(NOT_FREQUENT_PROBABILITY, checkNull && object == null)) {
+            isNull.inc();
+            if (!nullSeen) {
+                DeoptimizeNode.deopt(InvalidateReprofile, OptimizedTypeCheckViolated);
+            }
+            return falseValue;
+        }
+        Word objectHub = loadHub(object);
+        // if we get an exact match: succeed immediately
+        ExplodeLoopNode.explodeLoop();
+        for (int i = 0; i < hints.length; i++) {
+            Word hintHub = hints[i];
+            boolean positive = hintIsPositive[i];
+            if (probability(NOT_FREQUENT_PROBABILITY, hintHub.equal(objectHub))) {
+                hintsHit.inc();
+                return positive ? trueValue : falseValue;
+            }
+        }
+        DeoptimizeNode.deopt(InvalidateReprofile, OptimizedTypeCheckViolated);
+        return falseValue;
+    }
+
+    /**
      * A test against a final type.
      */
     @Snippet
@@ -136,6 +169,7 @@
 
     public static class Templates extends InstanceOfSnippetsTemplates {
 
+        private final SnippetInfo instanceofWithProfile = snippet(InstanceOfSnippets.class, "instanceofWithProfile");
         private final SnippetInfo instanceofExact = snippet(InstanceOfSnippets.class, "instanceofExact");
         private final SnippetInfo instanceofPrimary = snippet(InstanceOfSnippets.class, "instanceofPrimary");
         private final SnippetInfo instanceofSecondary = snippet(InstanceOfSnippets.class, "instanceofSecondary");
@@ -155,12 +189,16 @@
                 ConstantNode hub = ConstantNode.forConstant(type.klass(), runtime, instanceOf.graph());
 
                 Arguments args;
-                if (hintInfo.exact) {
-                    ConstantNode[] hints = createHints(hintInfo, runtime, true, hub.graph()).hubs;
-                    assert hints.length == 1;
+                if (hintInfo.hintHitProbability == 1.0D) {
+                    Hints hints = createHints(hintInfo, runtime, false, hub.graph());
+                    args = new Arguments(instanceofWithProfile);
+                    args.add("object", object);
+                    args.addVarargs("hints", Word.class, StampFactory.forKind(wordKind()), hints.hubs);
+                    args.addVarargs("hintIsPositive", boolean.class, StampFactory.forKind(Kind.Boolean), hints.isPositive);
+                } else if (hintInfo.exact != null) {
                     args = new Arguments(instanceofExact);
                     args.add("object", object);
-                    args.add("exactHub", hints[0]);
+                    args.add("exactHub", ConstantNode.forConstant(((HotSpotResolvedObjectType) hintInfo.exact).klass(), runtime, hub.graph()));
                 } else if (type.isPrimaryType()) {
                     args = new Arguments(instanceofPrimary);
                     args.add("hub", hub);
@@ -177,6 +215,9 @@
                 args.add("trueValue", replacer.trueValue);
                 args.add("falseValue", replacer.falseValue);
                 args.addConst("checkNull", !object.stamp().nonNull());
+                if (hintInfo.hintHitProbability == 1.0D) {
+                    args.addConst("nullSeen", hintInfo.profile.getNullSeen() != TriState.FALSE);
+                }
                 return args;
 
             } else {
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/TypeProfileProxyNode.java	Tue Jun 11 17:03:30 2013 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/TypeProfileProxyNode.java	Tue Jun 11 22:02:23 2013 +0200
@@ -30,7 +30,7 @@
 import com.oracle.graal.nodes.type.*;
 
 /**
- * A node that attached a type profile to a proxied input node.
+ * A node that attaches a type profile to a proxied input node.
  */
 public final class TypeProfileProxyNode extends FloatingNode implements Canonicalizable, Node.IterableNodeType {
 
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/InstanceOfSnippetsTemplates.java	Tue Jun 11 17:03:30 2013 +0200
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/InstanceOfSnippetsTemplates.java	Tue Jun 11 22:02:23 2013 +0200
@@ -108,12 +108,12 @@
     }
 
     /**
-     * The result of an instantiating an instanceof snippet. This enables a snippet instantiation to
-     * be re-used which reduces compile time and produces better code.
+     * 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 PhiNode result;
+        private ValueNode result;
         private CompareNode condition;
         private ValueNode trueValue;
         private ValueNode falseValue;
@@ -125,9 +125,9 @@
             return result != null;
         }
 
-        void initialize(PhiNode phi, ValueNode t, ValueNode f) {
+        void initialize(ValueNode r, ValueNode t, ValueNode f) {
             assert !isInitialized();
-            this.result = phi;
+            this.result = r;
             this.trueValue = t;
             this.falseValue = f;
         }
@@ -137,8 +137,12 @@
          * 
          * @param testValue the returned condition is true if the result is equal to this value
          */
-        CompareNode asCondition(ValueNode testValue) {
+        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.y() != testValue) {
                 // Re-use previously generated condition if the trueValue for the test is the same
                 condition = createCompareNode(Condition.EQ, result, testValue);
@@ -207,10 +211,18 @@
 
         @Override
         public void replace(ValueNode oldNode, ValueNode newNode) {
+            if (newNode.isConstant()) {
+                LogicConstantNode logicConstant = LogicConstantNode.forBoolean(newNode.asConstant().asInt() != 0, newNode.graph());
+                usage.replaceFirstInput(oldNode, logicConstant);
+                // PrintStream out = System.out;
+                // out.println(newNode.graph() + ": " + this);
+                GraalInternalError.shouldNotReachHere(instanceOf.graph().toString());
+                return;
+            }
             assert newNode instanceof PhiNode;
             assert oldNode == instanceOf;
             newNode.inferStamp();
-            instantiation.initialize((PhiNode) newNode, trueValue, falseValue);
+            instantiation.initialize(newNode, trueValue, falseValue);
             usage.replaceFirstInput(oldNode, instantiation.asCondition(trueValue));
         }
     }
@@ -239,10 +251,18 @@
 
         @Override
         public void replace(ValueNode oldNode, ValueNode newNode) {
+            if (newNode.isConstant()) {
+                LogicConstantNode logicConstant = LogicConstantNode.forBoolean(newNode.asConstant().asInt() != 0, newNode.graph());
+                usage.replaceFirstInput(oldNode, logicConstant);
+                // PrintStream out = System.out;
+                // out.println(newNode.graph() + ": " + this);
+                GraalInternalError.shouldNotReachHere(instanceOf.graph().toString());
+                return;
+            }
             assert newNode instanceof PhiNode;
             assert oldNode == instanceOf;
             newNode.inferStamp();
-            instantiation.initialize((PhiNode) newNode, trueValue, falseValue);
+            instantiation.initialize(newNode, trueValue, falseValue);
             usage.replaceAtUsages(newNode);
             usage.clearInputs();
             assert usage.usages().isEmpty();
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/SnippetTemplate.java	Tue Jun 11 17:03:30 2013 +0200
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/SnippetTemplate.java	Tue Jun 11 22:02:23 2013 +0200
@@ -192,7 +192,7 @@
         @Override
         public String toString() {
             StringBuilder result = new StringBuilder();
-            result.append("Parameters<").append(MetaUtil.format("%H.%n", info.method)).append(" [");
+            result.append("Parameters<").append(MetaUtil.format("%h.%n", info.method)).append(" [");
             String sep = "";
             for (int i = 0; i < info.getParameterCount(); i++) {
                 result.append(sep);
@@ -229,6 +229,38 @@
                 this.length = Array.getLength(value);
             }
         }
+
+        @Override
+        public String toString() {
+            if (value instanceof boolean[]) {
+                return Arrays.toString((boolean[]) value);
+            }
+            if (value instanceof byte[]) {
+                return Arrays.toString((byte[]) value);
+            }
+            if (value instanceof char[]) {
+                return Arrays.toString((char[]) value);
+            }
+            if (value instanceof float[]) {
+                return Arrays.toString((short[]) value);
+            }
+            if (value instanceof float[]) {
+                return Arrays.toString((int[]) value);
+            }
+            if (value instanceof float[]) {
+                return Arrays.toString((long[]) value);
+            }
+            if (value instanceof float[]) {
+                return Arrays.toString((float[]) value);
+            }
+            if (value instanceof double[]) {
+                return Arrays.toString((double[]) value);
+            }
+            if (value instanceof Object[]) {
+                return Arrays.toString((Object[]) value);
+            }
+            return String.valueOf(value);
+        }
     }
 
     static class CacheKey {