// lclNum - Local num for this struct local
// aggregateInfo - [out] Pointer to aggregate info to create and insert replacements into.
//
- void PickPromotions(Compiler* comp, unsigned lclNum, AggregateInfo** aggregateInfo)
+ // Returns:
+ // Number of promotions picked.
+ //
+ int PickPromotions(Compiler* comp, unsigned lclNum, AggregateInfo** aggregateInfo)
{
if (m_accesses.size() <= 0)
{
- return;
+ return 0;
}
+ AggregateInfo*& agg = *aggregateInfo;
+
JITDUMP("Picking promotions for V%02u\n", lclNum);
- assert(*aggregateInfo == nullptr);
+ assert(agg == nullptr);
+ int numReps = 0;
for (size_t i = 0; i < m_accesses.size(); i++)
{
const Access& access = m_accesses[i];
continue;
}
- if (*aggregateInfo == nullptr)
+ if (agg == nullptr)
{
- *aggregateInfo = new (comp, CMK_Promotion) AggregateInfo(comp->getAllocator(CMK_Promotion), lclNum);
+ agg = new (comp, CMK_Promotion) AggregateInfo(comp->getAllocator(CMK_Promotion), lclNum);
}
- (*aggregateInfo)->Replacements.push_back(Replacement(access.Offset, access.AccessType));
+ agg->Replacements.push_back(Replacement(access.Offset, access.AccessType));
+ numReps++;
+
+ if (agg->Replacements.size() >= PHYSICAL_PROMOTION_MAX_PROMOTIONS_PER_STRUCT)
+ {
+ JITDUMP(" Promoted %zu fields in V%02u; will not promote more\n", agg->Replacements.size(),
+ agg->LclNum);
+ break;
+ }
}
JITDUMP("\n");
+ return numReps;
}
//------------------------------------------------------------------------
// lclNum - Local num for this struct local
// aggregateInfo - [out] Pointer to aggregate info to create and insert replacements into.
//
- bool PickInducedPromotions(Compiler* comp, unsigned lclNum, AggregateInfo** aggregateInfo)
+ // Returns:
+ // Number of new promotions.
+ //
+ int PickInducedPromotions(Compiler* comp, unsigned lclNum, AggregateInfo** aggregateInfo)
{
if (m_inducedAccesses.size() <= 0)
{
- return false;
+ return 0;
+ }
+
+ AggregateInfo*& agg = *aggregateInfo;
+
+ if ((agg != nullptr) && (agg->Replacements.size() >= PHYSICAL_PROMOTION_MAX_PROMOTIONS_PER_STRUCT))
+ {
+ return 0;
}
- bool any = false;
+ int numReps = 0;
JITDUMP("Picking induced promotions for V%02u\n", lclNum);
for (PrimitiveAccess& inducedAccess : m_inducedAccesses)
{
}
}
- if (*aggregateInfo == nullptr)
+ if (agg == nullptr)
{
- *aggregateInfo = new (comp, CMK_Promotion) AggregateInfo(comp->getAllocator(CMK_Promotion), lclNum);
+ agg = new (comp, CMK_Promotion) AggregateInfo(comp->getAllocator(CMK_Promotion), lclNum);
}
size_t insertionIndex;
- if ((*aggregateInfo)->Replacements.size() > 0)
+ if (agg->Replacements.size() > 0)
{
#ifdef DEBUG
Replacement* overlapRep;
- assert(!(*aggregateInfo)
- ->OverlappingReplacements(inducedAccess.Offset, genTypeSize(inducedAccess.AccessType),
- &overlapRep, nullptr));
+ assert(!agg->OverlappingReplacements(inducedAccess.Offset, genTypeSize(inducedAccess.AccessType),
+ &overlapRep, nullptr));
#endif
insertionIndex =
- Promotion::BinarySearch<Replacement, &Replacement::Offset>((*aggregateInfo)->Replacements,
- inducedAccess.Offset);
+ Promotion::BinarySearch<Replacement, &Replacement::Offset>(agg->Replacements, inducedAccess.Offset);
assert((ssize_t)insertionIndex < 0);
insertionIndex = ~insertionIndex;
}
insertionIndex = 0;
}
- (*aggregateInfo)
- ->Replacements.insert((*aggregateInfo)->Replacements.begin() + insertionIndex,
- Replacement(inducedAccess.Offset, inducedAccess.AccessType));
- any = true;
+ agg->Replacements.insert(agg->Replacements.begin() + insertionIndex,
+ Replacement(inducedAccess.Offset, inducedAccess.AccessType));
+ numReps++;
+
+ if (agg->Replacements.size() >= PHYSICAL_PROMOTION_MAX_PROMOTIONS_PER_STRUCT)
+ {
+ JITDUMP(" Promoted %zu fields in V%02u; will not promote more\n", agg->Replacements.size());
+ break;
+ }
}
- return any;
+ return numReps;
}
//------------------------------------------------------------------------
unsigned numLocals = (unsigned)aggregates.size();
JITDUMP("Picking promotions\n");
- bool any = false;
+ int totalNumPromotions = 0;
+ // We limit the total number of promotions picked based on the tracking
+ // limit to avoid blowup in the superlinear liveness computation in
+ // pathological cases, and also because once we stop tracking the fields there is no benefit anymore.
+ //
+ // This logic could be improved by the use of ref counting to pick the
+ // smart fields to compute liveness for, but as of writing this there
+ // is no example in the built-in SPMI collections that hits this limit.
+ //
+ // Note that we may go slightly over this as once we start picking
+ // replacement locals for a single struct we do not stop until we get
+ // to the next struct, but PHYSICAL_PROMOTION_MAX_PROMOTIONS_PER_STRUCT
+ // puts a limit on the number of promotions in each struct so this is
+ // fine to avoid the pathological cases.
+ const int maxTotalNumPromotions = JitConfig.JitMaxLocalsToTrack();
for (unsigned lclNum = 0; lclNum < numLocals; lclNum++)
{
}
#endif
- uses->PickPromotions(m_compiler, lclNum, &aggregates[lclNum]);
+ totalNumPromotions += uses->PickPromotions(m_compiler, lclNum, &aggregates[lclNum]);
- any |= aggregates[lclNum] != nullptr;
+ if (totalNumPromotions >= maxTotalNumPromotions)
+ {
+ JITDUMP("Promoted %d fields which is over our limit of %d; will not promote more\n", totalNumPromotions,
+ maxTotalNumPromotions);
+ break;
+ }
}
- if (!any)
+ if (totalNumPromotions <= 0)
{
return false;
}
- if (m_candidateStores.Height() > 0)
+ if ((m_candidateStores.Height() > 0) && (totalNumPromotions < maxTotalNumPromotions))
{
// Now look for induced accesses due to assignment decomposition.
}
}
- bool any = false;
+ bool again = false;
for (unsigned lclNum = 0; lclNum < numLocals; lclNum++)
{
LocalUses* uses = m_uses[lclNum];
}
#endif
- any |= uses->PickInducedPromotions(m_compiler, lclNum, &aggregates[lclNum]);
+ int numInducedProms = uses->PickInducedPromotions(m_compiler, lclNum, &aggregates[lclNum]);
+ again |= numInducedProms > 0;
+
+ totalNumPromotions += numInducedProms;
+ if (totalNumPromotions >= maxTotalNumPromotions)
+ {
+ JITDUMP("Promoted %d fields and our limit is %d; will not promote more\n", totalNumPromotions,
+ maxTotalNumPromotions);
+ again = false;
+ break;
+ }
}
- if (!any)
+ if (!again)
{
break;
}
{
// Aggregate is fully promoted, leave UnpromotedMin == UnpromotedMax to indicate this.
}
-
- any = true;
}
- return any;
+ return totalNumPromotions > 0;
}
private: