001/*
002 * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.
008 *
009 * This code is distributed in the hope that it will be useful, but WITHOUT
010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
011 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
012 * version 2 for more details (a copy is included in the LICENSE file that
013 * accompanied this code).
014 *
015 * You should have received a copy of the GNU General Public License version
016 * 2 along with this work; if not, write to the Free Software Foundation,
017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
018 *
019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
020 * or visit www.oracle.com if you need additional information or have any
021 * questions.
022 */
023package jdk.internal.jvmci.meta;
024
025import java.util.*;
026
027import jdk.internal.jvmci.meta.JavaTypeProfile.*;
028
029/**
030 * This profile object represents the type profile at a specific BCI. The precision of the supplied
031 * values may vary, but a runtime that provides this information should be aware that it will be
032 * used to guide performance-critical decisions like speculative inlining, etc.
033 */
034public final class JavaTypeProfile extends AbstractJavaProfile<ProfiledType, ResolvedJavaType> {
035
036    private static final ProfiledType[] EMPTY_ARRAY = new ProfiledType[0];
037
038    private final TriState nullSeen;
039
040    public JavaTypeProfile(TriState nullSeen, double notRecordedProbability, ProfiledType[] pitems) {
041        super(notRecordedProbability, pitems);
042        this.nullSeen = nullSeen;
043    }
044
045    /**
046     * Returns whether a null value was at the type check.
047     */
048    public TriState getNullSeen() {
049        return nullSeen;
050    }
051
052    /**
053     * A list of types for which the runtime has recorded probability information. Note that this
054     * includes both positive and negative types where a positive type is a subtype of the checked
055     * type and a negative type is not.
056     */
057    public ProfiledType[] getTypes() {
058        return getItems();
059    }
060
061    public JavaTypeProfile restrict(JavaTypeProfile otherProfile) {
062        if (otherProfile.getNotRecordedProbability() > 0.0) {
063            // Not useful for restricting since there is an unknown set of types occurring.
064            return this;
065        }
066
067        if (this.getNotRecordedProbability() > 0.0) {
068            // We are unrestricted, so the other profile is always a better estimate.
069            return otherProfile;
070        }
071
072        ArrayList<ProfiledType> result = new ArrayList<>();
073        for (int i = 0; i < getItems().length; i++) {
074            ProfiledType ptype = getItems()[i];
075            ResolvedJavaType type = ptype.getItem();
076            if (otherProfile.isIncluded(type)) {
077                result.add(ptype);
078            }
079        }
080
081        TriState newNullSeen = (otherProfile.getNullSeen() == TriState.FALSE) ? TriState.FALSE : getNullSeen();
082        double newNotRecorded = getNotRecordedProbability();
083        return createAdjustedProfile(result, newNullSeen, newNotRecorded);
084    }
085
086    public JavaTypeProfile restrict(ResolvedJavaType declaredType, boolean nonNull) {
087        ArrayList<ProfiledType> result = new ArrayList<>();
088        for (int i = 0; i < getItems().length; i++) {
089            ProfiledType ptype = getItems()[i];
090            ResolvedJavaType type = ptype.getItem();
091            if (declaredType.isAssignableFrom(type)) {
092                result.add(ptype);
093            }
094        }
095
096        TriState newNullSeen = (nonNull) ? TriState.FALSE : getNullSeen();
097        double newNotRecorded = this.getNotRecordedProbability();
098        // Assume for the types not recorded, the incompatibility rate is the same.
099        if (getItems().length != 0) {
100            newNotRecorded *= ((double) result.size() / (double) getItems().length);
101        }
102        return createAdjustedProfile(result, newNullSeen, newNotRecorded);
103    }
104
105    private JavaTypeProfile createAdjustedProfile(ArrayList<ProfiledType> result, TriState newNullSeen, double newNotRecorded) {
106        if (result.size() != this.getItems().length || newNotRecorded != getNotRecordedProbability() || newNullSeen != getNullSeen()) {
107            if (result.size() == 0) {
108                return new JavaTypeProfile(newNullSeen, 1.0, EMPTY_ARRAY);
109            }
110            double factor;
111            if (result.size() == this.getItems().length) {
112                /* List of types did not change, no need to recompute probabilities. */
113                factor = 1.0;
114            } else {
115                double probabilitySum = 0.0;
116                for (int i = 0; i < result.size(); i++) {
117                    probabilitySum += result.get(i).getProbability();
118                }
119                probabilitySum += newNotRecorded;
120
121                factor = 1.0 / probabilitySum; // Normalize to 1.0
122                assert factor >= 1.0;
123            }
124            ProfiledType[] newResult = new ProfiledType[result.size()];
125            for (int i = 0; i < newResult.length; ++i) {
126                ProfiledType curType = result.get(i);
127                newResult[i] = new ProfiledType(curType.getItem(), Math.min(1.0, curType.getProbability() * factor));
128            }
129            double newNotRecordedTypeProbability = Math.min(1.0, newNotRecorded * factor);
130            return new JavaTypeProfile(newNullSeen, newNotRecordedTypeProbability, newResult);
131        }
132        return this;
133    }
134
135    @Override
136    public boolean equals(Object other) {
137        return super.equals(other) && nullSeen.equals(((JavaTypeProfile) other).nullSeen);
138    }
139
140    @Override
141    public int hashCode() {
142        return nullSeen.hashCode() + super.hashCode();
143    }
144
145    public static class ProfiledType extends AbstractProfiledItem<ResolvedJavaType> {
146
147        public ProfiledType(ResolvedJavaType type, double probability) {
148            super(type, probability);
149            assert type.isArray() || type.isConcrete() : type;
150        }
151
152        /**
153         * Returns the type for this profile entry.
154         */
155        public ResolvedJavaType getType() {
156            return getItem();
157        }
158
159        @Override
160        public String toString() {
161            return String.format("%.6f#%s", probability, item);
162        }
163    }
164
165    @Override
166    public String toString() {
167        StringBuilder buf = new StringBuilder("JavaTypeProfile<nullSeen=").append(getNullSeen()).append(", types=[");
168        for (int j = 0; j < getTypes().length; j++) {
169            if (j != 0) {
170                buf.append(", ");
171            }
172            ProfiledType ptype = getTypes()[j];
173            buf.append(String.format("%.6f:%s", ptype.getProbability(), ptype.getType()));
174        }
175        return buf.append(String.format("], notRecorded:%.6f>", getNotRecordedProbability())).toString();
176    }
177
178    /**
179     * Returns {@code true} if all types seen at this location have been recorded in the profile.
180     */
181    public boolean allTypesRecorded() {
182        return this.getNotRecordedProbability() == 0.0;
183    }
184
185    /**
186     * Returns the single monormorphic type representing this profile or {@code null} if no such
187     * type exists.
188     */
189    public ResolvedJavaType asSingleType() {
190        if (allTypesRecorded() && this.getTypes().length == 1) {
191            return getTypes()[0].getType();
192        }
193        return null;
194    }
195}