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

JIT: add option to choose guarded devirt class randomly #53399

Merged
merged 1 commit into from
Jun 1, 2021
Merged
Show file tree
Hide file tree
Changes from all 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
6 changes: 6 additions & 0 deletions src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -5742,6 +5742,12 @@ class Compiler
void fgIncorporateBlockCounts();
void fgIncorporateEdgeCounts();

CORINFO_CLASS_HANDLE getRandomClass(ICorJitInfo::PgoInstrumentationSchema* schema,
UINT32 countSchemaItems,
BYTE* pInstrumentationData,
int32_t ilOffset,
CLRRandom* random);

public:
const char* fgPgoFailReason;
bool fgPgoDisabled;
Expand Down
32 changes: 27 additions & 5 deletions src/coreclr/jit/importer.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

Expand Down Expand Up @@ -21645,17 +21644,40 @@ void Compiler::considerGuardedDevirtualization(
unsigned likelihood = 0;
unsigned numberOfClasses = 0;

CORINFO_CLASS_HANDLE likelyClass =
getLikelyClass(fgPgoSchema, fgPgoSchemaCount, fgPgoData, ilOffset, &likelihood, &numberOfClasses);
CORINFO_CLASS_HANDLE likelyClass = NO_CLASS_HANDLE;

bool doRandomDevirt = false;

#ifdef DEBUG
// Optional stress mode to pick a random known class, rather than
// the most likely known class.
//
doRandomDevirt = JitConfig.JitRandomGuardedDevirtualization() != 0;

if (doRandomDevirt)
{
// Reuse the random inliner's random state.
//
CLRRandom* const random =
impInlineRoot()->m_inlineStrategy->GetRandom(JitConfig.JitRandomGuardedDevirtualization());
likelihood = 100;
numberOfClasses = 1;
likelyClass = getRandomClass(fgPgoSchema, fgPgoSchemaCount, fgPgoData, ilOffset, random);
}
else
#endif
{
likelyClass = getLikelyClass(fgPgoSchema, fgPgoSchemaCount, fgPgoData, ilOffset, &likelihood, &numberOfClasses);
}

if (likelyClass == NO_CLASS_HANDLE)
{
JITDUMP("No likely class, sorry\n");
return;
}

JITDUMP("Likely class for %p (%s) is %p (%s) [likelihood:%u classes seen:%u]\n", dspPtr(objClass), objClassName,
likelyClass, eeGetClassName(likelyClass), likelihood, numberOfClasses);
JITDUMP("%s class for %p (%s) is %p (%s) [likelihood:%u classes seen:%u]\n", doRandomDevirt ? "Random" : "Likely",
dspPtr(objClass), objClassName, likelyClass, eeGetClassName(likelyClass), likelihood, numberOfClasses);

// Todo: a more advanced heuristic using likelihood, number of
// classes, and the profile count for this block.
Expand Down
9 changes: 7 additions & 2 deletions src/coreclr/jit/inline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1665,6 +1665,9 @@ void InlineStrategy::FinalizeXml(FILE* file)
//------------------------------------------------------------------------
// GetRandom: setup or access random state
//
// Arguments:
// seed -- seed value to use if not doing random inlines
//
// Return Value:
// New or pre-existing random state.
//
Expand All @@ -1673,11 +1676,11 @@ void InlineStrategy::FinalizeXml(FILE* file)
// specified externally (via stress or policy setting) and partially
// specified internally via method hash.

CLRRandom* InlineStrategy::GetRandom()
CLRRandom* InlineStrategy::GetRandom(int optionalSeed)
{
if (m_Random == nullptr)
{
int externalSeed = 0;
int externalSeed = optionalSeed;

#ifdef DEBUG

Expand Down Expand Up @@ -1707,6 +1710,8 @@ CLRRandom* InlineStrategy::GetRandom()

int seed = externalSeed ^ internalSeed;

JITDUMP("\n*** Using random seed ext(%u) ^ int(%u) = %u\n", externalSeed, internalSeed, seed);

m_Random = new (m_Compiler, CMK_Inlining) CLRRandom();
m_Random->Init(seed);
}
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/jit/inline.h
Original file line number Diff line number Diff line change
Expand Up @@ -912,7 +912,7 @@ class InlineStrategy
}

// Set up or access random state (for use by RandomPolicy)
CLRRandom* GetRandom();
CLRRandom* GetRandom(int optionalSeed = 0);

#endif // defined(DEBUG) || defined(INLINE_DATA)

Expand Down
1 change: 1 addition & 0 deletions src/coreclr/jit/jitconfigvalues.h
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,7 @@ CONFIG_INTEGER(JitGuardedDevirtualizationChainLikelihood, W("JitGuardedDevirtual
CONFIG_INTEGER(JitGuardedDevirtualizationChainStatements, W("JitGuardedDevirtualizationChainStatements"), 4)
#if defined(DEBUG)
CONFIG_STRING(JitGuardedDevirtualizationRange, W("JitGuardedDevirtualizationRange"))
CONFIG_INTEGER(JitRandomGuardedDevirtualization, W("JitRandomGuardedDevirtualization"), 0)
#endif // DEBUG

// Enable insertion of patchpoints into Tier0 methods with loops.
Expand Down
112 changes: 109 additions & 3 deletions src/coreclr/jit/likelyclass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ struct LikelyClassHistogramEntry
};

// Summarizes a ClassProfile table by forming a Histogram

//
struct LikelyClassHistogram
{
Expand All @@ -54,6 +53,13 @@ struct LikelyClassHistogram
}
};

//------------------------------------------------------------------------
// LikelyClassHistogram::LikelyClassHistgram: construct a new histogram
//
// Arguments:
// histogramEntries - pointer to the table portion of a ClassProfile* object (see corjit.h)
// entryCount - number of entries in the table to examine
//
LikelyClassHistogram::LikelyClassHistogram(INT_PTR* histogramEntries, unsigned entryCount)
{
m_unknownTypes = 0;
Expand Down Expand Up @@ -97,8 +103,29 @@ LikelyClassHistogram::LikelyClassHistogram(INT_PTR* histogramEntries, unsigned e
}
}

// This is used by the devirtualization logic below, and by crossgen2 when producing the R2R image (to reduce the size
// cost of carrying the type histogram)
//------------------------------------------------------------------------
// getLikelyClass: find class profile data for an IL offset, and return the most likely class
//
// Arguments:
// schema - profile schema
// countSchemaItems - number of items in the schema
// pInstrumentationData - associated data
// ilOffset - il offset of the callvirt
// pLikelihood - [OUT] likelihood of observing that entry [0...100]
// pNumberOfClasses - [OUT] estimated number of classes seen at runtime
//
// Returns:
// Class handle for the most likely class, or nullptr
//
// Notes:
// A "monomorphic" call site will return likelihood 100 and number of entries = 1.
//
// This is used by the devirtualization logic below, and by crossgen2 when producing
// the R2R image (to reduce the sizecost of carrying the type histogram)
//
// This code can runs without a jit instance present, so JITDUMP and related
// cannot be used.
//
Copy link
Member

@EgorBo EgorBo May 28, 2021

Choose a reason for hiding this comment

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

BTW, so do we want to be able to return LikelyClass even without the PGO (class probes) for interfaces/classes with known single implementations? E.g. ILLink, during trimming, could compose a list, save to a file and jit could consult with it.

Copy link
Member Author

Choose a reason for hiding this comment

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

Anything text-based might be tricky to get right in general (parsing generics, etc). It's not clear to me how robust our setup is to potentially truly random guesses.

extern "C" DLLEXPORT CORINFO_CLASS_HANDLE WINAPI getLikelyClass(ICorJitInfo::PgoInstrumentationSchema* schema,
UINT32 countSchemaItems,
BYTE* pInstrumentationData,
Expand Down Expand Up @@ -220,3 +247,82 @@ extern "C" DLLEXPORT CORINFO_CLASS_HANDLE WINAPI getLikelyClass(ICorJitInfo::Pgo
//
return NULL;
}

//------------------------------------------------------------------------
// getRandomClass: find class profile data for an IL offset, and return
// one of the possible classes at random
//
// Arguments:
// schema - profile schema
// countSchemaItems - number of items in the schema
// pInstrumentationData - associated data
// ilOffset - il offset of the callvirt
// random - randomness generator
//
// Returns:
// Randomly observed class, or nullptr.
//
CORINFO_CLASS_HANDLE Compiler::getRandomClass(ICorJitInfo::PgoInstrumentationSchema* schema,
UINT32 countSchemaItems,
BYTE* pInstrumentationData,
int32_t ilOffset,
CLRRandom* random)
{
if (schema == nullptr)
{
return NO_CLASS_HANDLE;
}

for (COUNT_T i = 0; i < countSchemaItems; i++)
{
if (schema[i].ILOffset != (int32_t)ilOffset)
{
continue;
}

if ((schema[i].InstrumentationKind == ICorJitInfo::PgoInstrumentationKind::GetLikelyClass) &&
(schema[i].Count == 1))
{
INT_PTR result = *(INT_PTR*)(pInstrumentationData + schema[i].Offset);
if (ICorJitInfo::IsUnknownTypeHandle(result))
{
return NO_CLASS_HANDLE;
}
else
{
return (CORINFO_CLASS_HANDLE)result;
}
}

bool isHistogramCount =
(schema[i].InstrumentationKind == ICorJitInfo::PgoInstrumentationKind::TypeHandleHistogramIntCount) ||
(schema[i].InstrumentationKind == ICorJitInfo::PgoInstrumentationKind::TypeHandleHistogramLongCount);

if (isHistogramCount && (schema[i].Count == 1) && ((i + 1) < countSchemaItems) &&
(schema[i + 1].InstrumentationKind == ICorJitInfo::PgoInstrumentationKind::TypeHandleHistogramTypeHandle))
{
// Form a histogram
//
LikelyClassHistogram h((INT_PTR*)(pInstrumentationData + schema[i + 1].Offset), schema[i + 1].Count);

if (h.countHistogramElements == 0)
{
return NO_CLASS_HANDLE;
}

// Choose an entry at random.
//
unsigned randomEntryIndex = random->Next(0, h.countHistogramElements - 1);
LikelyClassHistogramEntry randomEntry = h.HistogramEntryAt(randomEntryIndex);

if (ICorJitInfo::IsUnknownTypeHandle(randomEntry.m_mt))
{
return NO_CLASS_HANDLE;
}

return (CORINFO_CLASS_HANDLE)randomEntry.m_mt;
}
}

return NO_CLASS_HANDLE;
}