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

Commit dd298a8

Browse files
committed
Review feedback
Reworded and fixed typos in some comments, inverted sense of the null check logic. Also added stubbed out 4.6 compat version, needed for clean desktop builds. Removed stale loss of type distribution data. This kind of info is useful but should probably live elsewhere.
1 parent 40b1841 commit dd298a8

File tree

2 files changed

+44
-59
lines changed

2 files changed

+44
-59
lines changed

src/jit/flowgraph.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21451,10 +21451,10 @@ void Compiler::fgAttachStructInlineeToAsg(GenTreePtr tree, GenTreePtr child, COR
2145121451
// If the parent of the GT_RET_EXPR is a virtual call,
2145221452
// devirtualization is attempted. This should only succeed in the
2145321453
// successful inline case, when the inlinee's return value
21454-
// expression provides a better type then the declared type of the
21455-
// method. For failed inlines, the devirtualizer can only go by the
21456-
// declared return type, and any devirtualization this enables
21457-
// would have happened during importation.
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.
2145821458
//
2145921459
// If the return type is a struct type and we're on a platform
2146021460
// where structs can be returned in multiple registers, ensure the

src/jit/importer.cpp

Lines changed: 40 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -18407,20 +18407,27 @@ bool Compiler::IsMathIntrinsic(GenTreePtr tree)
1840718407
// Arguments:
1840818408
// call -- the call node to examine/modify
1840918409
// thisObj -- the value of 'this' for the call
18410-
// callInfo -- info about the call from the VM
18410+
// callInfo -- [IN/OUT] info about the call from the VM
1841118411
// exactContextHnd -- [OUT] updated context handle iff call devirtualized
1841218412
//
1841318413
// Notes:
1841418414
// Virtual calls in IL will always "invoke" the base class method.
1841518415
//
1841618416
// This transformation looks for evidence that the type of 'this'
18417-
// in the call is a final class or would invoke a final method, and
18418-
// if that and other safety checks pan out, modifies the call and
18419-
// the call info to create a direct call.
18417+
// in the call is exactly known, is a final class or would invoke
18418+
// a final method, and if that and other safety checks pan out,
18419+
// modifies the call and the call info to create a direct call.
1842018420
//
18421-
// This transformation is done in the importer and not in some
18422-
// subsequent optimization pass because we want it to be upstream
18423-
// of inline candidate identification.
18421+
// This transformation is initially done in the importer and not
18422+
// in some subsequent optimization pass because we want it to be
18423+
// upstream of inline candidate identification.
18424+
//
18425+
// However, later phases may supply improved type information that
18426+
// can enable further devirtualization. We currently reinvoke this
18427+
// code after inlining, if the return value of the inlined call is
18428+
// the 'this obj' of a subsequent virtual call.
18429+
18430+
#if COR_JIT_EE_VERSION > 460
1842418431

1842518432
void Compiler::impDevirtualizeCall(GenTreeCall* call,
1842618433
GenTreePtr thisObj,
@@ -18454,51 +18461,18 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call,
1845418461

1845518462
// Do we know anything about the type of the 'this'?
1845618463
//
18457-
// Unfortunately the jit tends to only keep track of class handles
18458-
// for struct types, so the type information needed here is
18459-
// missing for most tree nodes. We can add it back selectively,
18460-
// but care is needed: the presence of the class handle is often
18461-
// used to infer struct-ness.
18462-
//
18463-
// A rough accounting in corelib shows this breakdown for node types
18464-
// seen as 'this' obj in vtable calls is as follows:
18465-
//
18466-
// Node Type Count HasType
18467-
// lclVar 4256 3825
18468-
// field 1098 1098
18469-
// retExp 864
18470-
// intrin 318
18471-
// helper 294
18472-
// callv-vtbl 224
18473-
// [] 123
18474-
// call 61
18475-
// box 60
18476-
// call-nchk 27
18477-
// callv-stub 5
18478-
//
18479-
// Note some lclVars are missing class handles; presumably these
18480-
// are locals introduced by the compiler. We might be able to add
18481-
// in type info for them. The table above gives a rough priority
18482-
// order for adding type support.
18464+
// Unfortunately the jit has historcally only kept track of class
18465+
// handles for struct types, so the type information needed here
18466+
// is missing for many tree nodes.
1848318467
//
1848418468
// Even when we can deduce the type, we may not be able to
1848518469
// devirtualize, but if we can't deduce the type, we can't do
1848618470
// anything.
18487-
//
18488-
// In corelib, devirtualization happens roughly 5% of the time,
18489-
// that is, out of 8014 virtual call sites, we devirtualize at
18490-
// 453. Assuming we could fill in missing types, devirtualization
18491-
// might ultimately kick in for around ~10% of virtual call sites.
18492-
//
18493-
// To get beyond that we'd need to do something speculative,
18494-
// either by adding runtime type tests into the jitted code, or by
18495-
// adding the ability to pitch and replace code to safely undo
18496-
// assumptions made about class hierarchy.
18497-
CORINFO_CLASS_HANDLE objClass = nullptr;
18498-
GenTreePtr obj = thisObj->gtEffectiveVal(false);
18499-
const genTreeOps objOp = obj->OperGet();
18500-
bool nullCheck = true;
18501-
bool isExact = false;
18471+
CORINFO_CLASS_HANDLE objClass = nullptr;
18472+
GenTreePtr obj = thisObj->gtEffectiveVal(false);
18473+
const genTreeOps objOp = obj->OperGet();
18474+
bool objIsNonNull = false;
18475+
bool isExact = false;
1850218476

1850318477
switch (objOp)
1850418478
{
@@ -18548,8 +18522,8 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call,
1854818522

1854918523
case GT_CNS_STR:
1855018524
{
18551-
objClass = impGetStringClass();
18552-
nullCheck = false;
18525+
objClass = impGetStringClass();
18526+
objIsNonNull = true;
1855318527
break;
1855418528
}
1855518529

@@ -18689,10 +18663,8 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call,
1868918663
call->gtCallType = CT_USER_FUNC;
1869018664

1869118665
// Virtual calls include an implicit null check, which we may
18692-
// now need to make explicit. Not sure yet if we can restrict
18693-
// this to just a subset or need to do it for all cases, so
18694-
// will do it for all unless we know the object is not null.
18695-
if (nullCheck)
18666+
// now need to make explicit.
18667+
if (!objIsNonNull)
1869618668
{
1869718669
call->gtFlags |= GTF_CALL_NULLCHECK;
1869818670
}
@@ -18742,7 +18714,7 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call,
1874218714
callInfo->methodFlags = derivedMethodAttribs;
1874318715
callInfo->contextHandle = MAKE_METHODCONTEXT(derivedMethod);
1874418716

18745-
// Update context handle too...
18717+
// Update context handle.
1874618718
if ((exactContextHandle != nullptr) && (*exactContextHandle != nullptr))
1874718719
{
1874818720
*exactContextHandle = MAKE_METHODCONTEXT(derivedMethod);
@@ -18762,3 +18734,16 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call,
1876218734
}
1876318735
#endif // defined(DEBUG)
1876418736
}
18737+
18738+
#else
18739+
18740+
// Stubbed out implementation for 4.6 compatjit
18741+
void Compiler::impDevirtualizeCall(GenTreeCall* call,
18742+
GenTreePtr thisObj,
18743+
CORINFO_CALL_INFO* callInfo,
18744+
CORINFO_CONTEXT_HANDLE* exactContextHandle)
18745+
{
18746+
return;
18747+
}
18748+
18749+
#endif // COR_JIT_EE_VERSION > 460

0 commit comments

Comments
 (0)