Skip to content
11 changes: 10 additions & 1 deletion src/coreclr/jit/compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3979,6 +3979,15 @@ void Compiler::compSetOptimizationLevel()
}
}

#if TARGET_ARM
// A single JitStress=1 Linux ARM32 test fails when we expand virtual calls early
// JIT\HardwareIntrinsics\General\Vector128_1\Vector128_1_ro
//
opts.compExpandCallsEarly = (JitConfig.JitExpandCallsEarly() == 2);
#else
opts.compExpandCallsEarly = (JitConfig.JitExpandCallsEarly() != 0);
#endif

fgCanRelocateEHRegions = true;
}

Expand Down Expand Up @@ -5514,7 +5523,7 @@ int Compiler::compCompile(CORINFO_MODULE_HANDLE classPtr,
#ifdef TARGET_UNIX
info.compMatchedVM = info.compMatchedVM && (eeInfo->osType == CORINFO_UNIX);
#else
info.compMatchedVM = info.compMatchedVM && (eeInfo->osType == CORINFO_WINNT);
info.compMatchedVM = info.compMatchedVM && (eeInfo->osType == CORINFO_WINNT);
#endif

// If we are not compiling for a matched VM, then we are getting JIT flags that don't match our target
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)
{
// 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
20 changes: 16 additions & 4 deletions src/coreclr/jit/lower.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1556,7 +1556,8 @@ void Lowering::LowerCall(GenTree* node)
LowerArgsForCall(call);

// note that everything generated from this point on runs AFTER the outgoing args are placed
GenTree* controlExpr = nullptr;
GenTree* controlExpr = nullptr;
bool callWasExpandedEarly = false;

// for x86, this is where we record ESP for checking later to make sure stack is balanced

Expand All @@ -1577,8 +1578,17 @@ 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);
}
else
{
callWasExpandedEarly = true;
controlExpr = call->gtControlExpr;
}
break;

case GTF_CALL_NONVIRT:
Expand Down Expand Up @@ -1615,7 +1625,9 @@ void Lowering::LowerCall(GenTree* node)
controlExpr = LowerTailCallViaJitHelper(call, controlExpr);
}

if (controlExpr != nullptr)
// Check if we need to thread a newly created controlExpr into the LIR
//
if ((controlExpr != nullptr) && !callWasExpandedEarly)
{
LIR::Range controlExprRange = LIR::SeqTree(comp, controlExpr);

Expand Down
Loading