Mercurial > hg > truffle
view graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/State.java @ 18163:c88ab4f1f04a
re-enabled Checkstyle with the release of 6.0 that supports Java 8; fixed existing Checkstyle warnings
author | Doug Simon <doug.simon@oracle.com> |
---|---|
date | Fri, 24 Oct 2014 16:18:10 +0200 |
parents | 4182366b8eed |
children | 1518c3296cc8 |
line wrap: on
line source
/* * 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 static com.oracle.graal.graph.util.CollectionsAccess.*; import java.lang.reflect.*; import java.util.*; import com.oracle.graal.api.meta.*; import com.oracle.graal.compiler.common.type.*; import com.oracle.graal.debug.*; import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.calc.*; import com.oracle.graal.nodes.extended.*; import com.oracle.graal.nodes.java.*; import com.oracle.graal.nodes.spi.*; import com.oracle.graal.nodes.type.*; import com.oracle.graal.nodes.util.*; import com.oracle.graal.phases.graph.*; /** * A State instance is mutated in place as each FixedNode is visited in a basic block of * instructions. Basic block: starts with a {@link com.oracle.graal.nodes.BeginNode BeginNode}, ends * at an {@link com.oracle.graal.nodes.EndNode EndNode} or * {@link com.oracle.graal.nodes.ControlSinkNode ControlSinkNode} and lacks intervening control * splits or merges. */ public final class State extends MergeableState<State> implements Cloneable { 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"); /** * <p> * Each state update results in a higher {@link State#versionNr versionNr}. The * {@link State#versionNr versionNr} of different State instances can't be meaningfully compared * (ie, same {@link State#versionNr versionNr} just indicates they've gone through the same * number of updates). In particular, the {@link State#versionNr versionNr} of a merged state * doesn't guarantee any more than being different from those of the states being merged. * </p> * * <p> * Still, {@link State#versionNr versionNr} proves useful in two cases: * * <ul> * <li>recording the {@link State#versionNr versionNr} right after {@link State State} cloning, * allows finding out afterwards whether (a) both states have diverged, (b) just one of them, or * (c) none of them.</li> * <li>a {@link State State} may become {@link State#isUnreachable isUnreachable}. In such case, * it may make a difference whether any updates were performed on the state from the time it was * cloned. Those updates indicate information not available in the state is was cloned from. For * the purposes of {@link FlowSensitiveReduction FlowSensitiveReduction} an unreachable state * need not be merged with any other (because control-flow won't reach the merge point over the * path of the unreachable state).</li> * </ul> * </p> * */ int versionNr = 0; boolean isUnreachable = false; /** * Getting here implies an opportunity was detected for dead-code-elimination. A counterpoint * argument goes as follows: perhaps we don't get here that often, in which case the effort to * detect an "impossible path" could be shaved off. * * @see com.oracle.graal.phases.common.cfs.BaseReduction.PostponedDeopt */ void impossiblePath() { isUnreachable = true; metricImpossiblePathDetected.increment(); } /** * <p> * This map tracks properties about reference-values, ie combinations of: definitely-null, * known-to-be-non-null, seen-type. * </p> * * <p> * In contrast to {@link #trueFacts} and {@link #falseFacts}, this map can answer queries even * though the exact {@link LogicNode} standing for such query hasn't been tracked. For example, * queries about subtyping. Additionally, a {@link Witness} can combine separate pieces of * information more flexibly, eg, two separate observations about non-null and check-cast are * promoted to an instance-of witness. * </p> * */ private Map<ValueNode, Witness> typeRefinements; Map<LogicNode, GuardingNode> trueFacts; Map<LogicNode, GuardingNode> falseFacts; public State() { this.typeRefinements = newNodeIdentityMap(); this.trueFacts = newNodeIdentityMap(); this.falseFacts = newNodeIdentityMap(); } public State(State other) { this.isUnreachable = other.isUnreachable; this.versionNr = other.versionNr; this.typeRefinements = newNodeIdentityMap(); for (Map.Entry<ValueNode, Witness> entry : other.typeRefinements.entrySet()) { this.typeRefinements.put(entry.getKey(), new Witness(entry.getValue())); } this.trueFacts = newNodeIdentityMap(other.trueFacts); this.falseFacts = newNodeIdentityMap(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."; } return true; } /** * @return A new list containing only those states that are reachable. */ private static ArrayList<State> reachableStates(List<State> states) { ArrayList<State> result = new ArrayList<>(states); Iterator<State> iter = result.iterator(); while (iter.hasNext()) { if (iter.next().isUnreachable) { iter.remove(); } } return result; } private Map<ValueNode, Witness> mergeKnownTypes(MergeNode merge, ArrayList<State> withReachableStates) { Map<ValueNode, Witness> newKnownTypes = newNodeIdentityMap(); for (Map.Entry<ValueNode, Witness> entry : typeRefinements.entrySet()) { ValueNode node = entry.getKey(); Witness type = new Witness(entry.getValue()); for (State other : withReachableStates) { Witness otherType = other.typeInfo(node); if (otherType == null) { type = null; break; } type.merge(otherType, merge); } if (type != null && type.knowsBetterThan(node)) { assert node == GraphUtil.unproxify(node); newKnownTypes.put(node, type); } } return newKnownTypes; } /** * <p> * This method handles phis, by adding to the resulting state any information that can be gained * (about type-refinement and nullness) based on the data available at each of the incoming * branches. * </p> * * <p> * In more detail, <code>FlowSensitiveReduction#visitAbstractEndNode()</code> has already * deverbosified the phi-values contributed by each reachable branch. The paths that * {@link com.oracle.graal.phases.common.cfs.FlowSensitiveReduction} determined to be * unreachable will be eliminated by canonicalization and dead code elimination. For now they * still exist, thus polluting the result of * {@link com.oracle.graal.nodes.ValuePhiNode#inferStamp()} but we are careful to skip them when * merging type-witnesses and known-null maps. * </p> */ private void mergePhis(MergeNode merge, List<State> withStates, Map<ValueNode, Witness> newKnownPhiTypes) { if (merge instanceof LoopBeginNode) { return; } for (PhiNode phi : merge.phis()) { assert phi == GraphUtil.unproxify(phi); if (phi instanceof ValuePhiNode && phi.getKind() == Kind.Object) { ArrayList<ValueNode> reachingValues = new ArrayList<>(); if (!isUnreachable) { reachingValues.add(phi.valueAt(0)); } for (int i = 0; i < withStates.size(); i++) { State otherState = withStates.get(i); if (!otherState.isUnreachable) { reachingValues.add(phi.valueAt(i + 1)); } } assert !reachingValues.isEmpty(); ObjectStamp phiStamp = (ObjectStamp) phi.stamp(); ObjectStamp nonPollutedStamp = (ObjectStamp) StampTool.meet(reachingValues); Witness w = new Witness(nonPollutedStamp, merge); if (w.isNull() && !phiStamp.alwaysNull()) { // precision gain: null newKnownPhiTypes.put(phi, w); } else if (FlowUtil.isMorePrecise(w.type(), phiStamp.type())) { // precision gain regarding type newKnownPhiTypes.put(phi, w); // confirm no precision loss regarding nullness assert implies(phiStamp.nonNull(), w.isNonNull()); assert implies(phiStamp.alwaysNull(), w.isNull()); } else if (w.isNonNull() && !phiStamp.nonNull()) { // precision gain: non-null newKnownPhiTypes.put(phi, w); // confirm no precision loss regarding type assert !FlowUtil.isMorePrecise(phiStamp.type(), w.type()); } } } } private static boolean implies(boolean a, boolean b) { return !a || b; } @Override public boolean merge(MergeNode merge, List<State> withStates) { ArrayList<State> withReachableStates = reachableStates(withStates); if (withReachableStates.isEmpty()) { return true; } for (State other : withReachableStates) { versionNr = Math.max(versionNr, other.versionNr) + 1; if (!other.isUnreachable) { isUnreachable = false; } } if (isUnreachable) { typeRefinements.clear(); trueFacts.clear(); falseFacts.clear(); return true; } // may also get updated in a moment, during processing of phi nodes. Map<ValueNode, Witness> newKnownTypes = mergeKnownTypes(merge, withReachableStates); // may also get updated in a moment, during processing of phi nodes. mergePhis(merge, withStates, newKnownTypes); this.typeRefinements = newKnownTypes; this.trueFacts = mergeTrueFacts(withReachableStates, merge); this.falseFacts = mergeFalseFacts(withReachableStates, merge); assert repOK(); return true; } private Map<LogicNode, GuardingNode> mergeTrueFacts(ArrayList<State> withReachableStates, GuardingNode merge) { Map<LogicNode, GuardingNode> newTrueConditions = newNodeIdentityMap(); for (Map.Entry<LogicNode, GuardingNode> entry : trueFacts.entrySet()) { LogicNode check = entry.getKey(); GuardingNode guard = entry.getValue(); for (State other : withReachableStates) { GuardingNode otherGuard = other.trueFacts.get(check); if (otherGuard == null) { guard = null; break; } if (otherGuard != guard) { guard = merge; } } if (guard != null) { newTrueConditions.put(check, guard); } } return newTrueConditions; } private Map<LogicNode, GuardingNode> mergeFalseFacts(ArrayList<State> withReachableStates, GuardingNode merge) { Map<LogicNode, GuardingNode> newFalseConditions = newNodeIdentityMap(); for (Map.Entry<LogicNode, GuardingNode> entry : falseFacts.entrySet()) { LogicNode check = entry.getKey(); GuardingNode guard = entry.getValue(); for (State other : withReachableStates) { GuardingNode otherGuard = other.falseFacts.get(check); if (otherGuard == null) { guard = null; break; } if (otherGuard != guard) { guard = merge; } } if (guard != null) { newFalseConditions.put(check, guard); } } return newFalseConditions; } /** * @return null if no type-witness available for the argument, the witness otherwise. */ public Witness typeInfo(ValueNode object) { assert FlowUtil.hasLegalObjectStamp(object); return typeRefinements.get(GraphUtil.unproxify(object)); } /** * @return true iff the argument is known to stand for null. */ public boolean isNull(ValueNode object) { assert FlowUtil.hasLegalObjectStamp(object); if (StampTool.isObjectAlwaysNull(object)) { return true; } ValueNode scrutinee = GraphUtil.unproxify(object); Witness w = typeRefinements.get(scrutinee); return (w != null) && w.isNull(); } /** * <p> * It makes a difference calling {@link Witness#isNonNull()} as opposed to * {@link State#isNonNull(com.oracle.graal.nodes.ValueNode)}. The former guarantees the witness * provides a guard certifying non-nullness. The latter just tells us there exists some guard * that certifies the property we asked about. * </p> * * <p> * TODO improvement: isKnownNonNull could be made smarter by noticing some nodes always denote a * non-null value (eg, ObjectGetClassNode). Similarly for isKnownNull. Code that looks at the * stamp as well as code that looks for a non-null-witness would benefit from also checking such * extended isKnownNonNull. Alternatively, the stamp of those nodes should always have * is-non-null set. * </p> * * @return true iff the argument is known to stand for non-null. */ public boolean isNonNull(ValueNode object) { assert FlowUtil.hasLegalObjectStamp(object); if (StampTool.isObjectNonNull(object)) { return true; } Witness w = typeInfo(object); return w == null ? false : w.isNonNull(); } /** * @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); assert !to.isPrimitive(); ResolvedJavaType stampType = StampTool.typeOrNull(object); if (stampType != null && to.isAssignableFrom(stampType)) { return true; } final ValueNode scrutinee = GraphUtil.unproxify(object); if (isNull(scrutinee)) { return true; } Witness w = typeInfo(scrutinee); boolean witnessAnswer = w != null && w.type() != null && to.isAssignableFrom(w.type()); if (witnessAnswer) { return true; } return false; } /** * @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 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); if (stampType != null && knownNotToConform(stampType, to)) { return true; } Witness w = typeInfo(scrutinee); boolean witnessAnswer = w != null && !w.cluelessAboutType() && knownNotToConform(w.type(), to); if (witnessAnswer) { return true; } 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 /** * Determines if the first argument is known not to conform to the second argument. * * <pre> * \ | | | | * \ b| | | | * a \ | | | | * \|iface|final|non-f| * -----+-----------------| * iface| F | F | F | * -----+-----------------| * final| C | C | C | * -----+-----------------| * non-f| F | C | C | * -----------------------+ * * where: * F: false * C: check * iface: interface * final: exact non-interface reference-type * non-f: non-exact non-interface reference-type * </pre> * @return true iff the first argument is known not to conform to the second argument. */ // @formatter:on public static boolean knownNotToConform(ResolvedJavaType a, ResolvedJavaType b) { assert !a.isPrimitive(); assert !b.isPrimitive(); if (b.isAssignableFrom(a)) { return false; } if (a.isInterface()) { return false; } boolean aFinal = Modifier.isFinal(a.getModifiers()); if (b.isInterface() && !aFinal) { return false; } if (a.isAssignableFrom(b)) { return false; } else { return true; } } @Override public State clone() { return new State(this); } /** * Porcelain method. */ private Witness getOrElseAddTypeInfo(ValueNode object) { ValueNode scrutinee = GraphUtil.unproxify(object); Witness w = typeRefinements.get(scrutinee); if (w == null) { w = new Witness(); typeRefinements.put(scrutinee, w); } return w; } /** * <p> * Updates this {@link State State} to account for an observation about the scrutinee being * non-null. In case instanceof was observed, * {@link #trackIO(com.oracle.graal.nodes.ValueNode, com.oracle.graal.api.meta.ResolvedJavaType, com.oracle.graal.nodes.extended.GuardingNode) * <code>trackIO(ResolvedJavaType, GuardingNode)</code>} should be invoked instead. * </p> * * <p> * No check is made on whether a contradiction would be introduced into the factbase (in which * case the state should be marked unreachable), the caller takes care of that. * </p> * * @return whether state was updated (iff the observation added any new information) */ public boolean trackNN(ValueNode object, GuardingNode anchor) { if (isDependencyTainted(object, anchor)) { return false; } assert anchor instanceof FixedNode; ResolvedJavaType stampType = StampTool.typeOrNull(object); if (stampType != null && !stampType.isInterface()) { return trackIO(object, stampType, anchor); } Witness w = getOrElseAddTypeInfo(object); if (w.trackNN(anchor)) { versionNr++; assert repOK(); return true; } return false; } /** * Updates this {@link State State} to account for an observation about the scrutinee conforming * to a type. In case instanceof was observed, * {@link #trackIO(com.oracle.graal.nodes.ValueNode, com.oracle.graal.api.meta.ResolvedJavaType, com.oracle.graal.nodes.extended.GuardingNode) * <code>trackIO(ResolvedJavaType, GuardingNode)</code>} should be invoked instead. * * <p> * No check is made on whether a contradiction would be introduced into the factbase (in which * case the state should be marked unreachable), the caller must take care of that. * </p> * * @return false iff the observed type is an interface, or doesn't provide any new information * not already known. Ie, this method returns true iff the observation resulted in * information gain. */ public boolean trackCC(ValueNode object, ResolvedJavaType observed, GuardingNode anchor) { if (observed.isInterface()) { return false; } if (isDependencyTainted(object, anchor)) { return false; } assert anchor instanceof FixedNode; Witness w = getOrElseAddTypeInfo(object); if (w.trackCC(observed, anchor)) { versionNr++; metricTypeRegistered.increment(); assert repOK(); return true; } return false; } /** * Updates this {@link State State} to account for an observation about the non-null scrutinee * conforming to a type. * * <p> * No check is made on whether a contradiction would be introduced into the factbase (in which * case the state should be marked unreachable), the caller must take care of that. * </p> * * @return whether state was updated (iff the observation added any new information) */ public boolean trackIO(ValueNode object, ResolvedJavaType observed, GuardingNode anchor) { assert !observed.isInterface() : "no infrastructure yet in State.Witness to support interfaces in general"; if (isDependencyTainted(object, anchor)) { return false; } assert anchor instanceof FixedNode; Witness w = getOrElseAddTypeInfo(object); if (w.trackIO(observed, anchor)) { versionNr++; metricTypeRegistered.increment(); assert repOK(); return true; } return false; } /** * This method increases {@link #versionNr} (thus potentially invalidating * {@link EquationalReasoner EquationalReasoner}'s caches) only if the fact wasn't known * already. * * <p> * No check is made on whether a contradiction would be introduced into the factbase (in which * case the state should be marked unreachable), the caller must take care of that. * </p> * */ private void addFactPrimordial(LogicNode condition, Map<LogicNode, GuardingNode> to, GuardingNode anchor) { assert condition != null; if (!to.containsKey(condition)) { versionNr++; to.put(condition, anchor); } } /** * Ideas for the future. * <ul> * <li>track inferred less-than edges from (accumulated) CompareNode-s</li> * <li>track set-representative for equality classes determined by (chained) IntegerTestNode</li> * </ul> * */ public void addFact(boolean isTrue, LogicNode condition, GuardingNode anchor) { assert anchor != null; assert anchor instanceof FixedNode; assert !isUnreachable; if (condition instanceof LogicConstantNode) { if (((LogicConstantNode) condition).getValue() != isTrue) { impossiblePath(); } return; } if (condition instanceof LogicNegationNode) { addFact(!isTrue, ((LogicNegationNode) condition).getValue(), anchor); } else if (condition instanceof ShortCircuitOrNode) { /* * We can register the conditions being or-ed as long as the anchor is a fixed node, * because floating guards will be registered at a BeginNode but might be "valid" only * later due to data flow dependencies. Therefore, registering both conditions of a * ShortCircuitOrNode for a floating guard could lead to cycles in data flow, because * the guard will be used as anchor for both conditions, and one condition could be * depending on the other. */ if (isTrue) { CastCheckExtractor cce = CastCheckExtractor.extract(condition); if (cce == null || isDependencyTainted(cce.subject, anchor)) { addFactPrimordial(condition, isTrue ? trueFacts : falseFacts, anchor); } else { trackCC(cce.subject, cce.type, anchor); } } else { ShortCircuitOrNode disjunction = (ShortCircuitOrNode) condition; addFact(disjunction.isXNegated(), disjunction.getX(), anchor); // previous addFact might have resulted in impossiblePath() if (isUnreachable) { return; } addFact(disjunction.isYNegated(), disjunction.getY(), anchor); } } else if (condition instanceof InstanceOfNode) { addFactInstanceOf(isTrue, (InstanceOfNode) condition, anchor); } else if (condition instanceof IsNullNode) { IsNullNode nullCheck = (IsNullNode) condition; addNullness(isTrue, nullCheck.getValue(), anchor); } else if (condition instanceof ObjectEqualsNode) { addFactObjectEqualsNode(isTrue, (ObjectEqualsNode) condition, anchor); } else { addFactPrimordial(condition, isTrue ? trueFacts : falseFacts, anchor); } assert repOK(); } /** * An instanceof hint is tracked differently depending on whether it's an interface-test or not * (because type-refinements currently lacks the ability to track interface types). * */ private void addFactInstanceOf(boolean isTrue, InstanceOfNode instanceOf, GuardingNode anchor) { ValueNode object = instanceOf.getValue(); if (isTrue) { if (knownNotToPassInstanceOf(object, instanceOf.type())) { impossiblePath(); return; } addNullness(false, object, anchor); if (instanceOf.type().isInterface()) { if (!knownToConform(object, instanceOf.type())) { addFactPrimordial(instanceOf, trueFacts, anchor); } } else { trackIO(object, instanceOf.type(), anchor); } } } private void addFactObjectEqualsNode(boolean isTrue, ObjectEqualsNode equals, GuardingNode anchor) { if (isDependencyTainted(equals.getX(), anchor)) { return; } if (isDependencyTainted(equals.getY(), anchor)) { return; } assert anchor instanceof FixedNode; ValueNode x = GraphUtil.unproxify(equals.getX()); ValueNode y = GraphUtil.unproxify(equals.getY()); if (isTrue) { if (isNull(x) && isNonNull(y)) { impossiblePath(); return; } if (isNonNull(x) && isNull(y)) { impossiblePath(); return; } if (isNull(x) || isNull(y)) { metricObjectEqualsRegistered.increment(); addNullness(true, equals.getX(), anchor); addNullness(true, equals.getY(), anchor); } else if (isNonNull(x) || isNonNull(y)) { metricObjectEqualsRegistered.increment(); addNullness(false, equals.getX(), anchor); addNullness(false, equals.getY(), anchor); } Witness wx = typeInfo(x); Witness wy = typeInfo(y); if (wx == null && wy == null) { return; } else if (wx != null && wy != null) { // tighten their type-hints, provided at least one available // both witnesses may have seen == null, ie they may be NN witnesses ResolvedJavaType best = FlowUtil.tighten(wx.type(), wy.type()); if (best != null) { assert !best.isInterface(); // type tightening is enough, nullness already taken care of trackCC(equals.getX(), best, anchor); trackCC(equals.getY(), best, anchor); } } else if (wx == null) { typeRefinements.put(x, new Witness(wy)); } else if (wy == null) { typeRefinements.put(y, new Witness(wx)); } } else { if (isNull(x) && !isNonNull(y)) { metricObjectEqualsRegistered.increment(); addNullness(false, equals.getY(), anchor); } else if (!isNonNull(x) && isNull(y)) { metricObjectEqualsRegistered.increment(); addNullness(false, equals.getX(), anchor); } } } /** * Adds information about the nullness of a value. If isNull is true then the value is known to * be null, otherwise the value is known to be non-null. */ public void addNullness(boolean isNull, ValueNode value, GuardingNode anchor) { if (isDependencyTainted(value, anchor)) { return; } assert anchor instanceof FixedNode; ValueNode scrutinee = GraphUtil.unproxify(value); boolean wasNull = isNull(scrutinee); boolean wasNonNull = isNonNull(scrutinee); if (isNull) { if (wasNonNull) { impossiblePath(); } else { metricNullnessRegistered.increment(); versionNr++; Witness w = getOrElseAddTypeInfo(scrutinee); w.trackDN(anchor); } } else { if (wasNull) { impossiblePath(); } else { metricNullnessRegistered.increment(); trackNN(scrutinee, anchor); } } assert repOK(); } /** * * @return true iff `value` may lose dependency not covered by `anchor`. */ public static boolean isDependencyTainted(ValueNode value, GuardingNode anchor) { assert anchor instanceof FixedNode; if (value instanceof ValueProxy) { if (value instanceof GuardedNode) { GuardedNode gn = (GuardedNode) value; GuardingNode guardian = gn.getGuard(); if (guardian != null) { boolean isGuardedByFixed = guardian instanceof FixedNode; if (!isGuardedByFixed) { return true; } } } // if (value instanceof GuardingNode) { // return true; // } ValueProxy proxy = (ValueProxy) value; return isDependencyTainted(proxy.getOriginalNode(), anchor); } return false; } public void clear() { versionNr = 0; isUnreachable = false; typeRefinements.clear(); trueFacts.clear(); falseFacts.clear(); } /** * <p> * 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. * </p> * * <p> * Otherwise, if an anchor is found it is returned, null otherwise. * </p> */ public GuardingNode nonTrivialNullAnchor(ValueNode object) { assert FlowUtil.hasLegalObjectStamp(object); if (StampTool.isObjectAlwaysNull(object)) { return null; } ValueNode scrutinee = GraphUtil.unproxify(object); Witness w = typeRefinements.get(scrutinee); if (w != null && w.isNull()) { return w.guard(); } return null; } /** * This method: * <ul> * <li> * 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.</li> * <li> * otherwise, if the property of interest is known not to hold, negative evidence is returned.</li> * <li> * otherwise, null is returned.</li> * </ul> */ 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.getValue()); if (replacement != null) { return new Evidence(replacement); } if (isNonNull(isNullNode.getValue())) { return Evidence.COUNTEREXAMPLE; } } else { // grab an anchor attesting non-nullness final Witness w = typeInfo(isNullNode.getValue()); if (w != null && w.isNonNull()) { return new Evidence(w.guard()); } if (isNull(isNullNode.getValue())) { 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.getValue()); if (isTrue) { if (isNull(iOf.getValue())) { 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.getValue()); 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; } }