Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit 393ee92

Browse files
committed
Devirtualize calls in some simple cases
Devirtualize calls where the this object type at a call site is a subtype of the type described at the call site. Currently learns types from local and arg references and a subset of other operators. Will devirtualize if either the class or method is final (sealed in C#), or if the type is known exactly (eg from a newobj). Devirtualization is run twice, once during importation, and again in a limited way after inlinining. Calls devirtualized during importation are are subsequently eligible for inlining. Calls devirtualized during inlining currently cannot be inlined.
1 parent 70cf442 commit 393ee92

File tree

5 files changed

+456
-6
lines changed

5 files changed

+456
-6
lines changed

src/jit/compiler.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,8 @@ class LclVarDsc
322322
#endif // FEATURE_SIMD
323323
unsigned char lvRegStruct : 1; // This is a reg-sized non-field-addressed struct.
324324

325+
unsigned char lvClassIsExact : 1; // lvClassHandle is the exact type
326+
325327
union {
326328
unsigned lvFieldLclStart; // The index of the local var representing the first field in the promoted struct
327329
// local.
@@ -704,6 +706,8 @@ class LclVarDsc
704706

705707
typeInfo lvVerTypeInfo; // type info needed for verification
706708

709+
CORINFO_CLASS_HANDLE lvClassHnd; // class handle for the local, or null if not known
710+
707711
BYTE* lvGcLayout; // GC layout info for structs
708712

709713
#if ASSERTION_PROP
@@ -2875,6 +2879,11 @@ class Compiler
28752879
CORINFO_CALL_INFO* callInfo,
28762880
IL_OFFSET rawILOffset);
28772881

2882+
void impDevirtualizeCall(GenTreeCall* call,
2883+
GenTreePtr obj,
2884+
CORINFO_CALL_INFO* callInfo,
2885+
CORINFO_CONTEXT_HANDLE* exactContextHnd);
2886+
28782887
bool impMethodInfo_hasRetBuffArg(CORINFO_METHOD_INFO* methInfo);
28792888

28802889
GenTreePtr impFixupCallStructReturn(GenTreePtr call, CORINFO_CLASS_HANDLE retClsHnd);

src/jit/flowgraph.cpp

Lines changed: 75 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21148,6 +21148,7 @@ void Compiler::fgInline()
2114821148
}
2114921149

2115021150
// See if we need to replace the return value place holder.
21151+
// Also, see if this update enables further devirtualization.
2115121152
fgWalkTreePre(&stmt->gtStmtExpr, fgUpdateInlineReturnExpressionPlaceHolder, (void*)this);
2115221153

2115321154
// See if stmt is of the form GT_COMMA(call, nop)
@@ -21419,11 +21420,46 @@ void Compiler::fgAttachStructInlineeToAsg(GenTreePtr tree, GenTreePtr child, COR
2141921420

2142021421
#endif // FEATURE_MULTIREG_RET
2142121422

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.
2142521462

21426-
/* static */
2142721463
Compiler::fgWalkResult Compiler::fgUpdateInlineReturnExpressionPlaceHolder(GenTreePtr* pTree, fgWalkData* data)
2142821464
{
2142921465
GenTreePtr tree = *pTree;
@@ -21469,6 +21505,41 @@ Compiler::fgWalkResult Compiler::fgUpdateInlineReturnExpressionPlaceHolder(GenTr
2146921505
}
2147021506
#endif // DEBUG
2147121507
} 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+
}
2147221543
}
2147321544

2147421545
#if FEATURE_MULTIREG_RET

0 commit comments

Comments
 (0)