view graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/Witness.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 5699bb48a436
children
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 com.oracle.graal.api.meta.ResolvedJavaType;
import com.oracle.graal.nodes.*;
import com.oracle.graal.nodes.extended.GuardingNode;
import com.oracle.graal.compiler.common.type.ObjectStamp;

/**
 * <p>
 * A witness progressively tracks more detailed properties about an object value (the scrutinee);
 * the properties in question comprise whether the scrutinee has been observed:
 *
 * <ul>
 * <li>as non-null,</li>
 * <li>in a successful checkcast, or</li>
 * <li>in a successful instanceof.</li>
 * </ul>
 * </p>
 *
 * <p>
 * A witness is updated only when doing so increases the information content of the witness. For
 * example, upon visiting a {@link com.oracle.graal.nodes.java.CheckCastNode CheckCastNode} the
 * witness gets a chance to become updated.
 * </p>
 *
 * <p>
 * Therefore, at any given time, a witness represents the most detailed knowledge available so far
 * about the scrutinee, which is the knowledge most relevant for upcoming program-points.
 * </p>
 *
 * <p>
 * The availability of witnesses about both non-nullness and checkcast (for a given scrutinee)
 * promotes to an instanceof-style witness . The "levels" of knowledge a witness may exhibit are
 * captured by {@link com.oracle.graal.phases.common.cfs.Witness.LEVEL}. For conciseness, the
 * following query methods are available for a {@link com.oracle.graal.phases.common.cfs.Witness}:
 * <ul>
 * <li>{@link #atNonNull()}</li>
 * <li>{@link #atCheckCast()}</li>
 * <li>{@link #atInstanceOf()}</li>
 * </ul>
 * </p>
 *
 * <p>
 * Only non-interface object types (ie, class and array types) are tracked. Small extensions are
 * required to track interfaces, extensions that might be added after the current scheme proves
 * itself.
 * </p>
 *
 * <p>
 * Some useful facts about the Statechart implemented by {@link Witness Witness}:
 *
 * <ul>
 * <li>the start-state is "clueless"</li>
 * <li>
 * A self-transition via trackCC from whichever the current state is to itself, without information
 * gain, is always possible. Just give {@link Object java.lang.Object} as observed type.</li>
 * </ul>
 * </p>
 *
 */
public class Witness implements Cloneable {

    private static enum LEVEL {
        CLUELESS,
        NN,
        CC,
        IO,
        DN
    }

    private boolean atNonNull() {
        return level == LEVEL.NN;
    }

    private boolean atCheckCast() {
        return level == LEVEL.CC;
    }

    private boolean atInstanceOf() {
        return level == LEVEL.IO;
    }

    private boolean atDefinitelyNull() {
        return level == LEVEL.DN;
    }

    private void transition(LEVEL to, GuardingNode newAnchor) {
        this.level = to;
        this.gn = newAnchor;
        assert repOK();
    }

    public Witness() {
    }

    public Witness(Witness other) {
        level = other.level;
        seen = other.seen;
        gn = other.gn;
    }

    /**
     * Counterpart to {@link #asStamp()}.
     */
    public Witness(ObjectStamp stamp, GuardingNode anchor) {
        assert stamp.isLegal();
        assert anchor != null;
        if (stamp.alwaysNull()) {
            // seen left null
            transition(LEVEL.DN, anchor);
        } else {
            boolean isNonIface = (stamp.type() != null && !stamp.type().isInterface());
            if (stamp.nonNull()) {
                if (isNonIface) {
                    seen = stamp.type();
                    transition(LEVEL.IO, anchor);
                } else {
                    seen = null;
                    transition(LEVEL.NN, anchor);
                }
            } else {
                if (isNonIface) {
                    seen = stamp.type();
                    transition(LEVEL.CC, anchor);
                } else {
                    seen = null;
                    transition(LEVEL.CLUELESS, null);
                    assert clueless();
                }
            }
        }
        assert repOK();
    }

    public boolean isNonNull() {
        return atNonNull() || atInstanceOf();
    }

    public boolean isNull() {
        return atDefinitelyNull();
    }

    /**
     * The most precise type known so far about the scrutinee. (Please notice that nothing can be
     * deduced about the non-nullness-status of the scrutinee from invoking this method alone).
     */
    public ResolvedJavaType type() {
        return seen;
    }

    public ObjectStamp asStamp() {
        boolean isKnownExact = (seen != null && seen.equals(seen.asExactType()));
        return new ObjectStamp(seen, isKnownExact, isNonNull(), isNull());
    }

    private LEVEL level = LEVEL.CLUELESS;
    private ResolvedJavaType seen = null;

    /**
     * Evidence (ie, a {@link com.oracle.graal.nodes.extended.GuardingNode}) attesting to the status
     * reported by this witness.
     *
     * May be one of:
     *
     * <ul>
     * <li>
     * {@link com.oracle.graal.nodes.BeginNode BeginNode},</li>
     * <li>
     * {@link com.oracle.graal.nodes.LoopExitNode LoopExitNode},</li>
     * <li>
     * {@link com.oracle.graal.nodes.FixedGuardNode FixedGuardNode}</li>
     * <li>{@link com.oracle.graal.nodes.MergeNode MergeNode}, resulting from merging two witnesses
     * with different values for this anchor</li>
     * </ul>
     *
     * <p>
     * An {@link com.oracle.graal.nodes.calc.ObjectEqualsNode ObjectEqualsNode} test results in the
     * more-clueless of both scrutinees having its witness upgraded to that of the other (both
     * scrutinees share the same {@link Witness Witness} instance from then on). For this reason,
     * this field may also hold the anchors associated to an
     * {@link com.oracle.graal.nodes.calc.ObjectEqualsNode ObjectEqualsNode} occurrence, ie nodes
     * that can serve as {@link com.oracle.graal.nodes.extended.GuardingNode GuardingNode} for the
     * purposes of building a {@link com.oracle.graal.nodes.PiNode PiNode}.
     * </p>
     *
     */
    private GuardingNode gn = null;

    /**
     * Invariants of this class:
     * <ul>
     * <li>All fields holding null is ok, the hallmark of a {@link #clueless() clueless} witness.
     * Another way for a witness to be clueless is by recording <code>java.lang.Object</code> as the
     * seen type and nothing more.</li>
     * <li>{@link #seen seen} may be null as long as the only hint being recorded is non-nullness</li>
     * <li>A non-null value for {@link #seen seen} can't be tracked with NN, that combination
     * amounts to IO and is tracked as such</li>
     * <li>At most one of NN, CC, IO may be tracked at any given time</li>
     * <li>A non-null CC or a non-null IO force {@link #seen seen} to be non-null</li>
     * <li>{@link #seen seen} must be null or denote a non-interface reference type (ie, either a
     * class-type or an array-type)</li>
     * </ul>
     */
    private boolean repOK() {
        if (clueless()) {
            assert level == LEVEL.CLUELESS;
            return true;
        }
        if (atNonNull() || atDefinitelyNull()) {
            assert seen == null;
        }
        if (seen != null) {
            assert !seen.isInterface();
            assert !seen.isPrimitive();
        }
        assert gn instanceof BeginNode || gn instanceof LoopExitNode || gn instanceof MergeNode || gn instanceof FixedGuardNode;
        return true;
    }

    /**
     * Helper method for readability in complex conditions.
     */
    public boolean clueless() {
        return cluelessAboutType() && cluelessAboutNullity();
    }

    /**
     * Helper method for readability in complex conditions.
     */
    public boolean cluelessAboutType() {
        // TODO also clueless when `seen` is `java.lang.Object`
        return seen == null;
    }

    /**
     * Helper method for readability in complex conditions.
     */
    public boolean cluelessAboutNullity() {
        return !atNonNull() && !atInstanceOf() && !atDefinitelyNull();
    }

    /**
     * Whether this {@link Witness Witness} tracks information strictly more precise than that in
     * the given {@link com.oracle.graal.compiler.common.type.ObjectStamp}.
     */
    boolean knowsBetterThan(ObjectStamp other) {
        return FlowUtil.isMorePrecise(asStamp(), other);
    }

    /**
     * Whether this {@link Witness Witness} tracks information strictly more precise than that in
     * the {@link com.oracle.graal.compiler.common.type.ObjectStamp} of the given argument.
     */
    boolean knowsBetterThan(ValueNode other) {
        return knowsBetterThan((ObjectStamp) other.stamp());
    }

    /**
     * Porcelain method for internal use only, invoked upon a Facade method being notified about
     * checkcast or instanceof.
     *
     * @return whether the state was updated (iff the observation adds any new information)
     */
    private boolean refinesSeenType(ResolvedJavaType observed) {
        if (isNull()) {
            return false;
        }
        if (cluelessAboutType() || FlowUtil.isMorePrecise(observed, seen)) {
            seen = observed;
            return true;
        }
        return false;
    }

    /**
     * Updates this {@link Witness Witness} to account for an observation about the scrutinee being
     * non-null. In case instanceof was observed,
     * {@link #trackIO(com.oracle.graal.api.meta.ResolvedJavaType, com.oracle.graal.nodes.extended.GuardingNode)
     * <code>trackIO(ResolvedJavaType, GuardingNode)</code>} should be invoked instead
     *
     * @return whether this {@link Witness Witness} was updated (iff the observation adds any new
     *         information)
     */
    public boolean trackNN(GuardingNode anchor) {
        assert repOK();
        assert !isNull();
        try {
            if (atInstanceOf()) {
                return false;
            }
            if (atCheckCast()) {
                transition(LEVEL.IO, anchor);
                return true;
            }
            if (!atNonNull()) {
                transition(LEVEL.NN, anchor);
                return true;
            }
            return false;
        } finally {
            assert repOK();
        }
    }

    /**
     * Updates this {@link Witness Witness} to account for an observation about the scrutinee being
     * null.
     *
     * @return whether this {@link Witness Witness} was updated (iff the observation adds any new
     *         information)
     */
    public boolean trackDN(GuardingNode anchor) {
        assert repOK();
        assert !isNonNull();
        try {
            if (atDefinitelyNull()) {
                return false;
            }
            assert level == LEVEL.CLUELESS || atCheckCast();
            seen = null;
            transition(LEVEL.DN, anchor);
            return true;
        } finally {
            assert repOK();
        }
    }

    /**
     * Updates this {@link Witness Witness} to account for an observation about the scrutinee
     * conforming to the provided type. In case instanceof was observed,
     * {@link #trackIO(com.oracle.graal.api.meta.ResolvedJavaType, com.oracle.graal.nodes.extended.GuardingNode)
     * <code>trackIO(ResolvedJavaType, GuardingNode)</code>} should be invoked instead.
     *
     * @return true iff information was gained.
     */
    public boolean trackCC(ResolvedJavaType observed, GuardingNode anchor) {
        assert !observed.isInterface();
        assert anchor != null;
        assert repOK();
        try {
            boolean anchorObsolete = refinesSeenType(observed);
            if (atInstanceOf()) {
                /*
                 * Statechart: self-transition at IO, potential information gain.
                 */
                if (anchorObsolete) {
                    transition(LEVEL.IO, anchor);
                    return true;
                }
                return false;
            }
            if (anchorObsolete) {
                if (!atNonNull()) {
                    /* Statechart: transition from clueless to CC. */
                    transition(LEVEL.CC, anchor);
                    return true;
                } else {
                    /* Statechart: transition from NN to IO. */
                    transition(LEVEL.IO, anchor);
                    return true;
                }
            }
            /*
             * Statechart: self-transition from whichever the current state is to itself, without
             * information gain.
             */
            return false;
        } finally {
            assert repOK();
        }
    }

    /**
     * Updates this {@link Witness Witness} to account for an observation about the non-null
     * scrutinee conforming to a type.
     *
     * @return whether this {@link Witness Witness} was updated (iff the observation adds any new
     *         information)
     */
    public boolean trackIO(ResolvedJavaType observed, GuardingNode anchor) {
        assert repOK();
        assert !isNull();
        try {
            boolean gotMorePreciseType = refinesSeenType(observed);
            if (!atInstanceOf() || gotMorePreciseType) {
                transition(LEVEL.IO, anchor);
                return true;
            }
            return gotMorePreciseType;
        } finally {
            assert repOK();
        }
    }

    /**
     * Shallow cloning is enough because what's reachable from {@link Witness} is primitive or used
     * read-only when merging states.
     */
    @Override
    public Witness clone() {
        return new Witness(this);
    }

    /**
     * @return null for a clueless method, non-null otherwise.
     */
    GuardingNode guard() {
        return gn;
    }

    /**
     * Merges another state into this one, by mutating this object, the other is left as is.
     */
    public void merge(Witness that, MergeNode merge) {
        assert this.repOK();
        assert that.repOK();

        if (clueless()) {
            return;
        }

        // preserve guarding node only if matches other, otherwise settle on `merge`
        final GuardingNode resultGuard = (guard() == that.guard()) ? guard() : merge;

        if (isNull()) {
            switch (that.level) {
                case CLUELESS:
                case NN:
                    // lose is-null status, gain nothing
                    level = LEVEL.CLUELESS;
                    seen = null;
                    break;
                case CC:
                case IO:
                    // demote to check-cast
                    level = LEVEL.CC;
                    seen = that.seen;
                    break;
                case DN:
                    // keep is-null status
            }
            gn = resultGuard;
            return;
        }

        if (that.isNull()) {
            /*
             * Same decisions as above (based on this-being-null and that-level) are now made based
             * on (that-being-null and this-level). Because merge is commutative.
             */
            switch (level) {
                case CLUELESS:
                case NN:
                    // lose is-null status, gain nothing
                    level = LEVEL.CLUELESS;
                    seen = null;
                    break;
                case CC:
                    break;
                case IO:
                    // demote to check-cast
                    level = LEVEL.CC;
                    // keep this.seen as-is
                    break;
                case DN:
                    // keep is-null status
            }
            gn = resultGuard;
            return;
        }

        // by now we know neither this nor that are known-to-be-null
        assert !isNull() && !that.isNull();

        // umbrella type over the observations from both witnesses
        ResolvedJavaType newSeen = (seen == null || that.seen == null) ? null : FlowUtil.widen(seen, that.seen);

        /*
         * guarantee on (both conformance and non-nullness) required from each input in order for
         * the result to be able to offer such guarantee
         */
        final boolean resultIO = atInstanceOf() && that.atInstanceOf();
        /* failing that, attempt to rescue type-conformance */
        final boolean resultCC = !resultIO && (!cluelessAboutType() && !that.cluelessAboutType());
        /* if all else fails, attempt to rescue non-nullness */
        final boolean resultNN = !resultIO && !resultCC && (isNonNull() && that.isNonNull());

        seen = newSeen;
        level = resultIO ? LEVEL.IO : resultCC ? LEVEL.CC : resultNN ? LEVEL.NN : LEVEL.CLUELESS;
        gn = resultGuard;

        assert repOK();
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(cluelessAboutType() ? "seen=?" : "seen=" + seen);
        sb.append(";");
        sb.append("gn=" + gn);
        return sb.toString();
    }

}