Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -4488,9 +4488,16 @@ class Compiler

// Enumerator de-abstraction support
//
struct InferredGdvEntry
{
CORINFO_CLASS_HANDLE m_classHandle;
unsigned m_likelihood;
};

typedef JitHashTable<GenTree*, JitPtrKeyFuncs<GenTree>, unsigned> NodeToUnsignedMap;
typedef JitHashTable<unsigned, JitSmallPrimitiveKeyFuncs<unsigned>, InferredGdvEntry> VarToLikelyClassMap;

// Map is only set on the root instance.
// Maps are only set on the root instance.
//
NodeToUnsignedMap* impEnumeratorGdvLocalMap = nullptr;
bool hasImpEnumeratorGdvLocalMap() { return impInlineRoot()->impEnumeratorGdvLocalMap != nullptr; }
Expand All @@ -4506,6 +4513,20 @@ class Compiler
return compiler->impEnumeratorGdvLocalMap;
}

VarToLikelyClassMap* impEnumeratorLikelyTypeMap = nullptr;
bool hasEnumeratorLikelyTypeMap() { return impInlineRoot()->impEnumeratorLikelyTypeMap != nullptr; }
VarToLikelyClassMap* getImpEnumeratorLikelyTypeMap()
{
Compiler* compiler = impInlineRoot();
if (compiler->impEnumeratorLikelyTypeMap == nullptr)
{
CompAllocator alloc(compiler->getAllocator(CMK_Generic));
compiler->impEnumeratorLikelyTypeMap = new (alloc) VarToLikelyClassMap(alloc);
}

return compiler->impEnumeratorLikelyTypeMap;
}

bool hasUpdatedTypeLocals = false;

#define SMALL_STACK_SIZE 16 // number of elements in impSmallStack
Expand Down
86 changes: 86 additions & 0 deletions src/coreclr/jit/importercalls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6986,6 +6986,7 @@ void Compiler::pickGDV(GenTreeCall* call,
const int maxLikelyMethods = MAX_GDV_TYPE_CHECKS;
LikelyClassMethodRecord likelyMethods[maxLikelyMethods];
unsigned numberOfMethods = 0;
bool isInferredGDV = false;

// TODO-GDV: R2R support requires additional work to reacquire the
// entrypoint, similar to what happens at the end of impDevirtualizeCall.
Expand All @@ -7000,6 +7001,39 @@ void Compiler::pickGDV(GenTreeCall* call,
pgoInfo.PgoData, ilOffset);
}

if ((numberOfClasses < 1) && (numberOfMethods < 1))
{
// See if we can infer a GDV here for enumerator var uses
//
CallArg* const thisArg = call->gtArgs.FindWellKnownArg(WellKnownArg::ThisPointer);

if (thisArg != nullptr)
{
GenTree* const thisNode = thisArg->GetEarlyNode();
if (thisNode->OperIs(GT_LCL_VAR))
{
GenTreeLclVarCommon* thisLclNode = thisNode->AsLclVarCommon();
LclVarDsc* const thisVarDsc = lvaGetDesc(thisLclNode);
unsigned const thisLclNum = thisLclNode->GetLclNum();

if (thisVarDsc->lvIsEnumerator)
{
VarToLikelyClassMap* const map = getImpEnumeratorLikelyTypeMap();
InferredGdvEntry e;
if (map->Lookup(thisLclNum, &e))
{
JITDUMP("Recalling that V%02u has %u%% likely class %s\n", thisLclNum, e.m_likelihood,
eeGetClassName(e.m_classHandle));
numberOfClasses = 1;
likelyClasses[0].handle = (INT_PTR)e.m_classHandle;
likelyClasses[0].likelihood = e.m_likelihood;
isInferredGDV = true;
}
}
}
}
}

if ((numberOfClasses < 1) && (numberOfMethods < 1))
{
if (verboseLogging)
Expand Down Expand Up @@ -7186,6 +7220,58 @@ void Compiler::pickGDV(GenTreeCall* call,
JITDUMP("Accepting type %s with likelihood %u as a candidate\n",
eeGetClassName(classGuesses[guessIdx]), likelihoods[guessIdx])
}

// If the 'this' arg to the call is an enumerator var, record any
// dominant likely class so we can possibly infer a GDV at places where we
// never observed the var's value. (eg an unreached Dispose call if
// control is hijacked out of Tier0+i by OSR).
//
// Note enumerator vars are special as they are generally not redefined
// and we want to ensure all methods called on them get inlined to enable
// escape analysis to kick in, if possible.
//
const unsigned dominantLikelihood = 50;

if (!isInferredGDV && (likelihoods[guessIdx] >= dominantLikelihood))
{
CallArg* const thisArg = call->gtArgs.FindWellKnownArg(WellKnownArg::ThisPointer);

if (thisArg != nullptr)
{
GenTree* const thisNode = thisArg->GetEarlyNode();
if (thisNode->OperIs(GT_LCL_VAR))
{
GenTreeLclVarCommon* thisLclNode = thisNode->AsLclVarCommon();
LclVarDsc* const thisVarDsc = lvaGetDesc(thisLclNode);
unsigned const thisLclNum = thisLclNode->GetLclNum();

if (thisVarDsc->lvIsEnumerator)
{
VarToLikelyClassMap* const map = getImpEnumeratorLikelyTypeMap();

// If we have multiple type observations, we just use the first.
//
// Note importation order is somewhat reverse-post-orderish;
// a block is only imported if one of its imported preds is imported.
//
// Enumerator vars tend to have a dominating MoveNext call that will
// be the one subsequent uses will see, if they lack their own
// type observations.
//
if (!map->Lookup(thisLclNum))
{
InferredGdvEntry e;
e.m_classHandle = classGuesses[guessIdx];
e.m_likelihood = likelihoods[guessIdx];

JITDUMP("Remembering that V%02u has %u%% likely class %s\n", thisLclNum,
e.m_likelihood, eeGetClassName(e.m_classHandle));
map->Set(thisLclNum, e);
}
}
}
}
}
}
else
{
Expand Down
Loading