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  */
  23 package jdk.vm.ci.meta;
  24 
  25 import java.util.*;
  26 
  27 import jdk.vm.ci.meta.JavaTypeProfile.*;
  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  */
  34 public 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 }