view graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/CheckCastSnippets.java @ 5428:6ec0857cdf46

added support for snippet templates which are snippet graphs specialized by binding a constant to at least one of the snippet's parameters
author Doug Simon <doug.simon@oracle.com>
date Tue, 22 May 2012 16:44:30 +0200
parents 028c9ce0fc0f
children 4fc3a8040430
line wrap: on
line source

/*
 * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package com.oracle.graal.hotspot.snippets;
import static com.oracle.graal.hotspot.snippets.CheckCastSnippets.Counter.*;
import static com.oracle.graal.snippets.SnippetTemplate.*;

import java.io.*;
import java.util.*;
import java.util.concurrent.*;

import sun.misc.*;

import com.oracle.graal.compiler.*;
import com.oracle.graal.graph.*;
import com.oracle.graal.graph.Node.Fold;
import com.oracle.graal.hotspot.*;
import com.oracle.graal.hotspot.nodes.*;
import com.oracle.graal.hotspot.ri.*;
import com.oracle.graal.nodes.*;
import com.oracle.graal.nodes.extended.*;
import com.oracle.graal.nodes.java.*;
import com.oracle.graal.snippets.*;
import com.oracle.max.cri.ci.*;
import com.oracle.max.cri.ri.*;

/**
 * Snippets used for lowering {@link CheckCastNode}s.
 */
public class CheckCastSnippets implements SnippetsInterface {

    /**
     * Checks that a given object is null or is a subtype of a given type.
     *
     * @param hub the hub of the type being checked against
     * @param object the object whose type is being checked against {@code hub}
     * @param hintHubs the hubs of objects that have been profiled during previous executions
     * @param hintsAreExact specifies if {@code hintHubs} contains all subtypes of {@code hub}
     * @param checkNull specifies if {@code object} may be null
     * @return {@code object} if the type check succeeds
     * @throws ClassCastException if the type check fails
     */
    @Snippet
    public static Object checkcast(Object hub, Object object, Object[] hintHubs, boolean hintsAreExact, boolean checkNull, @SuppressWarnings("unused") Counter ignore) {
        if (object == null) {
            return object;
        }
        Object objectHub = UnsafeLoadNode.load(object, 0, hubOffset(), CiKind.Object);
        // if we get an exact match: succeed immediately
        for (int i = 0; i < hintHubs.length; i++) {
            Object hintHub = hintHubs[i];
            if (hintHub == objectHub) {
                return object;
            }
        }
        if (hintsAreExact || !TypeCheckSlowPath.check(objectHub, hub)) {
            DeoptimizeNode.deopt(RiDeoptAction.InvalidateReprofile, RiDeoptReason.ClassCastException);
        }
        return object;
    }

    /**
     * Counters for the various code paths through a type check.
     */
    public enum Counter {
        hintsHit("hit a hint type"),
        hintsMissed("missed the hint types"),
        exact("tested type is (statically) final"),
        noHints_class("profile information is not used (test type is a class)"),
        noHints_iface("profile information is not used (test type is an interface)"),
        noHints_unknown("test type is not a compile-time constant"),
        isNull("object tested is null"),
        exception("type test failed with a ClassCastException");

        final String description;
        final int index;
        long count;

        private Counter(String desc) {
            this.description = desc;
            this.index = ordinal();
        }

        @Fold
        static int countOffset() {
            try {
                return (int) Unsafe.getUnsafe().objectFieldOffset(Counter.class.getDeclaredField("count"));
            } catch (Exception e) {
                throw new GraalInternalError(e);
            }
        }

        /**
         * Increments this counter if counters are enabled. The body of this method has been carefully crafted
         * such that it contains no safepoints and no calls, neither of which are permissible in a snippet.
         * Also, increments are not guaranteed to be atomic which is acceptable for a counter.
         */
        void inc() {
            if (ENABLED) {
                DirectObjectStoreNode.store(this, countOffset(), 0, count + 1);
            }
        }

        static final Counter[] VALUES = values();
        static final boolean ENABLED = GraalOptions.CheckcastCounters;
    }

    @Snippet
    public static Object checkcastWithCounters(Object hub, Object object, Object[] hintHubs, boolean hintsAreExact, @SuppressWarnings("unused") boolean checkNull, Counter noHintsCounter) {
        if (object == null) {
            isNull.inc();
            return object;
        }
        Object objectHub = UnsafeLoadNode.load(object, 0, hubOffset(), CiKind.Object);
        if (hintHubs.length == 0) {
            noHintsCounter.inc();
            if (!TypeCheckSlowPath.check(objectHub, hub)) {
                exception.inc();
                DeoptimizeNode.deopt(RiDeoptAction.InvalidateReprofile, RiDeoptReason.ClassCastException);
            }
        } else {
            // if we get an exact match: succeed immediately
            for (int i = 0; i < hintHubs.length; i++) {
                Object hintHub = hintHubs[i];
                if (hintHub == objectHub) {
                    if (hintsAreExact) {
                        exact.inc();
                    } else {
                        hintsHit.inc();
                    }
                    return object;
                }
            }
            if (!hintsAreExact) {
                if (!TypeCheckSlowPath.check(objectHub, hub)) {
                    exception.inc();
                    DeoptimizeNode.deopt(RiDeoptAction.InvalidateReprofile, RiDeoptReason.ClassCastException);
                } else {
                    hintsMissed.inc();
                }
            } else {
                exception.inc();
                DeoptimizeNode.deopt(RiDeoptAction.InvalidateReprofile, RiDeoptReason.ClassCastException);
            }
        }
        return object;
    }

    @Fold
    private static int hubOffset() {
        return CompilerImpl.getInstance().getConfig().hubOffset;
    }

    public static void printCounter(PrintStream out, Counter c, long total) {
        double percent = total == 0D ? 0D : ((double) (c.count * 100)) / total;
        out.println(String.format("%16s: %5.2f%%%10d  // %s", c.name(), percent, c.count, c.description));
    }

    public static void printCounters(PrintStream out) {
        if (!Counter.ENABLED) {
            return;
        }
        Counter[] counters = Counter.values();
        Arrays.sort(counters, new Comparator<Counter>() {
            @Override
            public int compare(Counter o1, Counter o2) {
                if (o1.count > o2.count) {
                    return -1;
                } else if (o2.count > o1.count) {
                    return 1;
                }
                return 0;
            }

        });

        long total = 0;
        for (Counter c : counters) {
            total += c.count;
        }

        out.println();
        out.println("** Checkcast counters **");
        for (Counter c : counters) {
            printCounter(out, c, total);
        }
    }

    /**
     * Templates for partially specialized checkcast snippet graphs.
     */
    public static class Templates {

        private final ConcurrentHashMap<Integer, SnippetTemplate> templates;
        private final RiResolvedMethod method;
        private final RiRuntime runtime;

        public Templates(RiRuntime runtime) {
            this.runtime = runtime;
            this.templates = new ConcurrentHashMap<>();
            try {
                Class[] parameterTypes = {Object.class, Object.class, Object[].class, boolean.class, boolean.class, Counter.class};
                String name = GraalOptions.CheckcastCounters ? "checkcastWithCounters" : "checkcast";
                method = runtime.getRiMethod(CheckCastSnippets.class.getDeclaredMethod(name, parameterTypes));
            } catch (NoSuchMethodException e) {
                throw new GraalInternalError(e);
            }
        }

        /**
         * Gets a checkcast snippet specialized for a given set od inputs.
         */
        public SnippetTemplate get(int nHints, boolean isExact, boolean checkNull, Counter noHintsCounter) {
            Integer key = key(nHints, isExact, checkNull);
            SnippetTemplate result = templates.get(key);
            if (result == null) {
                HotSpotKlassOop[] hints = new HotSpotKlassOop[nHints];
                Arrays.fill(hints, new HotSpotKlassOop(null, Templates.class));
                result = SnippetTemplate.create(runtime, method, _, _, hints, isExact, checkNull, noHintsCounter);
                templates.put(key, result);
            }
            return result;
        }

        /**
         * Creates a canonical key for a combination of specialization parameters.
         */
        private static Integer key(int nHints, boolean isExact, boolean checkNull) {
            int key = nHints << 2;
            if (isExact) {
                key |= 2;
            }
            if (checkNull) {
                key |= 1;
            }
            return key;
        }
    }
}