view truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/profiles/PrimitiveValueProfile.java @ 22501:a63bda98cfdb

Extract profiles into separate package. Add isProfilingEnabled in TruffleRuntime to disable profiling in the default runtime; Add low overhead profiles for primitives; Add LoopConditionProfile; Profile footprint/threadsafety improvements; Make toString implementations more consistent; Greatly enhanced javadoc documentation for profiles; Deprecate old profiles
author Christian Humer <christian.humer@oracle.com>
date Wed, 16 Dec 2015 16:38:13 +0100
parents truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/utilities/PrimitiveValueProfile.java@dc83cc1f94f2
children
line wrap: on
line source

/*
 * Copyright (c) 2014, 2015, 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.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * 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.truffle.api.profiles;

import java.util.Objects;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.Truffle;

/**
 * <p>
 * Represents a {@link ValueProfile} that speculates on the primitive equality or object identity of
 * values. Note that for {@code float} and {@code double} values we compare primitive equality via
 * {@link Float#floatToRawIntBits} and {@link Double#doubleToRawLongBits}, so that for example
 * {@code -0.0} is not considered the same as {@code 0.0}, even though primitive equality would
 * normally say that it was.
 * </p>
 *
 * {@inheritDoc}
 */
public abstract class PrimitiveValueProfile extends ValueProfile {

    PrimitiveValueProfile() {
    }

    @Override
    public abstract <T> T profile(T value);

    public abstract byte profile(byte value);

    public abstract short profile(short value);

    public abstract int profile(int value);

    public abstract long profile(long value);

    public abstract float profile(float value);

    public abstract double profile(double value);

    public abstract boolean profile(boolean value);

    public abstract char profile(char value);

    /**
     * Returns a {@link PrimitiveValueProfile} that speculates on the primitive equality or object
     * identity of a value.
     */
    public static PrimitiveValueProfile createEqualityProfile() {
        if (Truffle.getRuntime().isProfilingEnabled()) {
            return Enabled.create();
        } else {
            return Disabled.INSTANCE;
        }
    }

    /**
     * @deprecated going to get removed without replacement
     */
    @Deprecated
    public static boolean exactCompare(float a, float b) {
        /*
         * -0.0 == 0.0, but you can tell the difference through other means, so we need to
         * differentiate.
         */
        return Float.floatToRawIntBits(a) == Float.floatToRawIntBits(b);
    }

    /**
     * @deprecated going to get removed without replacement
     */
    @Deprecated
    public static boolean exactCompare(double a, double b) {
        /*
         * -0.0 == 0.0, but you can tell the difference through other means, so we need to
         * differentiate.
         */
        return Double.doubleToRawLongBits(a) == Double.doubleToRawLongBits(b);
    }

    static final class Enabled extends PrimitiveValueProfile {

        private static final byte STATE_UNINITIALIZED = 0;
        private static final byte STATE_BYTE = 1;
        private static final byte STATE_SHORT = 2;
        private static final byte STATE_INTEGER = 3;
        private static final byte STATE_LONG = 4;
        private static final byte STATE_BOOLEAN = 5;
        private static final byte STATE_CHARACTER = 6;
        private static final byte STATE_FLOAT = 7;
        private static final byte STATE_DOUBLE = 8;
        private static final byte STATE_IDENTITY = 9;
        private static final byte STATE_GENERIC = 10;

        @CompilationFinal private byte state = STATE_UNINITIALIZED;
        @CompilationFinal private Object cachedValue;

        Enabled() {
        }

        @SuppressWarnings("unchecked")
        @Override
        public <T> T profile(T value) {
            byte s = this.state;
            Object snapshot;
            switch (s) {
                case STATE_BYTE:
                case STATE_SHORT:
                case STATE_INTEGER:
                case STATE_LONG:
                case STATE_BOOLEAN:
                case STATE_CHARACTER:
                    snapshot = this.cachedValue;
                    if (snapshot.equals(value)) {
                        return (T) snapshot;
                    }
                    break;
                case STATE_DOUBLE:
                    snapshot = this.cachedValue;
                    if (value instanceof Double && exactCompare((Double) snapshot, (Double) value)) {
                        return (T) snapshot;
                    }
                    break;
                case STATE_FLOAT:
                    snapshot = this.cachedValue;
                    if (value instanceof Float && exactCompare((Float) snapshot, (Float) value)) {
                        return (T) snapshot;
                    }
                    break;
                case STATE_IDENTITY:
                    snapshot = this.cachedValue;
                    if (snapshot == value) {
                        return (T) snapshot;
                    }
                    break;
                case STATE_GENERIC:
                    return value;
            }
            CompilerDirectives.transferToInterpreterAndInvalidate();
            slowpath(s, value);
            return value;
        }

        @Override
        public byte profile(byte value) {
            byte s = this.state;
            if (s != STATE_GENERIC) {
                if (s == STATE_BYTE) {
                    byte castCachedValue = (byte) this.cachedValue;
                    if (castCachedValue == value) {
                        return castCachedValue;
                    }
                }
                CompilerDirectives.transferToInterpreterAndInvalidate();
                slowpath(s, value);
            }
            return value;
        }

        @Override
        public short profile(short value) {
            byte s = this.state;
            if (s != STATE_GENERIC) {
                if (s == STATE_SHORT) {
                    short castCachedValue = (short) this.cachedValue;
                    if (castCachedValue == value) {
                        return castCachedValue;
                    }
                }
                CompilerDirectives.transferToInterpreterAndInvalidate();
                slowpath(s, value);
            }
            return value;
        }

        @Override
        public int profile(int value) {
            byte s = this.state;
            if (s != STATE_GENERIC) {
                if (s == STATE_INTEGER) {
                    int castCachedValue = (int) this.cachedValue;
                    if (castCachedValue == value) {
                        return castCachedValue;
                    }
                }
                CompilerDirectives.transferToInterpreterAndInvalidate();
                slowpath(s, value);
            }
            return value;
        }

        @Override
        public long profile(long value) {
            byte s = this.state;
            if (s != STATE_GENERIC) {
                if (s == STATE_LONG) {
                    long castCachedValue = (long) this.cachedValue;
                    if (castCachedValue == value) {
                        return castCachedValue;
                    }
                }
                CompilerDirectives.transferToInterpreterAndInvalidate();
                slowpath(s, value);
            }
            return value;
        }

        @Override
        public float profile(float value) {
            byte s = this.state;
            if (s != STATE_GENERIC) {
                if (s == STATE_FLOAT) {
                    float castCachedValue = (float) this.cachedValue;
                    if (exactCompare(castCachedValue, value)) {
                        return castCachedValue;
                    }
                }
                CompilerDirectives.transferToInterpreterAndInvalidate();
                slowpath(s, value);
            }
            return value;
        }

        @Override
        public double profile(double value) {
            byte s = this.state;
            if (s != STATE_GENERIC) {
                if (s == STATE_DOUBLE) {
                    double castCachedValue = (double) this.cachedValue;
                    if (exactCompare(castCachedValue, value)) {
                        return castCachedValue;
                    }
                }
                CompilerDirectives.transferToInterpreterAndInvalidate();
                slowpath(s, value);
            }
            return value;

        }

        @Override
        public boolean profile(boolean value) {
            byte s = this.state;
            if (s != STATE_GENERIC) {
                if (s == STATE_BOOLEAN) {
                    boolean castCachedValue = (boolean) this.cachedValue;
                    if (castCachedValue == value) {
                        return castCachedValue;
                    }
                }
                CompilerDirectives.transferToInterpreterAndInvalidate();
                slowpath(s, value);
            }
            return value;
        }

        @Override
        public char profile(char value) {
            byte s = this.state;
            if (s != STATE_GENERIC) {
                if (s == STATE_CHARACTER) {
                    char castCachedValue = (char) this.cachedValue;
                    if (castCachedValue == value) {
                        return castCachedValue;
                    }
                }
                CompilerDirectives.transferToInterpreterAndInvalidate();
                slowpath(s, value);
            }
            return value;
        }

        private void slowpath(byte s, Object value) {
            if (s == STATE_UNINITIALIZED) {
                cachedValue = value;
                state = specialize(value);
            } else {
                state = STATE_GENERIC;
                cachedValue = null;
            }
        }

        private static byte specialize(Object value) {
            if (value instanceof Byte) {
                return STATE_BYTE;
            } else if (value instanceof Short) {
                return STATE_SHORT;
            } else if (value instanceof Integer) {
                return STATE_INTEGER;
            } else if (value instanceof Long) {
                return STATE_LONG;
            } else if (value instanceof Boolean) {
                return STATE_BOOLEAN;
            } else if (value instanceof Character) {
                return STATE_CHARACTER;
            } else if (value instanceof Double) {
                return STATE_DOUBLE;
            } else if (value instanceof Float) {
                return STATE_FLOAT;
            } else {
                return STATE_IDENTITY;
            }
        }

        boolean isGeneric() {
            return state == STATE_GENERIC;
        }

        boolean isUninitialized() {
            return state == STATE_UNINITIALIZED;
        }

        Object getCachedValue() {
            return cachedValue;
        }

        @Override
        public String toString() {
            return toString(PrimitiveValueProfile.class, state == STATE_UNINITIALIZED, state == STATE_GENERIC, formatSpecialization());
        }

        private String formatSpecialization() {
            Object snapshot = this.cachedValue;
            if (state != STATE_UNINITIALIZED && state != STATE_GENERIC) {
                if (snapshot == null) {
                    return String.format("value == null");
                } else {
                    if (state == STATE_IDENTITY) {
                        String simpleName = snapshot.getClass().getSimpleName();
                        return String.format("value == %s@%x", simpleName, Objects.hash(snapshot));
                    } else {
                        return String.format("value == (%s)%s", snapshot.getClass().getSimpleName(), snapshot);
                    }
                }
            }
            return null;
        }

        /**
         * Returns a {@link PrimitiveValueProfile} that speculates on the primitive equality or
         * object identity of a value.
         */
        static PrimitiveValueProfile create() {
            return new Enabled();
        }
    }

    static final class Disabled extends PrimitiveValueProfile {

        static final PrimitiveValueProfile INSTANCE = new Disabled();

        @Override
        public <T> T profile(T value) {
            return value;
        }

        @Override
        public byte profile(byte value) {
            return value;
        }

        @Override
        public short profile(short value) {
            return value;
        }

        @Override
        public int profile(int value) {
            return value;
        }

        @Override
        public long profile(long value) {
            return value;
        }

        @Override
        public float profile(float value) {
            return value;
        }

        @Override
        public double profile(double value) {
            return value;
        }

        @Override
        public boolean profile(boolean value) {
            return value;
        }

        @Override
        public char profile(char value) {
            return value;
        }

        @Override
        public String toString() {
            return toStringDisabled(PrimitiveValueProfile.class);
        }

    }

}