@@ -382,16 +382,22 @@ class LocalUses
382382 // lclNum - Local num for this struct local
383383 // aggregateInfo - [out] Pointer to aggregate info to create and insert replacements into.
384384 //
385- void PickPromotions (Compiler* comp, unsigned lclNum, AggregateInfo** aggregateInfo)
385+ // Returns:
386+ // Number of promotions picked.
387+ //
388+ int PickPromotions (Compiler* comp, unsigned lclNum, AggregateInfo** aggregateInfo)
386389 {
387390 if (m_accesses.size () <= 0 )
388391 {
389- return ;
392+ return 0 ;
390393 }
391394
395+ AggregateInfo*& agg = *aggregateInfo;
396+
392397 JITDUMP (" Picking promotions for V%02u\n " , lclNum);
393398
394- assert (*aggregateInfo == nullptr );
399+ assert (agg == nullptr );
400+ int numReps = 0 ;
395401 for (size_t i = 0 ; i < m_accesses.size (); i++)
396402 {
397403 const Access& access = m_accesses[i];
@@ -406,15 +412,24 @@ class LocalUses
406412 continue ;
407413 }
408414
409- if (*aggregateInfo == nullptr )
415+ if (agg == nullptr )
410416 {
411- *aggregateInfo = new (comp, CMK_Promotion) AggregateInfo (comp->getAllocator (CMK_Promotion), lclNum);
417+ agg = new (comp, CMK_Promotion) AggregateInfo (comp->getAllocator (CMK_Promotion), lclNum);
412418 }
413419
414- (*aggregateInfo)->Replacements .push_back (Replacement (access.Offset , access.AccessType ));
420+ agg->Replacements .push_back (Replacement (access.Offset , access.AccessType ));
421+ numReps++;
422+
423+ if (agg->Replacements .size () >= PHYSICAL_PROMOTION_MAX_PROMOTIONS_PER_STRUCT)
424+ {
425+ JITDUMP (" Promoted %zu fields in V%02u; will not promote more\n " , agg->Replacements .size (),
426+ agg->LclNum );
427+ break ;
428+ }
415429 }
416430
417431 JITDUMP (" \n " );
432+ return numReps;
418433 }
419434
420435 // ------------------------------------------------------------------------
@@ -427,14 +442,24 @@ class LocalUses
427442 // lclNum - Local num for this struct local
428443 // aggregateInfo - [out] Pointer to aggregate info to create and insert replacements into.
429444 //
430- bool PickInducedPromotions (Compiler* comp, unsigned lclNum, AggregateInfo** aggregateInfo)
445+ // Returns:
446+ // Number of new promotions.
447+ //
448+ int PickInducedPromotions (Compiler* comp, unsigned lclNum, AggregateInfo** aggregateInfo)
431449 {
432450 if (m_inducedAccesses.size () <= 0 )
433451 {
434- return false ;
452+ return 0 ;
453+ }
454+
455+ AggregateInfo*& agg = *aggregateInfo;
456+
457+ if ((agg != nullptr ) && (agg->Replacements .size () >= PHYSICAL_PROMOTION_MAX_PROMOTIONS_PER_STRUCT))
458+ {
459+ return 0 ;
435460 }
436461
437- bool any = false ;
462+ int numReps = 0 ;
438463 JITDUMP (" Picking induced promotions for V%02u\n " , lclNum);
439464 for (PrimitiveAccess& inducedAccess : m_inducedAccesses)
440465 {
@@ -483,24 +508,22 @@ class LocalUses
483508 }
484509 }
485510
486- if (*aggregateInfo == nullptr )
511+ if (agg == nullptr )
487512 {
488- *aggregateInfo = new (comp, CMK_Promotion) AggregateInfo (comp->getAllocator (CMK_Promotion), lclNum);
513+ agg = new (comp, CMK_Promotion) AggregateInfo (comp->getAllocator (CMK_Promotion), lclNum);
489514 }
490515
491516 size_t insertionIndex;
492- if ((*aggregateInfo) ->Replacements .size () > 0 )
517+ if (agg ->Replacements .size () > 0 )
493518 {
494519#ifdef DEBUG
495520 Replacement* overlapRep;
496- assert (!(*aggregateInfo)
497- ->OverlappingReplacements (inducedAccess.Offset , genTypeSize (inducedAccess.AccessType ),
498- &overlapRep, nullptr ));
521+ assert (!agg->OverlappingReplacements (inducedAccess.Offset , genTypeSize (inducedAccess.AccessType ),
522+ &overlapRep, nullptr ));
499523#endif
500524
501525 insertionIndex =
502- Promotion::BinarySearch<Replacement, &Replacement::Offset>((*aggregateInfo)->Replacements ,
503- inducedAccess.Offset );
526+ Promotion::BinarySearch<Replacement, &Replacement::Offset>(agg->Replacements , inducedAccess.Offset );
504527 assert ((ssize_t )insertionIndex < 0 );
505528 insertionIndex = ~insertionIndex;
506529 }
@@ -509,13 +532,18 @@ class LocalUses
509532 insertionIndex = 0 ;
510533 }
511534
512- (*aggregateInfo)
513- ->Replacements .insert ((*aggregateInfo)->Replacements .begin () + insertionIndex,
514- Replacement (inducedAccess.Offset , inducedAccess.AccessType ));
515- any = true ;
535+ agg->Replacements .insert (agg->Replacements .begin () + insertionIndex,
536+ Replacement (inducedAccess.Offset , inducedAccess.AccessType ));
537+ numReps++;
538+
539+ if (agg->Replacements .size () >= PHYSICAL_PROMOTION_MAX_PROMOTIONS_PER_STRUCT)
540+ {
541+ JITDUMP (" Promoted %zu fields in V%02u; will not promote more\n " , agg->Replacements .size ());
542+ break ;
543+ }
516544 }
517545
518- return any ;
546+ return numReps ;
519547 }
520548
521549 // ------------------------------------------------------------------------
@@ -897,7 +925,21 @@ class LocalsUseVisitor : public GenTreeVisitor<LocalsUseVisitor>
897925 unsigned numLocals = (unsigned )aggregates.size ();
898926 JITDUMP (" Picking promotions\n " );
899927
900- bool any = false ;
928+ int totalNumPromotions = 0 ;
929+ // We limit the total number of promotions picked based on the tracking
930+ // limit to avoid blowup in the superlinear liveness computation in
931+ // pathological cases, and also because once we stop tracking the fields there is no benefit anymore.
932+ //
933+ // This logic could be improved by the use of ref counting to pick the
934+ // smart fields to compute liveness for, but as of writing this there
935+ // is no example in the built-in SPMI collections that hits this limit.
936+ //
937+ // Note that we may go slightly over this as once we start picking
938+ // replacement locals for a single struct we do not stop until we get
939+ // to the next struct, but PHYSICAL_PROMOTION_MAX_PROMOTIONS_PER_STRUCT
940+ // puts a limit on the number of promotions in each struct so this is
941+ // fine to avoid the pathological cases.
942+ const int maxTotalNumPromotions = JitConfig.JitMaxLocalsToTrack ();
901943
902944 for (unsigned lclNum = 0 ; lclNum < numLocals; lclNum++)
903945 {
@@ -914,17 +956,22 @@ class LocalsUseVisitor : public GenTreeVisitor<LocalsUseVisitor>
914956 }
915957#endif
916958
917- uses->PickPromotions (m_compiler, lclNum, &aggregates[lclNum]);
959+ totalNumPromotions += uses->PickPromotions (m_compiler, lclNum, &aggregates[lclNum]);
918960
919- any |= aggregates[lclNum] != nullptr ;
961+ if (totalNumPromotions >= maxTotalNumPromotions)
962+ {
963+ JITDUMP (" Promoted %d fields which is over our limit of %d; will not promote more\n " , totalNumPromotions,
964+ maxTotalNumPromotions);
965+ break ;
966+ }
920967 }
921968
922- if (!any )
969+ if (totalNumPromotions <= 0 )
923970 {
924971 return false ;
925972 }
926973
927- if (m_candidateStores.Height () > 0 )
974+ if (( m_candidateStores.Height () > 0 ) && (totalNumPromotions < maxTotalNumPromotions) )
928975 {
929976 // Now look for induced accesses due to assignment decomposition.
930977
@@ -974,7 +1021,7 @@ class LocalsUseVisitor : public GenTreeVisitor<LocalsUseVisitor>
9741021 }
9751022 }
9761023
977- bool any = false ;
1024+ bool again = false ;
9781025 for (unsigned lclNum = 0 ; lclNum < numLocals; lclNum++)
9791026 {
9801027 LocalUses* uses = m_uses[lclNum];
@@ -989,10 +1036,20 @@ class LocalsUseVisitor : public GenTreeVisitor<LocalsUseVisitor>
9891036 }
9901037#endif
9911038
992- any |= uses->PickInducedPromotions (m_compiler, lclNum, &aggregates[lclNum]);
1039+ int numInducedProms = uses->PickInducedPromotions (m_compiler, lclNum, &aggregates[lclNum]);
1040+ again |= numInducedProms > 0 ;
1041+
1042+ totalNumPromotions += numInducedProms;
1043+ if (totalNumPromotions >= maxTotalNumPromotions)
1044+ {
1045+ JITDUMP (" Promoted %d fields and our limit is %d; will not promote more\n " , totalNumPromotions,
1046+ maxTotalNumPromotions);
1047+ again = false ;
1048+ break ;
1049+ }
9931050 }
9941051
995- if (!any )
1052+ if (!again )
9961053 {
9971054 break ;
9981055 }
@@ -1067,11 +1124,9 @@ class LocalsUseVisitor : public GenTreeVisitor<LocalsUseVisitor>
10671124 {
10681125 // Aggregate is fully promoted, leave UnpromotedMin == UnpromotedMax to indicate this.
10691126 }
1070-
1071- any = true ;
10721127 }
10731128
1074- return any ;
1129+ return totalNumPromotions > 0 ;
10751130 }
10761131
10771132private:
0 commit comments