Skip to content

Commit 74defde

Browse files
committed
JIT: Fix VN-based dead store removal for unused SSA defs (#123621)
The VN-based dead store removal phase only handled value-redundant stores but had no mechanism to remove stores with zero remaining uses. After Redundant branch opts removes JTRUE references, inlining temps' dead stores were left behind, preventing fgFoldCondToReturnBlock from producing branchless codegen. - Add an SSA use recount pass to get accurate counts after earlier phases may have removed references without updating use counts - Change the inner loop to start at defIndex=0 so inlining temps' first explicit def (at m_array[0], with no implicit entry def) is considered for removal - Add a "no remaining uses" dead store removal path that removes the entire statement when data has no side effects, falling back to the COMMA approach otherwise - Revert optimizebools.cpp to the original hasSingleStmt() check, since the dead stores are now properly removed before Optimize bools
1 parent 8a2123e commit 74defde

3 files changed

Lines changed: 109 additions & 17 deletions

File tree

src/coreclr/jit/compiler.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,13 @@ class LclSsaVarDsc
293293
return m_numUses;
294294
}
295295

296+
void ResetUses()
297+
{
298+
m_numUses = 0;
299+
m_hasPhiUse = false;
300+
m_hasGlobalUse = false;
301+
}
302+
296303
void AddUse(BasicBlock* block)
297304
{
298305
if (block != m_block)

src/coreclr/jit/optimizebools.cpp

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1769,24 +1769,10 @@ bool Compiler::fgFoldCondToReturnBlock(BasicBlock* block)
17691769

17701770
// Is block a BBJ_RETURN(1/0) ?
17711771
auto isReturnBool = [](const BasicBlock* block, bool value) {
1772-
if (block->KindIs(BBJ_RETURN) && (block->lastStmt() != nullptr))
1772+
if (block->KindIs(BBJ_RETURN) && block->hasSingleStmt())
17731773
{
17741774
GenTree* node = block->lastStmt()->GetRootNode();
1775-
if (!(node->OperIs(GT_RETURN) && node->gtGetOp1()->IsIntegralConst(value ? 1 : 0)))
1776-
{
1777-
return false;
1778-
}
1779-
// Allow preceding statements if they have no globally visible side effects
1780-
// (e.g., dead local stores left over from inlining).
1781-
for (Statement* stmt = block->firstStmt(); stmt != block->lastStmt();
1782-
stmt = stmt->GetNextStmt())
1783-
{
1784-
if (GTF_GLOBALLY_VISIBLE_SIDE_EFFECTS(stmt->GetRootNode()->gtFlags))
1785-
{
1786-
return false;
1787-
}
1788-
}
1789-
return true;
1775+
return node->OperIs(GT_RETURN) && node->gtGetOp1()->IsIntegralConst(value ? 1 : 0);
17901776
}
17911777
return false;
17921778
};

src/coreclr/jit/optimizer.cpp

Lines changed: 100 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5727,6 +5727,51 @@ PhaseStatus Compiler::optVNBasedDeadStoreRemoval()
57275727

57285728
bool madeChanges = false;
57295729

5730+
// Recount actual SSA uses. Earlier phases (e.g., Redundant branch opts) may
5731+
// have removed trees that referenced SSA defs without updating the use counts.
5732+
// A fresh count lets us identify stores whose values are truly never read.
5733+
for (unsigned lclNum = 0; lclNum < lvaCount; lclNum++)
5734+
{
5735+
if (!lvaInSsa(lclNum))
5736+
{
5737+
continue;
5738+
}
5739+
5740+
LclVarDsc* varDsc = lvaGetDesc(lclNum);
5741+
unsigned defCount = varDsc->lvPerSsaData.GetCount();
5742+
for (unsigned i = 0; i < defCount; i++)
5743+
{
5744+
varDsc->lvPerSsaData.GetSsaDefByIndex(i)->ResetUses();
5745+
}
5746+
}
5747+
5748+
for (BasicBlock* block = fgFirstBB; block != nullptr; block = block->Next())
5749+
{
5750+
for (Statement* stmt = block->firstStmt(); stmt != nullptr; stmt = stmt->GetNextStmt())
5751+
{
5752+
for (GenTree* node = stmt->GetTreeList(); node != nullptr; node = node->gtNext)
5753+
{
5754+
if (node->OperIsLocalRead())
5755+
{
5756+
GenTreeLclVarCommon* lcl = node->AsLclVarCommon();
5757+
unsigned lclNum = lcl->GetLclNum();
5758+
if (lvaInSsa(lclNum) && lcl->HasSsaName())
5759+
{
5760+
LclVarDsc* varDsc = lvaGetDesc(lclNum);
5761+
if (node->OperIs(GT_PHI_ARG))
5762+
{
5763+
varDsc->GetPerSsaData(lcl->GetSsaNum())->AddPhiUse(block);
5764+
}
5765+
else
5766+
{
5767+
varDsc->GetPerSsaData(lcl->GetSsaNum())->AddUse(block);
5768+
}
5769+
}
5770+
}
5771+
}
5772+
}
5773+
}
5774+
57305775
for (unsigned lclNum = 0; lclNum < lvaCount; lclNum++)
57315776
{
57325777
if (!lvaInSsa(lclNum))
@@ -5749,7 +5794,7 @@ PhaseStatus Compiler::optVNBasedDeadStoreRemoval()
57495794
continue;
57505795
}
57515796

5752-
for (unsigned defIndex = 1; defIndex < defCount; defIndex++)
5797+
for (unsigned defIndex = 0; defIndex < defCount; defIndex++)
57535798
{
57545799
LclSsaVarDsc* defDsc = varDsc->lvPerSsaData.GetSsaDefByIndex(defIndex);
57555800
GenTreeLclVarCommon* store = defDsc->GetDefNode();
@@ -5766,6 +5811,60 @@ PhaseStatus Compiler::optVNBasedDeadStoreRemoval()
57665811
continue;
57675812
}
57685813

5814+
// If this SSA def has no remaining uses, the store is dead
5815+
// regardless of the stored value. Earlier phases (e.g.,
5816+
// Redundant branch opts) may have removed all references.
5817+
if (defDsc->GetNumUses() == 0)
5818+
{
5819+
JITDUMP("Removed dead store (no remaining uses):\n");
5820+
DISPTREE(store);
5821+
5822+
GenTree* data = store->AsLclVarCommon()->Data();
5823+
5824+
bool removed = false;
5825+
if ((data->gtFlags & GTF_SIDE_EFFECT) == 0)
5826+
{
5827+
// Data has no side effects; try to remove the entire statement.
5828+
BasicBlock* block = defDsc->GetBlock();
5829+
for (Statement* stmt = block->firstStmt(); stmt != nullptr; stmt = stmt->GetNextStmt())
5830+
{
5831+
if (stmt->GetRootNode() == store)
5832+
{
5833+
fgRemoveStmt(block, stmt);
5834+
removed = true;
5835+
break;
5836+
}
5837+
}
5838+
}
5839+
5840+
if (!removed)
5841+
{
5842+
// Data has side effects or store is not the statement root;
5843+
// keep them via a COMMA.
5844+
// TODO-ASG: delete this hack.
5845+
GenTree* nop = gtNewNothingNode();
5846+
data->gtNext = nop;
5847+
nop->gtPrev = data;
5848+
nop->gtNext = store;
5849+
store->gtPrev = nop;
5850+
5851+
store->ChangeOper(GT_COMMA);
5852+
store->AsOp()->gtOp2 = nop;
5853+
store->gtType = TYP_VOID;
5854+
store->SetAllEffectsFlags(data);
5855+
gtUpdateTreeAncestorsSideEffects(store);
5856+
}
5857+
5858+
madeChanges = true;
5859+
continue;
5860+
}
5861+
5862+
// Value-redundancy check requires a previous def to compare against.
5863+
if (defIndex == 0)
5864+
{
5865+
continue;
5866+
}
5867+
57695868
ValueNum oldStoreValue;
57705869
if ((store->gtFlags & GTF_VAR_USEASG) == 0)
57715870
{

0 commit comments

Comments
 (0)