# HG changeset patch # User Thomas Wuerthinger # Date 1367167128 -7200 # Node ID 708aea0e5a25083cabc6b87845e507536ea8644c # Parent 1152c17b51dc67b75214d4531fca101674a3cabc Introduce proxy nodes for propagating profiling information. diff -r 1152c17b51dc -r 708aea0e5a25 graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/JavaTypeProfile.java --- 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 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 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 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; + } } diff -r 1152c17b51dc -r 708aea0e5a25 graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java --- 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); diff -r 1152c17b51dc -r 708aea0e5a25 graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java --- 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); } diff -r 1152c17b51dc -r 708aea0e5a25 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/util/GraphUtil.java --- 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() { diff -r 1152c17b51dc -r 708aea0e5a25 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/InliningPhase.java --- 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 diff -r 1152c17b51dc -r 708aea0e5a25 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/InliningUtil.java --- 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 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 usedTypes = ptypes; - if (notRecordedTypeProbability > 0) { - // Clean out uncommon types. - ArrayList newTypes = new ArrayList<>(); - for (ProfiledType type : ptypes) { - if (type.getProbability() < GraalOptions.MegamorphicInliningMinTypeProbability) { - notRecordedTypeProbability += type.getProbability(); - } else { - newTypes.add(type); - } + // Clean out uncommon types. + ArrayList 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 getCompatibleTypes(JavaTypeProfile profile, ResolvedJavaType holder, double[] newNotRecordedTypeProbability) { - ArrayList 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 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)); diff -r 1152c17b51dc -r 708aea0e5a25 graal/com.oracle.graal.phases/src/com/oracle/graal/phases/GraalOptions.java --- 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;