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: Conditional Escape Analysis and Cloning #111473

Merged
merged 27 commits into from
Feb 4, 2025
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
95154f1
Jit: Conditional Escape Analysis and Cloning
AndyAyersMS Nov 5, 2024
f7af8e7
fix build; fix doc
AndyAyersMS Jan 15, 2025
a785363
restore fix lost in a merge somewhere
AndyAyersMS Jan 16, 2025
9f18c69
Clarify sentence in Linq methods section
AndyAyersMS Jan 17, 2025
2b5546e
Apply suggestions from code review
AndyAyersMS Jan 17, 2025
c2a15ad
properly detect if allocation escapes via an alloc temp
AndyAyersMS Jan 17, 2025
670b407
fix insertion point logic for the cloning
AndyAyersMS Jan 17, 2025
f5c2d93
Merge branch 'main' into ArrayDeAbstractionCloning
AndyAyersMS Jan 18, 2025
e9fa669
merge main; fix up some conflicts
AndyAyersMS Jan 22, 2025
95f8ea5
fix bad merge
AndyAyersMS Jan 23, 2025
45badb6
fix enclosing EH region extent
AndyAyersMS Jan 23, 2025
41f8397
better fix for enclosing regions
AndyAyersMS Jan 24, 2025
59a010f
Merge branch 'main' into ArrayDeAbstractionCloning
AndyAyersMS Jan 25, 2025
49d9ade
wip
AndyAyersMS Jan 25, 2025
819ece4
Add range control
AndyAyersMS Jan 25, 2025
f6b84e2
format
AndyAyersMS Jan 25, 2025
ebc78ef
Merge branch 'main' into ArrayDeAbstractionCloning
AndyAyersMS Jan 25, 2025
fc68385
tweak
AndyAyersMS Jan 25, 2025
12d2c04
make sure enumerator var uses get retyped
AndyAyersMS Jan 27, 2025
b3d9506
fix doc; fix dump
AndyAyersMS Jan 29, 2025
383c603
fix try region cloning in handlers
AndyAyersMS Jan 29, 2025
b91e775
Merge branch 'main' into ArrayDeAbstractionCloning
AndyAyersMS Jan 29, 2025
cd03844
cleanup some unneded code
AndyAyersMS Jan 29, 2025
32ad17b
process try regions outer to inner
AndyAyersMS Jan 31, 2025
6d43cdc
Merge branch 'main' into ArrayDeAbstractionCloning
AndyAyersMS Jan 31, 2025
3a18636
common latest statement utility
AndyAyersMS Feb 3, 2025
0bc4908
review feedback
AndyAyersMS Feb 3, 2025
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
800 changes: 800 additions & 0 deletions docs/design/coreclr/jit/DeabstractionAndConditionalEscapeAnalysis.md

Large diffs are not rendered by default.

25 changes: 23 additions & 2 deletions src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -667,6 +667,9 @@ class LclVarDsc

unsigned char lvRedefinedInEmbeddedStatement : 1; // Local has redefinitions inside embedded statements that
// disqualify it from local copy prop.

unsigned char lvIsEnumerator : 1; // Local is assigned exact class where : IEnumerable<T> via GDV

private:
unsigned char lvIsNeverNegative : 1; // The local is known to be never negative

Expand Down Expand Up @@ -4559,6 +4562,26 @@ class Compiler

unsigned impStkSize; // Size of the full stack

// Enumerator de-abstraction support
//
typedef JitHashTable<GenTree*, JitPtrKeyFuncs<GenTree>, unsigned> NodeToUnsignedMap;

// Map is only set on the root instance.
//
NodeToUnsignedMap* impEnumeratorGdvLocalMap = nullptr;
bool hasImpEnumeratorGdvLocalMap() { return impInlineRoot()->impEnumeratorGdvLocalMap != nullptr; }
NodeToUnsignedMap* getImpEnumeratorGdvLocalMap()
{
Compiler* compiler = impInlineRoot();
if (compiler->impEnumeratorGdvLocalMap == nullptr)
{
CompAllocator alloc(compiler->getAllocator(CMK_Generic));
compiler->impEnumeratorGdvLocalMap = new (alloc) NodeToUnsignedMap(alloc);
}

return compiler->impEnumeratorGdvLocalMap;
}

#define SMALL_STACK_SIZE 16 // number of elements in impSmallStack

struct SavedStack // used to save/restore stack contents.
Expand Down Expand Up @@ -11589,8 +11612,6 @@ class Compiler
return compRoot->m_fieldSeqStore;
}

typedef JitHashTable<GenTree*, JitPtrKeyFuncs<GenTree>, unsigned> NodeToUnsignedMap;

NodeToUnsignedMap* m_memorySsaMap[MemoryKindCount];

// In some cases, we want to assign intermediate SSA #'s to memory states, and know what nodes create those memory
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/jit/fgehopt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2761,6 +2761,8 @@ BasicBlock* Compiler::fgCloneTryRegion(BasicBlock* tryEntry, CloneTryInfo& info,
// all the EH indicies at or above insertBeforeIndex will shift,
// and the EH table may reallocate.
//
// This addition may also fail, if the table would become too large...
//
EHblkDsc* const clonedOutermostEbd =
fgTryAddEHTableEntries(insertBeforeIndex, regionCount, /* deferAdding */ deferCloning);

Expand Down
52 changes: 52 additions & 0 deletions src/coreclr/jit/importer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3445,6 +3445,21 @@ void Compiler::impImportAndPushBox(CORINFO_RESOLVED_TOKEN* pResolvedToken)
{
GenTreeCall* const call = exprToBox->AsRetExpr()->gtInlineCandidate->AsCall();

// If the call was flagged for possible enumerator cloning, flag the allocation as well.
//
if (compIsForInlining() && hasImpEnumeratorGdvLocalMap())
{
NodeToUnsignedMap* const map = getImpEnumeratorGdvLocalMap();
unsigned enumeratorLcl = BAD_VAR_NUM;
GenTreeCall* const call = impInlineInfo->iciCall;
if (map->Lookup(call, &enumeratorLcl))
{
JITDUMP("Flagging [%06u] for enumerator cloning via V%02u\n", dspTreeID(op1), enumeratorLcl);
map->Remove(call);
map->Set(op1, enumeratorLcl);
}
}

if (call->ShouldHaveRetBufArg())
{
JITDUMP("Must insert newobj stmts for box before call [%06u]\n", dspTreeID(call));
Expand Down Expand Up @@ -6740,6 +6755,26 @@ void Compiler::impImportBlockCode(BasicBlock* block)
{
lvaUpdateClass(lclNum, op1, tiRetVal.GetClassHandleForObjRef());
}

// If we see a local being assigned the result of a GDV-inlineable
// IEnumerable<T>.GetEnumerator, keep track of both the local and the call.
//
if (op1->OperIs(GT_RET_EXPR))
{
JITDUMP(".... checking for GDV of IEnumerable<T>...\n");

GenTreeCall* const call = op1->AsRetExpr()->gtInlineCandidate;
NamedIntrinsic const ni = lookupNamedIntrinsic(call->gtCallMethHnd);

if (ni == NI_System_Collections_Generic_IEnumerable_GetEnumerator)
{
JITDUMP("V%02u value is GDV of IEnumerable<T>.GetEnumerator\n", lclNum);
lvaTable[lclNum].lvIsEnumerator = true;
JITDUMP("Flagging [%06u] for enumerator cloning via V%02u\n", dspTreeID(call), lclNum);
getImpEnumeratorGdvLocalMap()->Set(call, lclNum);
Metrics.EnumeratorGDV++;
}
}
}

/* Filter out simple stores to itself */
Expand Down Expand Up @@ -8838,6 +8873,23 @@ void Compiler::impImportBlockCode(BasicBlock* block)
op1->gtFlags |= GTF_ALLOCOBJ_EMPTY_STATIC;
}

// If the method being imported is an inlinee, and the original call was flagged
// for possible enumerator cloning, flag the allocation as well.
//
if (compIsForInlining() && hasImpEnumeratorGdvLocalMap())
{
NodeToUnsignedMap* const map = getImpEnumeratorGdvLocalMap();
unsigned enumeratorLcl = BAD_VAR_NUM;
GenTreeCall* const call = impInlineInfo->iciCall;
if (map->Lookup(call, &enumeratorLcl))
{
JITDUMP("Flagging [%06u] for enumerator cloning via V%02u\n", dspTreeID(op1),
enumeratorLcl);
map->Remove(call);
map->Set(op1, enumeratorLcl);
}
}

// Remember that this basic block contains 'new' of an object
block->SetFlags(BBF_HAS_NEWOBJ);
optMethodFlags |= OMF_HAS_NEWOBJ;
Expand Down
7 changes: 7 additions & 0 deletions src/coreclr/jit/importercalls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10562,6 +10562,13 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method)
result = NI_System_Collections_Generic_EqualityComparer_get_Default;
}
}
else if (strcmp(className, "IEnumerable`1") == 0)
{
if (strcmp(methodName, "GetEnumerator") == 0)
{
result = NI_System_Collections_Generic_IEnumerable_GetEnumerator;
}
}
}
else if (strcmp(namespaceName, "Numerics") == 0)
{
Expand Down
16 changes: 16 additions & 0 deletions src/coreclr/jit/indirectcalltransformer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -907,6 +907,22 @@ class IndirectCallTransformer
GenTreeCall* call = compiler->gtCloneCandidateCall(origCall);
call->gtArgs.GetThisArg()->SetEarlyNode(compiler->gtNewLclvNode(thisTemp, TYP_REF));

// If the original call was flagged as one that might inspire enumerator de-abstraction
// cloning, move the flag to the devirtualized call.
//
if (compiler->hasImpEnumeratorGdvLocalMap())
{
Compiler::NodeToUnsignedMap* const map = compiler->getImpEnumeratorGdvLocalMap();
unsigned enumeratorLcl = BAD_VAR_NUM;
if (map->Lookup(origCall, &enumeratorLcl))
{
JITDUMP("Flagging [%06u] for enumerator cloning via V%02u\n", compiler->dspTreeID(call),
enumeratorLcl);
map->Remove(origCall);
map->Set(call, enumeratorLcl);
}
}

INDEBUG(call->SetIsGuarded());

JITDUMP("Direct call [%06u] in block " FMT_BB "\n", compiler->dspTreeID(call), block->bbNum);
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 @@ -668,6 +668,7 @@ CONFIG_STRING(JitObjectStackAllocationRange, "JitObjectStackAllocationRange")
RELEASE_CONFIG_INTEGER(JitObjectStackAllocation, "JitObjectStackAllocation", 1)
RELEASE_CONFIG_INTEGER(JitObjectStackAllocationRefClass, "JitObjectStackAllocationRefClass", 1)
RELEASE_CONFIG_INTEGER(JitObjectStackAllocationBoxedValueClass, "JitObjectStackAllocationBoxedValueClass", 1)
RELEASE_CONFIG_INTEGER(JitObjectStackAllocationConditionalEscape, "JitObjectStackAllocationConditoinalEscape", 1)

RELEASE_CONFIG_INTEGER(JitEECallTimingInfo, "JitEECallTimingInfo", 0)

Expand Down
3 changes: 3 additions & 0 deletions src/coreclr/jit/jitmetadatalist.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ JITMETADATAMETRIC(ClassGDV, int, 0)
JITMETADATAMETRIC(MethodGDV, int, 0)
JITMETADATAMETRIC(MultiGuessGDV, int, 0)
JITMETADATAMETRIC(ChainedGDV, int, 0)
JITMETADATAMETRIC(EnumeratorGDV, int, 0)
JITMETADATAMETRIC(InlinerBranchFold, int, 0)
JITMETADATAMETRIC(InlineAttempt, int, 0)
JITMETADATAMETRIC(InlineCount, int, 0)
Expand Down Expand Up @@ -90,6 +91,8 @@ JITMETADATAMETRIC(LocalAssertionCount, int, 0)
JITMETADATAMETRIC(LocalAssertionOverflow, int, 0)
JITMETADATAMETRIC(MorphTrackedLocals, int, 0)
JITMETADATAMETRIC(MorphLocals, int, 0)
JITMETADATAMETRIC(EnumeratorGDVProvisionalNoEscape, int, 0)
JITMETADATAMETRIC(EnumeratorGDVCanCloneToEnsureNoEscape, int, 0)

#undef JITMETADATA
#undef JITMETADATAINFO
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/jit/namedintrinsiclist.h
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ enum NamedIntrinsic : unsigned short
//
NI_System_SZArrayHelper_GetEnumerator,
NI_System_Array_T_GetEnumerator,
NI_System_Collections_Generic_IEnumerable_GetEnumerator,
};

#endif // _NAMEDINTRINSICLIST_H_
Loading
Loading