1 /*
2  * Copyright (c) 2011, 2019, 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  */
23 package jdk.vm.ci.meta;
24 
25 import java.lang.reflect.Modifier;
26 import java.util.ArrayList;
27 
28 import jdk.vm.ci.meta.JavaTypeProfile.ProfiledType;
29 
30 /**
31  * This profile object represents the type profile at a specific BCI. The precision of the supplied
32  * values may vary, but a runtime that provides this information should be aware that it will be
33  * used to guide performance-critical decisions like speculative inlining, etc.
34  */
35 public final class JavaTypeProfile extends AbstractJavaProfile<ProfiledType, ResolvedJavaType> {
36 
37     private static final ProfiledType[] EMPTY_ARRAY = new ProfiledType[0];
38 
39     private final TriState nullSeen;
40 
JavaTypeProfile(TriState nullSeen, double notRecordedProbability, ProfiledType[] pitems)41     public JavaTypeProfile(TriState nullSeen, double notRecordedProbability, ProfiledType[] pitems) {
42         super(notRecordedProbability, pitems);
43         this.nullSeen = nullSeen;
44     }
45 
46     /**
47      * Returns whether a null value was at the type check.
48      */
getNullSeen()49     public TriState getNullSeen() {
50         return nullSeen;
51     }
52 
53     /**
54      * A list of types for which the runtime has recorded probability information. Note that this
55      * includes both positive and negative types where a positive type is a subtype of the checked
56      * type and a negative type is not.
57      */
getTypes()58     public ProfiledType[] getTypes() {
59         return getItems();
60     }
61 
restrict(JavaTypeProfile otherProfile)62     public JavaTypeProfile restrict(JavaTypeProfile otherProfile) {
63         if (otherProfile.getNotRecordedProbability() > 0.0) {
64             // Not useful for restricting since there is an unknown set of types occurring.
65             return this;
66         }
67 
68         if (this.getNotRecordedProbability() > 0.0) {
69             // We are unrestricted, so the other profile is always a better estimate.
70             return otherProfile;
71         }
72 
73         ArrayList<ProfiledType> result = new ArrayList<>();
74         for (int i = 0; i < getItems().length; i++) {
75             ProfiledType ptype = getItems()[i];
76             ResolvedJavaType type = ptype.getItem();
77             if (otherProfile.isIncluded(type)) {
78                 result.add(ptype);
79             }
80         }
81 
82         TriState newNullSeen = (otherProfile.getNullSeen() == TriState.FALSE) ? TriState.FALSE : getNullSeen();
83         double newNotRecorded = getNotRecordedProbability();
84         return createAdjustedProfile(result, newNullSeen, newNotRecorded);
85     }
86 
restrict(ResolvedJavaType declaredType, boolean nonNull)87     public JavaTypeProfile restrict(ResolvedJavaType declaredType, boolean nonNull) {
88         ArrayList<ProfiledType> result = new ArrayList<>();
89         for (int i = 0; i < getItems().length; i++) {
90             ProfiledType ptype = getItems()[i];
91             ResolvedJavaType type = ptype.getItem();
92             if (declaredType.isAssignableFrom(type)) {
93                 result.add(ptype);
94             }
95         }
96 
97         TriState newNullSeen = (nonNull) ? TriState.FALSE : getNullSeen();
98         double newNotRecorded = this.getNotRecordedProbability();
99         // Assume for the types not recorded, the incompatibility rate is the same.
100         if (getItems().length != 0) {
101             newNotRecorded *= ((double) result.size() / (double) getItems().length);
102         }
103         return createAdjustedProfile(result, newNullSeen, newNotRecorded);
104     }
105 
createAdjustedProfile(ArrayList<ProfiledType> result, TriState newNullSeen, double newNotRecorded)106     private JavaTypeProfile createAdjustedProfile(ArrayList<ProfiledType> result, TriState newNullSeen, double newNotRecorded) {
107         if (result.size() != this.getItems().length || newNotRecorded != getNotRecordedProbability() || newNullSeen != getNullSeen()) {
108             if (result.size() == 0) {
109                 return new JavaTypeProfile(newNullSeen, 1.0, EMPTY_ARRAY);
110             }
111             double factor;
112             if (result.size() == this.getItems().length) {
113                 /* List of types did not change, no need to recompute probabilities. */
114                 factor = 1.0;
115             } else {
116                 double probabilitySum = 0.0;
117                 for (int i = 0; i < result.size(); i++) {
118                     probabilitySum += result.get(i).getProbability();
119                 }
120                 probabilitySum += newNotRecorded;
121 
122                 factor = 1.0 / probabilitySum; // Normalize to 1.0
123                 assert factor >= 1.0;
124             }
125             ProfiledType[] newResult = new ProfiledType[result.size()];
126             for (int i = 0; i < newResult.length; ++i) {
127                 ProfiledType curType = result.get(i);
128                 newResult[i] = new ProfiledType(curType.getItem(), Math.min(1.0, curType.getProbability() * factor));
129             }
130             double newNotRecordedTypeProbability = Math.min(1.0, newNotRecorded * factor);
131             return new JavaTypeProfile(newNullSeen, newNotRecordedTypeProbability, newResult);
132         }
133         return this;
134     }
135 
136     @Override
equals(Object other)137     public boolean equals(Object other) {
138         return super.equals(other) && nullSeen.equals(((JavaTypeProfile) other).nullSeen);
139     }
140 
141     @Override
hashCode()142     public int hashCode() {
143         return nullSeen.hashCode() + super.hashCode();
144     }
145 
146     public static class ProfiledType extends AbstractProfiledItem<ResolvedJavaType> {
147 
ProfiledType(ResolvedJavaType type, double probability)148         public ProfiledType(ResolvedJavaType type, double probability) {
149             super(type, probability);
150             assert type.isArray() || type.isConcrete() : type + " " + Modifier.toString(type.getModifiers());
151         }
152 
153         /**
154          * Returns the type for this profile entry.
155          */
getType()156         public ResolvedJavaType getType() {
157             return getItem();
158         }
159 
160         @Override
toString()161         public String toString() {
162             return String.format("%.6f#%s", probability, item);
163         }
164     }
165 
166     @Override
toString()167     public String toString() {
168         StringBuilder buf = new StringBuilder("JavaTypeProfile<nullSeen=").append(getNullSeen()).append(", types=[");
169         for (int j = 0; j < getTypes().length; j++) {
170             if (j != 0) {
171                 buf.append(", ");
172             }
173             ProfiledType ptype = getTypes()[j];
174             buf.append(String.format("%.6f:%s", ptype.getProbability(), ptype.getType()));
175         }
176         return buf.append(String.format("], notRecorded:%.6f>", getNotRecordedProbability())).toString();
177     }
178 
179     /**
180      * Returns {@code true} if all types seen at this location have been recorded in the profile.
181      */
allTypesRecorded()182     public boolean allTypesRecorded() {
183         return this.getNotRecordedProbability() == 0.0;
184     }
185 
186     /**
187      * Returns the single monormorphic type representing this profile or {@code null} if no such
188      * type exists.
189      */
asSingleType()190     public ResolvedJavaType asSingleType() {
191         if (allTypesRecorded() && this.getTypes().length == 1) {
192             return getTypes()[0].getType();
193         }
194         return null;
195     }
196 }
197