view graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotMethodData.java @ 7530:5e3d1a68664e

applied mx eclipseformat to all Java files
author Doug Simon <doug.simon@oracle.com>
date Wed, 23 Jan 2013 16:34:57 +0100
parents 36dafe48bc38
children 630ea5001e33
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.hotspot.meta;

import static com.oracle.graal.graph.FieldIntrospection.*;
import static com.oracle.graal.hotspot.HotSpotGraalRuntime.*;

import java.util.*;

import com.oracle.graal.api.meta.*;
import com.oracle.graal.api.meta.JavaTypeProfile.ProfiledType;
import com.oracle.graal.api.meta.ProfilingInfo.ExceptionSeen;
import com.oracle.graal.hotspot.*;
import com.oracle.graal.phases.*;

/**
 * Access to a HotSpot MethodData structure (defined in methodData.hpp).
 */
public final class HotSpotMethodData extends CompilerObject {

    private static final long serialVersionUID = -8873133496591225071L;

    private static final HotSpotVMConfig config = HotSpotGraalRuntime.getInstance().getConfig();
    private static final HotSpotMethodDataAccessor NO_DATA_NO_EXCEPTION_ACCESSOR = new NoMethodData(ExceptionSeen.FALSE);
    private static final HotSpotMethodDataAccessor NO_DATA_EXCEPTION_POSSIBLY_NOT_RECORDED_ACCESSOR = new NoMethodData(ExceptionSeen.NOT_SUPPORTED);

    // sorted by tag
    private static final HotSpotMethodDataAccessor[] PROFILE_DATA_ACCESSORS = {null, new BitData(), new CounterData(), new JumpData(), new TypeCheckData(), new VirtualCallData(), new RetData(),
                    new BranchData(), new MultiBranchData(), new ArgInfoData()};

    /**
     * Reference to the C++ MethodData object.
     */
    private final long metaspaceMethodData;

    private int normalDataSize;
    private int extraDataSize;

    HotSpotMethodData(long metaspaceMethodData) {
        this.metaspaceMethodData = metaspaceMethodData;
        HotSpotGraalRuntime.getInstance().getCompilerToVM().initializeMethodData(metaspaceMethodData, this);
    }

    public boolean hasNormalData() {
        return normalDataSize > 0;
    }

    public boolean hasExtraData() {
        return extraDataSize > 0;
    }

    public int getExtraDataBeginOffset() {
        return normalDataSize;
    }

    public boolean isWithin(int position) {
        return position >= 0 && position < normalDataSize + extraDataSize;
    }

    public int getDeoptimizationCount(DeoptimizationReason reason) {
        int reasonIndex = HotSpotGraalRuntime.getInstance().getRuntime().convertDeoptReason(reason);
        return unsafe.getByte(metaspaceMethodData + config.methodDataOopTrapHistoryOffset + reasonIndex) & 0xFF;
    }

    public HotSpotMethodDataAccessor getNormalData(int position) {
        if (position >= normalDataSize) {
            return null;
        }

        HotSpotMethodDataAccessor result = getData(position);
        assert result != null : "NO_DATA tag is not allowed";
        return result;
    }

    public HotSpotMethodDataAccessor getExtraData(int position) {
        if (position >= normalDataSize + extraDataSize) {
            return null;
        }
        return getData(position);
    }

    public static HotSpotMethodDataAccessor getNoDataAccessor(boolean exceptionPossiblyNotRecorded) {
        if (exceptionPossiblyNotRecorded) {
            return NO_DATA_EXCEPTION_POSSIBLY_NOT_RECORDED_ACCESSOR;
        } else {
            return NO_DATA_NO_EXCEPTION_ACCESSOR;
        }
    }

    private HotSpotMethodDataAccessor getData(int position) {
        assert position >= 0 : "out of bounds";
        int tag = AbstractMethodData.readTag(this, position);
        assert tag >= 0 && tag < PROFILE_DATA_ACCESSORS.length : "illegal tag";
        return PROFILE_DATA_ACCESSORS[tag];
    }

    private int readUnsignedByte(int position, int offsetInBytes) {
        long fullOffsetInBytes = computeFullOffset(position, offsetInBytes);
        return unsafe.getByte(metaspaceMethodData + fullOffsetInBytes) & 0xFF;
    }

    private int readUnsignedShort(int position, int offsetInBytes) {
        long fullOffsetInBytes = computeFullOffset(position, offsetInBytes);
        return unsafe.getShort(metaspaceMethodData + fullOffsetInBytes) & 0xFFFF;
    }

    private long readUnsignedInt(int position, int offsetInBytes) {
        long fullOffsetInBytes = computeFullOffset(position, offsetInBytes);
        return unsafe.getInt(metaspaceMethodData + fullOffsetInBytes) & 0xFFFFFFFFL;
    }

    private int readUnsignedIntAsSignedInt(int position, int offsetInBytes) {
        long value = readUnsignedInt(position, offsetInBytes);
        return truncateLongToInt(value);
    }

    private int readInt(int position, int offsetInBytes) {
        long fullOffsetInBytes = computeFullOffset(position, offsetInBytes);
        return unsafe.getInt(metaspaceMethodData + fullOffsetInBytes);
    }

    private long readWord(int position, int offsetInBytes) {
        long fullOffsetInBytes = computeFullOffset(position, offsetInBytes);
        return unsafeReadWord(metaspaceMethodData + fullOffsetInBytes);
    }

    private static int truncateLongToInt(long value) {
        return value > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) value;
    }

    private static int computeFullOffset(int position, int offsetInBytes) {
        return config.methodDataOopDataOffset + position + offsetInBytes;
    }

    private static int cellIndexToOffset(int cells) {
        return config.dataLayoutHeaderSize + cellsToBytes(cells);
    }

    private static int cellsToBytes(int cells) {
        return cells * config.dataLayoutCellSize;
    }

    private abstract static class AbstractMethodData implements HotSpotMethodDataAccessor {

        /**
         * Corresponds to DS_RECOMPILE_BIT defined in deoptimization.cpp.
         */
        private static final int EXCEPTIONS_MASK = 0x80;

        private final int tag;
        private final int staticSize;

        protected AbstractMethodData(int tag, int staticSize) {
            this.tag = tag;
            this.staticSize = staticSize;
        }

        public int getTag() {
            return tag;
        }

        public static int readTag(HotSpotMethodData data, int position) {
            return data.readUnsignedByte(position, config.dataLayoutTagOffset);
        }

        @Override
        public int getBCI(HotSpotMethodData data, int position) {
            return data.readUnsignedShort(position, config.dataLayoutBCIOffset);
        }

        @Override
        public int getSize(HotSpotMethodData data, int position) {
            return staticSize + getDynamicSize(data, position);
        }

        @Override
        public ExceptionSeen getExceptionSeen(HotSpotMethodData data, int position) {
            return ExceptionSeen.get((getFlags(data, position) & EXCEPTIONS_MASK) != 0);
        }

        @Override
        public JavaTypeProfile getTypeProfile(HotSpotMethodData data, int position) {
            return null;
        }

        @Override
        public double getBranchTakenProbability(HotSpotMethodData data, int position) {
            return -1;
        }

        @Override
        public double[] getSwitchProbabilities(HotSpotMethodData data, int position) {
            return null;
        }

        @Override
        public int getExecutionCount(HotSpotMethodData data, int position) {
            return -1;
        }

        protected int getFlags(HotSpotMethodData data, int position) {
            return data.readUnsignedByte(position, config.dataLayoutFlagsOffset);
        }

        protected int getDynamicSize(@SuppressWarnings("unused")
        HotSpotMethodData data, @SuppressWarnings("unused")
        int position) {
            return 0;
        }
    }

    private static class NoMethodData extends AbstractMethodData {

        private static final int NO_DATA_TAG = 0;
        private static final int NO_DATA_SIZE = cellIndexToOffset(0);

        private final ExceptionSeen exceptionSeen;

        protected NoMethodData(ExceptionSeen exceptionSeen) {
            super(NO_DATA_TAG, NO_DATA_SIZE);
            this.exceptionSeen = exceptionSeen;
        }

        @Override
        public int getBCI(HotSpotMethodData data, int position) {
            return -1;
        }

        @Override
        public ExceptionSeen getExceptionSeen(HotSpotMethodData data, int position) {
            return exceptionSeen;
        }
    }

    private static class BitData extends AbstractMethodData {

        private static final int BIT_DATA_TAG = 1;
        private static final int BIT_DATA_SIZE = cellIndexToOffset(0);
        private static final int BIT_DATA_NULL_SEEN_FLAG = 0x01;

        private BitData() {
            super(BIT_DATA_TAG, BIT_DATA_SIZE);
        }

        protected BitData(int tag, int staticSize) {
            super(tag, staticSize);
        }

        @SuppressWarnings("unused")
        public boolean getNullSeen(HotSpotMethodData data, int position) {
            return (getFlags(data, position) & BIT_DATA_NULL_SEEN_FLAG) != 0;
        }
    }

    private static class CounterData extends BitData {

        private static final int COUNTER_DATA_TAG = 2;
        private static final int COUNTER_DATA_SIZE = cellIndexToOffset(1);
        private static final int COUNTER_DATA_COUNT_OFFSET = cellIndexToOffset(0);

        public CounterData() {
            super(COUNTER_DATA_TAG, COUNTER_DATA_SIZE);
        }

        protected CounterData(int tag, int staticSize) {
            super(tag, staticSize);
        }

        @Override
        public int getExecutionCount(HotSpotMethodData data, int position) {
            return getCounterValue(data, position);
        }

        protected int getCounterValue(HotSpotMethodData data, int position) {
            return data.readUnsignedIntAsSignedInt(position, COUNTER_DATA_COUNT_OFFSET);
        }
    }

    private static class JumpData extends AbstractMethodData {

        private static final int JUMP_DATA_TAG = 3;
        private static final int JUMP_DATA_SIZE = cellIndexToOffset(2);
        protected static final int TAKEN_COUNT_OFFSET = cellIndexToOffset(0);
        protected static final int TAKEN_DISPLACEMENT_OFFSET = cellIndexToOffset(1);

        public JumpData() {
            super(JUMP_DATA_TAG, JUMP_DATA_SIZE);
        }

        protected JumpData(int tag, int staticSize) {
            super(tag, staticSize);
        }

        @Override
        public double getBranchTakenProbability(HotSpotMethodData data, int position) {
            return getExecutionCount(data, position) != 0 ? 1 : 0;
        }

        @Override
        public int getExecutionCount(HotSpotMethodData data, int position) {
            return data.readUnsignedIntAsSignedInt(position, TAKEN_COUNT_OFFSET);
        }

        @SuppressWarnings("unused")
        public int getTakenDisplacement(HotSpotMethodData data, int position) {
            return data.readInt(position, TAKEN_DISPLACEMENT_OFFSET);
        }
    }

    private abstract static class AbstractTypeData extends CounterData {

        private static final int RECEIVER_TYPE_DATA_ROW_SIZE = cellsToBytes(2);
        private static final int RECEIVER_TYPE_DATA_SIZE = cellIndexToOffset(2) + RECEIVER_TYPE_DATA_ROW_SIZE * config.typeProfileWidth;
        protected static final int NONPROFILED_RECEIVER_COUNT_OFFSET = cellIndexToOffset(1);
        private static final int RECEIVER_TYPE_DATA_FIRST_RECEIVER_OFFSET = cellIndexToOffset(2);
        private static final int RECEIVER_TYPE_DATA_FIRST_COUNT_OFFSET = cellIndexToOffset(3);

        protected AbstractTypeData(int tag) {
            super(tag, RECEIVER_TYPE_DATA_SIZE);
        }

        @Override
        public JavaTypeProfile getTypeProfile(HotSpotMethodData data, int position) {
            int typeProfileWidth = config.typeProfileWidth;

            ResolvedJavaType[] types = new ResolvedJavaType[typeProfileWidth];
            long[] counts = new long[typeProfileWidth];
            long totalCount = 0;
            int entries = 0;

            for (int i = 0; i < typeProfileWidth; i++) {
                long receiverKlass = data.readWord(position, getReceiverOffset(i));
                if (receiverKlass != 0) {
                    types[entries] = HotSpotResolvedObjectType.fromMetaspaceKlass(receiverKlass);
                    long count = data.readUnsignedInt(position, getCountOffset(i));
                    totalCount += count;
                    counts[entries] = count;

                    entries++;
                }
            }

            totalCount += getTypesNotRecordedExecutionCount(data, position);
            return createTypeProfile(types, counts, totalCount, entries);
        }

        protected abstract long getTypesNotRecordedExecutionCount(HotSpotMethodData data, int position);

        private static JavaTypeProfile createTypeProfile(ResolvedJavaType[] types, long[] counts, long totalCount, int entries) {
            if (entries <= 0 || totalCount < GraalOptions.MatureExecutionsTypeProfile) {
                return null;
            }

            ProfiledType[] ptypes = new ProfiledType[entries];
            double totalProbability = 0.0;
            for (int i = 0; i < entries; i++) {
                double p = counts[i];
                p = p / totalCount;
                totalProbability += p;
                ptypes[i] = new ProfiledType(types[i], p);
            }

            Arrays.sort(ptypes);

            double notRecordedTypeProbability = entries < config.typeProfileWidth ? 0.0 : Math.min(1.0, Math.max(0.0, 1.0 - totalProbability));
            assert notRecordedTypeProbability == 0 || entries == config.typeProfileWidth;
            return new JavaTypeProfile(notRecordedTypeProbability, ptypes);
        }

        private static int getReceiverOffset(int row) {
            return RECEIVER_TYPE_DATA_FIRST_RECEIVER_OFFSET + row * RECEIVER_TYPE_DATA_ROW_SIZE;
        }

        protected static int getCountOffset(int row) {
            return RECEIVER_TYPE_DATA_FIRST_COUNT_OFFSET + row * RECEIVER_TYPE_DATA_ROW_SIZE;
        }
    }

    private static class TypeCheckData extends AbstractTypeData {

        private static final int RECEIVER_TYPE_DATA_TAG = 4;

        public TypeCheckData() {
            super(RECEIVER_TYPE_DATA_TAG);
        }

        @Override
        public int getExecutionCount(HotSpotMethodData data, int position) {
            return -1;
        }

        @Override
        protected long getTypesNotRecordedExecutionCount(HotSpotMethodData data, int position) {
            return data.readUnsignedIntAsSignedInt(position, NONPROFILED_RECEIVER_COUNT_OFFSET);
        }
    }

    private static class VirtualCallData extends AbstractTypeData {

        private static final int VIRTUAL_CALL_DATA_TAG = 5;

        public VirtualCallData() {
            super(VIRTUAL_CALL_DATA_TAG);
        }

        @Override
        public int getExecutionCount(HotSpotMethodData data, int position) {
            int typeProfileWidth = config.typeProfileWidth;

            long total = 0;
            for (int i = 0; i < typeProfileWidth; i++) {
                total += data.readUnsignedInt(position, getCountOffset(i));
            }

            total += getCounterValue(data, position);
            return truncateLongToInt(total);
        }

        @Override
        protected long getTypesNotRecordedExecutionCount(HotSpotMethodData data, int position) {
            return getCounterValue(data, position);
        }
    }

    private static class RetData extends CounterData {

        private static final int RET_DATA_TAG = 6;
        private static final int RET_DATA_ROW_SIZE = cellsToBytes(3);
        private static final int RET_DATA_SIZE = cellIndexToOffset(1) + RET_DATA_ROW_SIZE * config.bciProfileWidth;

        public RetData() {
            super(RET_DATA_TAG, RET_DATA_SIZE);
        }
    }

    private static class BranchData extends JumpData {

        private static final int BRANCH_DATA_TAG = 7;
        private static final int BRANCH_DATA_SIZE = cellIndexToOffset(3);
        private static final int NOT_TAKEN_COUNT_OFFSET = cellIndexToOffset(2);

        public BranchData() {
            super(BRANCH_DATA_TAG, BRANCH_DATA_SIZE);
        }

        @Override
        public double getBranchTakenProbability(HotSpotMethodData data, int position) {
            long takenCount = data.readUnsignedInt(position, TAKEN_COUNT_OFFSET);
            long notTakenCount = data.readUnsignedInt(position, NOT_TAKEN_COUNT_OFFSET);
            long total = takenCount + notTakenCount;

            if (total < GraalOptions.MatureExecutionsBranch) {
                return -1;
            } else {
                return takenCount / (double) total;
            }
        }

        @Override
        public int getExecutionCount(HotSpotMethodData data, int position) {
            long count = data.readUnsignedInt(position, TAKEN_COUNT_OFFSET) + data.readUnsignedInt(position, NOT_TAKEN_COUNT_OFFSET);
            return truncateLongToInt(count);
        }
    }

    private static class ArrayData extends AbstractMethodData {

        private static final int ARRAY_DATA_LENGTH_OFFSET = cellIndexToOffset(0);
        protected static final int ARRAY_DATA_START_OFFSET = cellIndexToOffset(1);

        public ArrayData(int tag, int staticSize) {
            super(tag, staticSize);
        }

        @Override
        protected int getDynamicSize(HotSpotMethodData data, int position) {
            return cellsToBytes(getLength(data, position));
        }

        protected static int getLength(HotSpotMethodData data, int position) {
            return data.readInt(position, ARRAY_DATA_LENGTH_OFFSET);
        }
    }

    private static class MultiBranchData extends ArrayData {

        private static final int MULTI_BRANCH_DATA_TAG = 8;
        private static final int MULTI_BRANCH_DATA_SIZE = cellIndexToOffset(1);
        private static final int MULTI_BRANCH_DATA_ROW_SIZE_IN_CELLS = 2;
        private static final int MULTI_BRANCH_DATA_ROW_SIZE = cellsToBytes(MULTI_BRANCH_DATA_ROW_SIZE_IN_CELLS);
        private static final int MULTI_BRANCH_DATA_FIRST_COUNT_OFFSET = ARRAY_DATA_START_OFFSET + cellsToBytes(0);
        private static final int MULTI_BRANCH_DATA_FIRST_DISPLACEMENT_OFFSET = ARRAY_DATA_START_OFFSET + cellsToBytes(1);

        public MultiBranchData() {
            super(MULTI_BRANCH_DATA_TAG, MULTI_BRANCH_DATA_SIZE);
        }

        @Override
        public double[] getSwitchProbabilities(HotSpotMethodData data, int position) {
            int arrayLength = getLength(data, position);
            assert arrayLength > 0 : "switch must have at least the default case";
            assert arrayLength % MULTI_BRANCH_DATA_ROW_SIZE_IN_CELLS == 0 : "array must have full rows";

            int length = arrayLength / MULTI_BRANCH_DATA_ROW_SIZE_IN_CELLS;
            long totalCount = 0;
            double[] result = new double[length];

            // default case is first in HotSpot but last for the compiler
            long count = readCount(data, position, 0);
            totalCount += count;
            result[length - 1] = count;

            for (int i = 1; i < length; i++) {
                count = readCount(data, position, i);
                totalCount += count;
                result[i - 1] = count;
            }

            if (totalCount < GraalOptions.MatureExecutionsPerSwitchCase * length) {
                return null;
            } else {
                for (int i = 0; i < length; i++) {
                    result[i] = result[i] / totalCount;
                }
                return result;
            }
        }

        private static long readCount(HotSpotMethodData data, int position, int i) {
            int offset;
            long count;
            offset = getCountOffset(i);
            count = data.readUnsignedInt(position, offset);
            return count;
        }

        @Override
        public int getExecutionCount(HotSpotMethodData data, int position) {
            int arrayLength = getLength(data, position);
            assert arrayLength > 0 : "switch must have at least the default case";
            assert arrayLength % MULTI_BRANCH_DATA_ROW_SIZE_IN_CELLS == 0 : "array must have full rows";

            int length = arrayLength / MULTI_BRANCH_DATA_ROW_SIZE_IN_CELLS;
            long totalCount = 0;
            for (int i = 0; i < length; i++) {
                int offset = getCountOffset(i);
                totalCount += data.readUnsignedInt(position, offset);
            }

            return truncateLongToInt(totalCount);
        }

        private static int getCountOffset(int index) {
            return MULTI_BRANCH_DATA_FIRST_COUNT_OFFSET + index * MULTI_BRANCH_DATA_ROW_SIZE;
        }

        @SuppressWarnings("unused")
        private static int getDisplacementOffset(int index) {
            return MULTI_BRANCH_DATA_FIRST_DISPLACEMENT_OFFSET + index * MULTI_BRANCH_DATA_ROW_SIZE;
        }
    }

    private static class ArgInfoData extends ArrayData {

        private static final int ARG_INFO_DATA_TAG = 9;
        private static final int ARG_INFO_DATA_SIZE = cellIndexToOffset(1);

        public ArgInfoData() {
            super(ARG_INFO_DATA_TAG, ARG_INFO_DATA_SIZE);
        }
    }
}