@@ -3445,6 +3445,21 @@ void Compiler::impImportAndPushBox(CORINFO_RESOLVED_TOKEN* pResolvedToken)
34453445 {
34463446 GenTreeCall* const call = exprToBox->AsRetExpr()->gtInlineCandidate->AsCall();
34473447
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+
34483463 if (call->ShouldHaveRetBufArg())
34493464 {
34503465 JITDUMP("Must insert newobj stmts for box before call [%06u]\n", dspTreeID(call));
@@ -6740,6 +6755,26 @@ void Compiler::impImportBlockCode(BasicBlock* block)
67406755 {
67416756 lvaUpdateClass(lclNum, op1, tiRetVal.GetClassHandleForObjRef());
67426757 }
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+ }
67436778 }
67446779
67456780 /* Filter out simple stores to itself */
@@ -8838,6 +8873,23 @@ void Compiler::impImportBlockCode(BasicBlock* block)
88388873 op1->gtFlags |= GTF_ALLOCOBJ_EMPTY_STATIC;
88398874 }
88408875
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+
88418893 // Remember that this basic block contains 'new' of an object
88428894 block->SetFlags(BBF_HAS_NEWOBJ);
88438895 optMethodFlags |= OMF_HAS_NEWOBJ;
0 commit comments