Skip to content
Closed
Show file tree
Hide file tree
Changes from 11 commits
Commits
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
42 changes: 39 additions & 3 deletions src/coreclr/jit/lsra.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4151,6 +4151,14 @@ bool LinearScan::canRestorePreviousInterval(RegRecord* regRec, Interval* assigne
(regRec->previousInterval != nullptr && regRec->previousInterval != assignedInterval &&
regRec->previousInterval->assignedReg == regRec && regRec->previousInterval->getNextRefPosition() != nullptr);

// A constant interval that had its register borrowed by another definition no longer holds its
// value in that register, so it must not be restored (the value would need to be rematerialized).
// Restoring it would leave the register marked as holding a constant it no longer contains.
if (retVal && regRec->previousInterval->isConstant)
{
retVal = false;
}

#ifdef TARGET_ARM
if (retVal && regRec->previousInterval->registerType == TYP_DOUBLE)
{
Expand Down Expand Up @@ -6308,6 +6316,17 @@ void LinearScan::allocateRegisters()
currentRefPosition.reload = true;
}
}
else if (RefTypeIsDef(refType) && currentInterval->isConstant &&
(currentRefPosition.treeNode != nullptr) &&
isRegConstant(assignedRegister, currentInterval->registerType))
{
// This is a redefinition of a constant interval that is already
// materialized in this register (an identical constant was coalesced
// into this interval at build time). Reuse the value in place rather
// than re-materializing it.
assert(m_compiler->opts.OptimizationEnabled());
currentRefPosition.treeNode->SetReuseRegVal();
}
INDEBUG(dumpLsraAllocationEvent(LSRA_EVENT_KEPT_ALLOCATION, currentInterval, assignedRegister));
}
}
Expand Down Expand Up @@ -7791,7 +7810,16 @@ void LinearScan::updateMaxSpill(RefPosition* refPosition)
else if (refPosition->reload)
{
assert(currentSpill[type] > 0);
currentSpill[type]--;

// A coalesced constant interval can be reloaded for one use and immediately
// re-spilled for a later use at the same refposition. The reload releases the
// spill temp while the re-spill reserves a new one, so the concurrent spill
// count is unchanged. Only a reload without a subsequent spill frees the temp.
// For all other (single-use) intervals reload and spillAfter never coincide.
if (!(interval->isConstant && refPosition->spillAfter))
{
currentSpill[type]--;
}
}
else if (refPosition->RegOptional() && refPosition->assignedReg() == REG_NA)
{
Expand All @@ -7800,8 +7828,16 @@ void LinearScan::updateMaxSpill(RefPosition* refPosition)
// memory location. To properly account max spill for typ we
// decrement spill count.
assert(RefTypeIsUse(refType));
assert(currentSpill[type] > 0);
currentSpill[type]--;

// A coalesced constant interval can have several uses that read the value directly
// from a single spill temp. The temp stays live until the final such use, so only
// release it on the last use; earlier uses must leave the temp reserved. For all
// other (single-use) intervals the use is always the last use, so this is a no-op.
if (refPosition->lastUse)
{
assert(currentSpill[type] > 0);
currentSpill[type]--;
}
}
JITDUMP(" Max spill for %s is %d\n", varTypeName(type), maxSpill[type]);
}
Expand Down
16 changes: 12 additions & 4 deletions src/coreclr/jit/lsra.h
Original file line number Diff line number Diff line change
Expand Up @@ -2007,10 +2007,18 @@ class LinearScan : public RegAllocInterface
int BuildAddrUses(GenTree* addr, SingleTypeRegSet candidates = RBM_NONE);

RefPosition* BuildDef(GenTree* tree, SingleTypeRegSet dstCandidates = RBM_NONE, int multiRegIdx = 0);
void BuildDefs(GenTree* tree, int dstCount, SingleTypeRegSet dstCandidates = RBM_NONE);
int BuildCallArgUses(GenTreeCall* call);
void BuildCallDefs(GenTree* tree, int dstCount, regMaskTP dstCandidates);
void BuildKills(GenTree* tree, regMaskTP killMask);

// Constant reuse: coalesce an overlapping, identical floating-point/SIMD/mask constant
// into a single interval so the allocator can keep the value in one register.
Interval* getConstantIntervalForReuse(GenTree* tree);
GenTree* getConsumingNode(GenTree* node);
bool constantConsumerReusesOperandReg(GenTree* consumer);
static bool areSameConstantNodes(GenTree* tree1, GenTree* tree2);

void BuildDefs(GenTree* tree, int dstCount, SingleTypeRegSet dstCandidates = RBM_NONE);
int BuildCallArgUses(GenTreeCall* call);
void BuildCallDefs(GenTree* tree, int dstCount, regMaskTP dstCandidates);
void BuildKills(GenTree* tree, regMaskTP killMask);
#if defined(TARGET_ARMARCH) || defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64)
void BuildDefWithKills(GenTree* tree, SingleTypeRegSet dstCandidates, regMaskTP killMask);
#else
Expand Down
Loading
Loading