@@ -338,6 +338,28 @@ private void handleInvoke(Invoke invoke, SimplifierTool tool) {
338338 }
339339 }
340340
341+ boolean hasReceiver = invokeFlow .getTargetMethod ().hasReceiver ();
342+ /*
343+ * The receiver's analysis results are complete when either:
344+ *
345+ * 1. We are in the closed world.
346+ *
347+ * 2. The receiver TypeFlow's type is a closed type, so it may be not extended in a later
348+ * layer.
349+ *
350+ * 3. The receiver TypeFlow's type is a core type, so it may be not extended in a later
351+ * layer. (GR-70846: This check condition will be merged with the previous one once core
352+ * types are fully considered as closed.)
353+ *
354+ * 4. The receiver TypeFlow is not saturated.
355+ *
356+ * Otherwise, when the receiver's analysis results are incomplete, then it is possible for
357+ * more types to be observed in subsequent layers.
358+ */
359+ boolean receiverAnalysisResultsComplete = strengthenGraphs .isClosedTypeWorld ||
360+ (hasReceiver && (analysis .isClosed (invokeFlow .getReceiverType ()) || analysis .getHostVM ().isCoreType (invokeFlow .getReceiverType ()) ||
361+ !methodFlow .isSaturated (analysis , invokeFlow .getReceiver ())));
362+
341363 if (callTarget .invokeKind ().isDirect ()) {
342364 /*
343365 * Note: A direct invoke doesn't necessarily imply that the analysis should have
@@ -359,40 +381,38 @@ private void handleInvoke(Invoke invoke, SimplifierTool tool) {
359381 AnalysisError .guarantee (callees .size () == 1 , "@Delete methods should have a single callee." );
360382 AnalysisMethod singleCallee = callees .iterator ().next ();
361383 devirtualizeInvoke (singleCallee , invoke );
362- } else if (targetMethod .canBeStaticallyBound () || strengthenGraphs . isClosedTypeWorld ) {
384+ } else if (targetMethod .canBeStaticallyBound () || ( receiverAnalysisResultsComplete && callees . size () == 1 ) ) {
363385 /*
364- * We only de-virtualize invokes if we run a closed type world analysis or the target
365- * method can be trivially statically bound.
386+ * A method can be devirtualized if there is only one possible callee. This can be
387+ * determined by the following ways:
388+ *
389+ * 1. The method can be trivially statically bound, as determined independently of
390+ * analysis results.
391+ *
392+ * 2. Analysis results indicate there is only one callee. The analysis results are
393+ * required to be complete, as there could be more than only one callee in subsequent
394+ * layers.
366395 */
367- if (callees .size () == 1 ) {
368- AnalysisMethod singleCallee = callees .iterator ().next ();
369- devirtualizeInvoke (singleCallee , invoke );
370- } else {
371- TypeState receiverTypeState = null ;
372- /* If the receiver flow is saturated, its exact type state does not matter. */
373- if (invokeFlow .getTargetMethod ().hasReceiver () && !methodFlow .isSaturated (analysis , invokeFlow .getReceiver ())) {
374- receiverTypeState = methodFlow .foldTypeFlow (analysis , invokeFlow .getReceiver ());
375- }
376- assignInvokeProfiles (invoke , invokeFlow , callees , receiverTypeState , false );
377- }
396+ assert callees .size () == 1 ;
397+ AnalysisMethod singleCallee = callees .iterator ().next ();
398+ devirtualizeInvoke (singleCallee , invoke );
378399 } else {
379- /* Last resort, try to inject profiles optimistically. */
380400 TypeState receiverTypeState = null ;
381- if (invokeFlow . getTargetMethod (). hasReceiver () ) {
401+ if (hasReceiver ) {
382402 if (methodFlow .isSaturated (analysis , invokeFlow )) {
383403 /*
384404 * For saturated invokes use all seen instantiated subtypes of target method
385- * declaring class. In an open world this is incomplete as new types may be seen
386- * later, but it is an optimistic approximation.
405+ * declaring class. Note if this analysis results are not complete this is
406+ * incomplete as new types may be seen later, but it is an optimistic
407+ * approximation.
387408 */
388409 receiverTypeState = targetMethod .getDeclaringClass ().getTypeFlow (analysis , false ).getState ();
389410 } else {
411+ assert receiverAnalysisResultsComplete ;
390412 receiverTypeState = methodFlow .foldTypeFlow (analysis , invokeFlow .getReceiver ());
391413 }
392414 }
393- if (receiverTypeState != null && receiverTypeState .typesCount () <= MAX_TYPES_OPTIMISTIC_PROFILES ) {
394- assignInvokeProfiles (invoke , invokeFlow , callees , receiverTypeState , true );
395- }
415+ assignInvokeProfiles (invoke , invokeFlow , callees , receiverTypeState , !receiverAnalysisResultsComplete );
396416 }
397417
398418 if (allowOptimizeReturnParameter && (strengthenGraphs .isClosedTypeWorld || callTarget .invokeKind ().isDirect () || targetMethod .canBeStaticallyBound ())) {
@@ -445,15 +465,6 @@ private void devirtualizeInvoke(AnalysisMethod singleCallee, Invoke invoke) {
445465 invoke .callTarget ().setTargetMethod (singleCallee );
446466 }
447467
448- /**
449- * Maximum number of types seen in a {@link TypeState} for a virtual {@link Invoke} to consider
450- * optimistic profile injection. See {@link #handleInvoke(Invoke, SimplifierTool)} for more
451- * details. Note that this is a footprint consideration - we do not want to carry around
452- * gargantuan {@link JavaTypeProfile} in {@link MethodCallTargetNode} that cannot be used
453- * anyway.
454- */
455- private static final int MAX_TYPES_OPTIMISTIC_PROFILES = 100 ;
456-
457468 private void assignInvokeProfiles (Invoke invoke , InvokeTypeFlow invokeFlow , Collection <AnalysisMethod > callees , TypeState receiverTypeState , boolean assumeNotRecorded ) {
458469 /*
459470 * In an open type world we cannot trust the type state of the receiver for virtual calls as
0 commit comments