@@ -21148,6 +21148,7 @@ void Compiler::fgInline()
21148
21148
}
21149
21149
21150
21150
// See if we need to replace the return value place holder.
21151
+ // Also, see if this update enables further devirtualization.
21151
21152
fgWalkTreePre(&stmt->gtStmtExpr, fgUpdateInlineReturnExpressionPlaceHolder, (void*)this);
21152
21153
21153
21154
// See if stmt is of the form GT_COMMA(call, nop)
@@ -21419,11 +21420,46 @@ void Compiler::fgAttachStructInlineeToAsg(GenTreePtr tree, GenTreePtr child, COR
21419
21420
21420
21421
#endif // FEATURE_MULTIREG_RET
21421
21422
21422
- /*****************************************************************************
21423
- * Callback to replace the inline return expression place holder (GT_RET_EXPR)
21424
- */
21423
+ //------------------------------------------------------------------------
21424
+ // fgUpdateInlineReturnExpressionPlaceHolder: callback to replace the
21425
+ // inline return expression placeholder.
21426
+ //
21427
+ // Arguments:
21428
+ // pTree -- pointer to tree to examine for updates
21429
+ // data -- context data for the tree walk
21430
+ //
21431
+ // Returns:
21432
+ // fgWalkResult indicating the walk should continue; that
21433
+ // is we wish to fully explore the tree.
21434
+ //
21435
+ // Notes:
21436
+ // Looks for GT_RET_EXPR nodes that arose from tree splitting done
21437
+ // during importation for inline candidates, and replaces them.
21438
+ //
21439
+ // For successful inlines, substitutes the return value expression
21440
+ // from the inline body for the GT_RET_EXPR.
21441
+ //
21442
+ // For failed inlines, rejoins the original call into the tree from
21443
+ // whence it was split during importation.
21444
+ //
21445
+ // The code doesn't actually know if the corresponding inline
21446
+ // succeeded or not; it relies on the fact that gtInlineCandidate
21447
+ // initially points back at the call and is modified in place to
21448
+ // the inlinee return expression if the inline is successful (see
21449
+ // tail end of fgInsertInlineeBlocks for the update of iciCall).
21450
+ //
21451
+ // If the parent of the GT_RET_EXPR is a virtual call,
21452
+ // devirtualization is attempted. This should only succeed in the
21453
+ // successful inline case, when the inlinee's return value
21454
+ // expression provides a better type than the return type of the
21455
+ // method. Note for failed inlines, the devirtualizer can only go
21456
+ // by the return type, and any devirtualization that type enabled
21457
+ // would have already happened during importation.
21458
+ //
21459
+ // If the return type is a struct type and we're on a platform
21460
+ // where structs can be returned in multiple registers, ensure the
21461
+ // call has a suitable parent.
21425
21462
21426
- /* static */
21427
21463
Compiler::fgWalkResult Compiler::fgUpdateInlineReturnExpressionPlaceHolder(GenTreePtr* pTree, fgWalkData* data)
21428
21464
{
21429
21465
GenTreePtr tree = *pTree;
@@ -21469,6 +21505,41 @@ Compiler::fgWalkResult Compiler::fgUpdateInlineReturnExpressionPlaceHolder(GenTr
21469
21505
}
21470
21506
#endif // DEBUG
21471
21507
} while (tree->gtOper == GT_RET_EXPR);
21508
+
21509
+ // Now see if this return value expression feeds the 'this'
21510
+ // object at a virtual call site.
21511
+ //
21512
+ // Note for void returns where the inline failed, the
21513
+ // GT_RET_EXPR may be top-level.
21514
+ //
21515
+ // May miss cases where there are intermediaries between call
21516
+ // and this, eg commas.
21517
+ GenTreePtr parentTree = data->parent;
21518
+
21519
+ if ((parentTree != nullptr) && (parentTree->gtOper == GT_CALL))
21520
+ {
21521
+ GenTreeCall* call = parentTree->AsCall();
21522
+ bool tryLateDevirt = call->IsVirtual() && (call->gtCallObjp == tree);
21523
+
21524
+ #ifdef DEBUG
21525
+ tryLateDevirt = tryLateDevirt && (JitConfig.JitEnableLateDevirtualization() == 1);
21526
+ #endif // DEBUG
21527
+
21528
+ if (tryLateDevirt)
21529
+ {
21530
+ #ifdef DEBUG
21531
+ if (comp->verbose)
21532
+ {
21533
+ printf("**** Late devirt opportunity\n");
21534
+ comp->gtDispTree(call);
21535
+ }
21536
+ #endif // DEBUG
21537
+
21538
+ CORINFO_CALL_INFO x = {};
21539
+ x.hMethod = call->gtCallMethHnd;
21540
+ comp->impDevirtualizeCall(call, tree, &x, nullptr);
21541
+ }
21542
+ }
21472
21543
}
21473
21544
21474
21545
#if FEATURE_MULTIREG_RET
0 commit comments