@@ -3445,6 +3445,21 @@ void Compiler::impImportAndPushBox(CORINFO_RESOLVED_TOKEN* pResolvedToken)
3445
3445
{
3446
3446
GenTreeCall* const call = exprToBox->AsRetExpr()->gtInlineCandidate->AsCall();
3447
3447
3448
+ // If the call was flagged for possible enumerator cloning, flag the allocation as well.
3449
+ //
3450
+ if (compIsForInlining() && hasImpEnumeratorGdvLocalMap())
3451
+ {
3452
+ NodeToUnsignedMap* const map = getImpEnumeratorGdvLocalMap();
3453
+ unsigned enumeratorLcl = BAD_VAR_NUM;
3454
+ GenTreeCall* const call = impInlineInfo->iciCall;
3455
+ if (map->Lookup(call, &enumeratorLcl))
3456
+ {
3457
+ JITDUMP("Flagging [%06u] for enumerator cloning via V%02u\n", dspTreeID(op1), enumeratorLcl);
3458
+ map->Remove(call);
3459
+ map->Set(op1, enumeratorLcl);
3460
+ }
3461
+ }
3462
+
3448
3463
if (call->ShouldHaveRetBufArg())
3449
3464
{
3450
3465
JITDUMP("Must insert newobj stmts for box before call [%06u]\n", dspTreeID(call));
@@ -6740,6 +6755,26 @@ void Compiler::impImportBlockCode(BasicBlock* block)
6740
6755
{
6741
6756
lvaUpdateClass(lclNum, op1, tiRetVal.GetClassHandleForObjRef());
6742
6757
}
6758
+
6759
+ // If we see a local being assigned the result of a GDV-inlineable
6760
+ // IEnumerable<T>.GetEnumerator, keep track of both the local and the call.
6761
+ //
6762
+ if (op1->OperIs(GT_RET_EXPR))
6763
+ {
6764
+ JITDUMP(".... checking for GDV of IEnumerable<T>...\n");
6765
+
6766
+ GenTreeCall* const call = op1->AsRetExpr()->gtInlineCandidate;
6767
+ NamedIntrinsic const ni = lookupNamedIntrinsic(call->gtCallMethHnd);
6768
+
6769
+ if (ni == NI_System_Collections_Generic_IEnumerable_GetEnumerator)
6770
+ {
6771
+ JITDUMP("V%02u value is GDV of IEnumerable<T>.GetEnumerator\n", lclNum);
6772
+ lvaTable[lclNum].lvIsEnumerator = true;
6773
+ JITDUMP("Flagging [%06u] for enumerator cloning via V%02u\n", dspTreeID(call), lclNum);
6774
+ getImpEnumeratorGdvLocalMap()->Set(call, lclNum);
6775
+ Metrics.EnumeratorGDV++;
6776
+ }
6777
+ }
6743
6778
}
6744
6779
6745
6780
/* Filter out simple stores to itself */
@@ -8838,6 +8873,23 @@ void Compiler::impImportBlockCode(BasicBlock* block)
8838
8873
op1->gtFlags |= GTF_ALLOCOBJ_EMPTY_STATIC;
8839
8874
}
8840
8875
8876
+ // If the method being imported is an inlinee, and the original call was flagged
8877
+ // for possible enumerator cloning, flag the allocation as well.
8878
+ //
8879
+ if (compIsForInlining() && hasImpEnumeratorGdvLocalMap())
8880
+ {
8881
+ NodeToUnsignedMap* const map = getImpEnumeratorGdvLocalMap();
8882
+ unsigned enumeratorLcl = BAD_VAR_NUM;
8883
+ GenTreeCall* const call = impInlineInfo->iciCall;
8884
+ if (map->Lookup(call, &enumeratorLcl))
8885
+ {
8886
+ JITDUMP("Flagging [%06u] for enumerator cloning via V%02u\n", dspTreeID(op1),
8887
+ enumeratorLcl);
8888
+ map->Remove(call);
8889
+ map->Set(op1, enumeratorLcl);
8890
+ }
8891
+ }
8892
+
8841
8893
// Remember that this basic block contains 'new' of an object
8842
8894
block->SetFlags(BBF_HAS_NEWOBJ);
8843
8895
optMethodFlags |= OMF_HAS_NEWOBJ;
0 commit comments