changeset 9389:708aea0e5a25

Introduce proxy nodes for propagating profiling information.
author Thomas Wuerthinger <thomas.wuerthinger@oracle.com>
date Sun, 28 Apr 2013 18:38:48 +0200
parents 1152c17b51dc
children c8be66a66fcf
files graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/JavaTypeProfile.java graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/util/GraphUtil.java graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/InliningPhase.java graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/InliningUtil.java graal/com.oracle.graal.phases/src/com/oracle/graal/phases/GraalOptions.java
diffstat 7 files changed, 189 insertions(+), 66 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/JavaTypeProfile.java	Sun Apr 28 14:06:52 2013 +0200
+++ b/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/JavaTypeProfile.java	Sun Apr 28 18:38:48 2013 +0200
@@ -23,6 +23,7 @@
 package com.oracle.graal.api.meta;
 
 import java.io.*;
+import java.util.*;
 
 import com.oracle.graal.api.meta.ProfilingInfo.*;
 
@@ -43,6 +44,8 @@
 
         private static final long serialVersionUID = 7838575753661305744L;
 
+        public static final ProfiledType[] EMPTY_ARRAY = new ProfiledType[0];
+
         private final ResolvedJavaType type;
         private final double probability;
 
@@ -80,6 +83,35 @@
         }
 
         @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            long temp;
+            temp = Double.doubleToLongBits(probability);
+            result = prime * result + (int) (temp ^ (temp >>> 32));
+            result = prime * result + type.hashCode();
+            return result;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (obj == null) {
+                return false;
+            }
+            if (getClass() != obj.getClass()) {
+                return false;
+            }
+            ProfiledType other = (ProfiledType) obj;
+            if (Double.doubleToLongBits(probability) != Double.doubleToLongBits(other.probability)) {
+                return false;
+            }
+            return type.equals(other.type);
+        }
+
+        @Override
         public String toString() {
             return "{" + type.getName() + ", " + probability + "}";
         }
@@ -105,6 +137,7 @@
     public JavaTypeProfile(TriState nullSeen, double notRecordedProbability, ProfiledType... ptypes) {
         this.nullSeen = nullSeen;
         this.ptypes = ptypes;
+        assert notRecordedProbability != Double.NaN;
         this.notRecordedProbability = notRecordedProbability;
         assert isSorted(ptypes);
     }
@@ -168,4 +201,120 @@
         builder.append("]");
         return builder.toString();
     }
+
+    public JavaTypeProfile restrict(JavaTypeProfile otherProfile) {
+        if (otherProfile.getNotRecordedProbability() > 0.0) {
+            // Not useful for restricting since there is an unknown set of types occuring.
+            return this;
+        }
+
+        if (this.getNotRecordedProbability() > 0.0) {
+            // We are unrestricted, so the other profile is always a better estimate.
+            return otherProfile;
+        }
+
+        ArrayList<ProfiledType> result = new ArrayList<>();
+        for (int i = 0; i < getTypes().length; i++) {
+            ProfiledType ptype = getTypes()[i];
+            ResolvedJavaType type = ptype.getType();
+            if (otherProfile.isIncluded(type)) {
+                result.add(ptype);
+            }
+        }
+
+        TriState newNullSeen = (otherProfile.getNullSeen() == TriState.FALSE) ? TriState.FALSE : this.nullSeen;
+        double newNotRecorded = this.notRecordedProbability;
+        return createAdjustedProfile(result, newNullSeen, newNotRecorded);
+    }
+
+    public boolean isIncluded(ResolvedJavaType type) {
+        if (this.getNotRecordedProbability() > 0.0) {
+            return true;
+        } else {
+            for (int i = 0; i < getTypes().length; i++) {
+                ProfiledType ptype = getTypes()[i];
+                ResolvedJavaType curType = ptype.getType();
+                if (curType == type) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    public JavaTypeProfile restrict(ResolvedJavaType declaredType, boolean nonNull) {
+        ArrayList<ProfiledType> result = new ArrayList<>();
+        for (int i = 0; i < getTypes().length; i++) {
+            ProfiledType ptype = getTypes()[i];
+            ResolvedJavaType type = ptype.getType();
+            if (declaredType.isAssignableFrom(type)) {
+                result.add(ptype);
+            }
+        }
+
+        TriState newNullSeen = (nonNull) ? TriState.FALSE : this.nullSeen;
+        double newNotRecorded = this.getNotRecordedProbability();
+        // Assume for the types not recorded, the incompatibility rate is the same.
+        if (getTypes().length != 0) {
+            newNotRecorded *= ((double) result.size() / (double) getTypes().length);
+        }
+        return createAdjustedProfile(result, newNullSeen, newNotRecorded);
+    }
+
+    private JavaTypeProfile createAdjustedProfile(ArrayList<ProfiledType> result, TriState newNullSeen, double newNotRecorded) {
+        if (result.size() != this.getTypes().length || newNotRecorded != getNotRecordedProbability() || newNullSeen != this.nullSeen) {
+            if (result.size() == 0) {
+                return new JavaTypeProfile(newNullSeen, 1.0, ProfiledType.EMPTY_ARRAY);
+            }
+            double probabilitySum = 0.0;
+            for (int i = 0; i < result.size(); i++) {
+                probabilitySum += result.get(i).getProbability();
+            }
+            probabilitySum += newNotRecorded;
+
+            double factor = 1.0 / probabilitySum; // Normalize to 1.0
+            assert factor > 1.0;
+            ProfiledType[] newResult = new ProfiledType[result.size()];
+            for (int i = 0; i < newResult.length; ++i) {
+                ProfiledType curType = result.get(i);
+                newResult[i] = new ProfiledType(curType.getType(), Math.min(1.0, curType.getProbability() * factor));
+            }
+            double newNotRecordedTypeProbability = Math.min(1.0, newNotRecorded * factor);
+            return new JavaTypeProfile(newNullSeen, newNotRecordedTypeProbability, newResult);
+        }
+        return this;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (other == this) {
+            return true;
+        }
+        if (other instanceof JavaTypeProfile) {
+            JavaTypeProfile javaTypeProfile = (JavaTypeProfile) other;
+            if (javaTypeProfile.nullSeen != nullSeen) {
+                return false;
+            }
+            if (javaTypeProfile.notRecordedProbability != notRecordedProbability) {
+                return false;
+            }
+            if (javaTypeProfile.ptypes.length != ptypes.length) {
+                return false;
+            }
+
+            for (int i = 0; i < ptypes.length; ++i) {
+                if (!ptypes[i].equals(javaTypeProfile.ptypes[i])) {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return nullSeen.hashCode() + (int) Double.doubleToLongBits(notRecordedProbability) + ptypes.length * 13;
+    }
 }
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java	Sun Apr 28 14:06:52 2013 +0200
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java	Sun Apr 28 18:38:48 2013 +0200
@@ -133,6 +133,8 @@
                     new IterativeConditionalEliminationPhase().apply(graph, highTierContext);
                 }
             }
+        } else {
+            TypeProfileProxyNode.cleanFromGraph(graph);
         }
 
         plan.runPhases(PhasePosition.HIGH_LEVEL, graph);
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java	Sun Apr 28 14:06:52 2013 +0200
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java	Sun Apr 28 18:38:48 2013 +0200
@@ -806,7 +806,8 @@
         JavaType type = lookupType(cpi, CHECKCAST);
         ValueNode object = frameState.apop();
         if (type instanceof ResolvedJavaType) {
-            CheckCastNode checkCast = currentGraph.add(new CheckCastNode((ResolvedJavaType) type, object, getProfileForTypeCheck((ResolvedJavaType) type)));
+            JavaTypeProfile profileForTypeCheck = getProfileForTypeCheck((ResolvedJavaType) type);
+            CheckCastNode checkCast = currentGraph.add(new CheckCastNode((ResolvedJavaType) type, object, profileForTypeCheck));
             append(checkCast);
             frameState.apush(checkCast);
         } else {
@@ -1159,6 +1160,10 @@
         if (graphBuilderConfig.eagerResolving()) {
             returnType = returnType.resolve(targetMethod.getDeclaringClass());
         }
+        if (invokeKind != InvokeKind.Static && invokeKind != InvokeKind.Special) {
+            JavaTypeProfile profile = profilingInfo.getTypeProfile(bci());
+            args[0] = TypeProfileProxyNode.create(args[0], profile);
+        }
         MethodCallTargetNode callTarget = currentGraph.add(new MethodCallTargetNode(invokeKind, targetMethod, args, returnType));
         createInvokeNode(callTarget, resultType);
     }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/util/GraphUtil.java	Sun Apr 28 14:06:52 2013 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/util/GraphUtil.java	Sun Apr 28 18:38:48 2013 +0200
@@ -229,7 +229,7 @@
     public static RuntimeException approxSourceException(Node node, Throwable cause) {
         final StackTraceElement[] elements = approxSourceStackTraceElement(node);
         @SuppressWarnings("serial")
-        RuntimeException exception = new RuntimeException(cause.getMessage(), cause) {
+        RuntimeException exception = new RuntimeException((cause == null) ? null : cause.getMessage(), cause) {
 
             @Override
             public final synchronized Throwable fillInStackTrace() {
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/InliningPhase.java	Sun Apr 28 14:06:52 2013 +0200
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/InliningPhase.java	Sun Apr 28 18:38:48 2013 +0200
@@ -144,6 +144,9 @@
                 }
             }
         }
+
+        // Clean up type profiles.
+        TypeProfileProxyNode.cleanFromGraph(graph);
     }
 
     @Override
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/InliningUtil.java	Sun Apr 28 14:06:52 2013 +0200
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/InliningUtil.java	Sun Apr 28 18:38:48 2013 +0200
@@ -891,7 +891,6 @@
         if (!checkInvokeConditions(invoke)) {
             return null;
         }
-        ResolvedJavaMethod caller = getCaller(invoke);
         MethodCallTargetNode callTarget = (MethodCallTargetNode) invoke.callTarget();
         ResolvedJavaMethod targetMethod = callTarget.targetMethod();
 
@@ -934,7 +933,7 @@
         }
 
         // type check based inlining
-        return getTypeCheckedInlineInfo(replacements, invoke, caller, holder, targetMethod, optimisticOpts);
+        return getTypeCheckedInlineInfo(replacements, invoke, targetMethod, optimisticOpts);
     }
 
     private static InlineInfo getAssumptionInlineInfo(Replacements replacements, Invoke invoke, OptimisticOptimizations optimisticOpts, ResolvedJavaMethod concrete, Assumption takenAssumption) {
@@ -953,28 +952,28 @@
         return new ExactInlineInfo(invoke, targetMethod);
     }
 
-    private static InlineInfo getTypeCheckedInlineInfo(Replacements replacements, Invoke invoke, ResolvedJavaMethod caller, ResolvedJavaType holder, ResolvedJavaMethod targetMethod,
-                    OptimisticOptimizations optimisticOpts) {
-        ProfilingInfo profilingInfo = caller.getProfilingInfo();
-        JavaTypeProfile typeProfile = profilingInfo.getTypeProfile(invoke.bci());
-        if (typeProfile == null) {
+    private static InlineInfo getTypeCheckedInlineInfo(Replacements replacements, Invoke invoke, ResolvedJavaMethod targetMethod, OptimisticOptimizations optimisticOpts) {
+        JavaTypeProfile typeProfile = null;
+        ValueNode receiver = invoke.callTarget().arguments().get(0);
+        if (receiver instanceof TypeProfileProxyNode) {
+            TypeProfileProxyNode typeProfileProxyNode = (TypeProfileProxyNode) receiver;
+            typeProfile = typeProfileProxyNode.getProfile();
+        } else {
             return logNotInlinedMethodAndReturnNull(invoke, targetMethod, "no type profile exists");
         }
 
-        ProfiledType[] rawProfiledTypes = typeProfile.getTypes();
-        double[] newNotRecordedTypeProbability = new double[1];
-        ArrayList<ProfiledType> ptypes = getCompatibleTypes(typeProfile, holder, newNotRecordedTypeProbability);
-        if (ptypes == null || ptypes.size() <= 0) {
-            return logNotInlinedMethodAndReturnNull(invoke, targetMethod, "no types remained after filtering (%d types were recorded)", rawProfiledTypes.length);
+        ProfiledType[] ptypes = typeProfile.getTypes();
+        if (ptypes == null || ptypes.length <= 0) {
+            return logNotInlinedMethodAndReturnNull(invoke, targetMethod, "no types in profile");
         }
 
-        double notRecordedTypeProbability = newNotRecordedTypeProbability[0];
-        if (ptypes.size() == 1 && notRecordedTypeProbability == 0) {
+        double notRecordedTypeProbability = typeProfile.getNotRecordedProbability();
+        if (ptypes.length == 1 && notRecordedTypeProbability == 0) {
             if (!optimisticOpts.inlineMonomorphicCalls()) {
                 return logNotInlinedMethodAndReturnNull(invoke, targetMethod, "inlining monomorphic calls is disabled");
             }
 
-            ResolvedJavaType type = ptypes.get(0).getType();
+            ResolvedJavaType type = ptypes[0].getType();
             ResolvedJavaMethod concrete = type.resolveMethod(targetMethod);
             if (!checkTargetConditions(replacements, invoke, concrete, optimisticOpts)) {
                 return null;
@@ -984,33 +983,28 @@
             invoke.setPolymorphic(true);
 
             if (!optimisticOpts.inlinePolymorphicCalls() && notRecordedTypeProbability == 0) {
-                return logNotInlinedMethodAndReturnNull(invoke, targetMethod, "inlining polymorphic calls is disabled (%d types)", ptypes.size());
+                return logNotInlinedMethodAndReturnNull(invoke, targetMethod, "inlining polymorphic calls is disabled (%d types)", ptypes.length);
             }
             if (!optimisticOpts.inlineMegamorphicCalls() && notRecordedTypeProbability > 0) {
                 // due to filtering impossible types, notRecordedTypeProbability can be > 0 although
                 // the number of types is lower than what can be recorded in a type profile
-                return logNotInlinedMethodAndReturnNull(invoke, targetMethod, "inlining megamorphic calls is disabled (%d types, %f %% not recorded types)", ptypes.size(),
+                return logNotInlinedMethodAndReturnNull(invoke, targetMethod, "inlining megamorphic calls is disabled (%d types, %f %% not recorded types)", ptypes.length,
                                 notRecordedTypeProbability * 100);
             }
 
-            ArrayList<ProfiledType> usedTypes = ptypes;
-            if (notRecordedTypeProbability > 0) {
-                // Clean out uncommon types.
-                ArrayList<ProfiledType> newTypes = new ArrayList<>();
-                for (ProfiledType type : ptypes) {
-                    if (type.getProbability() < GraalOptions.MegamorphicInliningMinTypeProbability) {
-                        notRecordedTypeProbability += type.getProbability();
-                    } else {
-                        newTypes.add(type);
-                    }
+            // Clean out uncommon types.
+            ArrayList<ProfiledType> usedTypes = new ArrayList<>();
+            for (ProfiledType type : ptypes) {
+                if (notRecordedTypeProbability > 0 && type.getProbability() < GraalOptions.MegamorphicInliningMinTypeProbability) {
+                    notRecordedTypeProbability += type.getProbability();
+                } else {
+                    usedTypes.add(type);
                 }
+            }
 
-                if (newTypes.size() == 0) {
-                    // No type left that is worth checking for.
-                    return logNotInlinedMethodAndReturnNull(invoke, targetMethod, "no types remaining after filtering less frequent types (%d types previously)", ptypes.size());
-                }
-
-                usedTypes = newTypes;
+            if (usedTypes.size() == 0) {
+                // No type left that is worth checking for.
+                return logNotInlinedMethodAndReturnNull(invoke, targetMethod, "no types remaining after filtering less frequent types (%d types previously)", ptypes.length);
             }
 
             // determine concrete methods and map type to specific method
@@ -1086,36 +1080,6 @@
         }
     }
 
-    private static ArrayList<ProfiledType> getCompatibleTypes(JavaTypeProfile profile, ResolvedJavaType holder, double[] newNotRecordedTypeProbability) {
-        ArrayList<ProfiledType> result = new ArrayList<>();
-        double totalIncompatibleProbability = 0.0;
-        for (int i = 0; i < profile.getTypes().length; i++) {
-            ProfiledType ptype = profile.getTypes()[i];
-            ResolvedJavaType type = ptype.getType();
-            assert !type.isInterface() && (type.isArray() || !Modifier.isAbstract(type.getModifiers())) : type;
-            if (!GraalOptions.OptFilterProfiledTypes || holder.isAssignableFrom(type)) {
-                result.add(ptype);
-            } else {
-                totalIncompatibleProbability += ptype.getProbability();
-            }
-        }
-        newNotRecordedTypeProbability[0] = profile.getNotRecordedProbability();
-        if (result.size() != 0 && totalIncompatibleProbability > 0.0) {
-            double factor = 1.0 / (1.0 - totalIncompatibleProbability);
-            assert factor > 1.0;
-            ArrayList<ProfiledType> newResult = new ArrayList<>();
-            for (ProfiledType type : result) {
-                newResult.add(new ProfiledType(type.getType(), Math.min(1.0, type.getProbability() * factor)));
-            }
-            newNotRecordedTypeProbability[0] *= factor;
-        }
-        return result;
-    }
-
-    private static ResolvedJavaMethod getCaller(Invoke invoke) {
-        return invoke.stateAfter().method();
-    }
-
     private static PiNode createAnchoredReceiver(StructuredGraph graph, FixedNode anchor, ResolvedJavaType commonType, ValueNode receiver, boolean exact) {
         // to avoid that floating reads on receiver fields float above the type check
         return graph.unique(new PiNode(receiver, exact ? StampFactory.exactNonNull(commonType) : StampFactory.declaredNonNull(commonType), anchor));
--- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/GraalOptions.java	Sun Apr 28 14:06:52 2013 +0200
+++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/GraalOptions.java	Sun Apr 28 18:38:48 2013 +0200
@@ -44,7 +44,7 @@
     public static boolean Intrinsify                         = true;
            static boolean InlineMonomorphicCalls             = true;
            static boolean InlinePolymorphicCalls             = true;
-           static boolean InlineMegamorphicCalls             = false;
+           static boolean InlineMegamorphicCalls             = true;
     public static double  MegamorphicInliningMinTypeProbability = 0.05;
     public static double  MegamorphicInliningMinMethodProbability = 0.20;
     public static int     MaximumDesiredSize                 = 5000;