131
131
/**
132
132
* This class applies static analysis results directly to the {@link StructuredGraph Graal IR} used
133
133
* to build the type flow graph.
134
- *
134
+ *
135
135
* It uses a {@link CustomSimplification} for the {@link CanonicalizerPhase}, because that provides
136
136
* all the framework for iterative stamp propagation and adding/removing control flow nodes while
137
137
* processing the graph.
138
- *
138
+ *
139
139
* From the single-method view that the compiler has when later compiling the graph, static analysis
140
140
* results appear "out of thin air": At some random point in the graph, we suddenly have a more
141
141
* precise type (= stamp) for a value. Since many nodes are floating, and even currently fixed nodes
@@ -275,17 +275,17 @@ public final void applyResults(AnalysisMethod method) {
275
275
* Returns a type that can replace the original type in stamps as an exact type. When the
276
276
* returned type is the original type itself, the original type has no subtype and can be used
277
277
* as an exact type.
278
- *
278
+ *
279
279
* Returns null if there is no single implementor type.
280
280
*/
281
281
protected abstract AnalysisType getSingleImplementorType (AnalysisType originalType );
282
282
283
283
/*
284
284
* Returns a type that can replace the original type in stamps.
285
- *
285
+ *
286
286
* Returns null if the original type has no assignable type that is instantiated, i.e., the code
287
287
* using the type is unreachable.
288
- *
288
+ *
289
289
* Returns the original type itself if there is no optimization potential, i.e., if the original
290
290
* type itself is instantiated or has more than one instantiated direct subtype.
291
291
*/
@@ -388,7 +388,7 @@ class StrengthenSimplifier implements CustomSimplification {
388
388
this .toTargetFunction = bb .getHostVM ().getStrengthenGraphsToTargetFunction (method .getMultiMethodKey ());
389
389
}
390
390
391
- private TypeFlow <?> getNodeFlow (Node node ) {
391
+ protected TypeFlow <?> getNodeFlow (Node node ) {
392
392
return nodeFlows == null || nodeFlows .isNew (node ) ? null : nodeFlows .get (node );
393
393
}
394
394
@@ -563,6 +563,7 @@ private AnalysisType asConstantNonReachableType(ValueNode value, CoreProviders p
563
563
}
564
564
565
565
private void handleInvoke (Invoke invoke , SimplifierTool tool ) {
566
+
566
567
FixedNode node = invoke .asFixedNode ();
567
568
MethodCallTargetNode callTarget = (MethodCallTargetNode ) invoke .callTarget ();
568
569
@@ -668,32 +669,25 @@ private void handleInvoke(Invoke invoke, SimplifierTool tool) {
668
669
if (invokeFlow .getTargetMethod ().hasReceiver () && !methodFlow .isSaturated ((PointsToAnalysis ) bb , invokeFlow .getReceiver ())) {
669
670
receiverTypeState = methodFlow .foldTypeFlow ((PointsToAnalysis ) bb , invokeFlow .getReceiver ());
670
671
}
671
-
672
- /*
673
- * In an open type world we cannot trust the type state of the receiver for
674
- * virtual calls as new subtypes could be added later.
675
- *
676
- * Note: MethodFlowsGraph.saturateAllParameters() does saturate the receiver in
677
- * many cases, so the check above would also lead to a null typeProfile, but we
678
- * cannot guarantee that we cover all cases.
679
- */
680
- JavaTypeProfile typeProfile = makeTypeProfile (receiverTypeState );
681
- /*
682
- * In a closed type world analysis the method profile of an invoke is complete
683
- * and contains all the callees reachable at that invocation location. Even if
684
- * that invoke is saturated it is still correct as it contains all the reachable
685
- * implementations of the target method. However, in an open type world the
686
- * method profile of an invoke, saturated or not, is incomplete, as there can be
687
- * implementations that we haven't yet seen.
688
- */
689
- JavaMethodProfile methodProfile = makeMethodProfile (callees );
690
-
691
- assert typeProfile == null || typeProfile .getTypes ().length > 1 : "Should devirtualize with typeProfile=" + typeProfile + " and methodProfile=" + methodProfile + " and callees" +
692
- callees + " invoke " + invokeFlow + " " + invokeFlow .getReceiver () + " in method " + getQualifiedName (graph );
693
- assert methodProfile == null || methodProfile .getMethods ().length > 1 : "Should devirtualize with typeProfile=" + typeProfile + " and methodProfile=" + methodProfile +
694
- " and callees" + callees + " invoke " + invokeFlow + " " + invokeFlow .getReceiver () + " in method " + getQualifiedName (graph );
695
-
696
- setInvokeProfiles (invoke , typeProfile , methodProfile );
672
+ assignInvokeProfiles (invoke , invokeFlow , callees , receiverTypeState , false );
673
+ }
674
+ } else {
675
+ /* Last resort, try to inject profiles optimistically. */
676
+ TypeState receiverTypeState = null ;
677
+ if (invokeFlow .getTargetMethod ().hasReceiver ()) {
678
+ if (invokeFlow .isSaturated ()) {
679
+ /*
680
+ * For saturated invokes use all seen instantiated subtypes of target method
681
+ * declaring class. In an open world this is incomplete as new types may be
682
+ * seen later, but it is an optimistic approximation.
683
+ */
684
+ receiverTypeState = targetMethod .getDeclaringClass ().getTypeFlow (bb , false ).getState ();
685
+ } else {
686
+ receiverTypeState = methodFlow .foldTypeFlow ((PointsToAnalysis ) bb , invokeFlow .getReceiver ());
687
+ }
688
+ }
689
+ if (receiverTypeState != null && receiverTypeState .typesCount () <= MAX_TYPES_OPTIMISTIC_PROFILES ) {
690
+ assignInvokeProfiles (invoke , invokeFlow , callees , receiverTypeState , true );
697
691
}
698
692
}
699
693
@@ -718,6 +712,45 @@ private void handleInvoke(Invoke invoke, SimplifierTool tool) {
718
712
updateStampUsingPiNode (node , newStampOrConstant , anchorPointAfterInvoke , tool );
719
713
}
720
714
715
+ /**
716
+ * Maximum number of types seen in a {@link TypeState} for a virtual {@link Invoke} to
717
+ * consider optimistic profile injection. See {@link #handleInvoke(Invoke, SimplifierTool)}
718
+ * for more details. Note that this is a footprint consideration - we do not want to carry
719
+ * around gargantuan {@link JavaTypeProfile} in {@link MethodCallTargetNode} that cannot be
720
+ * used anyway.
721
+ */
722
+ private static final int MAX_TYPES_OPTIMISTIC_PROFILES = 100 ;
723
+
724
+ private void assignInvokeProfiles (Invoke invoke , InvokeTypeFlow invokeFlow , Collection <AnalysisMethod > callees , TypeState receiverTypeState , boolean assumeNotRecorded ) {
725
+ /*
726
+ * In an open type world we cannot trust the type state of the receiver for virtual
727
+ * calls as new subtypes could be added later.
728
+ *
729
+ * Note: assumeNotRecorded specifies if profiles are injected for a closed or open
730
+ * world. For a closed world with precise analysis results we never have a
731
+ * notRecordedProbabiltiy in any profile. For the open world we always assume that there
732
+ * is a not recorded probability in the profile. Such a not recorded probability will be
733
+ * injected if assumeNotRecorded==true.
734
+ */
735
+ JavaTypeProfile typeProfile = makeTypeProfile (receiverTypeState , assumeNotRecorded );
736
+ /*
737
+ * In a closed type world analysis the method profile of an invoke is complete and
738
+ * contains all the callees reachable at that invocation location. Even if that invoke
739
+ * is saturated it is still correct as it contains all the reachable implementations of
740
+ * the target method. However, in an open type world the method profile of an invoke,
741
+ * saturated or not, is incomplete, as there can be implementations that we haven't yet
742
+ * seen.
743
+ */
744
+ JavaMethodProfile methodProfile = makeMethodProfile (callees , assumeNotRecorded );
745
+
746
+ assert typeProfile == null || typeProfile .getTypes ().length > 1 || assumeNotRecorded : "Should devirtualize with typeProfile=" + typeProfile + " and methodProfile=" + methodProfile +
747
+ " and callees" + callees + " invoke " + invokeFlow + " " + invokeFlow .getReceiver () + " in method " + getQualifiedName (graph );
748
+ assert methodProfile == null || methodProfile .getMethods ().length > 1 || assumeNotRecorded : "Should devirtualize with typeProfile=" + typeProfile + " and methodProfile=" + methodProfile +
749
+ " and callees" + callees + " invoke " + invokeFlow + " " + invokeFlow .getReceiver () + " in method " + getQualifiedName (graph );
750
+
751
+ setInvokeProfiles (invoke , typeProfile , methodProfile );
752
+ }
753
+
721
754
/**
722
755
* If all possible callees return the same parameter, then we can replace the invoke with
723
756
* that parameter at all usages. This is the same that would happen when the callees are
@@ -1101,17 +1134,17 @@ private static String getQualifiedName(StructuredGraph graph) {
1101
1134
return ((AnalysisMethod ) graph .method ()).getQualifiedName ();
1102
1135
}
1103
1136
1104
- protected JavaTypeProfile makeTypeProfile (TypeState typeState ) {
1137
+ protected JavaTypeProfile makeTypeProfile (TypeState typeState , boolean injectNotRecordedProbability ) {
1105
1138
if (typeState == null || analysisSizeCutoff != -1 && typeState .typesCount () > analysisSizeCutoff ) {
1106
1139
return null ;
1107
1140
}
1108
- var created = createTypeProfile (typeState );
1141
+ var created = createTypeProfile (typeState , injectNotRecordedProbability );
1109
1142
var existing = cachedTypeProfiles .putIfAbsent (created , created );
1110
1143
return existing != null ? existing : created ;
1111
1144
}
1112
1145
1113
- private JavaTypeProfile createTypeProfile (TypeState typeState ) {
1114
- double probability = 1d / typeState .typesCount ();
1146
+ private JavaTypeProfile createTypeProfile (TypeState typeState , boolean injectNotRecordedProbability ) {
1147
+ double probability = 1d / ( typeState .typesCount () + ( injectNotRecordedProbability ? 1 : 0 ) );
1115
1148
1116
1149
Stream <? extends ResolvedJavaType > stream = typeState .typesStream (bb );
1117
1150
if (converter != null ) {
@@ -1121,31 +1154,32 @@ private JavaTypeProfile createTypeProfile(TypeState typeState) {
1121
1154
.map (type -> new JavaTypeProfile .ProfiledType (type , probability ))
1122
1155
.toArray (JavaTypeProfile .ProfiledType []::new );
1123
1156
1124
- return new JavaTypeProfile (TriState .get (typeState .canBeNull ()), 0 , pitems );
1157
+ return new JavaTypeProfile (TriState .get (typeState .canBeNull ()), injectNotRecordedProbability ? probability : 0 , pitems );
1125
1158
}
1126
1159
1127
- protected JavaMethodProfile makeMethodProfile (Collection <AnalysisMethod > callees ) {
1160
+ protected JavaMethodProfile makeMethodProfile (Collection <AnalysisMethod > callees , boolean injectNotRecordedProbability ) {
1128
1161
if (analysisSizeCutoff != -1 && callees .size () > analysisSizeCutoff ) {
1129
1162
return null ;
1130
1163
}
1131
- var created = createMethodProfile (callees );
1164
+ var created = createMethodProfile (callees , injectNotRecordedProbability );
1132
1165
var existing = cachedMethodProfiles .putIfAbsent (created , created );
1133
1166
return existing != null ? existing .profile : created .profile ;
1134
1167
}
1135
1168
1136
- private CachedJavaMethodProfile createMethodProfile (Collection <AnalysisMethod > callees ) {
1169
+ private CachedJavaMethodProfile createMethodProfile (Collection <AnalysisMethod > callees , boolean injectNotRecordedProbability ) {
1137
1170
JavaMethodProfile .ProfiledMethod [] pitems = new JavaMethodProfile .ProfiledMethod [callees .size ()];
1138
1171
int hashCode = 0 ;
1139
- double probability = 1d / pitems .length ;
1172
+ double probability = 1d / ( pitems .length + ( injectNotRecordedProbability ? 1 : 0 )) ;
1140
1173
1141
1174
int idx = 0 ;
1142
1175
for (AnalysisMethod aMethod : callees ) {
1143
1176
ResolvedJavaMethod convertedMethod = converter == null ? aMethod : converter .lookup (aMethod );
1144
1177
pitems [idx ++] = new JavaMethodProfile .ProfiledMethod (convertedMethod , probability );
1145
1178
hashCode = hashCode * 31 + convertedMethod .hashCode ();
1146
1179
}
1147
- return new CachedJavaMethodProfile (new JavaMethodProfile (0 , pitems ), hashCode );
1180
+ return new CachedJavaMethodProfile (new JavaMethodProfile (injectNotRecordedProbability ? probability : 0 , pitems ), hashCode );
1148
1181
}
1182
+
1149
1183
}
1150
1184
1151
1185
/**
0 commit comments