# HG changeset patch # User Thomas Wuerthinger # Date 1399321126 -7200 # Node ID a3f897fb3289b4e9b2f716c1d17c1bae4ee91363 # Parent ff5cacf47b68317e23b495eda21b1f3f6fed11eb# Parent fbe9e7088e3500ce54b650c2b1c4516f895ae9c4 Merge. diff -r ff5cacf47b68 -r a3f897fb3289 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/DefaultHotSpotLoweringProvider.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/DefaultHotSpotLoweringProvider.java Sat May 03 21:46:35 2014 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/DefaultHotSpotLoweringProvider.java Mon May 05 22:18:46 2014 +0200 @@ -203,7 +203,7 @@ boxingSnippets.lower((BoxNode) n, tool); } else if (n instanceof UnboxNode) { boxingSnippets.lower((UnboxNode) n, tool); - } else if (n instanceof DeoptimizeNode || n instanceof UnwindNode) { + } else if (n instanceof DeoptimizeNode || n instanceof UnwindNode || n instanceof FloatRemNode) { /* No lowering, we generate LIR directly for these nodes. */ } else { throw GraalInternalError.shouldNotReachHere("Node implementing Lowerable not handled: " + n); diff -r ff5cacf47b68 -r a3f897fb3289 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/FloatRemNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/FloatRemNode.java Sat May 03 21:46:35 2014 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/FloatRemNode.java Mon May 05 22:18:46 2014 +0200 @@ -31,7 +31,7 @@ import com.oracle.graal.nodes.spi.*; @NodeInfo(shortName = "%") -public final class FloatRemNode extends FloatArithmeticNode implements Canonicalizable { +public class FloatRemNode extends FloatArithmeticNode implements Canonicalizable, Lowerable { public FloatRemNode(Stamp stamp, ValueNode x, ValueNode y, boolean isStrictFP) { super(stamp, x, y, isStrictFP); @@ -57,6 +57,11 @@ } @Override + public void lower(LoweringTool tool) { + tool.getLowerer().lower(this, tool); + } + + @Override public void generate(NodeMappableLIRBuilder builder, ArithmeticLIRGenerator gen) { builder.setResult(this, gen.emitRem(builder.operand(x()), builder.operand(y()), null)); } diff -r ff5cacf47b68 -r a3f897fb3289 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/cfg/ControlFlowGraph.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/cfg/ControlFlowGraph.java Sat May 03 21:46:35 2014 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/cfg/ControlFlowGraph.java Mon May 05 22:18:46 2014 +0200 @@ -200,7 +200,7 @@ private void connectBlocks() { for (Block block : reversePostOrder) { List predecessors = new ArrayList<>(4); - double probability = 0; + double probability = block.getBeginNode() instanceof StartNode ? 1D : 0D; for (Node predNode : block.getBeginNode().cfgPredecessors()) { Block predBlock = nodeToBlock.get(predNode); if (predBlock.getId() >= 0) { diff -r ff5cacf47b68 -r a3f897fb3289 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/BaseReduction.java --- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/BaseReduction.java Sat May 03 21:46:35 2014 +0200 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/BaseReduction.java Mon May 05 22:18:46 2014 +0200 @@ -57,10 +57,11 @@ */ public abstract class BaseReduction extends PostOrderNodeIterator { - protected static final DebugMetric metricCheckCastRemoved = Debug.metric("CheckCastRemoved"); - protected static final DebugMetric metricGuardingPiNodeRemoved = Debug.metric("GuardingPiNodeRemoved"); - protected static final DebugMetric metricFixedGuardNodeRemoved = Debug.metric("FixedGuardNodeRemoved"); - protected static final DebugMetric metricMethodResolved = Debug.metric("MethodResolved"); + protected static final DebugMetric metricCheckCastRemoved = Debug.metric("FSR-CheckCastRemoved"); + protected static final DebugMetric metricGuardingPiNodeRemoved = Debug.metric("FSR-GuardingPiNodeRemoved"); + protected static final DebugMetric metricFixedGuardNodeRemoved = Debug.metric("FSR-FixedGuardNodeRemoved"); + protected static final DebugMetric metricMethodResolved = Debug.metric("FSR-MethodResolved"); + protected static final DebugMetric metricUnconditionalDeoptInserted = Debug.metric("FSR-UnconditionalDeoptInserted"); /** *

@@ -95,6 +96,7 @@ * a bug in FlowSensitiveReduction (the code was reachable, after all). */ public void doRewrite(LogicNode falseConstant) { + metricUnconditionalDeoptInserted.increment(); StructuredGraph graph = fixed.graph(); // have to insert a FixedNode other than a ControlSinkNode FixedGuardNode buckStopsHere = graph.add(new FixedGuardNode(falseConstant, deoptReason, DeoptimizationAction.None)); diff -r ff5cacf47b68 -r a3f897fb3289 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/CheckCastReduction.java --- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/CheckCastReduction.java Sat May 03 21:46:35 2014 +0200 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/CheckCastReduction.java Mon May 05 22:18:46 2014 +0200 @@ -122,7 +122,7 @@ assert !StampTool.isObjectAlwaysNull(subject) : "Null as per stamp subjects should have been handled above"; // --------- checkCast deemed unsatisfiable by subject-stamp alone --------- - if (state.knownNotToConform(subject, toType)) { + if (state.knownNotToPassCheckCast(subject, toType)) { postponedDeopts.addDeoptBefore(checkCast, checkCast.isForStoreCheck() ? ArrayStoreException : ClassCastException); state.impossiblePath(); // let FixedGuardNode(false).simplify() prune the dead-code control-path diff -r ff5cacf47b68 -r a3f897fb3289 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/EquationalReasoner.java --- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/EquationalReasoner.java Sat May 03 21:46:35 2014 +0200 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/EquationalReasoner.java Mon May 05 22:18:46 2014 +0200 @@ -65,11 +65,12 @@ */ public final class EquationalReasoner { - private static final DebugMetric metricInstanceOfRemoved = Debug.metric("InstanceOfRemoved"); - private static final DebugMetric metricNullCheckRemoved = Debug.metric("NullCheckRemoved"); - private static final DebugMetric metricObjectEqualsRemoved = Debug.metric("ObjectEqualsRemoved"); - private static final DebugMetric metricEquationalReasoning = Debug.metric("EquationalReasoning"); - private static final DebugMetric metricDowncasting = Debug.metric("Downcasting"); + private static final DebugMetric metricInstanceOfRemoved = Debug.metric("FSR-InstanceOfRemoved"); + private static final DebugMetric metricNullCheckRemoved = Debug.metric("FSR-NullCheckRemoved"); + private static final DebugMetric metricObjectEqualsRemoved = Debug.metric("FSR-ObjectEqualsRemoved"); + private static final DebugMetric metricEquationalReasoning = Debug.metric("FSR-EquationalReasoning"); + private static final DebugMetric metricDowncasting = Debug.metric("FSR-Downcasting"); + private static final DebugMetric metricNullInserted = Debug.metric("FSR-NullInserted"); private final StructuredGraph graph; private final CanonicalizerTool tool; @@ -114,6 +115,7 @@ */ public void forceState(State s) { state = s; + assert state.repOK(); substs.clear(); added.clear(); visited = null; @@ -235,6 +237,17 @@ // picked cached substitution return result; } + if (FlowUtil.hasLegalObjectStamp(v) && state.isNull(v)) { + // it's ok to return nullConstant in deverbosify unlike in downcast + metricNullInserted.increment(); + return nullConstant; + } + if (v instanceof ValueProxy) { + return v; + } + if (!(n instanceof FloatingNode)) { + return n; + } if ((visited != null && visited.contains(n)) || added.contains(v)) { return v; } @@ -252,25 +265,13 @@ * Past this point, if we ever want `n` to be deverbosified, it must be looked-up by one of * the cases above. One sure way to achieve that is with `rememberSubstitution(old, new)` */ - if (v instanceof ValueProxy) { - return downcast(v); - } - if (n instanceof FloatingNode) { - /* - * `deverbosifyFloatingNode()` will drill down over floating inputs, when that not - * possible anymore it resorts to calling `downcast()`. Thus it's ok to take the - * `deverbosifyFloatingNode()` route first, as no downcasting opportunity will be - * missed. - */ - return deverbosifyFloatingNode((FloatingNode) n); - } - - if (FlowUtil.hasLegalObjectStamp(v)) { - return downcast(v); - } - - return n; + /* + * `deverbosifyFloatingNode()` will drill down over floating inputs, when that not possible + * anymore it resorts to calling `downcast()`. Thus it's ok to take the + * `deverbosifyFloatingNode()` route first, as no downcasting opportunity will be missed. + */ + return deverbosifyFloatingNode((FloatingNode) n); } /** @@ -334,16 +335,7 @@ } if (changed == null) { assert visited.contains(f) || added.contains(f); - if (FlowUtil.hasLegalObjectStamp(f)) { - /* - * No input has changed doesn't imply there's no witness to refine the - * floating-object value. - */ - ValueNode d = downcast(f); - return d; - } else { - return f; - } + return f; } FlowUtil.inferStampAndCheck(changed); added.add(changed); @@ -441,6 +433,8 @@ return baseCaseIsNullNode((IsNullNode) condition); } else if (condition instanceof ObjectEqualsNode) { return baseCaseObjectEqualsNode((ObjectEqualsNode) condition); + } else if (condition instanceof ShortCircuitOrNode) { + return baseCaseShortCircuitOrNode((ShortCircuitOrNode) condition); } } return condition; @@ -465,6 +459,11 @@ if (state.isNull(scrutinee)) { metricInstanceOfRemoved.increment(); return falseConstant; + } else if (state.knownNotToPassInstanceOf(scrutinee, instanceOf.type())) { + // scrutinee turns out to be null -> falseConstant right answer + // scrutinee not null, but known-not-to-conform -> falseConstant + metricInstanceOfRemoved.increment(); + return falseConstant; } else if (state.isNonNull(scrutinee) && state.knownToConform(scrutinee, instanceOf.type())) { metricInstanceOfRemoved.increment(); return trueConstant; @@ -477,21 +476,19 @@ * performed; otherwise the unmodified argument. * */ - private FloatingNode baseCaseIsNullNode(IsNullNode isNull) { - ValueNode object = isNull.object(); + private FloatingNode baseCaseIsNullNode(IsNullNode isNu) { + ValueNode object = isNu.object(); if (!FlowUtil.hasLegalObjectStamp(object)) { - return isNull; + return isNu; } - ValueNode scrutinee = GraphUtil.unproxify(isNull.object()); - GuardingNode evidence = nonTrivialNullAnchor(scrutinee); - if (evidence != null) { + if (state.isNull(object)) { metricNullCheckRemoved.increment(); return trueConstant; - } else if (state.isNonNull(scrutinee)) { + } else if (state.isNonNull(object)) { metricNullCheckRemoved.increment(); return falseConstant; } - return isNull; + return isNu; } /** @@ -515,6 +512,38 @@ } /** + * The following is tried: + * + *

    + *
  1. + * A {@link com.oracle.graal.phases.common.cfs.Witness} that is at check-cast level level + * doesn't entail {@link com.oracle.graal.nodes.calc.IsNullNode} (on its own) nor + * {@link com.oracle.graal.nodes.java.InstanceOfNode} (also on its own) but of course it entails + * (IsNull || IsInstanceOf). Good thing + * {@link com.oracle.graal.phases.common.cfs.CastCheckExtractor} detects that very pattern.
  2. + *
  3. + * Otherwise return the unmodified argument (later on, + * {@link #deverbosifyFloatingNode(com.oracle.graal.nodes.calc.FloatingNode)} will attempt to + * simplify the {@link com.oracle.graal.nodes.ShortCircuitOrNode}).
  4. + *
+ * + * @return a {@link com.oracle.graal.nodes.LogicConstantNode}, in case a reduction was made; + * otherwise the unmodified argument. + */ + private LogicNode baseCaseShortCircuitOrNode(ShortCircuitOrNode orNode) { + CastCheckExtractor cast = CastCheckExtractor.extract(orNode); + if (cast != null) { + if (state.knownToConform(cast.subject, cast.type)) { + return trueConstant; + } else if (state.knownNotToPassCheckCast(cast.subject, cast.type)) { + return falseConstant; + } + return orNode; + } + return orNode; + } + + /** * It's always ok to use "downcast(object)" instead of " object" * because this method re-wraps the argument in a {@link com.oracle.graal.nodes.PiNode} only if * the new stamp is strictly more refined than the original. @@ -558,6 +587,7 @@ PiNode untrivialNull = nonTrivialNull(scrutinee); if (untrivialNull != null) { + metricNullInserted.increment(); return untrivialNull; } @@ -646,24 +676,6 @@ } /** - *

- * If the argument is known null due to its stamp, there's no need to have an anchor for that - * fact and this method returns null. - *

- * - *

- * Otherwise, if an anchor is found it is returned, null otherwise. - *

- */ - public GuardingNode nonTrivialNullAnchor(ValueNode object) { - assert FlowUtil.hasLegalObjectStamp(object); - if (StampTool.isObjectAlwaysNull(object)) { - return null; - } - return state.knownNull.get(GraphUtil.unproxify(object)); - } - - /** * * This method returns: *
    @@ -680,7 +692,7 @@ */ public PiNode nonTrivialNull(ValueNode object) { assert FlowUtil.hasLegalObjectStamp(object); - GuardingNode anchor = nonTrivialNullAnchor(object); + GuardingNode anchor = state.nonTrivialNullAnchor(object); if (anchor == null) { return null; } diff -r ff5cacf47b68 -r a3f897fb3289 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/Evidence.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/Evidence.java Mon May 05 22:18:46 2014 +0200 @@ -0,0 +1,61 @@ +/* + * 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.graal.phases.common.cfs; + +import com.oracle.graal.nodes.extended.GuardingNode; + +public class Evidence { + + public final GuardingNode success; + public final boolean failure; + + public Evidence(GuardingNode success, boolean failure) { + this.success = success; + this.failure = failure; + assert repOK(); + } + + public Evidence(GuardingNode success) { + this(success, false); + } + + private Evidence(boolean failure) { + this(null, failure); + } + + public boolean isPositive() { + return success != null; + } + + public boolean isNegative() { + return failure; + } + + private boolean repOK() { + // either success or failure, ie boolean-XOR + return (success != null) != failure; + } + + public static Evidence COUNTEREXAMPLE = new Evidence(true); + +} diff -r ff5cacf47b68 -r a3f897fb3289 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/FixedGuardReduction.java --- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/FixedGuardReduction.java Sat May 03 21:46:35 2014 +0200 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/FixedGuardReduction.java Mon May 05 22:18:46 2014 +0200 @@ -23,9 +23,7 @@ package com.oracle.graal.phases.common.cfs; import com.oracle.graal.nodes.*; -import com.oracle.graal.nodes.calc.IsNullNode; import com.oracle.graal.nodes.extended.GuardingNode; -import com.oracle.graal.nodes.java.*; import com.oracle.graal.phases.tiers.PhaseContext; /** @@ -99,118 +97,33 @@ return; } - /* - * Attempt to eliminate the current FixedGuardNode by using another GuardingNode already in - * scope and with equivalent condition. - */ + final boolean isTrue = !f.isNegated(); + final Evidence evidence = state.outcome(isTrue, f.condition()); - GuardingNode existingGuard = f.isNegated() ? state.falseFacts.get(f.condition()) : state.trueFacts.get(f.condition()); - if (existingGuard != null) { - // assert existingGuard instanceof FixedGuardNode; - metricFixedGuardNodeRemoved.increment(); - f.replaceAtUsages(existingGuard.asNode()); - graph.removeFixed(f); + // can't produce evidence, must be information gain + if (evidence == null) { + state.addFact(isTrue, f.condition(), f); return; } - final LogicNode cond = f.condition(); - final boolean isTrue = !f.isNegated(); - - /* - * A FixedGuardNode can only be removed provided a replacement anchor is found (so called - * "evidence"), ie an anchor that amounts to the same combination of (negated, condition) as - * for the FixedGuardNode at hand. Just deverbosifying the condition in place isn't - * semantics-preserving. - */ - - // TODO what about isDependencyTainted - - if (cond instanceof IsNullNode) { - final IsNullNode isNullNode = (IsNullNode) cond; - if (isTrue) { - // grab an anchor attesting nullness - final GuardingNode replacement = reasoner.nonTrivialNullAnchor(isNullNode.object()); - if (replacement != null) { - removeFixedGuardNode(f, replacement); - return; - } - if (state.isNonNull(isNullNode.object())) { - markFixedGuardNodeAlwaysFails(f); - return; - } - // can't produce evidence, fall-through to addFact - } else { - // grab an anchor attesting non-nullness - final Witness w = state.typeInfo(isNullNode.object()); - if (w != null && w.isNonNull()) { - removeFixedGuardNode(f, w.guard()); - return; - } - if (state.isNull(isNullNode.object())) { - markFixedGuardNodeAlwaysFails(f); - return; - } - // can't produce evidence, fall-through to addFact - } - } else if (cond instanceof InstanceOfNode) { - final InstanceOfNode iOf = (InstanceOfNode) cond; - final Witness w = state.typeInfo(iOf.object()); - if (isTrue) { - // grab an anchor attesting instanceof - if (w != null) { - if (w.isNonNull() && w.type() != null) { - if (iOf.type().isAssignableFrom(w.type())) { - removeFixedGuardNode(f, w.guard()); - return; - } - if (State.knownNotToConform(w.type(), iOf.type())) { - markFixedGuardNodeAlwaysFails(f); - return; - } - } - } - if (state.isNull(iOf.object())) { - markFixedGuardNodeAlwaysFails(f); - return; - } - // can't produce evidence, fall-through to addFact - } else { - // grab an anchor attesting not-instanceof - // (1 of 2) attempt determining nullness - final GuardingNode nullGuard = reasoner.nonTrivialNullAnchor(iOf.object()); - if (nullGuard != null) { - removeFixedGuardNode(f, nullGuard); - return; - } - // (2 of 2) attempt determining known-not-to-conform - if (w != null && !w.cluelessAboutType()) { - if (State.knownNotToConform(w.type(), iOf.type())) { - removeFixedGuardNode(f, w.guard()); - return; - } - } - // can't produce evidence, fall-through to addFact - } - } else if (isTrue && cond instanceof ShortCircuitOrNode) { - CastCheckExtractor cce = CastCheckExtractor.extract(cond); - if (cce != null && !State.isDependencyTainted(cce.subject, f)) { - // grab an anchor attesting check-cast - Witness w = state.typeInfo(cce.subject); - if (w != null && w.type() != null) { - if (cce.type.isAssignableFrom(w.type())) { - removeFixedGuardNode(f, w.guard()); - return; - } - if (State.knownNotToConform(w.type(), cce.type)) { - markFixedGuardNodeAlwaysFails(f); - return; - } - } - } - // can't produce evidence, fall-through to addFact + if (evidence.isPositive()) { + /* + * A FixedGuardNode can only be removed provided a replacement anchor is found (so + * called "evidence"), ie an anchor that amounts to the same combination of (negated, + * condition) as for the FixedGuardNode at hand. Just deverbosifying the condition in + * place isn't semantics-preserving. + * + * Eliminate the current FixedGuardNode by using another GuardingNode already in scope, + * a GuardingNode that guards a condition that is at least as strong as that of the + * FixedGuardNode. + */ + removeFixedGuardNode(f, evidence.success); + return; } - state.addFact(isTrue, cond, f); + assert evidence.isNegative(); + markFixedGuardNodeAlwaysFails(f); + } /** @@ -232,9 +145,7 @@ *

    */ private void removeFixedGuardNode(FixedGuardNode old, GuardingNode replacement) { - if (replacement == null) { - return; - } + assert replacement != null; metricFixedGuardNodeRemoved.increment(); old.replaceAtUsages(replacement.asNode()); graph.removeFixed(old); diff -r ff5cacf47b68 -r a3f897fb3289 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/FlowSensitiveReduction.java --- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/FlowSensitiveReduction.java Sat May 03 21:46:35 2014 +0200 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/FlowSensitiveReduction.java Mon May 05 22:18:46 2014 +0200 @@ -54,6 +54,14 @@ *
      *
    • simplification of side-effects free expressions, via * {@link com.oracle.graal.phases.common.cfs.EquationalReasoner#deverbosify(com.oracle.graal.graph.Node)} + *
        + *
      • + * at certain {@link com.oracle.graal.nodes.FixedNode}, see + * {@link #deverbosifyInputsInPlace(com.oracle.graal.nodes.ValueNode)}
      • + *
      • + * including for devirtualization, see + * {@link #deverbosifyInputsCopyOnWrite(com.oracle.graal.nodes.java.MethodCallTargetNode)}
      • + *
      *
    • *
    • simplification of control-flow: *
        @@ -76,6 +84,13 @@ * *

        * + *

        + * Metrics for this phase are displayed starting with FSR-prefix, their counters are + * hosted in {@link com.oracle.graal.phases.common.cfs.BaseReduction}, + * {@link com.oracle.graal.phases.common.cfs.EquationalReasoner} and + * {@link com.oracle.graal.phases.common.cfs.State}. + *

        + * * @see com.oracle.graal.phases.common.cfs.CheckCastReduction * @see com.oracle.graal.phases.common.cfs.GuardingPiReduction * @see com.oracle.graal.phases.common.cfs.FixedGuardReduction @@ -217,13 +232,8 @@ // `begin` denotes the default case of the TypeSwitchNode return; } - if (state.knownNotToConform(loadHub.object(), type)) { - postponedDeopts.addDeoptAfter(begin, UnreachedCode); - state.impossiblePath(); - return; - } // it's unwarranted to assume loadHub.object() to be non-null - // it also seems unwarranted state.trackCC(loadHub.object(), type, begin); + state.trackCC(loadHub.object(), type, begin); } } @@ -295,9 +305,16 @@ * */ private MethodCallTargetNode deverbosifyInputsCopyOnWrite(MethodCallTargetNode parent) { + final MethodCallTargetNode.InvokeKind ik = parent.invokeKind(); + final boolean shouldTryDevirt = (ik == MethodCallTargetNode.InvokeKind.Interface || ik == MethodCallTargetNode.InvokeKind.Virtual); + boolean shouldDowncastReceiver = shouldTryDevirt; MethodCallTargetNode changed = null; for (ValueNode i : FlowUtil.distinctValueAndConditionInputs(parent)) { - Node j = reasoner.deverbosify(i); + ValueNode j = (ValueNode) reasoner.deverbosify(i); + if (shouldDowncastReceiver) { + shouldDowncastReceiver = false; + j = reasoner.downcast(j); + } if (i != j) { assert j != parent; if (changed == null) { diff -r ff5cacf47b68 -r a3f897fb3289 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/State.java --- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/State.java Sat May 03 21:46:35 2014 +0200 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/State.java Mon May 05 22:18:46 2014 +0200 @@ -50,10 +50,10 @@ */ public final class State extends MergeableState implements Cloneable { - private static final DebugMetric metricTypeRegistered = Debug.metric("TypeRegistered"); - private static final DebugMetric metricNullnessRegistered = Debug.metric("NullnessRegistered"); - private static final DebugMetric metricObjectEqualsRegistered = Debug.metric("ObjectEqualsRegistered"); - private static final DebugMetric metricImpossiblePathDetected = Debug.metric("ImpossiblePathDetected"); + private static final DebugMetric metricTypeRegistered = Debug.metric("FSR-TypeRegistered"); + private static final DebugMetric metricNullnessRegistered = Debug.metric("FSR-NullnessRegistered"); + private static final DebugMetric metricObjectEqualsRegistered = Debug.metric("FSR-ObjectEqualsRegistered"); + private static final DebugMetric metricImpossiblePathDetected = Debug.metric("FSR-ImpossiblePathDetected"); /** *

        @@ -141,6 +141,18 @@ this.falseFacts = new IdentityHashMap<>(other.falseFacts); } + public boolean repOK() { + // trueFacts and falseFacts disjoint + for (LogicNode trueFact : trueFacts.keySet()) { + assert !falseFacts.containsKey(trueFact) : trueFact + " tracked as both true and false fact."; + } + // no scrutinee tracked as both known-null and known-non-null + for (ValueNode subject : knownNull.keySet()) { + assert !isNonNull(subject) : subject + " tracked as both known-null and known-non-null."; + } + return true; + } + /** * @return A new list containing only those states that are reachable. */ @@ -300,6 +312,9 @@ this.trueFacts = mergeTrueFacts(withReachableStates, merge); this.falseFacts = mergeFalseFacts(withReachableStates, merge); + + assert repOK(); + return true; } @@ -393,7 +408,8 @@ } /** - * @return true iff the argument is known to stand for an object conforming to the given type. + * @return true iff the argument definitely stands for an object-value that conforms to the + * given type. */ public boolean knownToConform(ValueNode object, ResolvedJavaType to) { assert FlowUtil.hasLegalObjectStamp(object); @@ -415,14 +431,20 @@ } /** - * @return true iff the argument is known to stand for an object that definitely does not - * conform to the given type. + * @return true iff the argument is known to stand for an object that is definitely non-null and + * moreover does not conform to the given type. */ - public boolean knownNotToConform(ValueNode object, ResolvedJavaType to) { + public boolean knownNotToPassCheckCast(ValueNode object, ResolvedJavaType to) { assert FlowUtil.hasLegalObjectStamp(object); assert !to.isPrimitive(); final ValueNode scrutinee = GraphUtil.unproxify(object); if (isNull(scrutinee)) { + // known-null means it conforms to whatever `to` + // and thus passes the check-cast + return false; + } + if (!isNonNull(scrutinee)) { + // unless `null` can be ruled out, a positive answer isn't safe return false; } ResolvedJavaType stampType = StampTool.typeOrNull(object); @@ -437,6 +459,34 @@ return false; } + /** + * @return true iff the argument is known to stand for an object that definitely does not + * conform to the given type (no matter whether the object turns out to be null or + * non-null). + */ + public boolean knownNotToPassInstanceOf(ValueNode object, ResolvedJavaType to) { + assert FlowUtil.hasLegalObjectStamp(object); + assert !to.isPrimitive(); + final ValueNode scrutinee = GraphUtil.unproxify(object); + if (isNull(scrutinee)) { + return true; + } + ResolvedJavaType stampType = StampTool.typeOrNull(object); + if (stampType != null && knownNotToConform(stampType, to)) { + // object turns out to be null, positive answer is correct + // object turns out non-null, positive answer is also correct + return true; + } + Witness w = typeInfo(scrutinee); + boolean witnessAnswer = w != null && !w.cluelessAboutType() && knownNotToConform(w.type(), to); + if (witnessAnswer) { + // object turns out to be null, positive answer is correct + // object turns out non-null, positive answer is also correct + return true; + } + return false; + } + // @formatter:off /** * \ | | | | @@ -526,6 +576,7 @@ Witness w = getOrElseAddTypeInfo(object); if (w.trackNN(anchor)) { versionNr++; + assert repOK(); return true; } return false; @@ -558,6 +609,7 @@ if (w.trackCC(observed, anchor)) { versionNr++; metricTypeRegistered.increment(); + assert repOK(); return true; } return false; @@ -584,6 +636,7 @@ if (w.trackIO(observed, anchor)) { versionNr++; metricTypeRegistered.increment(); + assert repOK(); return true; } return false; @@ -665,6 +718,7 @@ } else { addFactPrimordial(condition, isTrue ? trueFacts : falseFacts, anchor); } + assert repOK(); } /** @@ -675,7 +729,7 @@ private void addFactInstanceOf(boolean isTrue, InstanceOfNode instanceOf, GuardingNode anchor) { ValueNode object = instanceOf.object(); if (isTrue) { - if (knownNotToConform(object, instanceOf.type())) { + if (knownNotToPassInstanceOf(object, instanceOf.type())) { impossiblePath(); return; } @@ -776,6 +830,7 @@ trackNN(original, anchor); } } + assert repOK(); } /** @@ -813,4 +868,178 @@ falseFacts.clear(); } + /** + *

        + * If the argument is known null due to its stamp, there's no need to have an anchor for that + * fact and this method returns null. + *

        + * + *

        + * Otherwise, if an anchor is found it is returned, null otherwise. + *

        + */ + public GuardingNode nonTrivialNullAnchor(ValueNode object) { + assert FlowUtil.hasLegalObjectStamp(object); + if (StampTool.isObjectAlwaysNull(object)) { + return null; + } + return knownNull.get(GraphUtil.unproxify(object)); + } + + /** + * This method: + *
          + *
        • + * attempts to find an existing {@link com.oracle.graal.nodes.extended.GuardingNode} that + * implies the property stated by the arguments. If found, returns it as positive evidence.
        • + *
        • + * otherwise, if the property of interest is known not to hold, negative evidence is returned.
        • + *
        • + * otherwise, null is returned.
        • + *
        + */ + public Evidence outcome(boolean isTrue, LogicNode cond) { + + // attempt to find an anchor for the condition of interest, verbatim + if (isTrue) { + GuardingNode existingGuard = trueFacts.get(cond); + if (existingGuard != null) { + return new Evidence(existingGuard); + } + if (falseFacts.containsKey(cond)) { + return Evidence.COUNTEREXAMPLE; + } + } else { + GuardingNode existingGuard = falseFacts.get(cond); + if (existingGuard != null) { + return new Evidence(existingGuard); + } + if (trueFacts.containsKey(cond)) { + return Evidence.COUNTEREXAMPLE; + } + } + + if (cond instanceof IsNullNode) { + return outcomeIsNullNode(isTrue, (IsNullNode) cond); + } + + if (cond instanceof InstanceOfNode) { + return outcomeInstanceOfNode(isTrue, (InstanceOfNode) cond); + } + + if (cond instanceof ShortCircuitOrNode) { + return outcomeShortCircuitOrNode(isTrue, (ShortCircuitOrNode) cond); + } + + // can't produce evidence + return null; + } + + /** + * Utility method for {@link #outcome(boolean, com.oracle.graal.nodes.LogicNode)} + */ + private Evidence outcomeIsNullNode(boolean isTrue, IsNullNode isNullNode) { + if (isTrue) { + // grab an anchor attesting nullness + final GuardingNode replacement = nonTrivialNullAnchor(isNullNode.object()); + if (replacement != null) { + return new Evidence(replacement); + } + if (isNonNull(isNullNode.object())) { + return Evidence.COUNTEREXAMPLE; + } + } else { + // grab an anchor attesting non-nullness + final Witness w = typeInfo(isNullNode.object()); + if (w != null && w.isNonNull()) { + return new Evidence(w.guard()); + } + if (isNull(isNullNode.object())) { + return Evidence.COUNTEREXAMPLE; + } + } + // can't produce evidence + return null; + } + + /** + * Utility method for {@link #outcome(boolean, com.oracle.graal.nodes.LogicNode)} + */ + private Evidence outcomeInstanceOfNode(boolean isTrue, InstanceOfNode iOf) { + final Witness w = typeInfo(iOf.object()); + if (isTrue) { + if (isNull(iOf.object())) { + return Evidence.COUNTEREXAMPLE; + } + // grab an anchor attesting instanceof + if ((w != null) && (w.type() != null)) { + if (w.isNonNull()) { + if (iOf.type().isAssignableFrom(w.type())) { + return new Evidence(w.guard()); + } + } + if (State.knownNotToConform(w.type(), iOf.type())) { + // null -> fails instanceof + // non-null but non-conformant -> also fails instanceof + return Evidence.COUNTEREXAMPLE; + } + } + } else { + // grab an anchor attesting not-instanceof + // (1 of 2) attempt determining nullness + final GuardingNode nullGuard = nonTrivialNullAnchor(iOf.object()); + if (nullGuard != null) { + return new Evidence(nullGuard); + } + // (2 of 2) attempt determining known-not-to-conform + if (w != null && !w.cluelessAboutType()) { + if (State.knownNotToConform(w.type(), iOf.type())) { + return new Evidence(w.guard()); + } + } + } + // can't produce evidence + return null; + } + + /** + * Utility method for {@link #outcome(boolean, com.oracle.graal.nodes.LogicNode)} + */ + private Evidence outcomeShortCircuitOrNode(boolean isTrue, ShortCircuitOrNode orNode) { + if (!isTrue) { + // too tricky to reason about + return null; + } + CastCheckExtractor cce = CastCheckExtractor.extract(orNode); + if (cce != null) { + // grab an anchor attesting check-cast + Witness w = typeInfo(cce.subject); + if (w != null && w.type() != null) { + if (cce.type.isAssignableFrom(w.type())) { + return new Evidence(w.guard()); + } + if (isNonNull(cce.subject) && State.knownNotToConform(w.type(), cce.type)) { + return Evidence.COUNTEREXAMPLE; + } + } + } + // search for positive-evidence for the first or-input + Evidence evidenceX = outcome(!orNode.isXNegated(), orNode.getX()); + if (evidenceX != null && evidenceX.isPositive()) { + return evidenceX; + } + // search for positive-evidence for the second or-input + Evidence evidenceY = outcome(!orNode.isYNegated(), orNode.getY()); + if (evidenceY != null && evidenceY.isPositive()) { + return evidenceY; + } + // check for contradictions on both or-inputs + if (evidenceX != null && evidenceY != null) { + assert evidenceX.isNegative() && evidenceY.isNegative(); + return Evidence.COUNTEREXAMPLE; + } + // can't produce evidence + return null; + } + } diff -r ff5cacf47b68 -r a3f897fb3289 graal/com.oracle.graal.test/src/com/oracle/graal/test/GraalJUnitCore.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.test/src/com/oracle/graal/test/GraalJUnitCore.java Mon May 05 22:18:46 2014 +0200 @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2014, 2014, 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.graal.test; + +import java.util.*; + +import junit.runner.*; + +import org.junit.internal.*; +import org.junit.runner.*; +import org.junit.runner.notification.*; + +public class GraalJUnitCore { + + /** + * Run the tests contained in the classes named in the args. If all tests run + * successfully, exit with a status of 0. Otherwise exit with a status of 1. Write feedback + * while tests are running and write stack traces for all failed tests after the tests all + * complete. + * + * @param args names of classes in which to find tests to run + */ + public static void main(String... args) { + JUnitSystem system = new RealSystem(); + JUnitCore junitCore = new JUnitCore(); + system.out().println("GraalJUnitCore"); + system.out().println("JUnit version " + Version.id()); + List> classes = new ArrayList<>(); + List missingClasses = new ArrayList<>(); + boolean verbose = false; + boolean enableTiming = false; + for (String each : args) { + if (each.charAt(0) == '-') { + // command line arguments + if (each.contentEquals("-JUnitVerbose")) { + verbose = true; + } else if (each.contentEquals("-JUnitEnableTiming")) { + enableTiming = true; + } else { + system.out().println("Unknown command line argument: " + each); + } + + } else { + try { + classes.add(Class.forName(each)); + } catch (ClassNotFoundException e) { + system.out().println("Could not find class: " + each); + Description description = Description.createSuiteDescription(each); + Failure failure = new Failure(description, e); + missingClasses.add(failure); + } + } + } + GraalJUnitRunListener graalListener; + if (!verbose) { + graalListener = new GraalTextListener(system); + } else { + graalListener = new GraalVerboseTextListener(system); + } + if (enableTiming) { + graalListener = new TimingDecorator(graalListener); + } + junitCore.addListener(GraalTextListener.createRunListener(graalListener)); + Result result = junitCore.run(classes.toArray(new Class[0])); + for (Failure each : missingClasses) { + result.getFailures().add(each); + } + System.exit(result.wasSuccessful() ? 0 : 1); + } +} diff -r ff5cacf47b68 -r a3f897fb3289 graal/com.oracle.graal.test/src/com/oracle/graal/test/GraalJUnitRunListener.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.test/src/com/oracle/graal/test/GraalJUnitRunListener.java Mon May 05 22:18:46 2014 +0200 @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2014, 2014, 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.graal.test; + +import java.io.*; + +import org.junit.internal.*; +import org.junit.runner.*; +import org.junit.runner.notification.*; + +public interface GraalJUnitRunListener { + + /** + * Called before any tests have been run. + * + * @param description describes the tests to be run + */ + public void testRunStarted(Description description); + + /** + * Called when all tests have finished + * + * @param result the summary of the test run, including all the tests that failed + */ + public void testRunFinished(Result result); + + /** + * Called when a test class is about to be started. + * + * @param clazz the test class + */ + void testClassStarted(Class clazz); + + /** + * Called when all tests of a test class have finished. + * + * @param clazz the test class + */ + void testClassFinished(Class clazz); + + /** + * Called when an atomic test is about to be started. This is also called for ignored tests. + * + * @param description the description of the test that is about to be run (generally a class and + * method name) + */ + void testStarted(Description description); + + /** + * Called when an atomic test has finished, whether the test succeeds, fails or is ignored. + * + * @param description the description of the test that just ran + */ + void testFinished(Description description); + + /** + * Called when an atomic test fails. + * + * @param failure describes the test that failed and the exception that was thrown + */ + void testFailed(Failure failure); + + /** + * Called when a test will not be run, generally because a test method is annotated with + * {@link org.junit.Ignore}. + * + * @param description describes the test that will not be run + */ + public void testIgnored(Description description); + + /** + * Called when an atomic test succeeds. + * + * @param description describes the test that will not be run + */ + void testSucceeded(Description description); + + /** + * Called when an atomic test flags that it assumes a condition that is false + * + * @param failure describes the test that failed and the {@link AssumptionViolatedException} + * that was thrown + */ + public void testAssumptionFailure(Failure failure); + + /** + * Called after {@link #testClassFinished(Class)}. + */ + public void testClassFinishedDelimiter(); + + /** + * Called after {@link #testClassStarted(Class)} + */ + public void testClassStartedDelimiter(); + + /** + * Called after {@link #testStarted(Description)} + */ + public void testStartedDelimiter(); + + /** + * Called after {@link #testFailed(Failure)} + */ + public void testFinishedDelimiter(); + + public PrintStream getWriter(); + +} diff -r ff5cacf47b68 -r a3f897fb3289 graal/com.oracle.graal.test/src/com/oracle/graal/test/GraalJUnitRunListenerDecorator.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.test/src/com/oracle/graal/test/GraalJUnitRunListenerDecorator.java Mon May 05 22:18:46 2014 +0200 @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2014, 2014, 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.graal.test; + +import java.io.*; + +import org.junit.runner.*; +import org.junit.runner.notification.*; + +public class GraalJUnitRunListenerDecorator implements GraalJUnitRunListener { + + private final GraalJUnitRunListener l; + + public GraalJUnitRunListenerDecorator(GraalJUnitRunListener l) { + this.l = l; + } + + @Override + public void testRunStarted(Description description) { + l.testRunStarted(description); + } + + @Override + public void testRunFinished(Result result) { + l.testRunFinished(result); + } + + @Override + public void testAssumptionFailure(Failure failure) { + l.testAssumptionFailure(failure); + } + + @Override + public void testIgnored(Description description) { + l.testIgnored(description); + } + + @Override + public void testClassStarted(Class clazz) { + l.testClassStarted(clazz); + } + + @Override + public void testClassFinished(Class clazz) { + l.testClassFinished(clazz); + } + + @Override + public void testStarted(Description description) { + l.testStarted(description); + } + + @Override + public void testFinished(Description description) { + l.testFinished(description); + } + + @Override + public void testFailed(Failure failure) { + l.testFailed(failure); + } + + @Override + public void testSucceeded(Description description) { + l.testSucceeded(description); + } + + @Override + public PrintStream getWriter() { + return l.getWriter(); + } + + public void testClassFinishedDelimiter() { + l.testClassFinishedDelimiter(); + } + + public void testClassStartedDelimiter() { + l.testClassStartedDelimiter(); + } + + public void testStartedDelimiter() { + l.testStartedDelimiter(); + } + + public void testFinishedDelimiter() { + l.testFinishedDelimiter(); + } + +} diff -r ff5cacf47b68 -r a3f897fb3289 graal/com.oracle.graal.test/src/com/oracle/graal/test/GraalTextListener.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.test/src/com/oracle/graal/test/GraalTextListener.java Mon May 05 22:18:46 2014 +0200 @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2014, 2014, 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.graal.test; + +import java.io.*; + +import org.junit.internal.*; +import org.junit.runner.*; +import org.junit.runner.notification.*; + +public class GraalTextListener implements GraalJUnitRunListener { + + private final PrintStream fWriter; + + public GraalTextListener(JUnitSystem system) { + this(system.out()); + } + + public GraalTextListener(PrintStream writer) { + fWriter = writer; + } + + @Override + public PrintStream getWriter() { + return fWriter; + } + + @Override + public void testRunStarted(Description description) { + } + + @Override + public void testRunFinished(Result result) { + } + + @Override + public void testAssumptionFailure(Failure failure) { + } + + @Override + public void testClassStarted(Class clazz) { + } + + @Override + public void testClassFinished(Class clazz) { + } + + @Override + public void testStarted(Description description) { + getWriter().print('.'); + } + + @Override + public void testFinished(Description description) { + } + + @Override + public void testFailed(Failure failure) { + getWriter().print('E'); + } + + @Override + public void testSucceeded(Description description) { + } + + @Override + public void testIgnored(Description description) { + getWriter().print('I'); + } + + @Override + public void testClassFinishedDelimiter() { + } + + @Override + public void testClassStartedDelimiter() { + } + + @Override + public void testStartedDelimiter() { + } + + @Override + public void testFinishedDelimiter() { + } + + public static RunListener createRunListener(GraalJUnitRunListener l) { + return new TextListener(l.getWriter()) { + private Class lastClass; + private boolean failed; + + @Override + public final void testStarted(Description description) { + Class currentClass = description.getTestClass(); + if (currentClass != lastClass) { + if (lastClass != null) { + l.testClassFinished(lastClass); + l.testClassFinishedDelimiter(); + } + lastClass = currentClass; + l.testClassStarted(currentClass); + l.testClassStartedDelimiter(); + } + failed = false; + l.testStarted(description); + l.testStartedDelimiter(); + } + + @Override + public final void testFailure(Failure failure) { + failed = true; + l.testFailed(failure); + } + + @Override + public final void testFinished(Description description) { + // we have to do this because there is no callback for successful tests + if (!failed) { + l.testSucceeded(description); + } + l.testFinished(description); + l.testFinishedDelimiter(); + } + + @Override + public void testIgnored(Description description) { + l.testStarted(description); + l.testStartedDelimiter(); + l.testIgnored(description); + l.testFinished(description); + l.testFinishedDelimiter(); + } + + @Override + public void testRunStarted(Description description) { + l.testRunStarted(description); + } + + @Override + public void testRunFinished(Result result) { + if (lastClass != null) { + l.testClassFinished(lastClass); + } + l.testRunFinished(result); + super.testRunFinished(result); + } + + @Override + public void testAssumptionFailure(Failure failure) { + l.testAssumptionFailure(failure); + } + + }; + } + +} diff -r ff5cacf47b68 -r a3f897fb3289 graal/com.oracle.graal.test/src/com/oracle/graal/test/GraalVerboseTextListener.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.test/src/com/oracle/graal/test/GraalVerboseTextListener.java Mon May 05 22:18:46 2014 +0200 @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2014, 2014, 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.graal.test; + +import java.io.*; + +import org.junit.internal.*; +import org.junit.runner.*; +import org.junit.runner.notification.*; + +public class GraalVerboseTextListener extends GraalTextListener { + + public GraalVerboseTextListener(JUnitSystem system) { + this(system.out()); + } + + public GraalVerboseTextListener(PrintStream writer) { + super(writer); + } + + @Override + public void testClassStarted(Class clazz) { + getWriter().print(clazz.getName() + " started"); + } + + @Override + public void testClassFinished(Class clazz) { + getWriter().print(clazz.getName() + " finished"); + } + + @Override + public void testStarted(Description description) { + getWriter().print(" " + description.getMethodName() + ": "); + } + + @Override + public void testIgnored(Description description) { + getWriter().print("Ignored"); + } + + @Override + public void testSucceeded(Description description) { + getWriter().print("Passed"); + } + + @Override + public void testFailed(Failure failure) { + getWriter().print("FAILED"); + } + + @Override + public void testClassFinishedDelimiter() { + getWriter().println(); + } + + @Override + public void testClassStartedDelimiter() { + getWriter().println(); + } + + @Override + public void testFinishedDelimiter() { + getWriter().println(); + } + +} diff -r ff5cacf47b68 -r a3f897fb3289 graal/com.oracle.graal.test/src/com/oracle/graal/test/TimingDecorator.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.test/src/com/oracle/graal/test/TimingDecorator.java Mon May 05 22:18:46 2014 +0200 @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2014, 2014, 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.graal.test; + +import org.junit.runner.*; + +/** + * Timing support for JUnit test runs. + */ +public class TimingDecorator extends GraalJUnitRunListenerDecorator { + + private long startTime; + private long classStartTime; + + public TimingDecorator(GraalJUnitRunListener l) { + super(l); + } + + @Override + public void testClassStarted(Class clazz) { + classStartTime = System.nanoTime(); + super.testClassStarted(clazz); + } + + @Override + public void testClassFinished(Class clazz) { + long totalTime = System.nanoTime() - classStartTime; + super.testClassFinished(clazz); + getWriter().print(' ' + valueToString(totalTime)); + } + + @Override + public void testStarted(Description description) { + startTime = System.nanoTime(); + super.testStarted(description); + } + + @Override + public void testFinished(Description description) { + long totalTime = System.nanoTime() - startTime; + super.testFinished(description); + getWriter().print(" " + valueToString(totalTime)); + } + + private static String valueToString(long value) { + return String.format("%d.%d ms", value / 1000000, (value / 100000) % 10); + } + +} diff -r ff5cacf47b68 -r a3f897fb3289 mx/JUnitWrapper.java --- a/mx/JUnitWrapper.java Sat May 03 21:46:35 2014 +0200 +++ b/mx/JUnitWrapper.java Mon May 05 22:18:46 2014 +0200 @@ -27,8 +27,7 @@ * linux [depending on the settings]: ~2097k) * see http://msdn.microsoft.com/en-us/library/ms682425%28VS.85%29.aspx */ - -import org.junit.runner.*; +import com.oracle.graal.test.*; import java.io.*; import java.util.*; @@ -44,6 +43,10 @@ System.exit(1); } ArrayList tests = new ArrayList(1000); + // add JUnit command line arguments + for (int i = 1; i < args.length; i++) { + tests.add(args[i]); + } BufferedReader br = null; try { br = new BufferedReader(new FileReader(args[0])); @@ -72,6 +75,6 @@ } else { System.out.printf("executing junit tests now... (%d test classes)\n", strargs.length); } - JUnitCore.main(strargs); + GraalJUnitCore.main(strargs); } } diff -r ff5cacf47b68 -r a3f897fb3289 mx/mx_graal.py --- a/mx/mx_graal.py Sat May 03 21:46:35 2014 +0200 +++ b/mx/mx_graal.py Mon May 05 22:18:46 2014 +0200 @@ -946,7 +946,7 @@ f_testfile.close() harness(projectscp, vmArgs) -def _unittest(args, annotations, prefixcp="", whitelist=None): +def _unittest(args, annotations, prefixcp="", whitelist=None, verbose=False, enable_timing=False): mxdir = dirname(__file__) name = 'JUnitWrapper' javaSource = join(mxdir, name + '.java') @@ -955,6 +955,12 @@ if testfile is None: (_, testfile) = tempfile.mkstemp(".testclasses", "graal") os.close(_) + corecp = mx.classpath(['com.oracle.graal.test']) + coreArgs = [] + if verbose: + coreArgs.append('-JUnitVerbose') + if enable_timing: + coreArgs.append('-JUnitEnableTiming') def harness(projectscp, vmArgs): if not exists(javaClass) or getmtime(javaClass) < getmtime(javaSource): @@ -968,9 +974,9 @@ if len(testclasses) == 1: # Execute Junit directly when one test is being run. This simplifies # replaying the VM execution in a native debugger (e.g., gdb). - vm(prefixArgs + vmArgs + ['-cp', prefixcp + projectscp, 'org.junit.runner.JUnitCore'] + testclasses) + vm(prefixArgs + vmArgs + ['-cp', prefixcp + corecp + ':' + projectscp, 'com.oracle.graal.test.GraalJUnitCore'] + coreArgs + testclasses) else: - vm(prefixArgs + vmArgs + ['-cp', prefixcp + projectscp + os.pathsep + mxdir, name] + [testfile]) + vm(prefixArgs + vmArgs + ['-cp', prefixcp + corecp + ':' + projectscp + os.pathsep + mxdir, name] + [testfile] + coreArgs) try: _run_tests(args, harness, annotations, testfile, whitelist) @@ -983,6 +989,8 @@ --whitelist run only testcases which are included in the given whitelist + --verbose enable verbose JUnit output + --enable-timing enable JUnit test timing To avoid conflicts with VM options '--' can be used as delimiter. @@ -1020,6 +1028,8 @@ epilog=_unittestHelpSuffix, ) parser.add_argument('--whitelist', help='run testcases specified in whitelist only', metavar='') + parser.add_argument('--verbose', help='enable verbose JUnit output', action='store_true') + parser.add_argument('--enable-timing', help='enable JUnit test timing', action='store_true') ut_args = [] delimiter = False @@ -1046,7 +1056,7 @@ except IOError: mx.log('warning: could not read whitelist: ' + parsed_args.whitelist) - _unittest(args, ['@Test', '@Parameters'], whitelist=whitelist) + _unittest(args, ['@Test', '@Parameters'], whitelist=whitelist, verbose=parsed_args.verbose, enable_timing=parsed_args.enable_timing) def shortunittest(args): """alias for 'unittest --whitelist test/whitelist_shortunittest.txt'{0}""" @@ -1158,12 +1168,12 @@ with VM('server', 'product'): # hosted mode t = Task('UnitTests:hosted-product') - unittest([]) + unittest(['--enable-timing', '--verbose']) tasks.append(t.stop()) with VM('server', 'product'): # hosted mode t = Task('UnitTests-BaselineCompiler:hosted-product') - unittest(['--whitelist', 'test/whitelist_baseline.txt', '-G:+UseBaselineCompiler']) + unittest(['--enable-timing', '--verbose', '--whitelist', 'test/whitelist_baseline.txt', '-G:+UseBaselineCompiler']) tasks.append(t.stop()) for vmbuild in ['fastdebug', 'product']: diff -r ff5cacf47b68 -r a3f897fb3289 mxtool/mx.py --- a/mxtool/mx.py Sat May 03 21:46:35 2014 +0200 +++ b/mxtool/mx.py Mon May 05 22:18:46 2014 +0200 @@ -1300,8 +1300,21 @@ time.sleep(delay) # Makes the current subprocess accessible to the abort() function -# This is a tuple of the Popen object and args. -_currentSubprocess = (None, None) +# This is a list of tuples of the subprocess.Popen or +# multiprocessing.Process object and args. +_currentSubprocesses = [] + +def _addSubprocess(p, args): + entry = (p, args) + _currentSubprocesses.append(entry) + return entry + +def _removeSubprocess(entry): + if entry and entry in _currentSubprocesses: + try: + _currentSubprocesses.remove(entry) + except: + pass def waitOn(p): if get_os() == 'windows': @@ -1340,8 +1353,7 @@ if timeout is None and _opts.ptimeout != 0: timeout = _opts.ptimeout - global _currentSubprocess - + sub = None try: # On Unix, the new subprocess should be in a separate group so that a timeout alarm # can use os.killpg() to kill the whole subprocess group @@ -1359,7 +1371,7 @@ stdout = out if not callable(out) else subprocess.PIPE stderr = err if not callable(err) else subprocess.PIPE p = subprocess.Popen(args, cwd=cwd, stdout=stdout, stderr=stderr, preexec_fn=preexec_fn, creationflags=creationflags, env=env) - _currentSubprocess = (p, args) + sub = _addSubprocess(p, args) if callable(out): t = Thread(target=redirect, args=(p.stdout, out)) t.daemon = True # thread dies with the program @@ -1382,7 +1394,7 @@ except KeyboardInterrupt: abort(1) finally: - _currentSubprocess = (None, None) + _removeSubprocess(sub) if retcode and nonZeroIsFatal: if _opts.verbose: @@ -1663,21 +1675,20 @@ return result def _send_sigquit(): - p, args = _currentSubprocess - - def _isJava(): - if args: - name = args[0].split(os.sep)[-1] - return name == "java" - return False - - if p is not None and _isJava(): - if get_os() == 'windows': - log("mx: implement me! want to send SIGQUIT to my child process") - else: - _kill_process_group(p.pid, sig=signal.SIGQUIT) - time.sleep(0.1) - + for p, args in _currentSubprocesses: + + def _isJava(): + if args: + name = args[0].split(os.sep)[-1] + return name == "java" + return False + + if p is not None and _isJava(): + if get_os() == 'windows': + log("mx: implement me! want to send SIGQUIT to my child process") + else: + _kill_process_group(p.pid, sig=signal.SIGQUIT) + time.sleep(0.1) def abort(codeOrMessage): """ @@ -1692,12 +1703,14 @@ # import traceback # traceback.print_stack() - p, _ = _currentSubprocess - if p is not None: - if get_os() == 'windows': - p.kill() - else: - _kill_process_group(p.pid, signal.SIGKILL) + for p, args in _currentSubprocesses: + try: + if get_os() == 'windows': + p.terminate() + else: + _kill_process_group(p.pid, signal.SIGKILL) + except BaseException as e: + log('error while killing subprocess {} "{}": {}'.format(p.pid, ' '.join(args), e)) raise SystemExit(codeOrMessage) @@ -1798,7 +1811,7 @@ return get_env('JDT', join(_primary_suite.mxDir, 'ecj.jar')) class JavaCompileTask: - def __init__(self, args, proj, reason, javafilelist, jdk, outputDir, deps): + def __init__(self, args, proj, reason, javafilelist, jdk, outputDir, jdtJar, deps): self.proj = proj self.reason = reason self.javafilelist = javafilelist @@ -1806,6 +1819,7 @@ self.jdk = jdk self.outputDir = outputDir self.done = False + self.jdtJar = jdtJar self.args = args def __str__(self): @@ -1840,20 +1854,8 @@ cp = classpath(self.proj.name, includeSelf=True) toBeDeleted = [argfileName] - jdtJar = None - if not args.javac and args.jdt is not None: - if not args.jdt.endswith('.jar'): - abort('Path for Eclipse batch compiler does not look like a jar file: ' + args.jdt) - jdtJar = args.jdt - if not exists(jdtJar): - if os.path.abspath(jdtJar) == os.path.abspath(_defaultEcjPath()) and get_env('JDT', None) is None: - # Silently ignore JDT if default location is used but does not exist - jdtJar = None - else: - abort('Eclipse batch compiler jar does not exist: ' + args.jdt) - try: - if not jdtJar: + if not self.jdtJar: mainJava = java() if not args.error_prone: self.logCompilation('javac') @@ -1878,7 +1880,7 @@ else: self.logCompilation('JDT') - jdtVmArgs = ['-Xmx1g', '-jar', jdtJar] + jdtVmArgs = ['-Xmx1g', '-jar', self.jdtJar] jdtArgs = ['-' + compliance, '-cp', cp, '-g', '-enableJavadoc', @@ -1946,12 +1948,23 @@ compilerSelect.add_argument('--jdt', help='path to ecj.jar, the Eclipse batch compiler', default=_defaultEcjPath(), metavar='') compilerSelect.add_argument('--force-javac', action='store_true', dest='javac', help='use javac despite ecj.jar is found or not') - if suppliedParser: parser.add_argument('remainder', nargs=REMAINDER, metavar='...') args = parser.parse_args(args) + jdtJar = None + if not args.javac and args.jdt is not None: + if not args.jdt.endswith('.jar'): + abort('Path for Eclipse batch compiler does not look like a jar file: ' + args.jdt) + jdtJar = args.jdt + if not exists(jdtJar): + if os.path.abspath(jdtJar) == os.path.abspath(_defaultEcjPath()) and get_env('JDT', None) is None: + # Silently ignore JDT if default location is used but does not exist + jdtJar = None + else: + abort('Eclipse batch compiler jar does not exist: ' + args.jdt) + if args.only is not None: # N.B. This build will not include dependencies including annotation processor dependencies sortedProjects = [project(name) for name in args.only.split(',')] @@ -2087,7 +2100,7 @@ logv('[no Java sources for {0} - skipping]'.format(p.name)) continue - task = JavaCompileTask(args, p, buildReason, javafilelist, jdk, outputDir, taskDeps) + task = JavaCompileTask(args, p, buildReason, javafilelist, jdk, outputDir, jdtJar, taskDeps) if args.parallelize: # Best to initialize class paths on main process @@ -2113,6 +2126,7 @@ if t.proc.is_alive(): active.append(t) else: + _removeSubprocess(t.sub) if t.proc.exitcode != 0: return ([], joinTasks(tasks)) return (active, []) @@ -2126,10 +2140,18 @@ task._d = max([remainingDepsDepth(t) for t in incompleteDeps]) + 1 return task._d + def compareTasks(t1, t2): + d = remainingDepsDepth(t1) - remainingDepsDepth(t2) + if d == 0: + t1Work = (1 + len(t1.proj.annotation_processors())) * len(t1.javafilelist) + t2Work = (1 + len(t2.proj.annotation_processors())) * len(t2.javafilelist) + d = t1Work - t2Work + return d + def sortWorklist(tasks): for t in tasks: t._d = None - return sorted(tasks, lambda x, y: remainingDepsDepth(x) - remainingDepsDepth(y)) + return sorted(tasks, compareTasks) import multiprocessing cpus = multiprocessing.cpu_count() @@ -2162,6 +2184,7 @@ task.proc = multiprocessing.Process(target=executeTask, args=(task,)) task.proc.start() active.append(task) + task.sub = _addSubprocess(task.proc, ['JavaCompileTask', str(task)]) if len(active) == cpus: break diff -r ff5cacf47b68 -r a3f897fb3289 src/cpu/x86/vm/templateTable_x86_64.cpp --- a/src/cpu/x86/vm/templateTable_x86_64.cpp Sat May 03 21:46:35 2014 +0200 +++ b/src/cpu/x86/vm/templateTable_x86_64.cpp Mon May 05 22:18:46 2014 +0200 @@ -107,10 +107,6 @@ return Address(rsp, Interpreter::expr_offset_in_bytes(2)); } -static inline Address at_tos_p3() { - return Address(rsp, Interpreter::expr_offset_in_bytes(3)); -} - // Condition conversion static Assembler::Condition j_not(TemplateTable::Condition cc) { switch (cc) { diff -r ff5cacf47b68 -r a3f897fb3289 src/share/vm/code/nmethod.cpp --- a/src/share/vm/code/nmethod.cpp Sat May 03 21:46:35 2014 +0200 +++ b/src/share/vm/code/nmethod.cpp Mon May 05 22:18:46 2014 +0200 @@ -1064,7 +1064,6 @@ void nmethod::print_on(outputStream* st, const char* msg) const { if (st != NULL) { ttyLocker ttyl; - if (CIPrintCompilerName) st->print("%s:", compiler()->name()); if (WizardMode) { CompileTask::print_compilation(st, this, msg, /*short_form:*/ true); st->print_cr(" (" INTPTR_FORMAT ")", this); diff -r ff5cacf47b68 -r a3f897fb3289 src/share/vm/compiler/compileBroker.cpp --- a/src/share/vm/compiler/compileBroker.cpp Sat May 03 21:46:35 2014 +0200 +++ b/src/share/vm/compiler/compileBroker.cpp Mon May 05 22:18:46 2014 +0200 @@ -370,8 +370,6 @@ // CompileTask::print_line void CompileTask::print_line() { ttyLocker ttyl; // keep the following output all in one block - // print compiler name if requested - if (CIPrintCompilerName) tty->print("%s:", CompileBroker::compiler_name(comp_level())); print_compilation(); } @@ -384,6 +382,8 @@ if (!short_form) { st->print("%7d ", (int) st->time_stamp().milliseconds()); // print timestamp } + // print compiler name if requested + if (CIPrintCompilerName) tty->print("%s:", CompileBroker::compiler_name(comp_level)); st->print("%4d ", compile_id); // print compilation number // For unloaded methods the transition to zombie occurs after the diff -r ff5cacf47b68 -r a3f897fb3289 src/share/vm/graal/graalCompiler.cpp --- a/src/share/vm/graal/graalCompiler.cpp Sat May 03 21:46:35 2014 +0200 +++ b/src/share/vm/graal/graalCompiler.cpp Mon May 05 22:18:46 2014 +0200 @@ -48,10 +48,6 @@ return; } - ThreadToNativeFromVM trans(JavaThread::current()); - JavaThread* THREAD = JavaThread::current(); - TRACE_graal_1("GraalCompiler::initialize"); - uintptr_t heap_end = (uintptr_t) Universe::heap()->reserved_region().end(); uintptr_t allocation_end = heap_end + ((uintptr_t)16) * 1024 * 1024 * 1024; AMD64_ONLY(guarantee(heap_end < allocation_end, "heap end too close to end of address space (might lead to erroneous TLAB allocations)")); @@ -71,6 +67,10 @@ } #endif + ThreadToNativeFromVM trans(JavaThread::current()); + JavaThread* THREAD = JavaThread::current(); + TRACE_graal_1("GraalCompiler::initialize"); + JNIEnv *env = ((JavaThread *) Thread::current())->jni_environment(); jclass klass = env->FindClass("com/oracle/graal/hotspot/bridge/CompilerToVMImpl"); if (klass == NULL) { diff -r ff5cacf47b68 -r a3f897fb3289 src/share/vm/prims/jvmtiTagMap.cpp --- a/src/share/vm/prims/jvmtiTagMap.cpp Sat May 03 21:46:35 2014 +0200 +++ b/src/share/vm/prims/jvmtiTagMap.cpp Mon May 05 22:18:46 2014 +0200 @@ -2790,6 +2790,7 @@ return true; } +#ifdef ASSERT // verify that a static oop field is in range static inline bool verify_static_oop(InstanceKlass* ik, oop mirror, int offset) { @@ -2804,6 +2805,7 @@ return false; } } +#endif // #ifdef ASSERT // a class references its super class, interfaces, class loader, ... // and finally its static fields diff -r ff5cacf47b68 -r a3f897fb3289 src/share/vm/runtime/mutex.cpp --- a/src/share/vm/runtime/mutex.cpp Sat May 03 21:46:35 2014 +0200 +++ b/src/share/vm/runtime/mutex.cpp Mon May 05 22:18:46 2014 +0200 @@ -280,16 +280,6 @@ return x & 0x7FFFFFFF ; } -static inline jint MarsagliaXOR (jint * const a) { - jint x = *a ; - if (x == 0) x = UNS(a)|1 ; - x ^= x << 6; - x ^= ((unsigned)x) >> 21; - x ^= x << 7 ; - *a = x ; - return x & 0x7FFFFFFF ; -} - static int Stall (int its) { static volatile jint rv = 1 ; volatile int OnFrame = 0 ;