Skip to content

Commit

Permalink
JIT: defer crossgen update when trying to call unboxed entry
Browse files Browse the repository at this point in the history
Some unboxed entries require an additional generic context argument, and
finding the right form for this argument when prejitting is more complex than
it is for jitting. So, defer this update when prejitting.

Resolves dotnet#52450.
  • Loading branch information
AndyAyersMS committed May 7, 2021
1 parent 9d46a82 commit 24c9670
Showing 1 changed file with 52 additions and 40 deletions.
92 changes: 52 additions & 40 deletions src/coreclr/jit/importer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21146,58 +21146,70 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call,
//
if (requiresInstMethodTableArg)
{
const DWORD derivedClassAttribs = info.compCompHnd->getClassAttribs(derivedClass);

if ((derivedClassAttribs & CORINFO_FLG_SHAREDINST) != 0)
// Defer if prejitting as working backwards from the class handle to the right
// representation is messy.
if (opts.jitFlags->IsSet(JitFlags::JIT_FLAG_PREJIT))
{
JITDUMP(
"unboxed entry needs MT arg, but the handle we have is shared. Deferring update.\n");
JITDUMP("unboxed entry needs MT arg, embedding this is complicated for prejit. Deferring "
"update.\n");
}
else
{
// See if the method table we have is a viable argument to the unboxed
// entry point. It is, if it's not a shared MT.
//
GenTree* const thisArg = call->gtCallThisArg->GetNode();
GenTree* const methodTableArg = gtNewIconEmbClsHndNode(derivedClass);

JITDUMP("revising call to invoke unboxed entry with additional known class handle arg\n");
const DWORD derivedClassAttribs = info.compCompHnd->getClassAttribs(derivedClass);

// Update the 'this' pointer to refer to the box payload
//
GenTree* const payloadOffset = gtNewIconNode(TARGET_POINTER_SIZE, TYP_I_IMPL);
GenTree* const boxPayload = gtNewOperNode(GT_ADD, TYP_BYREF, thisArg, payloadOffset);

call->gtCallThisArg = gtNewCallArgs(boxPayload);
call->gtCallMethHnd = unboxedEntryMethod;
call->gtCallMoreFlags |= GTF_CALL_M_UNBOXED;

// Method attributes will differ because unboxed entry point is shared
//
const DWORD unboxedMethodAttribs = info.compCompHnd->getMethodAttribs(unboxedEntryMethod);
JITDUMP("Updating method attribs from 0x%08x to 0x%08x\n", derivedMethodAttribs,
unboxedMethodAttribs);
derivedMethod = unboxedEntryMethod;
derivedMethodAttribs = unboxedMethodAttribs;

// Add the method table argument...
//
// Prepend for R2L arg passing or empty L2R passing
// Append for non-empty L2R
//
if ((Target::g_tgtArgOrder == Target::ARG_ORDER_R2L) || (call->gtCallArgs == nullptr))
if ((derivedClassAttribs & CORINFO_FLG_SHAREDINST) != 0)
{
call->gtCallArgs = gtPrependNewCallArg(methodTableArg, call->gtCallArgs);
JITDUMP("unboxed entry needs MT arg, but the handle we have is shared. Deferring "
"update.\n");
}
else
{
GenTreeCall::Use* beforeArg = call->gtCallArgs;
while (beforeArg->GetNext() != nullptr)
// See if the method table we have is a viable argument to the unboxed
// entry point. It is, if it's not a shared MT.
//
GenTree* const thisArg = call->gtCallThisArg->GetNode();
GenTree* const methodTableArg = gtNewIconEmbClsHndNode(derivedClass);

JITDUMP(
"revising call to invoke unboxed entry with additional known class handle arg\n");

// Update the 'this' pointer to refer to the box payload
//
GenTree* const payloadOffset = gtNewIconNode(TARGET_POINTER_SIZE, TYP_I_IMPL);
GenTree* const boxPayload = gtNewOperNode(GT_ADD, TYP_BYREF, thisArg, payloadOffset);

call->gtCallThisArg = gtNewCallArgs(boxPayload);
call->gtCallMethHnd = unboxedEntryMethod;
call->gtCallMoreFlags |= GTF_CALL_M_UNBOXED;

// Method attributes will differ because unboxed entry point is shared
//
const DWORD unboxedMethodAttribs =
info.compCompHnd->getMethodAttribs(unboxedEntryMethod);
JITDUMP("Updating method attribs from 0x%08x to 0x%08x\n", derivedMethodAttribs,
unboxedMethodAttribs);
derivedMethod = unboxedEntryMethod;
derivedMethodAttribs = unboxedMethodAttribs;

// Add the method table argument...
//
// Prepend for R2L arg passing or empty L2R passing
// Append for non-empty L2R
//
if ((Target::g_tgtArgOrder == Target::ARG_ORDER_R2L) || (call->gtCallArgs == nullptr))
{
beforeArg = beforeArg->GetNext();
call->gtCallArgs = gtPrependNewCallArg(methodTableArg, call->gtCallArgs);
}
else
{
GenTreeCall::Use* beforeArg = call->gtCallArgs;
while (beforeArg->GetNext() != nullptr)
{
beforeArg = beforeArg->GetNext();
}

beforeArg->SetNext(gtNewCallArgs(methodTableArg));
beforeArg->SetNext(gtNewCallArgs(methodTableArg));
}
}
}
}
Expand Down

0 comments on commit 24c9670

Please sign in to comment.