Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expand Virtual Call targets earlier in Morph and allow CSE of the indirections #47808

Merged
merged 9 commits into from
Feb 16, 2021
2 changes: 2 additions & 0 deletions src/coreclr/jit/compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3979,6 +3979,8 @@ void Compiler::compSetOptimizationLevel()
}
}

opts.compExpandCallsEarly = (JitConfig.JitExpandCallsEarly() != 0);

fgCanRelocateEHRegions = true;
}

Expand Down
7 changes: 7 additions & 0 deletions src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -2120,6 +2120,10 @@ class fgArgInfo
noway_assert(!"GetArgEntry: argNum not found");
return nullptr;
}
void SetNeedsTemps()
{
needsTemps = true;
}

// Get the node for the arg at position argIndex.
// Caller must ensure that this index is a valid arg index.
Expand Down Expand Up @@ -5792,6 +5796,7 @@ class Compiler
Statement* paramAssignmentInsertionPoint);
static int fgEstimateCallStackSize(GenTreeCall* call);
GenTree* fgMorphCall(GenTreeCall* call);
GenTree* fgExpandVirtualVtableCallTarget(GenTreeCall* call);
void fgMorphCallInline(GenTreeCall* call, InlineResult* result);
void fgMorphCallInlineHelper(GenTreeCall* call, InlineResult* result);
#if DEBUG
Expand Down Expand Up @@ -9061,6 +9066,8 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
bool dspGCtbls; // Display the GC tables
#endif

bool compExpandCallsEarly; // True if we should expand virtual call targets early for this method

// Default numbers used to perform loop alignment. All the numbers are choosen
// based on experimenting with various benchmarks.

Expand Down
25 changes: 22 additions & 3 deletions src/coreclr/jit/fgdiagnostic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2481,6 +2481,12 @@ void Compiler::fgDebugCheckFlags(GenTree* tree)
chkFlags |= (call->gtCallAddr->gtFlags & GTF_SIDE_EFFECT);
}

if ((call->gtControlExpr != nullptr) && call->IsExpandedEarly() && call->IsVirtualVtable())
{
fgDebugCheckFlags(call->gtControlExpr);
chkFlags |= (call->gtControlExpr->gtFlags & GTF_SIDE_EFFECT);
}

if (call->IsUnmanaged() && (call->gtCallMoreFlags & GTF_CALL_M_UNMGD_THISCALL))
{
if (call->gtCallArgs->GetNode()->OperGet() == GT_NOP)
Expand Down Expand Up @@ -2968,15 +2974,28 @@ class UniquenessCheckWalker
}

//------------------------------------------------------------------------
// CheckTreeId: Check that this tree was not visit before and memorize it as visited.
// CheckTreeId: Check that this tree was not visited before and memorize it as visited.
//
// Arguments:
// gtTreeID - identificator of GenTree.
//
// Note:
// This method causes an assert failure when we find a duplicated node in our tree
//
void CheckTreeId(unsigned gtTreeID)
{
assert(!BitVecOps::IsMember(&nodesVecTraits, uniqueNodes, gtTreeID));
BitVecOps::AddElemD(&nodesVecTraits, uniqueNodes, gtTreeID);
if (BitVecOps::IsMember(&nodesVecTraits, uniqueNodes, gtTreeID))
{
if (comp->verbose)
{
printf("Duplicate gtTreeID was found: %d\n", gtTreeID);
}
assert(!"Duplicate gtTreeID was found");
}
else
{
BitVecOps::AddElemD(&nodesVecTraits, uniqueNodes, gtTreeID);
}
}

private:
Expand Down
44 changes: 31 additions & 13 deletions src/coreclr/jit/gentree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4442,6 +4442,9 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree)
costEx = 5;
costSz = 2;

GenTreeCall* call;
call = tree->AsCall();

/* Evaluate the 'this' argument, if present */

if (tree->AsCall()->gtCallThisArg != nullptr)
Expand All @@ -4459,10 +4462,10 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree)

/* Evaluate the arguments, right to left */

if (tree->AsCall()->gtCallArgs != nullptr)
if (call->gtCallArgs != nullptr)
{
const bool lateArgs = false;
lvl2 = gtSetCallArgsOrder(tree->AsCall()->Args(), lateArgs, &costEx, &costSz);
lvl2 = gtSetCallArgsOrder(call->Args(), lateArgs, &costEx, &costSz);
if (level < lvl2)
{
level = lvl2;
Expand All @@ -4473,23 +4476,23 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree)
* This is a "hidden" list and its only purpose is to
* extend the life of temps until we make the call */

if (tree->AsCall()->gtCallLateArgs != nullptr)
if (call->gtCallLateArgs != nullptr)
{
const bool lateArgs = true;
lvl2 = gtSetCallArgsOrder(tree->AsCall()->LateArgs(), lateArgs, &costEx, &costSz);
lvl2 = gtSetCallArgsOrder(call->LateArgs(), lateArgs, &costEx, &costSz);
if (level < lvl2)
{
level = lvl2;
}
}

if (tree->AsCall()->gtCallType == CT_INDIRECT)
if (call->gtCallType == CT_INDIRECT)
{
// pinvoke-calli cookie is a constant, or constant indirection
assert(tree->AsCall()->gtCallCookie == nullptr || tree->AsCall()->gtCallCookie->gtOper == GT_CNS_INT ||
tree->AsCall()->gtCallCookie->gtOper == GT_IND);
assert(call->gtCallCookie == nullptr || call->gtCallCookie->gtOper == GT_CNS_INT ||
call->gtCallCookie->gtOper == GT_IND);

GenTree* indirect = tree->AsCall()->gtCallAddr;
GenTree* indirect = call->gtCallAddr;

lvl2 = gtSetEvalOrder(indirect);
if (level < lvl2)
Expand All @@ -4501,13 +4504,27 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree)
}
else
{
if (call->IsVirtual())
{
GenTree* controlExpr = call->gtControlExpr;
if (controlExpr != nullptr)
{
lvl2 = gtSetEvalOrder(controlExpr);
if (level < lvl2)
{
level = lvl2;
}
costEx += controlExpr->GetCostEx();
costSz += controlExpr->GetCostSz();
}
}
#ifdef TARGET_ARM
if (tree->AsCall()->IsVirtualStub())
if (call->IsVirtualStub())
{
// We generate movw/movt/ldr
costEx += (1 + IND_COST_EX);
costSz += 8;
if (tree->AsCall()->gtCallMoreFlags & GTF_CALL_M_VIRTSTUB_REL_INDIRECT)
if (call->gtCallMoreFlags & GTF_CALL_M_VIRTSTUB_REL_INDIRECT)
{
// Must use R12 for the ldr target -- REG_JUMP_THUNK_PARAM
costSz += 2;
Expand All @@ -4520,6 +4537,7 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree)
}
costSz += 2;
#endif

#ifdef TARGET_XARCH
costSz += 3;
#endif
Expand All @@ -4528,7 +4546,7 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree)
level += 1;

/* Virtual calls are a bit more expensive */
if (tree->AsCall()->IsVirtual())
if (call->IsVirtual())
{
costEx += 2 * IND_COST_EX;
costSz += 2;
Expand Down Expand Up @@ -8108,7 +8126,7 @@ GenTreeCall* Compiler::gtCloneExprCallHelper(GenTreeCall* tree, unsigned addFlag

copy->gtCallType = tree->gtCallType;
copy->gtReturnType = tree->gtReturnType;
copy->gtControlExpr = tree->gtControlExpr;
copy->gtControlExpr = gtCloneExpr(tree->gtControlExpr, addFlags, deepVarNum, deepVarVal);

/* Copy the union */
if (tree->gtCallType == CT_INDIRECT)
Expand Down Expand Up @@ -9816,7 +9834,7 @@ void Compiler::gtDispNodeName(GenTree* tree)
}
if (tree->AsCall()->IsVirtualVtable())
{
gtfType = " ind";
gtfType = " vt-ind";
}
else if (tree->AsCall()->IsVirtualStub())
{
Expand Down
16 changes: 16 additions & 0 deletions src/coreclr/jit/gentree.h
Original file line number Diff line number Diff line change
Expand Up @@ -4213,6 +4213,7 @@ struct GenTreeCall final : public GenTree
#define GTF_CALL_M_SUPPRESS_GC_TRANSITION 0x00800000 // GT_CALL -- suppress the GC transition (i.e. during a pinvoke) but a separate GC safe point is required.
#define GTF_CALL_M_EXP_RUNTIME_LOOKUP 0x01000000 // GT_CALL -- this call needs to be tranformed into CFG for the dynamic dictionary expansion feature.
#define GTF_CALL_M_STRESS_TAILCALL 0x02000000 // GT_CALL -- the call is NOT "tail" prefixed but GTF_CALL_M_EXPLICIT_TAILCALL was added because of tail call stress mode
#define GTF_CALL_M_EXPANDED_EARLY 0x04000000 // GT_CALL -- the Virtual Call target address is expanded and placed in gtControlExpr in Morph rather than in Lower

// clang-format on

Expand Down Expand Up @@ -4519,6 +4520,21 @@ struct GenTreeCall final : public GenTree
return (gtCallMoreFlags & GTF_CALL_M_EXP_RUNTIME_LOOKUP) != 0;
}

void SetExpandedEarly()
{
gtCallMoreFlags |= GTF_CALL_M_EXPANDED_EARLY;
}

void ClearExpandedEarly()
{
gtCallMoreFlags &= ~GTF_CALL_M_EXPANDED_EARLY;
}

bool IsExpandedEarly() const
{
return (gtCallMoreFlags & GTF_CALL_M_EXPANDED_EARLY) != 0;
}

unsigned gtCallMoreFlags; // in addition to gtFlags

unsigned char gtCallType : 3; // value from the gtCallTypes enumeration
Expand Down
8 changes: 8 additions & 0 deletions src/coreclr/jit/importer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8244,6 +8244,14 @@ var_types Compiler::impImportCall(OPCODE opcode,
assert(!(clsFlags & CORINFO_FLG_VALUECLASS));
call = gtNewCallNode(CT_USER_FUNC, callInfo->hMethod, callRetTyp, nullptr, ilOffset);
call->gtFlags |= GTF_CALL_VIRT_VTABLE;

// Should we expand virtual call targets early for this method?
//
if (opts.compExpandCallsEarly == true)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (opts.compExpandCallsEarly == true)
if (opts.compExpandCallsEarly)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure

{
// Mark this method to expand the virtual call target early in fgMorpgCall
call->AsCall()->SetExpandedEarly();
}
break;
}

Expand Down
4 changes: 4 additions & 0 deletions src/coreclr/jit/jitconfigvalues.h
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,10 @@ CONFIG_INTEGER(JitMinimalProfiling, W("JitMinimalProfiling"), 0)
CONFIG_INTEGER(JitClassProfiling, W("JitClassProfiling"), 0)
CONFIG_INTEGER(JitEdgeProfiling, W("JitEdgeProfiling"), 0)

// Control when Virtual Calls are expanded
CONFIG_INTEGER(JitExpandCallsEarly, W("JitExpandCallsEarly"), 1) // Expand Call targets early (in the global morph
// phase)

#if defined(DEBUG)
// JitFunctionFile: Name of a file that contains a list of functions. If the currently compiled function is in the
// file, certain other JIT config variables will be active. If the currently compiled function is not in the file,
Expand Down
8 changes: 6 additions & 2 deletions src/coreclr/jit/lower.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1577,8 +1577,12 @@ void Lowering::LowerCall(GenTree* node)
break;

case GTF_CALL_VIRT_VTABLE:
// stub dispatching is off or this is not a virtual call (could be a tailcall)
controlExpr = LowerVirtualVtableCall(call);
assert(call->IsVirtualVtable());
if (!call->IsExpandedEarly())
{
assert(call->gtControlExpr == nullptr);
controlExpr = LowerVirtualVtableCall(call);
}
break;

case GTF_CALL_NONVIRT:
Expand Down
Loading