JavaTypeProfile.java revision 12657:6ef01bd40ce2
1/*
2 * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23package jdk.vm.ci.meta;
24
25import java.util.ArrayList;
26
27import jdk.vm.ci.meta.JavaTypeProfile.ProfiledType;
28
29/**
30 * This profile object represents the type profile at a specific BCI. The precision of the supplied
31 * values may vary, but a runtime that provides this information should be aware that it will be
32 * used to guide performance-critical decisions like speculative inlining, etc.
33 */
34public final class JavaTypeProfile extends AbstractJavaProfile<ProfiledType, ResolvedJavaType> {
35
36    private static final ProfiledType[] EMPTY_ARRAY = new ProfiledType[0];
37
38    private final TriState nullSeen;
39
40    public JavaTypeProfile(TriState nullSeen, double notRecordedProbability, ProfiledType[] pitems) {
41        super(notRecordedProbability, pitems);
42        this.nullSeen = nullSeen;
43    }
44
45    /**
46     * Returns whether a null value was at the type check.
47     */
48    public TriState getNullSeen() {
49        return nullSeen;
50    }
51
52    /**
53     * A list of types for which the runtime has recorded probability information. Note that this
54     * includes both positive and negative types where a positive type is a subtype of the checked
55     * type and a negative type is not.
56     */
57    public ProfiledType[] getTypes() {
58        return getItems();
59    }
60
61    public JavaTypeProfile restrict(JavaTypeProfile otherProfile) {
62        if (otherProfile.getNotRecordedProbability() > 0.0) {
63            // Not useful for restricting since there is an unknown set of types occurring.
64            return this;
65        }
66
67        if (this.getNotRecordedProbability() > 0.0) {
68            // We are unrestricted, so the other profile is always a better estimate.
69            return otherProfile;
70        }
71
72        ArrayList<ProfiledType> result = new ArrayList<>();
73        for (int i = 0; i < getItems().length; i++) {
74            ProfiledType ptype = getItems()[i];
75            ResolvedJavaType type = ptype.getItem();
76            if (otherProfile.isIncluded(type)) {
77                result.add(ptype);
78            }
79        }
80
81        TriState newNullSeen = (otherProfile.getNullSeen() == TriState.FALSE) ? TriState.FALSE : getNullSeen();
82        double newNotRecorded = getNotRecordedProbability();
83        return createAdjustedProfile(result, newNullSeen, newNotRecorded);
84    }
85
86    public JavaTypeProfile restrict(ResolvedJavaType declaredType, boolean nonNull) {
87        ArrayList<ProfiledType> result = new ArrayList<>();
88        for (int i = 0; i < getItems().length; i++) {
89            ProfiledType ptype = getItems()[i];
90            ResolvedJavaType type = ptype.getItem();
91            if (declaredType.isAssignableFrom(type)) {
92                result.add(ptype);
93            }
94        }
95
96        TriState newNullSeen = (nonNull) ? TriState.FALSE : getNullSeen();
97        double newNotRecorded = this.getNotRecordedProbability();
98        // Assume for the types not recorded, the incompatibility rate is the same.
99        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}
196