# HG changeset patch # User Thomas Wuerthinger # Date 1328645371 -3600 # Node ID 9d640941c9c7fa04d3bec8dbfa08bf2c4aef6995 # Parent ab7c258e1cefb541d423bb6c9b7c1db0a54dc4d7# Parent 58ecb156a3e8c2991f9d0b90dab56c7753bbe671 Merge. diff -r ab7c258e1cef -r 9d640941c9c7 .hgignore --- a/.hgignore Sun Feb 05 05:40:36 2012 +0100 +++ b/.hgignore Tue Feb 07 21:09:31 2012 +0100 @@ -36,6 +36,7 @@ /nbproject/private/ ^graal/hotspot/java$ ^scratch/ +^test-output/ scratch/ bin/ ^local/ diff -r ab7c258e1cef -r 9d640941c9c7 graal/com.oracle.max.cri/src/com/oracle/max/cri/ci/CiKind.java --- a/graal/com.oracle.max.cri/src/com/oracle/max/cri/ci/CiKind.java Sun Feb 05 05:40:36 2012 +0100 +++ b/graal/com.oracle.max.cri/src/com/oracle/max/cri/ci/CiKind.java Tue Feb 07 21:09:31 2012 +0100 @@ -311,6 +311,7 @@ } public CiConstant readUnsafeConstant(Object value, long displacement) { + assert value != null; Unsafe u = Unsafe.getUnsafe(); switch(this) { case Boolean: diff -r ab7c258e1cef -r 9d640941c9c7 graal/com.oracle.max.cri/src/com/oracle/max/cri/ri/RiProfilingInfo.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.max.cri/src/com/oracle/max/cri/ri/RiProfilingInfo.java Tue Feb 07 21:09:31 2012 +0100 @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.max.cri.ri; + + +/** + * Represents profiling information for one specific method. + * Every accessor method returns the information that is available at the time of its invocation. + * If a method is invoked multiple times, it may return a significantly different results for every invocation. + */ +public interface RiProfilingInfo { + /** + * Returns an estimate of how often the branch at the given byte code was taken. + * @return The estimated probability, with 0.0 meaning never and 1.0 meaning always, or -1 if this information is not available. + */ + double getBranchTakenProbability(int bci); + + /** + * Returns an estimate of how often the switch cases are taken at the given BCI. + * The default case is stored as the last entry. + * @return A double value that contains the estimated probabilities, with 0.0 meaning never and 1.0 meaning always, + * or -1 if this information is not available. + */ + double[] getSwitchProbabilities(int bci); + + /** + * Returns the TypeProfile for the given BCI. + * @return Returns an RiTypeProfile object, or null if not available. + */ + RiTypeProfile getTypeProfile(int bci); + + /** + * Returns true if the instruction at least once an exception was thrown at the given BCI. + * @return true if an exception was encountered during profiling, false otherwise. + */ + boolean getExceptionSeen(int bci); + + /** + * Returns an estimate how often the current BCI was executed. Avoid comparing execution counts to each other, + * as the returned value highly depends on the time of invocation. + * @return the estimated execution count or -1 if not available. + */ + int getExecutionCount(int bci); +} diff -r ab7c258e1cef -r 9d640941c9c7 graal/com.oracle.max.cri/src/com/oracle/max/cri/ri/RiResolvedMethod.java --- a/graal/com.oracle.max.cri/src/com/oracle/max/cri/ri/RiResolvedMethod.java Sun Feb 05 05:40:36 2012 +0100 +++ b/graal/com.oracle.max.cri/src/com/oracle/max/cri/ri/RiResolvedMethod.java Tue Feb 07 21:09:31 2012 +0100 @@ -159,29 +159,10 @@ int invocationCount(); /** - * Returns an estimate of hot often an exception was seen at the given bytecode. - * @return The estimate in percent (0-100), with 0 meaning never and 100 meaning always, or -1 if this information isn't available. - */ - int exceptionProbability(int bci); - - /** - * Returns the type profile of the instruction at the given byte code index. - * @return The RiTypeProfile information, or null if it isn't available. + * Returns an object that provides access to the method's profiling information. + * @return The profiling information recorded for this method. */ - RiTypeProfile typeProfile(int bci); - - /** - * Returns an estimate of how often the branch at the given byte code was taken. - * @return The estimated probability, with 0.0 meaning never and 1.0 meaning always, or -1 if this information isn't available. - */ - double branchProbability(int bci); - - /** - * Returns an estimate of how often the branches of the switch at the given byte code were taken. - * @return The estimated probability, with 0.0 meaning never and 1.0 meaning always, or NULL if this information isn't available. - * The default case is specified at the last index. - */ - double[] switchProbability(int bci); + RiProfilingInfo profilingInfo(); /** * Returns a map that the compiler can use to store objects that should survive the current compilation. diff -r ab7c258e1cef -r 9d640941c9c7 graal/com.oracle.max.cri/src/com/oracle/max/cri/ri/RiTypeProfile.java --- a/graal/com.oracle.max.cri/src/com/oracle/max/cri/ri/RiTypeProfile.java Sun Feb 05 05:40:36 2012 +0100 +++ b/graal/com.oracle.max.cri/src/com/oracle/max/cri/ri/RiTypeProfile.java Tue Feb 07 21:09:31 2012 +0100 @@ -25,37 +25,47 @@ import java.io.*; /** - * This profile object represents the type profile of one call site, cast or instanceof instruction. The precision of - * the supplied values may vary, but a runtime that provides this information should be aware that it will be used to - * guide performance-critical decisions like speculative inlining, etc. + * This profile object represents the type profile at a specific BCI. The precision of the supplied values may vary, + * but a runtime that provides this information should be aware that it will be used to guide performance-critical + * decisions like speculative inlining, etc. */ -public class RiTypeProfile implements Serializable { - +public final class RiTypeProfile implements Serializable { /** - * + * */ private static final long serialVersionUID = -6877016333706838441L; - /** - * How often the instruction was executed, which may be used to judge the maturity of this profile. - */ - public int count; + private final RiResolvedType[] types; + private final double notRecordedProbability; + private final double[] probabilities; + + public RiTypeProfile(RiResolvedType[] types, double notRecordedProbability, double[] probabilites) { + this.types = types; + this.notRecordedProbability = notRecordedProbability; + this.probabilities = probabilites; + } /** - * An estimation of how many different receiver types were encountered. This may or may not be the same as - * probabilities.length/types.length, as the runtime may store probabilities for a limited number of receivers. + * The estimated probabilities of the different receivers. This array needs to have the same length as the array returned by + * {@link RiTypeProfile#types}. */ - public int morphism; + public double[] getProbabilities() { + return probabilities; + } + + /** + * Returns the estimated probability of all types that could not be recorded due to profiling limitations. + * @return double value >= 0.0 and <= 1.0 + */ + public double getNotRecordedProbability() { + return notRecordedProbability; + } /** * A list of receivers for which the runtime has recorded probability information. This array needs to have the same * length as {@link RiTypeProfile#probabilities}. */ - public RiResolvedType[] types; - - /** - * The estimated probabilities of the different receivers. This array needs to have the same length as - * {@link RiTypeProfile#types}. - */ - public float[] probabilities; + public RiResolvedType[] getTypes() { + return types; + } } diff -r ab7c258e1cef -r 9d640941c9c7 graal/com.oracle.max.cri/src/com/oracle/max/cri/xir/RiXirGenerator.java --- a/graal/com.oracle.max.cri/src/com/oracle/max/cri/xir/RiXirGenerator.java Sun Feb 05 05:40:36 2012 +0100 +++ b/graal/com.oracle.max.cri/src/com/oracle/max/cri/xir/RiXirGenerator.java Tue Feb 07 21:09:31 2012 +0100 @@ -102,12 +102,12 @@ * an object is identical to a given hub constant. In pseudo code: *
      *     if (object.getHub() != hub) {
-     *         uncommonTrap();
+     *       jump(falseSuccessor)
      *     }
      * 
* This snippet should only be used when the object is guaranteed not to be null. */ - XirSnippet genTypeCheck(XirSite site, XirArgument object, XirArgument hub, RiType type); + XirSnippet genTypeBranch(XirSite site, XirArgument thisHub, XirArgument otherHub, RiType type); /** * Initializes the XIR generator for the given XIR assembler. diff -r ab7c258e1cef -r 9d640941c9c7 graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/GraalCompiler.java --- a/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/GraalCompiler.java Sun Feb 05 05:40:36 2012 +0100 +++ b/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/GraalCompiler.java Tue Feb 07 21:09:31 2012 +0100 @@ -81,7 +81,7 @@ if (osrBCI != -1) { throw new CiBailout("No OSR supported"); } - return Debug.scope(createScopeName(method), method, new Callable() { + return Debug.scope(createScopeName(method), new Callable() { public CiTargetMethod call() { final CiAssumptions assumptions = GraalOptions.OptAssumptions ? new CiAssumptions() : null; final LIR lir = Debug.scope("FrontEnd", graph, new Callable() { diff -r ab7c258e1cef -r 9d640941c9c7 graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/GraalOptions.java --- a/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/GraalOptions.java Sun Feb 05 05:40:36 2012 +0100 +++ b/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/GraalOptions.java Tue Feb 07 21:09:31 2012 +0100 @@ -42,9 +42,12 @@ public static boolean Inline = true; public static boolean Intrinsify = true; public static boolean CacheGraphs = ____; - public static boolean InlineWithTypeCheck = ____; + public static boolean InlineMonomorphicCalls = true; + public static boolean InlinePolymorphicCalls = true; + public static int InliningPolicy = 0; public static int MaximumInlineSize = 35; public static int MaximumFreqInlineSize = 300; + public static float NestedInliningSizeRatio = 0.9f; public static int FreqInlineRatio = 20; public static int MaximumTrivialSize = 6; public static int MaximumInlineLevel = 30; diff -r ab7c258e1cef -r 9d640941c9c7 graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/gen/LIRGenerator.java --- a/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/gen/LIRGenerator.java Sun Feb 05 05:40:36 2012 +0100 +++ b/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/gen/LIRGenerator.java Tue Feb 07 21:09:31 2012 +0100 @@ -701,9 +701,7 @@ @Override public void emitGuardCheck(BooleanNode comp) { - if (comp instanceof IsTypeNode) { - emitTypeGuard((IsTypeNode) comp); - } else if (comp instanceof NullCheckNode && !((NullCheckNode) comp).expectedNull) { + if (comp instanceof NullCheckNode && !((NullCheckNode) comp).expectedNull) { emitNullCheckGuard((NullCheckNode) comp); } else if (comp instanceof ConstantNode && comp.asConstant().asBoolean()) { // True constant, nothing to emit. @@ -717,15 +715,6 @@ protected abstract void emitNullCheckGuard(NullCheckNode node); - private void emitTypeGuard(IsTypeNode node) { - load(operand(node.object())); - LIRDebugInfo info = state(); - XirArgument clazz = toXirArgument(node.type().getEncoding(Representation.ObjectHub)); - XirSnippet typeCheck = xir.genTypeCheck(site(node), toXirArgument(node.object()), clazz, node.type()); - emitXir(typeCheck, node, info, false); - } - - public void emitBranch(BooleanNode node, LabelRef trueSuccessor, LabelRef falseSuccessor, LIRDebugInfo info) { if (node instanceof NullCheckNode) { emitNullCheckBranch((NullCheckNode) node, trueSuccessor, falseSuccessor, info); @@ -735,6 +724,8 @@ emitInstanceOfBranch((InstanceOfNode) node, trueSuccessor, falseSuccessor, info); } else if (node instanceof ConstantNode) { emitConstantBranch(((ConstantNode) node).asConstant().asBoolean(), trueSuccessor, falseSuccessor, info); + } else if (node instanceof IsTypeNode) { + emitTypeBranch((IsTypeNode) node, trueSuccessor, falseSuccessor, info); } else { throw Util.unimplemented(node.toString()); } @@ -761,7 +752,6 @@ emitXir(snippet, x, info, null, false, x.negated() ? falseSuccessor : trueSuccessor, x.negated() ? trueSuccessor : falseSuccessor); } - public void emitConstantBranch(boolean value, LabelRef trueSuccessorBlock, LabelRef falseSuccessorBlock, LIRDebugInfo info) { LabelRef block = value ? trueSuccessorBlock : falseSuccessorBlock; if (block != null) { @@ -769,6 +759,16 @@ } } + public void emitTypeBranch(IsTypeNode x, LabelRef trueSuccessor, LabelRef falseSuccessor, LIRDebugInfo info) { + XirArgument thisClass = toXirArgument(x.objectClass()); + XirArgument otherClass = toXirArgument(x.type().getEncoding(Representation.ObjectHub)); + XirSnippet snippet = xir.genTypeBranch(site(x), thisClass, otherClass, x.type()); + emitXir(snippet, x, info, null, false, trueSuccessor, falseSuccessor); + if (trueSuccessor != null) { + emitJump(trueSuccessor, null); + } + } + @Override public void emitConditional(ConditionalNode conditional) { CiValue tVal = operand(conditional.trueValue()); diff -r ab7c258e1cef -r 9d640941c9c7 graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/phases/CanonicalizerPhase.java --- a/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/phases/CanonicalizerPhase.java Sun Feb 05 05:40:36 2012 +0100 +++ b/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/phases/CanonicalizerPhase.java Tue Feb 07 21:09:31 2012 +0100 @@ -219,7 +219,7 @@ pred.replaceFirstSuccessor(replacedSux, next); FrameState stateAfter = merge.stateAfter(); merge.setStateAfter(null); - if (stateAfter.usages().isEmpty()) { + if (stateAfter != null && stateAfter.usages().isEmpty()) { stateAfter.safeDelete(); } merge.safeDelete(); diff -r ab7c258e1cef -r 9d640941c9c7 graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/phases/EscapeAnalysisPhase.java --- a/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/phases/EscapeAnalysisPhase.java Sun Feb 05 05:40:36 2012 +0100 +++ b/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/phases/EscapeAnalysisPhase.java Tue Feb 07 21:09:31 2012 +0100 @@ -316,7 +316,7 @@ assert ((NullCheckNode) usage).object() == node; return null; } else if (usage instanceof IsTypeNode) { - assert ((IsTypeNode) usage).object() == node; + assert ((IsTypeNode) usage).objectClass() == node; return null; } else if (usage instanceof AccessMonitorNode) { assert ((AccessMonitorNode) usage).object() == node; diff -r ab7c258e1cef -r 9d640941c9c7 graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/phases/InliningPhase.java --- a/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/phases/InliningPhase.java Sun Feb 05 05:40:36 2012 +0100 +++ b/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/phases/InliningPhase.java Tue Feb 07 21:09:31 2012 +0100 @@ -53,6 +53,7 @@ private CiAssumptions assumptions; private final PhasePlan plan; + private final InliningPolicy inliningPolicy; // Metrics private static final DebugMetric metricInliningPerformed = Debug.metric("InliningPerformed"); @@ -64,6 +65,7 @@ this.hints = hints; this.assumptions = assumptions; this.plan = plan; + this.inliningPolicy = createInliningPolicy(); } @SuppressWarnings("unchecked") @@ -80,39 +82,36 @@ while (!inlineCandidates.isEmpty() && graph.getNodeCount() < GraalOptions.MaximumDesiredSize) { InlineInfo info = inlineCandidates.remove(); - double penalty = Math.pow(GraalOptions.InliningSizePenaltyExp, graph.getNodeCount() / (double) GraalOptions.MaximumDesiredSize) / GraalOptions.InliningSizePenaltyExp; - if (info.weight > GraalOptions.MaximumInlineWeight / (1 + penalty * GraalOptions.InliningSizePenalty)) { - Debug.log("not inlining (cut off by weight): %e", info.weight); - return; - } - Iterable newNodes = null; - if (info.invoke.node().isAlive()) { - try { - info.inline(graph, runtime, this); - Debug.log("inlining %f: %s", info.weight, info); - Debug.dump(graph, "after inlining %s", info); - // get the new nodes here, the canonicalizer phase will reset the mark - newNodes = graph.getNewNodes(); - if (GraalOptions.OptCanonicalizer) { - new CanonicalizerPhase(target, runtime, true, assumptions).apply(graph); + if (inliningPolicy.isWorthInlining(graph, info)) { + Iterable newNodes = null; + if (info.invoke.node().isAlive()) { + try { + info.inline(graph, runtime, this); + Debug.log("inlining %f: %s", info.weight, info); + Debug.dump(graph, "after inlining %s", info); + // get the new nodes here, the canonicalizer phase will reset the mark + newNodes = graph.getNewNodes(); + if (GraalOptions.OptCanonicalizer) { + new CanonicalizerPhase(target, runtime, true, assumptions).apply(graph); + } + if (GraalOptions.Intrinsify) { + new IntrinsificationPhase(runtime).apply(graph); + } + metricInliningPerformed.increment(); + } catch (CiBailout bailout) { + // TODO determine if we should really bail out of the whole compilation. + throw bailout; + } catch (AssertionError e) { + throw new GraalInternalError(e).addContext(info.toString()); + } catch (RuntimeException e) { + throw new GraalInternalError(e).addContext(info.toString()); + } catch (GraalInternalError e) { + throw e.addContext(info.toString()); } - if (GraalOptions.Intrinsify) { - new IntrinsificationPhase(runtime).apply(graph); - } - metricInliningPerformed.increment(); - } catch (CiBailout bailout) { - // TODO determine if we should really bail out of the whole compilation. - throw bailout; - } catch (AssertionError e) { - throw new GraalInternalError(e).addContext(info.toString()); - } catch (RuntimeException e) { - throw new GraalInternalError(e).addContext(info.toString()); - } catch (GraalInternalError e) { - throw e.addContext(info.toString()); } - } - if (newNodes != null && info.level <= GraalOptions.MaximumInlineLevel) { - scanInvokes(newNodes, info.level + 1, graph); + if (newNodes != null && info.level <= GraalOptions.MaximumInlineLevel) { + scanInvokes(newNodes, info.level + 1, graph); + } } } } @@ -160,61 +159,10 @@ @Override public double inliningWeight(RiResolvedMethod caller, RiResolvedMethod method, Invoke invoke) { - double ratio; - if (hints != null && hints.contains(invoke)) { - ratio = 1000000; - } else { - if (GraalOptions.ProbabilityAnalysis) { - ratio = invoke.node().probability(); - } else { - RiTypeProfile profile = caller.typeProfile(invoke.bci()); - if (profile != null && profile.count > 0) { - RiResolvedMethod parent = invoke.stateAfter().method(); - ratio = profile.count / (float) parent.invocationCount(); - } else { - ratio = 1; - } - } - } - - final double normalSize; - // TODO(ls) get rid of this magic, it's here to emulate the old behavior for the time being - if (ratio < 0.01) { - ratio = 0.01; - } - if (ratio < 0.5) { - normalSize = 10 * ratio / 0.5; - } else if (ratio < 2) { - normalSize = 10 + (35 - 10) * (ratio - 0.5) / 1.5; - } else if (ratio < 20) { - normalSize = 35; - } else if (ratio < 40) { - normalSize = 35 + (350 - 35) * (ratio - 20) / 20; - } else { - normalSize = 350; - } - - int count; - if (GraalOptions.ParseBeforeInlining) { - if (!parsedMethods.containsKey(method)) { - StructuredGraph newGraph = new StructuredGraph(method); - if (plan != null) { - plan.runPhases(PhasePosition.AFTER_PARSING, newGraph); - } - new CanonicalizerPhase(target, runtime, assumptions).apply(newGraph); - count = graphComplexity(newGraph); - parsedMethods.put(method, count); - } else { - count = parsedMethods.get(method); - } - } else { - count = method.codeSize(); - } - - return count / normalSize; + boolean preferred = hints != null && hints.contains(invoke); + return inliningPolicy.computeWeight(caller, method, invoke, preferred); } - public static int graphComplexity(StructuredGraph graph) { int result = 0; for (Node node : graph.getNodes()) { @@ -239,4 +187,105 @@ assumptions.recordConcreteMethod(method, context, impl); } + private InliningPolicy createInliningPolicy() { + if (GraalOptions.InliningPolicy == 0) { + return new WeightBasedInliningPolicy(); + } else if (GraalOptions.InliningPolicy == 1) { + return new SizeBasedInliningPolicy(); + } else { + Util.shouldNotReachHere(); + return null; + } + } + + private interface InliningPolicy { + double computeWeight(RiResolvedMethod caller, RiResolvedMethod method, Invoke invoke, boolean preferredInvoke); + boolean isWorthInlining(StructuredGraph callerGraph, InlineInfo info); + } + + private class WeightBasedInliningPolicy implements InliningPolicy { + @Override + public double computeWeight(RiResolvedMethod caller, RiResolvedMethod method, Invoke invoke, boolean preferredInvoke) { + double ratio; + if (preferredInvoke) { + ratio = 1000000; + } else { + if (GraalOptions.ProbabilityAnalysis) { + ratio = invoke.node().probability(); + } else { + RiProfilingInfo profilingInfo = method.profilingInfo(); + int executionCount = profilingInfo.getExecutionCount(invoke.bci()); + if (executionCount > 0) { + RiResolvedMethod parent = invoke.stateAfter().method(); + ratio = executionCount / (float) parent.invocationCount(); + } else { + ratio = 1; + } + } + } + + final double normalSize; + // TODO(ls) get rid of this magic, it's here to emulate the old behavior for the time being + if (ratio < 0.01) { + ratio = 0.01; + } + if (ratio < 0.5) { + normalSize = 10 * ratio / 0.5; + } else if (ratio < 2) { + normalSize = 10 + (35 - 10) * (ratio - 0.5) / 1.5; + } else if (ratio < 20) { + normalSize = 35; + } else if (ratio < 40) { + normalSize = 35 + (350 - 35) * (ratio - 20) / 20; + } else { + normalSize = 350; + } + + int count; + if (GraalOptions.ParseBeforeInlining) { + if (!parsedMethods.containsKey(method)) { + StructuredGraph newGraph = new StructuredGraph(method); + if (plan != null) { + plan.runPhases(PhasePosition.AFTER_PARSING, newGraph); + } + new CanonicalizerPhase(target, runtime, assumptions).apply(newGraph); + count = graphComplexity(newGraph); + parsedMethods.put(method, count); + } else { + count = parsedMethods.get(method); + } + } else { + count = method.codeSize(); + } + + return count / normalSize; + } + + @Override + public boolean isWorthInlining(StructuredGraph callerGraph, InlineInfo info) { + double penalty = Math.pow(GraalOptions.InliningSizePenaltyExp, callerGraph.getNodeCount() / (double) GraalOptions.MaximumDesiredSize) / GraalOptions.InliningSizePenaltyExp; + if (info.weight > GraalOptions.MaximumInlineWeight / (1 + penalty * GraalOptions.InliningSizePenalty)) { + Debug.log("not inlining (cut off by weight): %e", info.weight); + return false; + } + return true; + } + } + + private class SizeBasedInliningPolicy implements InliningPolicy { + @Override + public double computeWeight(RiResolvedMethod caller, RiResolvedMethod method, Invoke invoke, boolean preferredInvoke) { + if (preferredInvoke) { + return method.codeSize() / 2; + } else { + return method.codeSize(); + } + } + + @Override + public boolean isWorthInlining(StructuredGraph callerGraph, InlineInfo info) { + double maxSize = Math.max(GraalOptions.MaximumTrivialSize, Math.pow(GraalOptions.NestedInliningSizeRatio, info.level) * GraalOptions.MaximumInlineSize); + return info.weight <= maxSize; + } + } } diff -r ab7c258e1cef -r 9d640941c9c7 graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/util/InliningUtil.java --- a/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/util/InliningUtil.java Sun Feb 05 05:40:36 2012 +0100 +++ b/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/util/InliningUtil.java Tue Feb 07 21:09:31 2012 +0100 @@ -34,7 +34,9 @@ import com.oracle.max.graal.graph.*; import com.oracle.max.graal.nodes.*; import com.oracle.max.graal.nodes.DeoptimizeNode.DeoptAction; +import com.oracle.max.graal.nodes.PhiNode.PhiType; import com.oracle.max.graal.nodes.calc.*; +import com.oracle.max.graal.nodes.extended.*; import com.oracle.max.graal.nodes.java.*; import com.oracle.max.graal.nodes.java.MethodCallTargetNode.InvokeKind; import com.oracle.max.graal.nodes.util.*; @@ -85,6 +87,15 @@ return (weight < o.weight) ? -1 : (weight > o.weight) ? 1 : 0; } + protected static StructuredGraph getGraph(final RiResolvedMethod concrete, final InliningCallback callback) { + return Debug.scope("Inlining", concrete, new Callable() { + @Override + public StructuredGraph call() throws Exception { + return callback.buildGraph(concrete); + } + }); + } + public abstract boolean canDeopt(); /** @@ -94,10 +105,8 @@ * @param graph * @param runtime * @param callback - * @return The node that represents the return value, or null for void methods and methods that have no - * non-exceptional exit. */ - public abstract Node inline(StructuredGraph graph, GraalRuntime runtime, InliningCallback callback); + public abstract void inline(StructuredGraph graph, GraalRuntime runtime, InliningCallback callback); } /** @@ -113,14 +122,9 @@ } @Override - public Node inline(StructuredGraph compilerGraph, GraalRuntime runtime, final InliningCallback callback) { - StructuredGraph graph = Debug.scope("Inlining", concrete, new Callable() { - @Override - public StructuredGraph call() throws Exception { - return callback.buildGraph(concrete); - } - }); - return InliningUtil.inline(invoke, graph, true); + public void inline(StructuredGraph compilerGraph, GraalRuntime runtime, final InliningCallback callback) { + StructuredGraph graph = getGraph(concrete, callback); + InliningUtil.inline(invoke, graph, true); } @Override @@ -138,26 +142,32 @@ * Represents an inlining opportunity for which profiling information suggests a monomorphic receiver, but for which * the receiver type cannot be proven. A type check guard will be generated if this inlining is performed. */ - private static class TypeGuardInlineInfo extends ExactInlineInfo { - + private static class TypeGuardInlineInfo extends InlineInfo { + public final RiResolvedMethod concrete; public final RiResolvedType type; - public final double probability; - public TypeGuardInlineInfo(Invoke invoke, double weight, int level, RiResolvedMethod concrete, RiResolvedType type, double probability) { - super(invoke, weight, level, concrete); + public TypeGuardInlineInfo(Invoke invoke, double weight, int level, RiResolvedMethod concrete, RiResolvedType type) { + super(invoke, weight, level); + this.concrete = concrete; this.type = type; - this.probability = probability; } @Override - public Node inline(StructuredGraph graph, GraalRuntime runtime, InliningCallback callback) { - IsTypeNode isType = graph.unique(new IsTypeNode(invoke.callTarget().receiver(), type)); - FixedGuardNode guard = graph.add(new FixedGuardNode(isType)); + public void inline(StructuredGraph graph, GraalRuntime runtime, InliningCallback callback) { + // receiver null check must be before the type check + InliningUtil.receiverNullCheck(invoke); + ReadHubNode objectClass = graph.add(new ReadHubNode(invoke.callTarget().receiver())); + IsTypeNode isTypeNode = graph.unique(new IsTypeNode(objectClass, type)); + FixedGuardNode guard = graph.add(new FixedGuardNode(isTypeNode)); assert invoke.predecessor() != null; + + graph.addBeforeFixed(invoke.node(), objectClass); graph.addBeforeFixed(invoke.node(), guard); - Debug.log("inlining with type check, type probability: %5.3f", probability); - return super.inline(graph, runtime, callback); + Debug.log("inlining 1 method using 1 type check"); + + StructuredGraph calleeGraph = getGraph(concrete, callback); + InliningUtil.inline(invoke, calleeGraph, false); } @Override @@ -172,6 +182,240 @@ } /** + * Polymorphic inlining of m methods with n type checks (n >= m) in case that the profiling information suggests a reasonable + * amounts of different receiver types and different methods. If an unknown type is encountered a deoptimization is triggered. + */ + private static class MultiTypeGuardInlineInfo extends InlineInfo { + public final List concretes; + public final RiResolvedType[] types; + public final int[] typesToConcretes; + public final double[] branchProbabilities; + public final double notRecordedTypeProbability; + + public MultiTypeGuardInlineInfo(Invoke invoke, double weight, int level, List concretes, RiResolvedType[] types, + int[] typesToConcretes, double[] branchProbabilities, double notRecordedTypeProbability) { + super(invoke, weight, level); + assert concretes.size() > 0 && concretes.size() <= types.length : "must have at least one method but no more than types methods"; + assert types.length == typesToConcretes.length && types.length == branchProbabilities.length : "array length must match"; + + this.concretes = concretes; + this.types = types; + this.typesToConcretes = typesToConcretes; + this.branchProbabilities = branchProbabilities; + this.notRecordedTypeProbability = notRecordedTypeProbability; + } + + @Override + public void inline(StructuredGraph graph, GraalRuntime runtime, InliningCallback callback) { + int numberOfMethods = concretes.size(); + boolean hasReturnValue = invoke.node().kind() != CiKind.Void; + + // receiver null check must be the first node + InliningUtil.receiverNullCheck(invoke); + if (numberOfMethods > 1 || shouldFallbackToInvoke()) { + inlineMultipleMethods(graph, callback, numberOfMethods, hasReturnValue); + } else { + inlineSingleMethod(graph, callback); + } + + Debug.log("inlining %d methods with %d type checks and falling back to %s if violated", numberOfMethods, types.length, shouldFallbackToInvoke() ? "invocation" : "deoptimization"); + } + + private boolean shouldFallbackToInvoke() { + return notRecordedTypeProbability > 0; + } + + private void inlineMultipleMethods(StructuredGraph graph, InliningCallback callback, int numberOfMethods, boolean hasReturnValue) { + FixedNode continuation = invoke.next(); + + // setup merge and phi nodes for results and exceptions + MergeNode returnMerge = graph.add(new MergeNode()); + returnMerge.setProbability(invoke.probability()); + returnMerge.setStateAfter(invoke.stateAfter()); + + PhiNode returnValuePhi = null; + if (hasReturnValue) { + returnValuePhi = graph.unique(new PhiNode(invoke.node().kind(), returnMerge, PhiType.Value)); + } + + MergeNode exceptionMerge = null; + PhiNode exceptionObjectPhi = null; + if (invoke instanceof InvokeWithExceptionNode) { + InvokeWithExceptionNode invokeWithException = (InvokeWithExceptionNode) invoke; + BeginNode exceptionEdge = invokeWithException.exceptionEdge(); + ExceptionObjectNode exceptionObject = (ExceptionObjectNode) exceptionEdge.next(); + + exceptionMerge = graph.add(new MergeNode()); + exceptionMerge.setProbability(exceptionEdge.probability()); + exceptionMerge.setStateAfter(exceptionEdge.stateAfter()); + + FixedNode exceptionSux = exceptionObject.next(); + graph.addBeforeFixed(exceptionSux, exceptionMerge); + exceptionObjectPhi = graph.unique(new PhiNode(CiKind.Object, exceptionMerge, PhiType.Value)); + } + + // create one separate block for each invoked method + BeginNode[] calleeEntryNodes = new BeginNode[numberOfMethods]; + for (int i = 0; i < numberOfMethods; i++) { + int predecessors = getPredecessorCount(i); + calleeEntryNodes[i] = createInvocationBlock(graph, invoke, returnMerge, returnValuePhi, exceptionMerge, exceptionObjectPhi, predecessors, true); + } + + // create the successor for an unknown type + FixedNode unknownTypeNode; + if (shouldFallbackToInvoke()) { + unknownTypeNode = createInvocationBlock(graph, invoke, returnMerge, returnValuePhi, exceptionMerge, exceptionObjectPhi, 1, false); + } else { + unknownTypeNode = graph.add(new DeoptimizeNode(DeoptAction.InvalidateReprofile)); + } + + // replace the invoke exception edge + if (invoke instanceof InvokeWithExceptionNode) { + InvokeWithExceptionNode invokeWithExceptionNode = (InvokeWithExceptionNode) invoke; + BeginNode exceptionEdge = invokeWithExceptionNode.exceptionEdge(); + ExceptionObjectNode exceptionObject = (ExceptionObjectNode) exceptionEdge.next(); + exceptionObject.replaceAtUsages(exceptionObjectPhi); + exceptionObject.setNext(null); + GraphUtil.killCFG(invokeWithExceptionNode.exceptionEdge()); + } + + // replace the invoke with a cascade of if nodes + ReadHubNode objectClassNode = graph.add(new ReadHubNode(invoke.callTarget().receiver())); + graph.addBeforeFixed(invoke.node(), objectClassNode); + FixedNode dispatchOnType = createDispatchOnType(graph, objectClassNode, calleeEntryNodes, unknownTypeNode); + + assert invoke.next() == continuation; + invoke.setNext(null); + returnMerge.setNext(continuation); + invoke.node().replaceAtUsages(returnValuePhi); + invoke.node().replaceAndDelete(dispatchOnType); + + // do the actual inlining for every invoke + for (int i = 0; i < calleeEntryNodes.length; i++) { + BeginNode node = calleeEntryNodes[i]; + Invoke invokeForInlining = (Invoke) node.next(); + StructuredGraph calleeGraph = getGraph(concretes.get(i), callback); + InliningUtil.inline(invokeForInlining, calleeGraph, false); + } + } + + private void inlineSingleMethod(StructuredGraph graph, InliningCallback callback) { + assert concretes.size() == 1 && types.length > 1 && !shouldFallbackToInvoke() && notRecordedTypeProbability == 0; + + MergeNode calleeEntryNode = graph.add(new MergeNode()); + calleeEntryNode.setProbability(invoke.probability()); + ReadHubNode objectClassNode = graph.add(new ReadHubNode(invoke.callTarget().receiver())); + graph.addBeforeFixed(invoke.node(), objectClassNode); + + FixedNode unknownTypeNode = graph.add(new DeoptimizeNode(DeoptAction.InvalidateReprofile)); + FixedNode dispatchOnType = createDispatchOnType(graph, objectClassNode, new BeginNode[] {calleeEntryNode}, unknownTypeNode); + + FixedWithNextNode pred = (FixedWithNextNode) invoke.node().predecessor(); + pred.setNext(dispatchOnType); + calleeEntryNode.setNext(invoke.node()); + + StructuredGraph calleeGraph = getGraph(concretes.get(0), callback); + InliningUtil.inline(invoke, calleeGraph, false); + } + + private FixedNode createDispatchOnType(StructuredGraph graph, ReadHubNode objectClassNode, BeginNode[] calleeEntryNodes, FixedNode unknownTypeSux) { + assert types.length > 1; + + // TODO (ch) set probabilities for all created fixed nodes... + int lastIndex = types.length - 1; + FixedNode nextNode = createTypeCheck(graph, objectClassNode, types[lastIndex], calleeEntryNodes[typesToConcretes[lastIndex]], unknownTypeSux, branchProbabilities[lastIndex]); + for (int i = lastIndex - 1; i >= 0; i--) { + nextNode = createTypeCheck(graph, objectClassNode, types[i], calleeEntryNodes[typesToConcretes[i]], nextNode, branchProbabilities[i]); + } + + return nextNode; + } + + private static FixedNode createTypeCheck(StructuredGraph graph, ReadHubNode objectClassNode, RiResolvedType type, BeginNode tsux, FixedNode nextNode, double tsuxProbability) { + IfNode result; + IsTypeNode isTypeNode = graph.unique(new IsTypeNode(objectClassNode, type)); + if (tsux instanceof MergeNode) { + EndNode endNode = graph.add(new EndNode()); + result = graph.add(new IfNode(isTypeNode, endNode, nextNode, tsuxProbability)); + ((MergeNode) tsux).addEnd(endNode); + } else { + result = graph.add(new IfNode(isTypeNode, tsux, nextNode, tsuxProbability)); + } + return result; + } + + private int getPredecessorCount(int concreteMethodIndex) { + if (concretes.size() == types.length) { + return 1; + } else { + int count = 0; + for (int i = 0; i < typesToConcretes.length; i++) { + if (typesToConcretes[i] == concreteMethodIndex) { + count++; + } + } + return count; + } + } + + private static BeginNode createInvocationBlock(StructuredGraph graph, Invoke invoke, MergeNode returnMerge, PhiNode returnValuePhi, + MergeNode exceptionMerge, PhiNode exceptionObjectPhi, int predecessors, boolean useForInlining) { + Invoke duplicatedInvoke = duplicateInvokeForInlining(graph, invoke, exceptionMerge, exceptionObjectPhi, useForInlining); + // TODO (ch) set probabilities + BeginNode calleeEntryNode = graph.add(predecessors > 1 ? new MergeNode() : new BeginNode()); + calleeEntryNode.setNext(duplicatedInvoke.node()); + + EndNode endNode = graph.add(new EndNode()); + // TODO (ch) set probability + duplicatedInvoke.setNext(endNode); + returnMerge.addEnd(endNode); + if (returnValuePhi != null) { + returnValuePhi.addInput(duplicatedInvoke.node()); + } + return calleeEntryNode; + } + + private static Invoke duplicateInvokeForInlining(StructuredGraph graph, Invoke invoke, MergeNode exceptionMerge, PhiNode exceptionObjectPhi, boolean useForInlining) { + Invoke result = (Invoke) invoke.node().copyWithInputs(); + result.setUseForInlining(useForInlining); + if (invoke instanceof InvokeWithExceptionNode) { + assert exceptionMerge != null && exceptionObjectPhi != null; + + InvokeWithExceptionNode invokeWithException = (InvokeWithExceptionNode) invoke; + BeginNode exceptionEdge = invokeWithException.exceptionEdge(); + ExceptionObjectNode exceptionObject = (ExceptionObjectNode) exceptionEdge.next(); + + BeginNode newExceptionEdge = (BeginNode) exceptionEdge.copyWithInputs(); + ExceptionObjectNode newExceptionObject = (ExceptionObjectNode) exceptionObject.copyWithInputs(); + newExceptionEdge.setNext(newExceptionObject); + + EndNode endNode = graph.add(new EndNode()); + newExceptionObject.setNext(endNode); + exceptionMerge.addEnd(endNode); + exceptionObjectPhi.addInput(newExceptionObject); + + ((InvokeWithExceptionNode) result).setExceptionEdge(newExceptionEdge); + } + return result; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(String.format("type-checked inlining of %d methods with %d type checks: ", concretes.size(), types.length)); + for (int i = 0; i < concretes.size(); i++) { + builder.append(CiUtil.format(" %H.%n(%p):%r", concretes.get(i))); + } + return builder.toString(); + } + + @Override + public boolean canDeopt() { + return true; + } + } + + + /** * Represents an inlining opportunity where the current class hierarchy leads to a monomorphic target method, * but for which an assumption has to be registered because of non-final classes. */ @@ -184,14 +428,15 @@ } @Override - public Node inline(StructuredGraph graph, GraalRuntime runtime, InliningCallback callback) { + public void inline(StructuredGraph graph, GraalRuntime runtime, InliningCallback callback) { if (Debug.isLogEnabled()) { String targetName = CiUtil.format("%H.%n(%p):%r", invoke.callTarget().targetMethod()); String concreteName = CiUtil.format("%H.%n(%p):%r", concrete); Debug.log("recording concrete method assumption: %s on receiver type %s -> %s", targetName, context, concreteName); } callback.recordConcreteMethodAssumption(invoke.callTarget().targetMethod(), context, concrete); - return super.inline(graph, runtime, callback); + + super.inline(graph, runtime, callback); } @Override @@ -248,35 +493,107 @@ } } // TODO (tw) fix this - if (assumptions == null) { - return null; + if (assumptions != null) { + RiResolvedMethod concrete = holder.uniqueConcreteMethod(callTarget.targetMethod()); + if (concrete != null) { + if (checkTargetConditions(concrete)) { + double weight = callback == null ? 0 : callback.inliningWeight(parent, concrete, invoke); + return new AssumptionInlineInfo(invoke, weight, level, holder, concrete); + } + return null; + } } - RiResolvedMethod concrete = holder.uniqueConcreteMethod(callTarget.targetMethod()); - if (concrete != null) { - if (checkTargetConditions(concrete)) { - double weight = callback == null ? 0 : callback.inliningWeight(parent, concrete, invoke); - return new AssumptionInlineInfo(invoke, weight, level, holder, concrete); + + // type check based inlining + RiProfilingInfo profilingInfo = parent.profilingInfo(); + RiTypeProfile typeProfile = profilingInfo.getTypeProfile(invoke.bci()); + if (typeProfile != null) { + RiResolvedType[] types = typeProfile.getTypes(); + double[] probabilities = typeProfile.getProbabilities(); + + if (types != null && probabilities != null && types.length > 0) { + assert types.length == probabilities.length : "length must match"; + double notRecordedTypeProbability = typeProfile.getNotRecordedProbability(); + if (types.length == 1 && notRecordedTypeProbability == 0) { + if (GraalOptions.InlineMonomorphicCalls) { + RiResolvedType type = types[0]; + RiResolvedMethod concrete = type.resolveMethodImpl(callTarget.targetMethod()); + if (concrete != null && checkTargetConditions(concrete)) { + double weight = callback == null ? 0 : callback.inliningWeight(parent, concrete, invoke); + return new TypeGuardInlineInfo(invoke, weight, level, concrete, type); + } + + Debug.log("not inlining %s because method can't be inlined", methodName(callTarget.targetMethod(), invoke)); + return null; + } else { + Debug.log("not inlining %s because GraalOptions.InlinePolymorphicCalls == false", methodName(callTarget.targetMethod(), invoke)); + return null; + } + } else { + if (GraalOptions.InlinePolymorphicCalls) { + // TODO (ch) inlining of multiple methods should work differently + // 1. check which methods can be inlined + // 2. for those methods, use weight and probability to compute which of them should be inlined + // 3. do the inlining + // a) all seen methods can be inlined -> do so and guard with deopt + // b) some methods can be inlined -> inline them and fall back to invocation if violated + // TODO (ch) sort types by probability + + // determine concrete methods and map type to specific method + ArrayList concreteMethods = new ArrayList<>(); + int[] typesToConcretes = new int[types.length]; + for (int i = 0; i < types.length; i++) { + RiResolvedMethod concrete = types[i].resolveMethodImpl(callTarget.targetMethod()); + + int index = concreteMethods.indexOf(concrete); + if (index < 0) { + index = concreteMethods.size(); + concreteMethods.add(concrete); + } + typesToConcretes[i] = index; + } + + double totalWeight = 0; + boolean canInline = true; + for (RiResolvedMethod concrete: concreteMethods) { + if (concrete == null || !checkTargetConditions(concrete)) { + canInline = false; + break; + } + totalWeight += callback == null ? 0 : callback.inliningWeight(parent, concrete, invoke); + } + + if (canInline) { + convertTypeToBranchProbabilities(probabilities, notRecordedTypeProbability); + return new MultiTypeGuardInlineInfo(invoke, totalWeight, level, concreteMethods, types, typesToConcretes, probabilities, notRecordedTypeProbability); + } else { + Debug.log("not inlining %s because it is a polymorphic method call and at least one invoked method cannot be inlined", methodName(callTarget.targetMethod(), invoke)); + return null; + } + } else { + Debug.log("not inlining %s because GraalOptions.InlineMonomorphicCalls == false", methodName(callTarget.targetMethod(), invoke)); + return null; + } + } } + + Debug.log("not inlining %s because no types/probabilities were recorded", methodName(callTarget.targetMethod(), invoke)); + return null; + } else { + Debug.log("not inlining %s because no type profile exists", methodName(callTarget.targetMethod(), invoke)); return null; } - RiTypeProfile profile = parent.typeProfile(invoke.bci()); - if (profile != null && profile.probabilities != null && profile.probabilities.length > 0 && profile.morphism == 1) { - if (GraalOptions.InlineWithTypeCheck) { - // type check and inlining... - concrete = profile.types[0].resolveMethodImpl(callTarget.targetMethod()); - if (concrete != null && checkTargetConditions(concrete)) { - double weight = callback == null ? 0 : callback.inliningWeight(parent, concrete, invoke); - return new TypeGuardInlineInfo(invoke, weight, level, concrete, profile.types[0], profile.probabilities[0]); - } - return null; - } else { - Debug.log("not inlining %s because GraalOptions.InlineWithTypeCheck == false", methodName(callTarget.targetMethod(), invoke)); - return null; - } - } else { - Debug.log("not inlining %s because no monomorphic receiver could be found", methodName(callTarget.targetMethod(), invoke)); - return null; + } + + private static void convertTypeToBranchProbabilities(double[] typeProbabilities, double notRecordedTypeProbability) { + // avoid branches with 0.0/1.0 probability + double total = Math.max(1E-10, notRecordedTypeProbability); + + for (int i = typeProbabilities.length - 1; i >= 0; i--) { + total += typeProbabilities[i]; + typeProbabilities[i] = typeProbabilities[i] / total; } + assert total > 0.99 && total < 1.01; } private static boolean checkInvokeConditions(Invoke invoke) { @@ -288,6 +605,10 @@ Debug.log("not inlining %s because the invoke is dead code", methodName(invoke.callTarget().targetMethod(), invoke)); return false; } + if (!invoke.useForInlining()) { + Debug.log("not inlining %s because invoke is marked to be not used for inlining", methodName(invoke.callTarget().targetMethod(), invoke)); + return false; + } return true; } @@ -323,7 +644,7 @@ * @param receiverNullCheck true if a null check needs to be generated for non-static inlinings, false if no such check is required * @return The node that represents the return value, or null for void methods and methods that have no non-exceptional exit. */ - public static Node inline(Invoke invoke, StructuredGraph inlineGraph, boolean receiverNullCheck) { + public static void inline(Invoke invoke, StructuredGraph inlineGraph, boolean receiverNullCheck) { NodeInputList parameters = invoke.callTarget().arguments(); StructuredGraph graph = (StructuredGraph) invoke.node().graph(); @@ -332,7 +653,6 @@ IdentityHashMap replacements = new IdentityHashMap<>(); ArrayList nodes = new ArrayList<>(); - ArrayList frameStates = new ArrayList<>(); ReturnNode returnNode = null; UnwindNode unwindNode = null; BeginNode entryPointNode = inlineGraph.start(); @@ -348,8 +668,6 @@ returnNode = (ReturnNode) node; } else if (node instanceof UnwindNode) { unwindNode = (UnwindNode) node; - } else if (node instanceof FrameState) { - frameStates.add(node); } } } @@ -358,11 +676,9 @@ assert invoke.node().predecessor() != null; Map duplicates = graph.addDuplicates(nodes, replacements); - FixedNode firstCFGNodeDuplicate = (FixedNode) duplicates.get(firstCFGNode); - MethodCallTargetNode callTarget = invoke.callTarget(); - if (!callTarget.isStatic() && receiverNullCheck && parameters.get(0).kind() == CiKind.Object && !parameters.get(0).stamp().nonNull()) { - graph.addBeforeFixed(invoke.node(), graph.add(new FixedGuardNode(graph.unique(new NullCheckNode(parameters.get(0), false))))); + if (receiverNullCheck) { + receiverNullCheck(invoke); } invoke.node().replaceAtPredecessors(firstCFGNodeDuplicate); @@ -375,9 +691,7 @@ ExceptionObjectNode obj = (ExceptionObjectNode) invokeWithException.exceptionEdge().next(); stateAtExceptionEdge = obj.stateAfter(); UnwindNode unwindDuplicate = (UnwindNode) duplicates.get(unwindNode); - for (Node usage : obj.usages().snapshot()) { - usage.replaceFirstInput(obj, unwindDuplicate.exception()); - } + obj.replaceAtUsages(unwindDuplicate.exception()); unwindDuplicate.clearInputs(); Node n = obj.next(); obj.setNext(null); @@ -404,6 +718,7 @@ } FrameState stateBefore = null; + FrameState outerFrameState = null; double invokeProbability = invoke.node().probability(); for (Node node : duplicates.values()) { if (GraalOptions.ProbabilityAnalysis) { @@ -428,6 +743,11 @@ } else { assert stateAtExceptionEdge == null; } + } else { + if (outerFrameState == null) { + outerFrameState = stateAfter.duplicateModified(invoke.bci(), stateAfter.rethrowException(), invoke.node().kind()); + } + frameState.setOuterFrameState(outerFrameState); } } } @@ -439,13 +759,7 @@ } else { returnValue = duplicates.get(returnNode.result()); } - for (Node usage : invoke.node().usages().snapshot()) { - if (returnNode.result() instanceof LocalNode) { - usage.replaceFirstInput(invoke.node(), returnValue); - } else { - usage.replaceFirstInput(invoke.node(), returnValue); - } - } + invoke.node().replaceAtUsages(returnValue); Node returnDuplicate = duplicates.get(returnNode); returnDuplicate.clearInputs(); Node n = invoke.next(); @@ -465,20 +779,18 @@ invoke.node().replaceAtUsages(null); GraphUtil.killCFG(invoke.node()); - // adjust all frame states that were copied - if (frameStates.size() > 0) { - FrameState outerFrameState = stateAfter.duplicateModified(invoke.bci(), stateAfter.rethrowException(), invoke.node().kind()); - for (Node node : frameStates) { - FrameState frameState = (FrameState) duplicates.get(node); - if (!frameState.isDeleted()) { - frameState.setOuterFrameState(outerFrameState); - } - } - } - if (stateAfter.usages().isEmpty()) { stateAfter.safeDelete(); } - return returnValue; + } + + public static void receiverNullCheck(Invoke invoke) { + MethodCallTargetNode callTarget = invoke.callTarget(); + StructuredGraph graph = (StructuredGraph) invoke.graph(); + NodeInputList parameters = callTarget.arguments(); + ValueNode firstParam = parameters.size() <= 0 ? null : parameters.get(0); + if (!callTarget.isStatic() && firstParam.kind() == CiKind.Object && !firstParam.stamp().nonNull()) { + graph.addBeforeFixed(invoke.node(), graph.add(new FixedGuardNode(graph.unique(new NullCheckNode(firstParam, false))))); + } } } diff -r ab7c258e1cef -r 9d640941c9c7 graal/com.oracle.max.graal.debug/src/com/oracle/max/graal/debug/Debug.java --- a/graal/com.oracle.max.graal.debug/src/com/oracle/max/graal/debug/Debug.java Sun Feb 05 05:40:36 2012 +0100 +++ b/graal/com.oracle.max.graal.debug/src/com/oracle/max/graal/debug/Debug.java Tue Feb 07 21:09:31 2012 +0100 @@ -26,8 +26,8 @@ import java.util.*; import java.util.concurrent.*; +public class Debug { -public class Debug { private static boolean ENABLED = false; public static void enable() { @@ -73,7 +73,7 @@ public static void scope(String name, Object context, Runnable runnable) { if (ENABLED) { - DebugScope.getInstance().scope(name, runnable, null, false, new Object[]{context}); + DebugScope.getInstance().scope(name, runnable, null, false, new Object[] {context}); } else { runnable.run(); } @@ -89,7 +89,7 @@ public static T scope(String name, Object context, Callable callable) { if (ENABLED) { - return DebugScope.getInstance().scope(name, null, callable, false, new Object[]{context}); + return DebugScope.getInstance().scope(name, null, callable, false, new Object[] {context}); } else { return DebugScope.call(callable); } @@ -130,6 +130,18 @@ } } + @SuppressWarnings("unchecked") + public static T contextLookup(Class clazz) { + if (ENABLED) { + for (Object o : context()) { + if (clazz.isInstance(o)) { + return ((T) o); + } + } + } + return null; + } + public static DebugMetric metric(String name) { if (ENABLED) { return new MetricImpl(name); @@ -168,8 +180,8 @@ } @Override - public RuntimeException interceptException(RuntimeException e) { - return e; + public RuntimeException interceptException(Throwable e) { + return null; } @Override @@ -180,8 +192,12 @@ } private static final DebugMetric VOID_METRIC = new DebugMetric() { - public void increment() { } - public void add(int value) { } + + public void increment() { + } + + public void add(int value) { + } }; public static DebugTimer timer(String name) { @@ -193,6 +209,9 @@ } private static final DebugTimer VOID_TIMER = new DebugTimer() { - public TimerCloseable start() { return TimerImpl.VOID_CLOSEABLE; } + + public TimerCloseable start() { + return TimerImpl.VOID_CLOSEABLE; + } }; } diff -r ab7c258e1cef -r 9d640941c9c7 graal/com.oracle.max.graal.debug/src/com/oracle/max/graal/debug/DebugConfig.java --- a/graal/com.oracle.max.graal.debug/src/com/oracle/max/graal/debug/DebugConfig.java Sun Feb 05 05:40:36 2012 +0100 +++ b/graal/com.oracle.max.graal.debug/src/com/oracle/max/graal/debug/DebugConfig.java Tue Feb 07 21:09:31 2012 +0100 @@ -30,6 +30,6 @@ boolean isMeterEnabled(); boolean isDumpEnabled(); boolean isTimeEnabled(); - RuntimeException interceptException(RuntimeException e); + RuntimeException interceptException(Throwable e); Collection dumpHandlers(); } diff -r ab7c258e1cef -r 9d640941c9c7 graal/com.oracle.max.graal.debug/src/com/oracle/max/graal/debug/DebugDumpScope.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.max.graal.debug/src/com/oracle/max/graal/debug/DebugDumpScope.java Tue Feb 07 21:09:31 2012 +0100 @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.max.graal.debug; + +public final class DebugDumpScope { + + private final String name; + + public DebugDumpScope(String name) { + this.name = name; + } + + public String getName() { + return name; + } +} diff -r ab7c258e1cef -r 9d640941c9c7 graal/com.oracle.max.graal.debug/src/com/oracle/max/graal/debug/internal/DebugScope.java --- a/graal/com.oracle.max.graal.debug/src/com/oracle/max/graal/debug/internal/DebugScope.java Sun Feb 05 05:40:36 2012 +0100 +++ b/graal/com.oracle.max.graal.debug/src/com/oracle/max/graal/debug/internal/DebugScope.java Tue Feb 07 21:09:31 2012 +0100 @@ -32,7 +32,7 @@ private static ThreadLocal instanceTL = new ThreadLocal<>(); private static ThreadLocal configTL = new ThreadLocal<>(); - private static ThreadLocal lastExceptionThrownTL = new ThreadLocal<>(); + private static ThreadLocal lastExceptionThrownTL = new ThreadLocal<>(); private static DebugTimer scopeTime = Debug.timer("ScopeTime"); private static DebugMetric scopeCount = Debug.metric("ScopeCount"); @@ -142,13 +142,18 @@ if (callable != null) { return call(callable); } - } catch (RuntimeException e) { + } catch (Throwable e) { if (e == lastExceptionThrownTL.get()) { throw e; } else { RuntimeException newException = interceptException(e); - lastExceptionThrownTL.set(newException); - throw newException; + if (newException == null) { + lastExceptionThrownTL.set(e); + throw e; + } else { + lastExceptionThrownTL.set(newException); + throw newException; + } } } return null; @@ -173,7 +178,7 @@ context = null; } - private RuntimeException interceptException(final RuntimeException e) { + private RuntimeException interceptException(final Throwable e) { final DebugConfig config = getConfig(); if (config != null) { return scope("InterceptException", null, new Callable() { @@ -183,12 +188,12 @@ try { return config.interceptException(e); } catch (Throwable t) { - return e; + return new RuntimeException("Exception while intercepting exception", e); } } }, false, new Object[] {e}); } - return e; + return null; } private DebugValueMap getValueMap() { diff -r ab7c258e1cef -r 9d640941c9c7 graal/com.oracle.max.graal.hotspot/src/com/oracle/max/graal/hotspot/HotSpotDebugConfig.java --- a/graal/com.oracle.max.graal.hotspot/src/com/oracle/max/graal/hotspot/HotSpotDebugConfig.java Sun Feb 05 05:40:36 2012 +0100 +++ b/graal/com.oracle.max.graal.hotspot/src/com/oracle/max/graal/hotspot/HotSpotDebugConfig.java Tue Feb 07 21:09:31 2012 +0100 @@ -120,9 +120,9 @@ } @Override - public RuntimeException interceptException(RuntimeException e) { + public RuntimeException interceptException(Throwable e) { if (e instanceof CiBailout) { - return e; + return null; } Debug.setConfig(Debug.fixedConfig(true, true, false, false, dumpHandlers)); // sync "Exception occured in scope: " with mx/sanitycheck.py::Test.__init__ @@ -135,7 +135,7 @@ Debug.dump(o, "Exception graph"); } } - return e; + return null; } @Override diff -r ab7c258e1cef -r 9d640941c9c7 graal/com.oracle.max.graal.hotspot/src/com/oracle/max/graal/hotspot/HotSpotVMConfig.java --- a/graal/com.oracle.max.graal.hotspot/src/com/oracle/max/graal/hotspot/HotSpotVMConfig.java Sun Feb 05 05:40:36 2012 +0100 +++ b/graal/com.oracle.max.graal.hotspot/src/com/oracle/max/graal/hotspot/HotSpotVMConfig.java Tue Feb 07 21:09:31 2012 +0100 @@ -30,7 +30,7 @@ public final class HotSpotVMConfig extends CompilerObject { /** - * + * */ private static final long serialVersionUID = -4744897993263044184L; @@ -70,8 +70,20 @@ public int runtimeCallStackSize; public int klassModifierFlagsOffset; public int klassOopOffset; + public int graalMirrorKlassOffset; public int nmethodEntryOffset; + // methodData information + public int methodDataOopDataOffset; + public int dataLayoutHeaderSize; + public int dataLayoutTagOffset; + public int dataLayoutFlagsOffset; + public int dataLayoutBCIOffset; + public int dataLayoutCellsOffset; + public int dataLayoutCellSize; + public int bciProfileWidth; + public int typeProfileWidth; + // runtime stubs public long debugStub; public long instanceofStub; diff -r ab7c258e1cef -r 9d640941c9c7 graal/com.oracle.max.graal.hotspot/src/com/oracle/max/graal/hotspot/bridge/CompilerToVM.java --- a/graal/com.oracle.max.graal.hotspot/src/com/oracle/max/graal/hotspot/bridge/CompilerToVM.java Sun Feb 05 05:40:36 2012 +0100 +++ b/graal/com.oracle.max.graal.hotspot/src/com/oracle/max/graal/hotspot/bridge/CompilerToVM.java Tue Feb 07 21:09:31 2012 +0100 @@ -49,13 +49,9 @@ int RiMethod_invocationCount(HotSpotMethodResolved method); - int RiMethod_exceptionProbability(HotSpotMethodResolved method, int bci); - - RiTypeProfile RiMethod_typeProfile(HotSpotMethodResolved method, int bci); + HotSpotMethodData RiMethod_methodData(HotSpotMethodResolved method); - double RiMethod_branchProbability(HotSpotMethodResolved method, int bci); - - double[] RiMethod_switchProbability(HotSpotMethodResolved method, int bci); + boolean HotSpotMethodData_isMature(HotSpotMethodData methodData); RiType RiSignature_lookupType(String returnType, HotSpotTypeResolved accessingClass); diff -r ab7c258e1cef -r 9d640941c9c7 graal/com.oracle.max.graal.hotspot/src/com/oracle/max/graal/hotspot/bridge/CompilerToVMImpl.java --- a/graal/com.oracle.max.graal.hotspot/src/com/oracle/max/graal/hotspot/bridge/CompilerToVMImpl.java Sun Feb 05 05:40:36 2012 +0100 +++ b/graal/com.oracle.max.graal.hotspot/src/com/oracle/max/graal/hotspot/bridge/CompilerToVMImpl.java Tue Feb 07 21:09:31 2012 +0100 @@ -60,18 +60,6 @@ public native int RiMethod_invocationCount(HotSpotMethodResolved method); @Override - public native int RiMethod_exceptionProbability(HotSpotMethodResolved method, int bci); - - @Override - public native RiTypeProfile RiMethod_typeProfile(HotSpotMethodResolved method, int bci); - - @Override - public native double RiMethod_branchProbability(HotSpotMethodResolved method, int bci); - - @Override - public native double[] RiMethod_switchProbability(HotSpotMethodResolved method, int bci); - - @Override public native RiType RiSignature_lookupType(String returnType, HotSpotTypeResolved accessingClass); @Override @@ -123,6 +111,12 @@ public native boolean RiType_isInitialized(HotSpotTypeResolved klass); @Override + public native HotSpotMethodData RiMethod_methodData(HotSpotMethodResolved method); + + @Override + public native boolean HotSpotMethodData_isMature(HotSpotMethodData methodData); + + @Override public native RiType getType(Class javaClass); @Override diff -r ab7c258e1cef -r 9d640941c9c7 graal/com.oracle.max.graal.hotspot/src/com/oracle/max/graal/hotspot/bridge/VMToCompilerImpl.java --- a/graal/com.oracle.max.graal.hotspot/src/com/oracle/max/graal/hotspot/bridge/VMToCompilerImpl.java Sun Feb 05 05:40:36 2012 +0100 +++ b/graal/com.oracle.max.graal.hotspot/src/com/oracle/max/graal/hotspot/bridge/VMToCompilerImpl.java Tue Feb 07 21:09:31 2012 +0100 @@ -239,19 +239,13 @@ // to add something to its own queue. return; } - } else { - if (GraalOptions.Debug) { - Debug.enable(); - HotSpotDebugConfig hotspotDebugConfig = new HotSpotDebugConfig(GraalOptions.Log, GraalOptions.Meter, GraalOptions.Time, GraalOptions.Dump, GraalOptions.MethodFilter); - Debug.setConfig(hotspotDebugConfig); - } } Runnable runnable = new Runnable() { public void run() { try { - PhasePlan plan = getDefaultPhasePlan(); + final PhasePlan plan = getDefaultPhasePlan(); GraphBuilderPhase graphBuilderPhase = new GraphBuilderPhase(compiler.getRuntime()); plan.addPhase(PhasePosition.AFTER_PARSING, graphBuilderPhase); long startTime = 0; @@ -265,7 +259,12 @@ CiTargetMethod result = null; TTY.Filter filter = new TTY.Filter(GraalOptions.PrintFilter, method); try { - result = compiler.getCompiler().compileMethod(method, -1, plan); + result = Debug.scope("Compiling", method, new Callable() { + @Override + public CiTargetMethod call() throws Exception { + return compiler.getCompiler().compileMethod(method, -1, plan); + } + }); } finally { filter.remove(); if (printCompilation) { @@ -276,6 +275,7 @@ } compiler.getRuntime().installMethod(method, result); } catch (CiBailout bailout) { + Debug.metric("Bailouts").increment(); if (GraalOptions.ExitVMOnBailout) { bailout.printStackTrace(TTY.cachedOut); System.exit(-1); @@ -391,7 +391,9 @@ private PhasePlan getDefaultPhasePlan() { PhasePlan phasePlan = new PhasePlan(); - phasePlan.addPhase(PhasePosition.HIGH_LEVEL, intrinsifyArrayCopy); + if (GraalOptions.Intrinsify) { + phasePlan.addPhase(PhasePosition.HIGH_LEVEL, intrinsifyArrayCopy); + } return phasePlan; } } diff -r ab7c258e1cef -r 9d640941c9c7 graal/com.oracle.max.graal.hotspot/src/com/oracle/max/graal/hotspot/ri/HotSpotMethodData.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.max.graal.hotspot/src/com/oracle/max/graal/hotspot/ri/HotSpotMethodData.java Tue Feb 07 21:09:31 2012 +0100 @@ -0,0 +1,590 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.max.graal.hotspot.ri; + +import java.util.*; + +import sun.misc.*; + +import com.oracle.max.cri.ri.*; +import com.oracle.max.graal.hotspot.*; +import com.oracle.max.graal.hotspot.Compiler; + + +public final class HotSpotMethodData extends CompilerObject { + + /** + * + */ + private static final long serialVersionUID = -8873133496591225071L; + + static { + config = CompilerImpl.getInstance().getConfig(); + } + + // TODO (ch) use same logic as in NodeClass? + private static final Unsafe unsafe = Unsafe.getUnsafe(); + private static final HotSpotMethodDataAccessor NO_DATA_ACCESSOR = new NoMethodData(); + private static final HotSpotVMConfig config; + // sorted by tag + private static final HotSpotMethodDataAccessor[] PROFILE_DATA_ACCESSORS = { + null, new BitData(), new CounterData(), new JumpData(), + new TypeCheckData(), new VirtualCallData(), new RetData(), + new BranchData(), new MultiBranchData(), new ArgInfoData() + }; + + private Object hotspotMirror; + private int normalDataSize; + private int extraDataSize; + private boolean mature; + + private HotSpotMethodData(Compiler compiler) { + super(compiler); + throw new IllegalStateException("this constructor is never actually called, because the objects are allocated from within the VM"); + } + + public boolean hasNormalData() { + return normalDataSize > 0; + } + + public boolean hasExtraData() { + return extraDataSize > 0; + } + + public int getExtraDataBeginOffset() { + return normalDataSize; + } + + public boolean isMature() { + // TODO (ch) maturity of profiling information is an issue in general. Not all optimizations require mature data as long as the code + // does deoptimize/recompile on violations (might decrease startup and increase peak performance). + // Maturity is currently used on several levels: + // 1) whole method data + // 2) individual branch/switch profiling data + // 3) MatureInvocationCount for eliminating exception edges + if (!mature) { + mature = compiler.getVMEntries().HotSpotMethodData_isMature(this); + } + return mature; + } + + public boolean isWithin(int position) { + return position >= 0 && position < normalDataSize + extraDataSize; + } + + public HotSpotMethodDataAccessor getNormalData(int position) { + if (position >= normalDataSize) { + return null; + } + + HotSpotMethodDataAccessor result = getData(position); + assert result != null : "NO_DATA tag is not allowed"; + return result; + } + + public HotSpotMethodDataAccessor getExtraData(int position) { + if (position >= extraDataSize) { + return null; + } + return getData(position); + } + + public static HotSpotMethodDataAccessor getNoMethodData() { + return NO_DATA_ACCESSOR; + } + + private HotSpotMethodDataAccessor getData(int position) { + assert position >= 0 : "out of bounds"; + int tag = AbstractMethodData.readTag(this, position); + assert tag >= 0 && tag < PROFILE_DATA_ACCESSORS.length : "illegal tag"; + return PROFILE_DATA_ACCESSORS[tag]; + } + + private int readUnsignedByte(int position, int offsetInBytes) { + long fullOffsetInBytes = computeFullOffset(position, offsetInBytes); + return unsafe.getByte(hotspotMirror, fullOffsetInBytes) & 0xFF; + } + + private int readUnsignedShort(int position, int offsetInBytes) { + long fullOffsetInBytes = computeFullOffset(position, offsetInBytes); + return unsafe.getShort(hotspotMirror, fullOffsetInBytes) & 0xFFFF; + } + + private long readUnsignedInt(int position, int offsetInBytes) { + long fullOffsetInBytes = computeFullOffset(position, offsetInBytes); + return unsafe.getInt(hotspotMirror, fullOffsetInBytes) & 0xFFFFFFFFL; + } + + private int readUnsignedIntAsSignedInt(int position, int offsetInBytes) { + long value = readUnsignedInt(position, offsetInBytes); + return truncateLongToInt(value); + } + + private int readInt(int position, int offsetInBytes) { + long fullOffsetInBytes = computeFullOffset(position, offsetInBytes); + return unsafe.getInt(hotspotMirror, fullOffsetInBytes); + } + + private Object readObject(int position, int offsetInBytes) { + long fullOffsetInBytes = computeFullOffset(position, offsetInBytes); + return unsafe.getObject(hotspotMirror, fullOffsetInBytes); + } + + private static int truncateLongToInt(long value) { + return value > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) value; + } + + private static int computeFullOffset(int position, int offsetInBytes) { + return config.methodDataOopDataOffset + position + offsetInBytes; + } + + private static int cellIndexToOffset(int cells) { + return config.dataLayoutHeaderSize + cellsToBytes(cells); + } + + private static int cellsToBytes(int cells) { + return cells * config.dataLayoutCellSize; + } + + private abstract static class AbstractMethodData implements HotSpotMethodDataAccessor { + private static final int EXCEPTIONS_MASK = 0x80; + + private final int tag; + private final int staticSize; + + protected AbstractMethodData(int tag, int staticSize) { + this.tag = tag; + this.staticSize = staticSize; + } + + public int getTag() { + return tag; + } + + public static int readTag(HotSpotMethodData data, int position) { + return data.readUnsignedByte(position, config.dataLayoutTagOffset); + } + + @Override + public int getBCI(HotSpotMethodData data, int position) { + return data.readUnsignedShort(position, config.dataLayoutBCIOffset); + } + + @Override + public int getSize(HotSpotMethodData data, int position) { + return staticSize + getDynamicSize(data, position); + } + + @Override + public boolean getExceptionSeen(HotSpotMethodData data, int position) { + return (getFlags(data, position) & EXCEPTIONS_MASK) != 0; + } + + @Override + public RiTypeProfile getTypeProfile(HotSpotMethodData data, int position) { + return null; + } + + @Override + public double getBranchTakenProbability(HotSpotMethodData data, int position) { + return -1; + } + + @Override + public double[] getSwitchProbabilities(HotSpotMethodData data, int position) { + return null; + } + + @Override + public int getExecutionCount(HotSpotMethodData data, int position) { + return -1; + } + + protected int getFlags(HotSpotMethodData data, int position) { + return data.readUnsignedByte(position, config.dataLayoutFlagsOffset); + } + + protected int getDynamicSize(@SuppressWarnings("unused") HotSpotMethodData data, @SuppressWarnings("unused") int position) { + return 0; + } + } + + private static class NoMethodData extends AbstractMethodData { + private static final int NO_DATA_TAG = 0; + private static final int NO_DATA_SIZE = cellIndexToOffset(0); + + protected NoMethodData() { + super(NO_DATA_TAG, NO_DATA_SIZE); + } + + @Override + public int getBCI(HotSpotMethodData data, int position) { + return -1; + } + + + @Override + public boolean getExceptionSeen(HotSpotMethodData data, int position) { + return false; + } + } + + private static class BitData extends AbstractMethodData { + private static final int BIT_DATA_TAG = 1; + private static final int BIT_DATA_SIZE = cellIndexToOffset(0); + private static final int BIT_DATA_NULL_SEEN_FLAG = 0x01; + + private BitData() { + super(BIT_DATA_TAG, BIT_DATA_SIZE); + } + + protected BitData(int tag, int staticSize) { + super(tag, staticSize); + } + + @SuppressWarnings("unused") + public boolean getNullSeen(HotSpotMethodData data, int position) { + return (getFlags(data, position) & BIT_DATA_NULL_SEEN_FLAG) != 0; + } + } + + private static class CounterData extends BitData { + private static final int COUNTER_DATA_TAG = 2; + private static final int COUNTER_DATA_SIZE = cellIndexToOffset(1); + private static final int COUNTER_DATA_COUNT_OFFSET = cellIndexToOffset(0); + + public CounterData() { + super(COUNTER_DATA_TAG, COUNTER_DATA_SIZE); + } + + protected CounterData(int tag, int staticSize) { + super(tag, staticSize); + } + + @Override + public int getExecutionCount(HotSpotMethodData data, int position) { + return getCounterValue(data, position); + } + + protected int getCounterValue(HotSpotMethodData data, int position) { + return data.readUnsignedIntAsSignedInt(position, COUNTER_DATA_COUNT_OFFSET); + } + } + + private static class JumpData extends AbstractMethodData { + private static final int JUMP_DATA_TAG = 3; + private static final int JUMP_DATA_SIZE = cellIndexToOffset(2); + protected static final int TAKEN_COUNT_OFFSET = cellIndexToOffset(0); + protected static final int TAKEN_DISPLACEMENT_OFFSET = cellIndexToOffset(1); + + public JumpData() { + super(JUMP_DATA_TAG, JUMP_DATA_SIZE); + } + + protected JumpData(int tag, int staticSize) { + super(tag, staticSize); + } + + @Override + public double getBranchTakenProbability(HotSpotMethodData data, int position) { + return 1; + } + + @Override + public int getExecutionCount(HotSpotMethodData data, int position) { + return data.readUnsignedIntAsSignedInt(position, TAKEN_COUNT_OFFSET); + } + + @SuppressWarnings("unused") + public int getTakenDisplacement(HotSpotMethodData data, int position) { + return data.readInt(position, TAKEN_DISPLACEMENT_OFFSET); + } + } + + private abstract static class AbstractTypeData extends CounterData { + private static final int RECEIVER_TYPE_DATA_ROW_SIZE = cellsToBytes(2); + private static final int RECEIVER_TYPE_DATA_SIZE = cellIndexToOffset(1) + RECEIVER_TYPE_DATA_ROW_SIZE * config.typeProfileWidth; + private static final int RECEIVER_TYPE_DATA_FIRST_RECEIVER_OFFSET = cellIndexToOffset(1); + private static final int RECEIVER_TYPE_DATA_FIRST_COUNT_OFFSET = cellIndexToOffset(2); + + protected AbstractTypeData(int tag) { + super(tag, RECEIVER_TYPE_DATA_SIZE); + } + + @Override + public RiTypeProfile getTypeProfile(HotSpotMethodData data, int position) { + int typeProfileWidth = config.typeProfileWidth; + + RiResolvedType[] sparseTypes = new RiResolvedType[typeProfileWidth]; + double[] counts = new double[typeProfileWidth]; + long totalCount = 0; + int entries = 0; + + for (int i = 0; i < typeProfileWidth; i++) { + Object receiverKlassOop = data.readObject(position, getReceiverOffset(i)); + if (receiverKlassOop != null) { + Object graalMirror = unsafe.getObject(receiverKlassOop, (long) config.graalMirrorKlassOffset); + if (graalMirror == null) { + Class javaClass = (Class) unsafe.getObject(receiverKlassOop, (long) config.classMirrorOffset); + graalMirror = CompilerImpl.getInstance().getVMEntries().getType(javaClass); + assert graalMirror != null : "must not return null"; + } + sparseTypes[entries] = (RiResolvedType) graalMirror; + + long count = data.readUnsignedInt(position, getCountOffset(i)); + totalCount += count; + counts[entries] = count; + + entries++; + } + } + + totalCount += getTypesNotRecordedExecutionCount(data, position); + return createRiTypeProfile(sparseTypes, counts, totalCount, entries); + } + + protected abstract long getTypesNotRecordedExecutionCount(HotSpotMethodData data, int position); + + private static RiTypeProfile createRiTypeProfile(RiResolvedType[] sparseTypes, double[] counts, long totalCount, int entries) { + RiResolvedType[] types; + double[] probabilities; + + if (entries <= 0) { + return null; + } else if (entries < sparseTypes.length) { + types = Arrays.copyOf(sparseTypes, entries); + probabilities = new double[entries]; + } else { + types = sparseTypes; + probabilities = counts; + } + + double totalProbability = 0.0; + for (int i = 0; i < entries; i++) { + double p = counts[i] / totalCount; + probabilities[i] = p; + totalProbability += p; + } + + double notRecordedTypeProbability = entries < config.typeProfileWidth ? 0.0 : Math.min(1.0, Math.max(0.0, 1.0 - totalProbability)); + return new RiTypeProfile(types, notRecordedTypeProbability, probabilities); + } + + private static int getReceiverOffset(int row) { + return RECEIVER_TYPE_DATA_FIRST_RECEIVER_OFFSET + row * RECEIVER_TYPE_DATA_ROW_SIZE; + } + + protected static int getCountOffset(int row) { + return RECEIVER_TYPE_DATA_FIRST_COUNT_OFFSET + row * RECEIVER_TYPE_DATA_ROW_SIZE; + } + } + + private static class TypeCheckData extends AbstractTypeData { + private static final int RECEIVER_TYPE_DATA_TAG = 4; + + public TypeCheckData() { + super(RECEIVER_TYPE_DATA_TAG); + } + + @Override + public int getExecutionCount(HotSpotMethodData data, int position) { + return -1; + } + + @Override + protected long getTypesNotRecordedExecutionCount(HotSpotMethodData data, int position) { + // TODO (ch) if types do not fit, profiling is skipped for typechecks + return 0; + } + } + + private static class VirtualCallData extends AbstractTypeData { + private static final int VIRTUAL_CALL_DATA_TAG = 5; + + public VirtualCallData() { + super(VIRTUAL_CALL_DATA_TAG); + } + + @Override + public int getExecutionCount(HotSpotMethodData data, int position) { + int typeProfileWidth = config.typeProfileWidth; + + long total = 0; + for (int i = 0; i < typeProfileWidth; i++) { + total += data.readUnsignedInt(position, getCountOffset(i)); + } + + total += getCounterValue(data, position); + return truncateLongToInt(total); + } + + @Override + protected long getTypesNotRecordedExecutionCount(HotSpotMethodData data, int position) { + return getCounterValue(data, position); + } + } + + private static class RetData extends CounterData { + private static final int RET_DATA_TAG = 6; + private static final int RET_DATA_ROW_SIZE = cellsToBytes(3); + private static final int RET_DATA_SIZE = cellIndexToOffset(1) + RET_DATA_ROW_SIZE * config.bciProfileWidth; + + public RetData() { + super(RET_DATA_TAG, RET_DATA_SIZE); + } + } + + private static class BranchData extends JumpData { + private static final int BRANCH_DATA_TAG = 7; + private static final int BRANCH_DATA_SIZE = cellIndexToOffset(3); + private static final int NOT_TAKEN_COUNT_OFFSET = cellIndexToOffset(2); + private static final int BRANCH_DATA_MATURE_COUNT = 40; + + public BranchData() { + super(BRANCH_DATA_TAG, BRANCH_DATA_SIZE); + } + + @Override + public double getBranchTakenProbability(HotSpotMethodData data, int position) { + long takenCount = data.readUnsignedInt(position, TAKEN_COUNT_OFFSET); + long notTakenCount = data.readUnsignedInt(position, NOT_TAKEN_COUNT_OFFSET); + long total = takenCount + notTakenCount; + + if (total < BRANCH_DATA_MATURE_COUNT) { + return -1; + } else { + return takenCount / (double) total; + } + } + + @Override + public int getExecutionCount(HotSpotMethodData data, int position) { + long count = data.readUnsignedInt(position, TAKEN_COUNT_OFFSET) + data.readUnsignedInt(position, NOT_TAKEN_COUNT_OFFSET); + return truncateLongToInt(count); + } + } + + private static class ArrayData extends AbstractMethodData { + private static final int ARRAY_DATA_LENGTH_OFFSET = cellIndexToOffset(0); + protected static final int ARRAY_DATA_START_OFFSET = cellIndexToOffset(1); + + public ArrayData(int tag, int staticSize) { + super(tag, staticSize); + } + + @Override + protected int getDynamicSize(HotSpotMethodData data, int position) { + return cellsToBytes(getLength(data, position)); + } + + protected static int getLength(HotSpotMethodData data, int position) { + return data.readInt(position, ARRAY_DATA_LENGTH_OFFSET); + } + } + + private static class MultiBranchData extends ArrayData { + private static final int MULTI_BRANCH_DATA_TAG = 8; + private static final int MULTI_BRANCH_DATA_SIZE = cellIndexToOffset(1); + private static final int MULTI_BRANCH_DATA_ROW_SIZE_IN_CELLS = 2; + private static final int MULTI_BRANCH_DATA_ROW_SIZE = cellsToBytes(MULTI_BRANCH_DATA_ROW_SIZE_IN_CELLS); + private static final int MULTI_BRANCH_DATA_FIRST_COUNT_OFFSET = ARRAY_DATA_START_OFFSET + cellsToBytes(0); + private static final int MULTI_BRANCH_DATA_FIRST_DISPLACEMENT_OFFSET = ARRAY_DATA_START_OFFSET + cellsToBytes(1); + + public MultiBranchData() { + super(MULTI_BRANCH_DATA_TAG, MULTI_BRANCH_DATA_SIZE); + } + + @Override + public double[] getSwitchProbabilities(HotSpotMethodData data, int position) { + int arrayLength = getLength(data, position); + assert arrayLength > 0 : "switch must have at least the default case"; + assert arrayLength % MULTI_BRANCH_DATA_ROW_SIZE_IN_CELLS == 0 : "array must have full rows"; + + int length = arrayLength / MULTI_BRANCH_DATA_ROW_SIZE_IN_CELLS; + long totalCount = 0; + double[] result = new double[length]; + + // default case is first in HotSpot but last for the compiler + long count = readCount(data, position, 0); + totalCount += count; + result[length - 1] = count; + + for (int i = 1; i < length; i++) { + count = readCount(data, position, i); + totalCount += count; + result[i - 1] = count; + } + + if (totalCount < 10 * (length + 2)) { + return null; + } else { + for (int i = 0; i < length; i++) { + result[i] = result[i] / totalCount; + } + return result; + } + } + + private static long readCount(HotSpotMethodData data, int position, int i) { + int offset; + long count; + offset = getCountOffset(i); + count = data.readUnsignedInt(position, offset); + return count; + } + + @Override + public int getExecutionCount(HotSpotMethodData data, int position) { + int arrayLength = getLength(data, position); + assert arrayLength > 0 : "switch must have at least the default case"; + assert arrayLength % MULTI_BRANCH_DATA_ROW_SIZE_IN_CELLS == 0 : "array must have full rows"; + + int length = arrayLength / MULTI_BRANCH_DATA_ROW_SIZE_IN_CELLS; + long totalCount = 0; + for (int i = 0; i < length; i++) { + int offset = getCountOffset(i); + totalCount += data.readUnsignedInt(position, offset); + } + + return truncateLongToInt(totalCount); + } + + private static int getCountOffset(int index) { + return MULTI_BRANCH_DATA_FIRST_COUNT_OFFSET + index * MULTI_BRANCH_DATA_ROW_SIZE; + } + + @SuppressWarnings("unused") + private static int getDisplacementOffset(int index) { + return MULTI_BRANCH_DATA_FIRST_DISPLACEMENT_OFFSET + index * MULTI_BRANCH_DATA_ROW_SIZE; + } + } + + private static class ArgInfoData extends ArrayData { + private static final int ARG_INFO_DATA_TAG = 9; + private static final int ARG_INFO_DATA_SIZE = cellIndexToOffset(1); + + public ArgInfoData() { + super(ARG_INFO_DATA_TAG, ARG_INFO_DATA_SIZE); + } + } +} diff -r ab7c258e1cef -r 9d640941c9c7 graal/com.oracle.max.graal.hotspot/src/com/oracle/max/graal/hotspot/ri/HotSpotMethodDataAccessor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.max.graal.hotspot/src/com/oracle/max/graal/hotspot/ri/HotSpotMethodDataAccessor.java Tue Feb 07 21:09:31 2012 +0100 @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.max.graal.hotspot.ri; + +import com.oracle.max.cri.ri.*; + +/** + * Interface for accessor objects that encapsulate the logic for accessing the different kinds of data in a HotSpot methodDataOop. + * This interface is similar to the interface {@link RiProfilingInfo}, but most methods require a MethodDataObject and the + * exact position within the methodData. + */ +public interface HotSpotMethodDataAccessor { + /** + * Returns the tag stored in the LayoutData header. + * @return An integer >= 0 or -1 if not supported. + */ + int getTag(); + + /** + * Returns the BCI stored in the LayoutData header. + * @return An integer >= 0 and <= Short.MAX_VALUE, or -1 if not supported. + */ + int getBCI(HotSpotMethodData data, int position); + + /** + * Computes the size for the specific data at the given position. + * @return An integer > 0. + */ + int getSize(HotSpotMethodData data, int position); + + RiTypeProfile getTypeProfile(HotSpotMethodData data, int position); + double getBranchTakenProbability(HotSpotMethodData data, int position); + double[] getSwitchProbabilities(HotSpotMethodData data, int position); + boolean getExceptionSeen(HotSpotMethodData data, int position); + int getExecutionCount(HotSpotMethodData data, int position); +} diff -r ab7c258e1cef -r 9d640941c9c7 graal/com.oracle.max.graal.hotspot/src/com/oracle/max/graal/hotspot/ri/HotSpotMethodResolvedImpl.java --- a/graal/com.oracle.max.graal.hotspot/src/com/oracle/max/graal/hotspot/ri/HotSpotMethodResolvedImpl.java Sun Feb 05 05:40:36 2012 +0100 +++ b/graal/com.oracle.max.graal.hotspot/src/com/oracle/max/graal/hotspot/ri/HotSpotMethodResolvedImpl.java Tue Feb 07 21:09:31 2012 +0100 @@ -55,6 +55,7 @@ private Boolean hasBalancedMonitors; private Map compilerStorage; private RiResolvedType holder; + private HotSpotMethodData methodData; private byte[] code; private boolean canBeInlined; private CiGenericCallback callback; @@ -187,24 +188,22 @@ return null; } + @Override public int invocationCount() { return compiler.getVMEntries().RiMethod_invocationCount(this); } - public int exceptionProbability(int bci) { - return compiler.getVMEntries().RiMethod_exceptionProbability(this, bci); - } + @Override + public RiProfilingInfo profilingInfo() { + if (methodData == null) { + methodData = compiler.getVMEntries().RiMethod_methodData(this); + } - public RiTypeProfile typeProfile(int bci) { - return compiler.getVMEntries().RiMethod_typeProfile(this, bci); - } - - public double branchProbability(int bci) { - return compiler.getVMEntries().RiMethod_branchProbability(this, bci); - } - - public double[] switchProbability(int bci) { - return compiler.getVMEntries().RiMethod_switchProbability(this, bci); + if (methodData == null || !methodData.isMature()) { + return new HotSpotNoProfilingInfo(compiler); + } else { + return new HotSpotProfilingInfo(compiler, methodData); + } } @Override @@ -224,25 +223,40 @@ TTY.println("profile info for %s", this); TTY.println("canBeStaticallyBound: " + canBeStaticallyBound()); TTY.println("invocationCount: " + invocationCount()); + RiProfilingInfo profilingInfo = this.profilingInfo(); for (int i = 0; i < codeSize(); i++) { - if (branchProbability(i) != -1) { - TTY.println(" branchProbability@%d: %f", i, branchProbability(i)); - } - if (exceptionProbability(i) > 0) { - TTY.println(" exceptionProbability@%d: %d", i, exceptionProbability(i)); + if (profilingInfo.getExecutionCount(i) != -1) { + TTY.println(" executionCount@%d: %d", i, profilingInfo.getExecutionCount(i)); } - RiTypeProfile profile = typeProfile(i); - if (profile != null && profile.count > 0) { - TTY.print(" profile@%d: count: %d, morphism: %d", i, profile.count, profile.morphism); - if (profile.types != null) { - TTY.print(", types:"); - for (int i2 = 0; i2 < profile.types.length; i2++) { - TTY.print(" %s (%f)", profile.types[i2], profile.probabilities[i2]); - } + + if (profilingInfo.getBranchTakenProbability(i) != -1) { + TTY.println(" branchProbability@%d: %f", i, profilingInfo.getBranchTakenProbability(i)); + } + + double[] switchProbabilities = profilingInfo.getSwitchProbabilities(i); + if (switchProbabilities != null) { + TTY.print(" switchProbabilities@%d:", i); + for (int j = 0; j < switchProbabilities.length; j++) { + TTY.print(" %f", switchProbabilities[j]); } TTY.println(); - if (exceptionProbability(i) > 0) { - TTY.println(" exceptionProbability@%d: %d", i, exceptionProbability(i)); + } + + if (profilingInfo.getExceptionSeen(i)) { + TTY.println(" exceptionSeen@%d: true", i); + } + + RiTypeProfile typeProfile = profilingInfo.getTypeProfile(i); + if (typeProfile != null) { + RiResolvedType[] types = typeProfile.getTypes(); + double[] probabilities = typeProfile.getProbabilities(); + if (types != null && probabilities != null) { + assert types.length == probabilities.length : "length must match"; + TTY.print(" types@%d:", i); + for (int j = 0; j < types.length; j++) { + TTY.print(" %s (%f)", types[j], probabilities[j]); + } + TTY.println(" not recorded (%f)", typeProfile.getNotRecordedProbability()); } } } diff -r ab7c258e1cef -r 9d640941c9c7 graal/com.oracle.max.graal.hotspot/src/com/oracle/max/graal/hotspot/ri/HotSpotNoProfilingInfo.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.max.graal.hotspot/src/com/oracle/max/graal/hotspot/ri/HotSpotNoProfilingInfo.java Tue Feb 07 21:09:31 2012 +0100 @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.max.graal.hotspot.ri; + +import com.oracle.max.cri.ri.*; +import com.oracle.max.graal.hotspot.*; +import com.oracle.max.graal.hotspot.Compiler; + + +public final class HotSpotNoProfilingInfo extends CompilerObject implements RiProfilingInfo { + /** + * + */ + private static final long serialVersionUID = 4357945025049704109L; + private static final HotSpotMethodDataAccessor noData = HotSpotMethodData.getNoMethodData(); + + public HotSpotNoProfilingInfo(Compiler compiler) { + super(compiler); + } + + @Override + public RiTypeProfile getTypeProfile(int bci) { + return noData.getTypeProfile(null, -1); + } + + @Override + public double getBranchTakenProbability(int bci) { + return noData.getBranchTakenProbability(null, -1); + } + + @Override + public double[] getSwitchProbabilities(int bci) { + return noData.getSwitchProbabilities(null, -1); + } + + @Override + public boolean getExceptionSeen(int bci) { + return noData.getExceptionSeen(null, -1); + } + + @Override + public int getExecutionCount(int bci) { + return noData.getExecutionCount(null, -1); + } +} diff -r ab7c258e1cef -r 9d640941c9c7 graal/com.oracle.max.graal.hotspot/src/com/oracle/max/graal/hotspot/ri/HotSpotProfilingInfo.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.max.graal.hotspot/src/com/oracle/max/graal/hotspot/ri/HotSpotProfilingInfo.java Tue Feb 07 21:09:31 2012 +0100 @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.max.graal.hotspot.ri; + +import com.oracle.max.cri.ri.*; +import com.oracle.max.graal.hotspot.*; +import com.oracle.max.graal.hotspot.Compiler; + + +public final class HotSpotProfilingInfo extends CompilerObject implements RiProfilingInfo { + + /** + * + */ + private static final long serialVersionUID = -8307682725047864875L; + + private int position; + private int hintPosition; + private int hintBCI; + private HotSpotMethodDataAccessor dataAccessor; + private HotSpotMethodData methodData; + + public HotSpotProfilingInfo(Compiler compiler, HotSpotMethodData methodData) { + super(compiler); + this.methodData = methodData; + hintPosition = 0; + hintBCI = -1; + } + + @Override + public RiTypeProfile getTypeProfile(int bci) { + findBCI(bci, false); + return dataAccessor.getTypeProfile(methodData, position); + } + + @Override + public double getBranchTakenProbability(int bci) { + findBCI(bci, false); + return dataAccessor.getBranchTakenProbability(methodData, position); + } + + @Override + public double[] getSwitchProbabilities(int bci) { + findBCI(bci, false); + return dataAccessor.getSwitchProbabilities(methodData, position); + } + + @Override + public boolean getExceptionSeen(int bci) { + findBCI(bci, true); + return dataAccessor.getExceptionSeen(methodData, position); + } + + @Override + public int getExecutionCount(int bci) { + findBCI(bci, false); + return dataAccessor.getExecutionCount(methodData, position); + } + + private void findBCI(int targetBCI, boolean searchExtraData) { + assert targetBCI >= 0 : "invalid BCI"; + + if (methodData.hasNormalData()) { + int currentPosition = targetBCI < hintBCI ? 0 : hintPosition; + HotSpotMethodDataAccessor currentAccessor; + while ((currentAccessor = methodData.getNormalData(currentPosition)) != null) { + int currentBCI = currentAccessor.getBCI(methodData, currentPosition); + if (currentBCI == targetBCI) { + normalDataFound(currentAccessor, currentPosition, currentBCI); + return; + } else if (currentBCI > targetBCI) { + break; + } + currentPosition = currentPosition + currentAccessor.getSize(methodData, currentPosition); + } + } + + if (searchExtraData && methodData.hasExtraData()) { + int currentPosition = methodData.getExtraDataBeginOffset(); + HotSpotMethodDataAccessor currentAccessor; + while ((currentAccessor = methodData.getExtraData(currentPosition)) != null) { + int currentBCI = currentAccessor.getBCI(methodData, currentPosition); + if (currentBCI == targetBCI) { + extraDataFound(currentAccessor, currentPosition); + return; + } + currentPosition = currentPosition + currentAccessor.getSize(methodData, currentPosition); + } + } + + // TODO (ch) getExceptionSeen() should return UNKNOWN if not enough extra data + + noDataFound(); + } + + private void normalDataFound(HotSpotMethodDataAccessor data, int pos, int bci) { + setCurrentData(data, pos); + this.hintPosition = position; + this.hintBCI = bci; + } + + private void extraDataFound(HotSpotMethodDataAccessor data, int pos) { + setCurrentData(data, pos); + } + + private void noDataFound() { + setCurrentData(HotSpotMethodData.getNoMethodData(), -1); + } + + private void setCurrentData(HotSpotMethodDataAccessor dataAccessor, int position) { + this.dataAccessor = dataAccessor; + this.position = position; + } +} diff -r ab7c258e1cef -r 9d640941c9c7 graal/com.oracle.max.graal.hotspot/src/com/oracle/max/graal/hotspot/ri/HotSpotRuntime.java --- a/graal/com.oracle.max.graal.hotspot/src/com/oracle/max/graal/hotspot/ri/HotSpotRuntime.java Sun Feb 05 05:40:36 2012 +0100 +++ b/graal/com.oracle.max.graal.hotspot/src/com/oracle/max/graal/hotspot/ri/HotSpotRuntime.java Tue Feb 07 21:09:31 2012 +0100 @@ -314,6 +314,12 @@ } else if (n instanceof ArrayHeaderSizeNode) { ArrayHeaderSizeNode arrayHeaderSize = (ArrayHeaderSizeNode) n; graph.replaceFloating(arrayHeaderSize, ConstantNode.forLong(config.getArrayOffset(arrayHeaderSize.elementKind()), n.graph())); + } else if (n instanceof ReadHubNode) { + ReadHubNode objectClassNode = (ReadHubNode) n; + LocationNode location = LocationNode.create(LocationNode.FINAL_LOCATION, CiKind.Object, config.hubOffset, graph); + ReadNode memoryRead = graph.add(new ReadNode(CiKind.Object, objectClassNode.object(), location)); + memoryRead.setGuard((GuardNode) tool.createGuard(graph.unique(new NullCheckNode(objectClassNode.object(), false)))); + graph.replaceFixed(objectClassNode, memoryRead); } } diff -r ab7c258e1cef -r 9d640941c9c7 graal/com.oracle.max.graal.hotspot/src/com/oracle/max/graal/hotspot/ri/HotSpotXirGenerator.java --- a/graal/com.oracle.max.graal.hotspot/src/com/oracle/max/graal/hotspot/ri/HotSpotXirGenerator.java Sun Feb 05 05:40:36 2012 +0100 +++ b/graal/com.oracle.max.graal.hotspot/src/com/oracle/max/graal/hotspot/ri/HotSpotXirGenerator.java Tue Feb 07 21:09:31 2012 +0100 @@ -1174,32 +1174,21 @@ @Override protected XirTemplate create(CiXirAssembler asm, long flags) { asm.restart(); - XirParameter object = asm.createInputParameter("object", CiKind.Object); + XirParameter objHub = asm.createInputParameter("objectHub", CiKind.Object); XirOperand hub = asm.createConstantInputParameter("hub", CiKind.Object); + XirLabel falseSucc = asm.createInlineLabel(XirLabel.FalseSuccessor); - XirOperand objHub = asm.createTemp("objHub", CiKind.Object); XirOperand checkHub = asm.createTemp("checkHub", CiKind.Object); - XirLabel slowPath = asm.createOutOfLineLabel("deopt"); - if (is(NULL_CHECK, flags)) { asm.mark(MARK_IMPLICIT_NULL); } - asm.pload(CiKind.Object, objHub, object, asm.i(config.hubOffset), false); asm.mov(checkHub, hub); // if we get an exact match: continue - asm.jneq(slowPath, objHub, checkHub); + asm.jneq(falseSucc, objHub, checkHub); - // -- out of line ------------------------------------------------------- - asm.bindOutOfLine(slowPath); - XirOperand scratch = asm.createRegisterTemp("scratch", target.wordKind, AMD64.r10); - asm.mov(scratch, wordConst(asm, 2)); - - asm.callRuntime(CiRuntimeCall.Deoptimize, null); - asm.shouldNotReachHere(); - - return asm.finishTemplate(object, "typeCheck"); + return asm.finishTemplate(objHub, "typeCheck"); } }; @@ -1411,9 +1400,9 @@ } @Override - public XirSnippet genTypeCheck(XirSite site, XirArgument object, XirArgument hub, RiType type) { + public XirSnippet genTypeBranch(XirSite site, XirArgument thisHub, XirArgument otherHub, RiType type) { assert type instanceof RiResolvedType; - return new XirSnippet(typeCheckTemplates.get(site), object, hub); + return new XirSnippet(typeCheckTemplates.get(site), thisHub, otherHub); } @Override diff -r ab7c258e1cef -r 9d640941c9c7 graal/com.oracle.max.graal.hotspot/src/com/oracle/max/graal/hotspot/snippets/ArrayCopySnippets.java --- a/graal/com.oracle.max.graal.hotspot/src/com/oracle/max/graal/hotspot/snippets/ArrayCopySnippets.java Sun Feb 05 05:40:36 2012 +0100 +++ b/graal/com.oracle.max.graal.hotspot/src/com/oracle/max/graal/hotspot/snippets/ArrayCopySnippets.java Tue Feb 07 21:09:31 2012 +0100 @@ -21,7 +21,6 @@ * questions. */ package com.oracle.max.graal.hotspot.snippets; - import com.oracle.max.cri.ci.*; import com.oracle.max.graal.cri.*; import com.oracle.max.graal.hotspot.*; @@ -285,7 +284,6 @@ DirectObjectStoreNode.store(dest, i + (destOffset + header), a); } } - /** * Copies {@code length} bytes from {@code src} starting at {@code srcPos} to {@code dest} starting at {@code destPos}. * @param src source object @@ -347,7 +345,6 @@ DirectObjectStoreNode.store(dest, i + (destOffset + header), a); } } - private static class GetObjectAddressNode extends FixedWithNextNode implements LIRLowerable { @Input private ValueNode object; @@ -369,7 +366,6 @@ gen.setResult(this, obj); } } - private static class DirectStoreNode extends FixedWithNextNode implements LIRLowerable { @Input private ValueNode address; @Input private ValueNode value; diff -r ab7c258e1cef -r 9d640941c9c7 graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/BciBlockMapping.java --- a/graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/BciBlockMapping.java Sun Feb 05 05:40:36 2012 +0100 +++ b/graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/BciBlockMapping.java Tue Feb 07 21:09:31 2012 +0100 @@ -236,6 +236,7 @@ // mark the entrypoints of basic blocks and build lists of successors for // all bytecodes that end basic blocks (i.e. goto, ifs, switches, throw, jsr, returns, ret) byte[] code = method.code(); + RiProfilingInfo profilingInfo = method.profilingInfo(); Block current = null; int bci = 0; while (bci < code.length) { @@ -282,7 +283,7 @@ case IFNULL: // fall through case IFNONNULL: { current = null; - double probability = useBranchPrediction ? method.branchProbability(bci) : -1; + double probability = useBranchPrediction ? profilingInfo.getBranchTakenProbability(bci) : -1; Block b1 = probability == 0.0 ? new DeoptBlock(bci + Bytes.beS2(code, bci + 1)) : makeBlock(bci + Bytes.beS2(code, bci + 1)); Block b2 = probability == 1.0 ? new DeoptBlock(bci + 3) : makeBlock(bci + 3); @@ -351,7 +352,7 @@ break; } default: { - if (canTrap(opcode, bci)) { + if (canTrap(opcode, bci, profilingInfo)) { canTrap.set(bci); } } @@ -360,7 +361,7 @@ } } - public boolean canTrap(int opcode, int bci) { + private static boolean canTrap(int opcode, int bci, RiProfilingInfo profilingInfo) { switch (opcode) { case INVOKESTATIC: case INVOKESPECIAL: @@ -387,7 +388,7 @@ case PUTFIELD: case GETFIELD: { if (GraalOptions.AllowExplicitExceptionChecks) { - return method.exceptionProbability(bci) > 0; + return profilingInfo.getExceptionSeen(bci); } } } diff -r ab7c258e1cef -r 9d640941c9c7 graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/GraphBuilderPhase.java --- a/graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/GraphBuilderPhase.java Sun Feb 05 05:40:36 2012 +0100 +++ b/graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/GraphBuilderPhase.java Tue Feb 07 21:09:31 2012 +0100 @@ -72,6 +72,7 @@ private RiConstantPool constantPool; private RiExceptionHandler[] exceptionHandlers; private RiResolvedMethod method; + private RiProfilingInfo profilingInfo; private BytecodeStream stream; // the bytecode stream private final LogStream log; @@ -116,6 +117,7 @@ @Override protected void run(StructuredGraph graph) { method = graph.method(); + profilingInfo = method.profilingInfo(); assert method.code() != null : "method must contain bytecodes: " + method; this.stream = new BytecodeStream(method.code()); this.constantPool = method.getConstantPool(); @@ -321,7 +323,7 @@ assert bci == FrameState.BEFORE_BCI || bci == bci() : "invalid bci"; if (GraalOptions.UseExceptionProbability && method.invocationCount() > GraalOptions.MatureInvocationCount) { - if (bci != FrameState.BEFORE_BCI && exceptionObject == null && method.exceptionProbability(bci) == 0) { + if (bci != FrameState.BEFORE_BCI && exceptionObject == null && !profilingInfo.getExceptionSeen(bci)) { return null; } } @@ -602,7 +604,7 @@ private void ifNode(ValueNode x, Condition cond, ValueNode y) { assert !x.isDeleted() && !y.isDeleted(); - double probability = method.branchProbability(bci()); + double probability = profilingInfo.getBranchTakenProbability(bci()); if (probability < 0) { Debug.log("missing probability in %s at bci %d", method, bci()); probability = 0.5; @@ -696,16 +698,21 @@ if (uniqueSubtype != null) { return new RiResolvedType[] {uniqueSubtype}; } else { - RiTypeProfile typeProfile = method.typeProfile(bci()); - if (typeProfile != null && typeProfile.types != null && typeProfile.types.length > 0 && typeProfile.morphism <= maxHints) { - RiResolvedType[] hints = new RiResolvedType[typeProfile.types.length]; - int hintCount = 0; - for (RiResolvedType hint : typeProfile.types) { - if (hint.isSubtypeOf(type)) { - hints[hintCount++] = hint; + RiTypeProfile typeProfile = profilingInfo.getTypeProfile(bci()); + if (typeProfile != null) { + double notRecordedTypes = typeProfile.getNotRecordedProbability(); + RiResolvedType[] types = typeProfile.getTypes(); + + if (notRecordedTypes == 0 && types != null && types.length > 0 && types.length <= maxHints) { + RiResolvedType[] hints = new RiResolvedType[types.length]; + int hintCount = 0; + for (RiResolvedType hint : types) { + if (hint.isSubtypeOf(type)) { + hints[hintCount++] = hint; + } } + return Arrays.copyOf(hints, Math.min(maxHints, hintCount)); } - return Arrays.copyOf(hints, Math.min(maxHints, hintCount)); } return EMPTY_TYPE_ARRAY; } @@ -1185,7 +1192,7 @@ } private double[] switchProbability(int numberOfCases, int bci) { - double[] prob = method.switchProbability(bci); + double[] prob = profilingInfo.getSwitchProbabilities(bci); if (prob != null) { assert prob.length == numberOfCases; } else { diff -r ab7c258e1cef -r 9d640941c9c7 graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/IfNode.java --- a/graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/IfNode.java Sun Feb 05 05:40:36 2012 +0100 +++ b/graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/IfNode.java Tue Feb 07 21:09:31 2012 +0100 @@ -44,8 +44,8 @@ return compare; } - public IfNode(BooleanNode condition, FixedNode trueSuccessor, FixedNode falseSuccessor, double probability) { - super(StampFactory.illegal(), new BeginNode[] {BeginNode.begin(trueSuccessor), BeginNode.begin(falseSuccessor)}, new double[] {probability, 1 - probability}); + public IfNode(BooleanNode condition, FixedNode trueSuccessor, FixedNode falseSuccessor, double takenProbability) { + super(StampFactory.illegal(), new BeginNode[] {BeginNode.begin(trueSuccessor), BeginNode.begin(falseSuccessor)}, new double[] {takenProbability, 1 - takenProbability}); this.compare = condition; } diff -r ab7c258e1cef -r 9d640941c9c7 graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/Invoke.java --- a/graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/Invoke.java Sun Feb 05 05:40:36 2012 +0100 +++ b/graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/Invoke.java Tue Feb 07 21:09:31 2012 +0100 @@ -46,4 +46,10 @@ void intrinsify(Node node); Graph graph(); + + double probability(); + + boolean useForInlining(); + + void setUseForInlining(boolean value); } diff -r ab7c258e1cef -r 9d640941c9c7 graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/InvokeNode.java --- a/graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/InvokeNode.java Sun Feb 05 05:40:36 2012 +0100 +++ b/graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/InvokeNode.java Tue Feb 07 21:09:31 2012 +0100 @@ -37,6 +37,7 @@ @Input private final MethodCallTargetNode callTarget; private final int bci; + private boolean useForInlining; /** * Constructs a new Invoke instruction. @@ -50,12 +51,22 @@ super(callTarget.returnStamp()); this.callTarget = callTarget; this.bci = bci; + this.useForInlining = true; } public MethodCallTargetNode callTarget() { return callTarget; } + public boolean useForInlining() { + return useForInlining; + } + + @Override + public void setUseForInlining(boolean value) { + this.useForInlining = value; + } + @Override public Map getDebugProperties() { Map debugProperties = super.getDebugProperties(); diff -r ab7c258e1cef -r 9d640941c9c7 graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/InvokeWithExceptionNode.java --- a/graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/InvokeWithExceptionNode.java Sun Feb 05 05:40:36 2012 +0100 +++ b/graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/InvokeWithExceptionNode.java Tue Feb 07 21:09:31 2012 +0100 @@ -38,6 +38,7 @@ @Input private final MethodCallTargetNode callTarget; @Input private FrameState stateAfter; private final int bci; + private boolean useForInlining; /** * @param kind @@ -48,6 +49,7 @@ super(callTarget.returnStamp(), new BeginNode[]{null, exceptionEdge}, new double[]{1.0, 0.0}); this.bci = bci; this.callTarget = callTarget; + this.useForInlining = true; } public BeginNode exceptionEdge() { @@ -71,6 +73,16 @@ } @Override + public boolean useForInlining() { + return useForInlining; + } + + @Override + public void setUseForInlining(boolean value) { + this.useForInlining = value; + } + + @Override public String toString(Verbosity verbosity) { if (verbosity == Verbosity.Long) { return super.toString(Verbosity.Short) + "(bci=" + bci() + ")"; diff -r ab7c258e1cef -r 9d640941c9c7 graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/extended/LookupSwitchNode.java --- a/graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/extended/LookupSwitchNode.java Sun Feb 05 05:40:36 2012 +0100 +++ b/graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/extended/LookupSwitchNode.java Tue Feb 07 21:09:31 2012 +0100 @@ -29,7 +29,7 @@ * The {@code LookupSwitchNode} represents a lookup switch bytecode, which has a sorted * array of key values. */ -public final class LookupSwitchNode extends SwitchNode implements LIRLowerable { +public final class LookupSwitchNode extends SwitchNode implements LIRLowerable, Simplifiable { @Data private final int[] keys; @@ -66,4 +66,32 @@ public void generate(LIRGeneratorTool gen) { gen.emitLookupSwitch(this); } + + @Override + public void simplify(SimplifierTool tool) { + if (value() instanceof ConstantNode) { + ConstantNode constant = (ConstantNode) value(); + int value = constant.value.asInt(); + + BeginNode remainingSux = (BeginNode) defaultSuccessor(); + int remainingSuxIndex = blockSuccessorCount() - 1; + for (int i = 0; i < keys.length; i++) { + if (value == keys[i]) { + remainingSux = blockSuccessor(i); + remainingSuxIndex = i; + break; + } + } + + for (int i = 0; i < blockSuccessorCount(); i++) { + BeginNode sux = blockSuccessor(i); + if (sux != remainingSux) { + tool.deleteBranch(sux); + } + } + + tool.addToWorkList(remainingSux); + ((StructuredGraph) graph()).removeSplit(this, remainingSuxIndex); + } + } } diff -r ab7c258e1cef -r 9d640941c9c7 graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/extended/ReadHubNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/extended/ReadHubNode.java Tue Feb 07 21:09:31 2012 +0100 @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.max.graal.nodes.extended; + +import com.oracle.max.cri.ci.*; +import com.oracle.max.graal.cri.*; +import com.oracle.max.graal.nodes.*; +import com.oracle.max.graal.nodes.spi.*; +import com.oracle.max.graal.nodes.type.*; + +// TODO (ch) this should be a FloatingNode but Lowering crashes in that case +public final class ReadHubNode extends FixedWithNextNode implements Lowerable { + @Input private ValueNode object; + + public ValueNode object() { + return object; + } + + public ReadHubNode(ValueNode object) { + super(StampFactory.forKind(CiKind.Object)); + this.object = object; + } + + @Override + public void lower(CiLoweringTool tool) { + tool.getRuntime().lower(this, tool); + } +} diff -r ab7c258e1cef -r 9d640941c9c7 graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/extended/TableSwitchNode.java --- a/graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/extended/TableSwitchNode.java Sun Feb 05 05:40:36 2012 +0100 +++ b/graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/extended/TableSwitchNode.java Tue Feb 07 21:09:31 2012 +0100 @@ -28,7 +28,7 @@ /** * The {@code TableSwitchNode} represents a table switch. */ -public final class TableSwitchNode extends SwitchNode implements LIRLowerable { +public final class TableSwitchNode extends SwitchNode implements LIRLowerable, Simplifiable { @Data private final int lowKey; @@ -67,4 +67,30 @@ public void generate(LIRGeneratorTool gen) { gen.emitTableSwitch(this); } + + @Override + public void simplify(SimplifierTool tool) { + if (value() instanceof ConstantNode) { + ConstantNode constant = (ConstantNode) value(); + int value = constant.value.asInt(); + + int remainingSuxIndex; + if (value >= lowKey() && value <= highKey()) { + remainingSuxIndex = value - lowKey(); + } else { + remainingSuxIndex = blockSuccessorCount() - 1; + } + + BeginNode remainingSux = blockSuccessor(remainingSuxIndex); + for (int i = 0; i < blockSuccessorCount(); i++) { + BeginNode sux = blockSuccessor(i); + if (sux != remainingSux) { + tool.deleteBranch(sux); + } + } + + tool.addToWorkList(remainingSux); + ((StructuredGraph) graph()).removeSplit(this, remainingSuxIndex); + } + } } diff -r ab7c258e1cef -r 9d640941c9c7 graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/java/ArrayLengthNode.java --- a/graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/java/ArrayLengthNode.java Sun Feb 05 05:40:36 2012 +0100 +++ b/graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/java/ArrayLengthNode.java Tue Feb 07 21:09:31 2012 +0100 @@ -58,7 +58,7 @@ return length; } CiConstant constantValue = null; - if (array().isConstant()) { + if (array().isConstant() && !array().isNullConstant()) { constantValue = array().asConstant(); if (constantValue != null && constantValue.isNonNull()) { RiRuntime runtime = tool.runtime(); diff -r ab7c258e1cef -r 9d640941c9c7 graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/java/IsTypeNode.java --- a/graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/java/IsTypeNode.java Sun Feb 05 05:40:36 2012 +0100 +++ b/graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/java/IsTypeNode.java Tue Feb 07 21:09:31 2012 +0100 @@ -27,30 +27,30 @@ import com.oracle.max.cri.ci.*; import com.oracle.max.cri.ri.*; import com.oracle.max.graal.nodes.*; +import com.oracle.max.graal.nodes.extended.*; import com.oracle.max.graal.nodes.spi.*; import com.oracle.max.graal.nodes.type.*; public final class IsTypeNode extends BooleanNode implements Canonicalizable, LIRLowerable { - @Input private ValueNode object; + @Input private ValueNode objectClass; + @Data private final RiResolvedType type; - public ValueNode object() { - return object; + public ValueNode objectClass() { + return objectClass; } - private final RiResolvedType type; - /** * Constructs a new IsTypeNode. * * @param object the instruction producing the object to check against the given type * @param type the type for this check */ - public IsTypeNode(ValueNode object, RiResolvedType type) { + public IsTypeNode(ValueNode objectClass, RiResolvedType type) { super(StampFactory.illegal()); - assert object == null || object.kind() == CiKind.Object; + assert objectClass == null || objectClass.kind() == CiKind.Object; this.type = type; - this.object = object; + this.objectClass = objectClass; } public RiResolvedType type() { @@ -71,8 +71,9 @@ @Override public ValueNode canonical(CanonicalizerTool tool) { - if (object().exactType() != null) { - return ConstantNode.forBoolean(object().exactType() == type(), graph()); + RiResolvedType exactType = objectClass() instanceof ReadHubNode ? ((ReadHubNode) objectClass()).object().exactType() : null; + if (exactType != null) { + return ConstantNode.forBoolean(exactType == type(), graph()); } // constants return the correct exactType, so they are handled by the code above return this; diff -r ab7c258e1cef -r 9d640941c9c7 graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/java/LoadFieldNode.java --- a/graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/java/LoadFieldNode.java Sun Feb 05 05:40:36 2012 +0100 +++ b/graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/java/LoadFieldNode.java Tue Feb 07 21:09:31 2012 +0100 @@ -69,7 +69,7 @@ CiConstant constant = null; if (isStatic()) { constant = field().constantValue(null); - } else if (object().isConstant()) { + } else if (object().isConstant() && !object().isNullConstant()) { constant = field().constantValue(object().asConstant()); } if (constant != null) { diff -r ab7c258e1cef -r 9d640941c9c7 graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/spi/EscapeOp.java --- a/graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/spi/EscapeOp.java Sun Feb 05 05:40:36 2012 +0100 +++ b/graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/spi/EscapeOp.java Tue Feb 07 21:09:31 2012 +0100 @@ -41,7 +41,7 @@ assert ((NullCheckNode) usage).object() == node; return false; } else if (usage instanceof IsTypeNode) { - assert ((IsTypeNode) usage).object() == node; + assert ((IsTypeNode) usage).objectClass() == node; return false; } else if (usage instanceof FrameState) { assert usage.inputs().contains(node); diff -r ab7c258e1cef -r 9d640941c9c7 graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/spi/LIRGeneratorTool.java diff -r ab7c258e1cef -r 9d640941c9c7 graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/type/StampFactory.java --- a/graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/type/StampFactory.java Sun Feb 05 05:40:36 2012 +0100 +++ b/graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/type/StampFactory.java Tue Feb 07 21:09:31 2012 +0100 @@ -173,7 +173,7 @@ RiResolvedType exactType = first.exactType(); while (iterator.hasNext()) { Stamp current = iterator.next().stamp(); - assert current.kind() == first.kind() : values + " first=" + first + " current=" + current; + assert current.kind() == first.kind() : values + " first=" + first + " current=" + current + " first kind=" + first.kind() + " current kind=" + current.kind(); nonNull &= current.nonNull(); declaredType = orTypes(declaredType, current.declaredType()); exactType = orTypes(exactType, current.exactType()); diff -r ab7c258e1cef -r 9d640941c9c7 graal/com.oracle.max.graal.printer/src/com/oracle/max/graal/printer/IdealGraphPrinterDumpHandler.java --- a/graal/com.oracle.max.graal.printer/src/com/oracle/max/graal/printer/IdealGraphPrinterDumpHandler.java Sun Feb 05 05:40:36 2012 +0100 +++ b/graal/com.oracle.max.graal.printer/src/com/oracle/max/graal/printer/IdealGraphPrinterDumpHandler.java Tue Feb 07 21:09:31 2012 +0100 @@ -25,6 +25,7 @@ import java.io.*; import java.net.*; import java.util.*; + import com.oracle.max.cri.ci.*; import com.oracle.max.cri.ri.*; import com.oracle.max.criutils.*; @@ -40,7 +41,7 @@ private static final String DEFAULT_FILE_NAME = "output.igv.xml"; private IdealGraphPrinter printer; - private List previousInlineContext = new ArrayList<>(); + private List previousInlineContext = new ArrayList<>(); private String fileName; private String host; private int port; @@ -61,8 +62,6 @@ this.port = port; } - - private void ensureInitialized() { if (!initialized) { initialized = true; @@ -85,7 +84,7 @@ } private void initializeNetworkPrinter() { - try { + try { Socket socket = new Socket(host, port); BufferedOutputStream stream = new BufferedOutputStream(socket.getOutputStream(), 0x4000); printer = new IdealGraphPrinter(stream); @@ -103,26 +102,29 @@ if (printer.isValid()) { // Get all current RiResolvedMethod instances in the context. - List inlineContext = Debug.contextSnapshot(RiResolvedMethod.class); + List inlineContext = getInlineContext(); + Debug.contextSnapshot(RiResolvedMethod.class); // Reverse list such that inner method comes after outer method. Collections.reverse(inlineContext); // Check for method scopes that must be closed since the previous dump. for (int i = 0; i < previousInlineContext.size(); ++i) { - if (i >= inlineContext.size() || inlineContext.get(i) != previousInlineContext.get(i)) { + if (i >= inlineContext.size() || !inlineContext.get(i).equals(previousInlineContext.get(i))) { for (int j = previousInlineContext.size() - 1; j >= i; --j) { - closeMethodScope(); + closeScope(); } + break; } } // Check for method scopes that must be opened since the previous dump. for (int i = 0; i < inlineContext.size(); ++i) { - if (i >= previousInlineContext.size() || inlineContext.get(i) != previousInlineContext.get(i)) { + if (i >= previousInlineContext.size() || !inlineContext.get(i).equals(previousInlineContext.get(i))) { for (int j = i; j < inlineContext.size(); ++j) { - openMethodScope(inlineContext.get(j)); + openScope(inlineContext.get(j)); } + break; } } @@ -145,13 +147,25 @@ } } - private void openMethodScope(RiResolvedMethod method) { - printer.beginGroup(CiUtil.format("%H::%n", method), CiUtil.format("%h::%n", method), method, -1); - + private static List getInlineContext() { + List result = new ArrayList<>(); + for (Object o : Debug.context()) { + if (o instanceof RiResolvedMethod) { + RiResolvedMethod method = (RiResolvedMethod) o; + result.add(CiUtil.format("%H::%n", method)); + } else if (o instanceof DebugDumpScope) { + DebugDumpScope debugDumpScope = (DebugDumpScope) o; + result.add(debugDumpScope.getName()); + } + } + return result; } - private void closeMethodScope() { + private void openScope(String name) { + printer.beginGroup(name, name, Debug.contextLookup(RiResolvedMethod.class), -1); + } + + private void closeScope() { printer.endGroup(); - } } diff -r ab7c258e1cef -r 9d640941c9c7 graal/com.oracle.max.graal.tests/src/com/oracle/max/graal/compiler/tests/DegeneratedLoopsTest.java --- a/graal/com.oracle.max.graal.tests/src/com/oracle/max/graal/compiler/tests/DegeneratedLoopsTest.java Sun Feb 05 05:40:36 2012 +0100 +++ b/graal/com.oracle.max.graal.tests/src/com/oracle/max/graal/compiler/tests/DegeneratedLoopsTest.java Tue Feb 07 21:09:31 2012 +0100 @@ -52,9 +52,6 @@ } private static class UnresolvedException extends RuntimeException { - /** - * - */ private static final long serialVersionUID = 5215434338750728440L; static { diff -r ab7c258e1cef -r 9d640941c9c7 make/windows/makefiles/projectcreator.make --- a/make/windows/makefiles/projectcreator.make Sun Feb 05 05:40:36 2012 +0100 +++ b/make/windows/makefiles/projectcreator.make Tue Feb 07 21:09:31 2012 +0100 @@ -89,7 +89,7 @@ -define ALIGN_STACK_FRAMES \ -define VM_LITTLE_ENDIAN \ -prelink "" "Generating vm.def..." "cd %o set HOTSPOTMKSHOME=$(HOTSPOTMKSHOME) $(HOTSPOTMKSHOME)\sh $(HOTSPOTWORKSPACE)\make\windows\build_vm_def.sh $(LINK_VER)" \ - -postbuild "" "Building hotspot.exe..." "cd %o HOTSPOTMKSHOME=$(HOTSPOTMKSHOME) nmake -f $(HOTSPOTWORKSPACE)\make\windows\projectfiles\common\Makefile LOCAL_MAKE=$(HOTSPOTBUILDSPACE)\%f\local.make JAVA_HOME=$(HOTSPOTJDKDIST) launcher" \ + -postbuild "" "Building hotspot.exe..." "cd %o set HOTSPOTMKSHOME=$(HOTSPOTMKSHOME) nmake -f $(HOTSPOTWORKSPACE)\make\windows\projectfiles\common\Makefile LOCAL_MAKE=$(HOTSPOTBUILDSPACE)\%f\local.make JAVA_HOME=$(HOTSPOTJDKDIST) launcher" \ -ignoreFile jsig.c \ -ignoreFile jvmtiEnvRecommended.cpp \ -ignoreFile jvmtiEnvStub.cpp \ diff -r ab7c258e1cef -r 9d640941c9c7 mx/commands.py --- a/mx/commands.py Sun Feb 05 05:40:36 2012 +0100 +++ b/mx/commands.py Tue Feb 07 21:09:31 2012 +0100 @@ -218,6 +218,57 @@ if len(failed) != 0: mx.abort('DaCapo failures: ' + str(failed)) + + +def scaladacapo(args): + """run one or all Scala DaCapo benchmarks + + Scala DaCapo options are distinguished from VM options by a '@' prefix. + For example, '@--iterations @5' will pass '--iterations 5' to the + DaCapo harness.""" + + numTests = {} + + if len(args) > 0: + level = getattr(sanitycheck.SanityCheckLevel, args[0], None) + if level is not None: + del args[0] + for (bench, ns) in sanitycheck.dacapoScalaSanityWarmup.items(): + if ns[level] > 0: + numTests[bench] = ns[level] + else: + while len(args) != 0 and args[0][0] not in ['-', '@']: + n = 1 + if args[0].isdigit(): + n = int(args[0]) + assert len(args) > 1 and args[1][0] not in ['-', '@'] and not args[1].isdigit() + bm = args[1] + del args[0] + else: + bm = args[0] + + del args[0] + if bm not in sanitycheck.dacapoScalaSanityWarmup.keys(): + mx.abort('unknown benchmark: ' + bm + '\nselect one of: ' + str(sanitycheck.dacapoScalaSanityWarmup.keys())) + numTests[bm] = n + + if len(numTests) is 0: + for bench in sanitycheck.dacapoScalaSanityWarmup.keys(): + numTests[bench] = 1 + + # Extract DaCapo options + dacapoArgs = [(arg[1:]) for arg in args if arg.startswith('@')] + + # The remainder are VM options + vmOpts = [arg for arg in args if not arg.startswith('@')] + + failed = [] + for (test, n) in numTests.items(): + if not sanitycheck.getScalaDacapo(test, n, dacapoArgs).test('graal', opts=vmOpts): + failed.append(test) + + if len(failed) != 0: + mx.abort('Scala DaCapo failures: ' + str(failed)) def _jdk(build='product', create=False): """ @@ -498,7 +549,7 @@ build(['--no-native']) tasks.append(t.stop()) - for vmbuild in ['product', 'fastdebug']: + for vmbuild in ['fastdebug', 'product']: global _vmbuild _vmbuild = vmbuild @@ -571,6 +622,15 @@ if dacapo not in sanitycheck.dacapoSanityWarmup.keys(): mx.abort('Unknown dacapo : ' + dacapo) benchmarks += [sanitycheck.getDacapo(dacapo, sanitycheck.dacapoSanityWarmup[dacapo][sanitycheck.SanityCheckLevel.Benchmark])] + + if ('scaladacapo' in args or 'all' in args): + benchmarks += sanitycheck.getScalaDacapos(level=sanitycheck.SanityCheckLevel.Benchmark) + else: + dacapos = [a[7:] for a in args if a.startswith('scaladacapo:')] + for dacapo in dacapos: + if dacapo not in sanitycheck.dacapoScalaSanityWarmup.keys(): + mx.abort('Unknown dacapo : ' + dacapo) + benchmarks += [sanitycheck.getScalaDacapo(dacapo, sanitycheck.dacapoScalaSanityWarmup[dacapo][sanitycheck.SanityCheckLevel.Benchmark])] #Bootstrap if ('bootstrap' in args or 'all' in args): @@ -597,14 +657,29 @@ vmArgs = [a for a in args if a[0] != '@'] sanitycheck.getSPECjvm2008(benchArgs).bench('graal', opts=vmArgs) +def hsdis(args): + """Installs the hsdis library + + This is needed to support HotSpot's assembly dumping features. + By default it installs the Intel syntax version, use the 'att' argument to install AT&T syntax.""" + flavor = 'intel' + if 'att' in args: + flavor = 'att' + build = _vmbuild if _vmSourcesAvailable else 'product' + lib = mx.lib_suffix('hsdis-amd64') + path = join(_jdk(build), 'jre', 'lib', 'amd64', lib) + mx.download(path, ['http://lafo.ssw.uni-linz.ac.at/hsdis/' + flavor + "/" + lib]) + def mx_init(): _vmbuild = 'product' commands = { 'build': [build, '[-options]'], 'clean': [clean, ''], 'copyrightcheck': [copyrightcheck, ''], + 'hsdis': [hsdis, '[att]'], 'dacapo': [dacapo, '[[n] benchmark] [VM options|@DaCapo options]'], - 'specjvm2008': [specjvm2008, ''], + 'scaladacapo': [scaladacapo, '[[n] benchmark] [VM options|@Scala DaCapo options]'], + 'specjvm2008': [specjvm2008, '[VM options|@specjvm2008 options]'], 'example': [example, '[-v] example names...'], 'gate' : [gate, ''], 'bench' : [bench, '[-vm vm] [-resultfile file] [all(default)|dacapo|specjvm2008|bootstrap]'], diff -r ab7c258e1cef -r 9d640941c9c7 mx/projects --- a/mx/projects Sun Feb 05 05:40:36 2012 +0100 +++ b/mx/projects Tue Feb 07 21:09:31 2012 +0100 @@ -41,6 +41,9 @@ library@DACAPO@path=lib/dacapo-9.12-bach.jar library@DACAPO@urls=http://dfn.dl.sourceforge.net/project/dacapobench/9.12-bach/dacapo-9.12-bach.jar +library@DACAPO_SCALA@path=lib/dacapo-scala-0.1.0.jar +library@DACAPO_SCALA@urls=http://repo.scalabench.org/snapshots/org/scalabench/benchmarks/scala-benchmark-suite/0.1.0-SNAPSHOT/scala-benchmark-suite-0.1.0-20110908.085753-2.jar + # graal.hotspot project@com.oracle.max.graal.hotspot@subDir=graal project@com.oracle.max.graal.hotspot@sourceDirs=src diff -r ab7c258e1cef -r 9d640941c9c7 mx/sanitycheck.py --- a/mx/sanitycheck.py Sun Feb 05 05:40:36 2012 +0100 +++ b/mx/sanitycheck.py Tue Feb 07 21:09:31 2012 +0100 @@ -39,13 +39,28 @@ 'luindex': [0, 0, 5, 10, 10], 'lusearch': [0, 4, 5, 5, 8], 'pmd': [0, 0, 5, 10, 13], - 'sunflow': [0, 0, 5, 10, 15], + 'sunflow': [0, 2, 5, 10, 15], 'tomcat': [0, 0, 5, 10, 15], 'tradebeans': [0, 0, 5, 10, 13], 'tradesoap': [2, 4, 5, 10, 15], 'xalan': [0, 0, 5, 10, 18], } +dacapoScalaSanityWarmup = { + 'actors': [0, 0, 2, 8, 10], + 'apparat': [0, 0, 1, 2, 3], + 'factorie': [0, 0, 2, 5, 5], + 'kiama': [0, 0, 3, 13, 15], + 'scalac': [0, 0, 5, 15, 20], + 'scaladoc': [0, 0, 5, 15, 15], + 'scalap': [0, 0, 5, 15, 20], + 'scalariform':[0, 0, 6, 15, 20], + 'scalatest': [0, 0, 2, 10, 12], + 'scalaxb': [0, 0, 5, 15, 25], + 'specs': [0, 0, 3, 13, 18], + 'tmt': [0, 0, 3, 10, 12] +} + dacapoGateBuildLevels = { 'avrora': ['product', 'fastdebug', 'debug'], 'batik': ['product', 'fastdebug', 'debug'], @@ -63,6 +78,21 @@ 'xalan': ['product', 'fastdebug', 'debug'], } +dacapoScalaGateBuildLevels = { + 'actors': ['product', 'fastdebug', 'debug'], + 'apparat': ['product', 'fastdebug', 'debug'], + 'factorie': ['product', 'fastdebug', 'debug'], + 'kiama': ['product', 'fastdebug', 'debug'], + 'scalac': ['product', 'fastdebug', 'debug'], + 'scaladoc': ['product', 'fastdebug', 'debug'], + 'scalap': ['product', 'fastdebug', 'debug'], + 'scalariform':['product', 'fastdebug', 'debug'], + 'scalatest': ['product', 'fastdebug', 'debug'], + 'scalaxb': ['product', 'fastdebug', 'debug'], + 'specs': ['product', 'fastdebug', 'debug'], + 'tmt': ['product', 'fastdebug', 'debug'], +} + class SanityCheckLevel: Fast, Gate, Normal, Extensive, Benchmark = range(5) @@ -118,6 +148,36 @@ return Test("DaCapo-" + name, "DaCapo", ['-jar', dacapo, name, '-n', str(n), ] + dacapoArgs, [dacapoSuccess], [dacapoFail], [dacapoMatcher], ['-Xms2g', '-XX:MaxPermSize=256m']) +def getScalaDacapos(level=SanityCheckLevel.Normal, gateBuildLevel=None, dacapoArgs=[]): + checks = [] + + for (bench, ns) in dacapoScalaSanityWarmup.items(): + if ns[level] > 0: + if gateBuildLevel is None or gateBuildLevel in dacapoScalaGateBuildLevels[bench]: + checks.append(getScalaDacapo(bench, ns[level], dacapoArgs)) + + return checks + +def getScalaDacapo(name, n, dacapoArgs=[]): + dacapo = mx.get_env('DACAPO_SCALA_CP') + if dacapo is None: + l = mx.library('DACAPO_SCALA', False) + if l is not None: + dacapo = l.get_path(True) + else: + mx.abort('Scala DaCapo 0.1.0 jar file must be specified with DACAPO_SCALA_CP environment variable or as DACAPO_SCALA library') + + if not isfile(dacapo) or not dacapo.endswith('.jar'): + mx.abort('Specified Scala DaCapo jar file does not exist or is not a jar file: ' + dacapo) + + dacapoSuccess = re.compile(r"^===== DaCapo 0\.1\.0(-SNAPSHOT)? ([a-zA-Z0-9_]+) PASSED in ([0-9]+) msec =====$") + dacapoFail = re.compile(r"^===== DaCapo 0\.1\.0(-SNAPSHOT)? ([a-zA-Z0-9_]+) FAILED (warmup|) =====$") + dacapoTime = re.compile(r"===== DaCapo 0\.1\.0(-SNAPSHOT)? (?P[a-zA-Z0-9_]+) PASSED in (?P