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