From 7ac9a352dcc820e0d57e9e28f1f2f04599db0b3e Mon Sep 17 00:00:00 2001 From: adamperlin Date: Fri, 24 Apr 2026 13:20:01 -0700 Subject: [PATCH 1/9] Fix stackifier temporary reuse By LIR semantics, we can't always reuse temporaries that appear to be available due to interference between nodes which share the same root tree. --- src/coreclr/jit/lowerwasm.cpp | 89 ++++++++++++++++++++++++++++++----- 1 file changed, 78 insertions(+), 11 deletions(-) diff --git a/src/coreclr/jit/lowerwasm.cpp b/src/coreclr/jit/lowerwasm.cpp index ab9c56164fa1a8..5a8880eee5afe7 100644 --- a/src/coreclr/jit/lowerwasm.cpp +++ b/src/coreclr/jit/lowerwasm.cpp @@ -533,6 +533,8 @@ void Lowering::AfterLowerBlocks() Temporary* m_availableTemps[TYP_COUNT] = {}; Temporary* m_unusedTempNodes = nullptr; bool m_anyChanges = false; + BitVecTraits m_pendingReleaseTempTraits; + BitVec m_pendingReleaseTemps; public: Stackifier(Lowering* lower) @@ -540,24 +542,36 @@ void Lowering::AfterLowerBlocks() , m_compiler(lower->m_compiler) , m_stack(m_compiler->getAllocator(CMK_Lower)) , m_minimumTempLclNum(m_compiler->lvaCount) + // initially allocate 32 temp local slots for "pending release" + , m_pendingReleaseTempTraits(32, m_compiler) + , m_pendingReleaseTemps(BitVecOps::MakeEmpty(&m_pendingReleaseTempTraits)) { } void StackifyBlock(BasicBlock* block) { + + JITDUMP("LIR BEFORE stackification of " FMT_BB ": ", block->bbNum); m_anyChanges = false; m_lower->m_block = block; GenTree* node = block->lastNode(); while (node != nullptr) { + assert(BitVecOps::IsEmpty(&m_pendingReleaseTempTraits, m_pendingReleaseTemps)); assert(IsDataFlowRoot(node)); node = StackifyTree(node); + RemovePendingTemporaries(); } m_lower->m_block = nullptr; JITDUMP(FMT_BB ": %s\n", block->bbNum, m_anyChanges ? "stackified with some changes" : "already in WASM value stack order"); assert((m_unusedTempNodes == nullptr) && "Some temporaries were not released"); + if (m_anyChanges) + { + JITDUMP("LIR after stackification of " FMT_BB ": ", block->bbNum); + DISPRANGE(LIR::AsRange(block)); + } } GenTree* StackifyTree(GenTree* root) @@ -567,7 +581,8 @@ void Lowering::AfterLowerBlocks() // Simple greedy algorithm working backwards. The invariant is that the stack top must be placed right next // to (in normal linear order - before) the node we last stackified. m_stack.Push(&root); - ReleaseTemporariesDefinedBy(root); + + PendingReleaseTemporariesDefinedBy(root); GenTree* lastStackified = root->gtNext; while (m_stack.Height() != initialDepth) @@ -668,8 +683,6 @@ void Lowering::AfterLowerBlocks() *use = lclNode; JITDUMP("Replaced [%06u] with a temporary:\n", Compiler::dspTreeID(node)); - DISPNODE(node); - DISPNODE(lclNode); if ((node->gtLIRFlags & LIR::Flags::MultiplyUsed) == LIR::Flags::MultiplyUsed) { @@ -704,7 +717,19 @@ void Lowering::AfterLowerBlocks() return lclNum; } - void ReleaseTemporariesDefinedBy(GenTree* node) + constexpr int lvaToTempNum(unsigned lclNum) + { + assert(lclNum >= m_minimumTempLclNum); + return lclNum - m_minimumTempLclNum; + } + + constexpr int tmpToLvaNum(unsigned tmpNum) + { + assert(tmpNum >= 0); + return tmpNum + m_minimumTempLclNum; + } + + void PendingReleaseTemporariesDefinedBy(GenTree* node) { // We rely in this function on the lifetime of temporaries beginning (recall this is backwards traversal) // at exactly "node"'s position, and not shrinking or extending after this call. This is currently true @@ -722,15 +747,34 @@ void Lowering::AfterLowerBlocks() return; } - Temporary* local = Remove(&m_unusedTempNodes); // See if we have any free nodes in the pool. - if (local == nullptr) + JITDUMP("Stackifier pending release of lclNum: %d temporary defined by [%06u]\n", lclNum, Compiler::dspTreeID(node)); + EnsureTempBitVecCapacity(lvaToTempNum(lclNum)); + BitVecOps::AddElemD(&m_pendingReleaseTempTraits, m_pendingReleaseTemps, lvaToTempNum(lclNum)); + } + + void RemovePendingTemporaries() + { + // iterate through m_pendingReleaseTemps + BitVecOps::Iter iter(&m_pendingReleaseTempTraits, m_pendingReleaseTemps); + unsigned tmpNum; + while (iter.NextElem(&tmpNum)) { - local = new (m_compiler, CMK_Lower) Temporary(); - } - local->LclNum = lclNum; + // remove from m_pendingReleaseTemps + BitVecOps::RemoveElemD(&m_pendingReleaseTempTraits, m_pendingReleaseTemps, tmpNum); + + unsigned lclNum = tmpToLvaNum(tmpNum); + assert(lclNum >= m_minimumTempLclNum); - JITDUMP("Temporary V%02u is now free and can be re-used\n", lclNum); - Append(&m_availableTemps[genActualType(node->TypeGet())], local); + Temporary* local = Remove(&m_unusedTempNodes); // See if we have any free nodes in the pool. + if (local == nullptr) + { + local = new (m_compiler, CMK_Lower) Temporary(); + } + local->LclNum = lclNum; + + JITDUMP("Temporary V%02u is now free and can be re-used\n", lclNum); + Append(&m_availableTemps[genActualType(m_compiler->lvaGetDesc(lclNum)->TypeGet())], local); + } } Temporary* Remove(Temporary** pTemps) @@ -748,6 +792,29 @@ void Lowering::AfterLowerBlocks() local->Prev = *pTemps; *pTemps = local; } + + void EnsureTempBitVecCapacity(unsigned needed) + { + if (needed < BitVecTraits::GetSize(&m_pendingReleaseTempTraits)) + { + return; + } + + unsigned oldSize = BitVecTraits::GetSize(&m_pendingReleaseTempTraits); + unsigned newSize = max(needed + 1, oldSize * 2); + BitVecTraits newTraits(newSize, m_compiler); + BitVec newVec = BitVecOps::MakeEmpty(&newTraits); + + BitVecOps::Iter iter(&m_pendingReleaseTempTraits, m_pendingReleaseTemps); + unsigned elem; + while (iter.NextElem(&elem)) + { + BitVecOps::AddElemD(&newTraits, newVec, elem); + } + + m_pendingReleaseTempTraits = newTraits; + m_pendingReleaseTemps = newVec; + } }; Stackifier stackifier(this); From 3a9fb5bb8cb5794299bbb17d827023b1d6c5138f Mon Sep 17 00:00:00 2001 From: adamperlin Date: Fri, 24 Apr 2026 15:43:01 -0700 Subject: [PATCH 2/9] Update comment --- src/coreclr/jit/lowerwasm.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/coreclr/jit/lowerwasm.cpp b/src/coreclr/jit/lowerwasm.cpp index 5a8880eee5afe7..c5c58f044dfc42 100644 --- a/src/coreclr/jit/lowerwasm.cpp +++ b/src/coreclr/jit/lowerwasm.cpp @@ -582,7 +582,7 @@ void Lowering::AfterLowerBlocks() // to (in normal linear order - before) the node we last stackified. m_stack.Push(&root); - PendingReleaseTemporariesDefinedBy(root); + AddTemporariesForPendingRelease(root); GenTree* lastStackified = root->gtNext; while (m_stack.Height() != initialDepth) @@ -729,12 +729,16 @@ void Lowering::AfterLowerBlocks() return tmpNum + m_minimumTempLclNum; } - void PendingReleaseTemporariesDefinedBy(GenTree* node) + void AddTemporariesForPendingRelease(GenTree* node) { // We rely in this function on the lifetime of temporaries beginning (recall this is backwards traversal) - // at exactly "node"'s position, and not shrinking or extending after this call. This is currently true - // because we never move dataflow roots, and we only begin processing them after all subsequent nodes - // have already been stackified and thus won't move either. + // at exactly "node"'s position. This is a safe assumption, since we will already have stackified all + // subsequent nodes, and so any further new use of this temporary will be before this one's lifetime begins. + // However, we don't know precisely where the liftime ends here, because uses of locals happen at their position + // in tree order, and not the LIR stream. So conservatively, we wait until we've processed an entire root gentree + // before reusing any temporaries to avoid the possibility of reusing a temporary before its last + // live use in the tree order. + assert(IsDataFlowRoot(node)); if (!node->OperIs(GT_STORE_LCL_VAR)) { @@ -748,7 +752,7 @@ void Lowering::AfterLowerBlocks() } JITDUMP("Stackifier pending release of lclNum: %d temporary defined by [%06u]\n", lclNum, Compiler::dspTreeID(node)); - EnsureTempBitVecCapacity(lvaToTempNum(lclNum)); + EnsurePendingReleaseCapacity(lvaToTempNum(lclNum)); BitVecOps::AddElemD(&m_pendingReleaseTempTraits, m_pendingReleaseTemps, lvaToTempNum(lclNum)); } @@ -793,7 +797,7 @@ void Lowering::AfterLowerBlocks() *pTemps = local; } - void EnsureTempBitVecCapacity(unsigned needed) + void EnsurePendingReleaseCapacity(unsigned needed) { if (needed < BitVecTraits::GetSize(&m_pendingReleaseTempTraits)) { From 2096c11e8187ad2e92ea3998f878d26bd467df11 Mon Sep 17 00:00:00 2001 From: adamperlin Date: Fri, 24 Apr 2026 15:47:24 -0700 Subject: [PATCH 3/9] Remove extra JITDUMP --- src/coreclr/jit/lowerwasm.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/coreclr/jit/lowerwasm.cpp b/src/coreclr/jit/lowerwasm.cpp index c5c58f044dfc42..557e147095e2b6 100644 --- a/src/coreclr/jit/lowerwasm.cpp +++ b/src/coreclr/jit/lowerwasm.cpp @@ -550,8 +550,6 @@ void Lowering::AfterLowerBlocks() void StackifyBlock(BasicBlock* block) { - - JITDUMP("LIR BEFORE stackification of " FMT_BB ": ", block->bbNum); m_anyChanges = false; m_lower->m_block = block; GenTree* node = block->lastNode(); @@ -560,6 +558,9 @@ void Lowering::AfterLowerBlocks() assert(BitVecOps::IsEmpty(&m_pendingReleaseTempTraits, m_pendingReleaseTemps)); assert(IsDataFlowRoot(node)); node = StackifyTree(node); + // We've finished processing the current root tree, so + // we can release any pending temps used in stackification of the tree, + // since there is no more risk of interference between tree operands. RemovePendingTemporaries(); } m_lower->m_block = nullptr; @@ -567,11 +568,6 @@ void Lowering::AfterLowerBlocks() JITDUMP(FMT_BB ": %s\n", block->bbNum, m_anyChanges ? "stackified with some changes" : "already in WASM value stack order"); assert((m_unusedTempNodes == nullptr) && "Some temporaries were not released"); - if (m_anyChanges) - { - JITDUMP("LIR after stackification of " FMT_BB ": ", block->bbNum); - DISPRANGE(LIR::AsRange(block)); - } } GenTree* StackifyTree(GenTree* root) From 244f5357dd65a4b227efb0179049dd20c63e71d4 Mon Sep 17 00:00:00 2001 From: adamperlin Date: Fri, 24 Apr 2026 16:26:44 -0700 Subject: [PATCH 4/9] jit-format --- src/coreclr/jit/lowerwasm.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/coreclr/jit/lowerwasm.cpp b/src/coreclr/jit/lowerwasm.cpp index 557e147095e2b6..9514ea84c89013 100644 --- a/src/coreclr/jit/lowerwasm.cpp +++ b/src/coreclr/jit/lowerwasm.cpp @@ -730,10 +730,10 @@ void Lowering::AfterLowerBlocks() // We rely in this function on the lifetime of temporaries beginning (recall this is backwards traversal) // at exactly "node"'s position. This is a safe assumption, since we will already have stackified all // subsequent nodes, and so any further new use of this temporary will be before this one's lifetime begins. - // However, we don't know precisely where the liftime ends here, because uses of locals happen at their position - // in tree order, and not the LIR stream. So conservatively, we wait until we've processed an entire root gentree - // before reusing any temporaries to avoid the possibility of reusing a temporary before its last - // live use in the tree order. + // However, we don't know precisely where the liftime ends here, because uses of locals happen at their + // position in tree order, and not the LIR stream. So conservatively, we wait until we've processed an + // entire root gentree before reusing any temporaries to avoid the possibility of reusing a temporary before + // its last live use in the tree order. assert(IsDataFlowRoot(node)); if (!node->OperIs(GT_STORE_LCL_VAR)) @@ -747,7 +747,8 @@ void Lowering::AfterLowerBlocks() return; } - JITDUMP("Stackifier pending release of lclNum: %d temporary defined by [%06u]\n", lclNum, Compiler::dspTreeID(node)); + JITDUMP("Stackifier pending release of lclNum: %d temporary defined by [%06u]\n", lclNum, + Compiler::dspTreeID(node)); EnsurePendingReleaseCapacity(lvaToTempNum(lclNum)); BitVecOps::AddElemD(&m_pendingReleaseTempTraits, m_pendingReleaseTemps, lvaToTempNum(lclNum)); } @@ -800,8 +801,8 @@ void Lowering::AfterLowerBlocks() return; } - unsigned oldSize = BitVecTraits::GetSize(&m_pendingReleaseTempTraits); - unsigned newSize = max(needed + 1, oldSize * 2); + unsigned oldSize = BitVecTraits::GetSize(&m_pendingReleaseTempTraits); + unsigned newSize = max(needed + 1, oldSize * 2); BitVecTraits newTraits(newSize, m_compiler); BitVec newVec = BitVecOps::MakeEmpty(&newTraits); @@ -813,7 +814,7 @@ void Lowering::AfterLowerBlocks() } m_pendingReleaseTempTraits = newTraits; - m_pendingReleaseTemps = newVec; + m_pendingReleaseTemps = newVec; } }; From 2fbab953df4f71b94a344e7a263e6a6636dd7740 Mon Sep 17 00:00:00 2001 From: adamperlin Date: Tue, 28 Apr 2026 13:30:49 -0700 Subject: [PATCH 5/9] Release all temporaries after processing a root tree in stackifier --- src/coreclr/jit/lowerwasm.cpp | 112 +++++++++++----------------------- 1 file changed, 34 insertions(+), 78 deletions(-) diff --git a/src/coreclr/jit/lowerwasm.cpp b/src/coreclr/jit/lowerwasm.cpp index 9514ea84c89013..86954be348036f 100644 --- a/src/coreclr/jit/lowerwasm.cpp +++ b/src/coreclr/jit/lowerwasm.cpp @@ -530,11 +530,10 @@ void Lowering::AfterLowerBlocks() Compiler* m_compiler; ArrayStack m_stack; unsigned m_minimumTempLclNum; + unsigned m_maximumTempLclNum; Temporary* m_availableTemps[TYP_COUNT] = {}; Temporary* m_unusedTempNodes = nullptr; bool m_anyChanges = false; - BitVecTraits m_pendingReleaseTempTraits; - BitVec m_pendingReleaseTemps; public: Stackifier(Lowering* lower) @@ -542,9 +541,7 @@ void Lowering::AfterLowerBlocks() , m_compiler(lower->m_compiler) , m_stack(m_compiler->getAllocator(CMK_Lower)) , m_minimumTempLclNum(m_compiler->lvaCount) - // initially allocate 32 temp local slots for "pending release" - , m_pendingReleaseTempTraits(32, m_compiler) - , m_pendingReleaseTemps(BitVecOps::MakeEmpty(&m_pendingReleaseTempTraits)) + , m_maximumTempLclNum(m_compiler->lvaCount) { } @@ -555,13 +552,12 @@ void Lowering::AfterLowerBlocks() GenTree* node = block->lastNode(); while (node != nullptr) { - assert(BitVecOps::IsEmpty(&m_pendingReleaseTempTraits, m_pendingReleaseTemps)); assert(IsDataFlowRoot(node)); node = StackifyTree(node); // We've finished processing the current root tree, so - // we can release any pending temps used in stackification of the tree, + // we can release any temps used in stackification of the tree, // since there is no more risk of interference between tree operands. - RemovePendingTemporaries(); + ReleaseTemporaries(); } m_lower->m_block = nullptr; @@ -578,8 +574,6 @@ void Lowering::AfterLowerBlocks() // to (in normal linear order - before) the node we last stackified. m_stack.Push(&root); - AddTemporariesForPendingRelease(root); - GenTree* lastStackified = root->gtNext; while (m_stack.Height() != initialDepth) { @@ -713,59 +707,32 @@ void Lowering::AfterLowerBlocks() return lclNum; } - constexpr int lvaToTempNum(unsigned lclNum) + unsigned TempListLength(Temporary* list) { - assert(lclNum >= m_minimumTempLclNum); - return lclNum - m_minimumTempLclNum; - } - - constexpr int tmpToLvaNum(unsigned tmpNum) - { - assert(tmpNum >= 0); - return tmpNum + m_minimumTempLclNum; - } - - void AddTemporariesForPendingRelease(GenTree* node) - { - // We rely in this function on the lifetime of temporaries beginning (recall this is backwards traversal) - // at exactly "node"'s position. This is a safe assumption, since we will already have stackified all - // subsequent nodes, and so any further new use of this temporary will be before this one's lifetime begins. - // However, we don't know precisely where the liftime ends here, because uses of locals happen at their - // position in tree order, and not the LIR stream. So conservatively, we wait until we've processed an - // entire root gentree before reusing any temporaries to avoid the possibility of reusing a temporary before - // its last live use in the tree order. - - assert(IsDataFlowRoot(node)); - if (!node->OperIs(GT_STORE_LCL_VAR)) - { - return; - } - - unsigned lclNum = node->AsLclVar()->GetLclNum(); - if (lclNum < m_minimumTempLclNum) + unsigned length = 0; + while (list != nullptr) { - return; + length++; + list = list->Prev; } - - JITDUMP("Stackifier pending release of lclNum: %d temporary defined by [%06u]\n", lclNum, - Compiler::dspTreeID(node)); - EnsurePendingReleaseCapacity(lvaToTempNum(lclNum)); - BitVecOps::AddElemD(&m_pendingReleaseTempTraits, m_pendingReleaseTemps, lvaToTempNum(lclNum)); + return length; } - void RemovePendingTemporaries() + void ReleaseTemporaries() { - // iterate through m_pendingReleaseTemps - BitVecOps::Iter iter(&m_pendingReleaseTempTraits, m_pendingReleaseTemps); - unsigned tmpNum; - while (iter.NextElem(&tmpNum)) + unsigned numRemoved = 0; + // Recycle all available temporaries as unused nodes + for (int i = 0; i < TYP_COUNT; i++) { - // remove from m_pendingReleaseTemps - BitVecOps::RemoveElemD(&m_pendingReleaseTempTraits, m_pendingReleaseTemps, tmpNum); - - unsigned lclNum = tmpToLvaNum(tmpNum); - assert(lclNum >= m_minimumTempLclNum); + while (m_availableTemps[i] != nullptr) + { + Temporary* temp = Remove(&m_availableTemps[i]); + Append(&m_unusedTempNodes, temp); + } + } + for (unsigned lclNum = m_minimumTempLclNum; lclNum < m_compiler->lvaCount; lclNum++) + { Temporary* local = Remove(&m_unusedTempNodes); // See if we have any free nodes in the pool. if (local == nullptr) { @@ -776,6 +743,18 @@ void Lowering::AfterLowerBlocks() JITDUMP("Temporary V%02u is now free and can be re-used\n", lclNum); Append(&m_availableTemps[genActualType(m_compiler->lvaGetDesc(lclNum)->TypeGet())], local); } + + unsigned count = 0; + for (int i = 0; i < TYP_COUNT; i++) + { + Temporary* temp = m_availableTemps[i]; + while (temp != nullptr) + { + count++; + temp = temp->Prev; + } + } + assert(count == (m_compiler->lvaCount - m_minimumTempLclNum)); } Temporary* Remove(Temporary** pTemps) @@ -793,29 +772,6 @@ void Lowering::AfterLowerBlocks() local->Prev = *pTemps; *pTemps = local; } - - void EnsurePendingReleaseCapacity(unsigned needed) - { - if (needed < BitVecTraits::GetSize(&m_pendingReleaseTempTraits)) - { - return; - } - - unsigned oldSize = BitVecTraits::GetSize(&m_pendingReleaseTempTraits); - unsigned newSize = max(needed + 1, oldSize * 2); - BitVecTraits newTraits(newSize, m_compiler); - BitVec newVec = BitVecOps::MakeEmpty(&newTraits); - - BitVecOps::Iter iter(&m_pendingReleaseTempTraits, m_pendingReleaseTemps); - unsigned elem; - while (iter.NextElem(&elem)) - { - BitVecOps::AddElemD(&newTraits, newVec, elem); - } - - m_pendingReleaseTempTraits = newTraits; - m_pendingReleaseTemps = newVec; - } }; Stackifier stackifier(this); From 3227554827b64103fab1a1b06657aea3e0eead99 Mon Sep 17 00:00:00 2001 From: adamperlin Date: Tue, 28 Apr 2026 13:42:10 -0700 Subject: [PATCH 6/9] Remove debugging code --- src/coreclr/jit/lowerwasm.cpp | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/src/coreclr/jit/lowerwasm.cpp b/src/coreclr/jit/lowerwasm.cpp index 86954be348036f..9663a6ec63d3ab 100644 --- a/src/coreclr/jit/lowerwasm.cpp +++ b/src/coreclr/jit/lowerwasm.cpp @@ -707,17 +707,6 @@ void Lowering::AfterLowerBlocks() return lclNum; } - unsigned TempListLength(Temporary* list) - { - unsigned length = 0; - while (list != nullptr) - { - length++; - list = list->Prev; - } - return length; - } - void ReleaseTemporaries() { unsigned numRemoved = 0; @@ -743,18 +732,6 @@ void Lowering::AfterLowerBlocks() JITDUMP("Temporary V%02u is now free and can be re-used\n", lclNum); Append(&m_availableTemps[genActualType(m_compiler->lvaGetDesc(lclNum)->TypeGet())], local); } - - unsigned count = 0; - for (int i = 0; i < TYP_COUNT; i++) - { - Temporary* temp = m_availableTemps[i]; - while (temp != nullptr) - { - count++; - temp = temp->Prev; - } - } - assert(count == (m_compiler->lvaCount - m_minimumTempLclNum)); } Temporary* Remove(Temporary** pTemps) From b01beb4e03e3a18c86510f6aec91e8ce7399b645 Mon Sep 17 00:00:00 2001 From: adamperlin Date: Tue, 28 Apr 2026 13:59:25 -0700 Subject: [PATCH 7/9] Add early return and assert --- src/coreclr/jit/lowerwasm.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/coreclr/jit/lowerwasm.cpp b/src/coreclr/jit/lowerwasm.cpp index 9663a6ec63d3ab..8c24b4361b4ac7 100644 --- a/src/coreclr/jit/lowerwasm.cpp +++ b/src/coreclr/jit/lowerwasm.cpp @@ -709,7 +709,13 @@ void Lowering::AfterLowerBlocks() void ReleaseTemporaries() { - unsigned numRemoved = 0; + if (m_minimumTempLclNum == m_compiler->lvaCount) + { + // No temporaries were created + return; + } + assert(m_minimumTempLclNum < m_compiler->lvaCount); + // Recycle all available temporaries as unused nodes for (int i = 0; i < TYP_COUNT; i++) { From bb29310c5f45636c9c1564b5d852f1ddf70b3dc0 Mon Sep 17 00:00:00 2001 From: adamperlin Date: Tue, 28 Apr 2026 17:44:24 -0700 Subject: [PATCH 8/9] Remove unused var --- src/coreclr/jit/lowerwasm.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/coreclr/jit/lowerwasm.cpp b/src/coreclr/jit/lowerwasm.cpp index 8c24b4361b4ac7..1d7107ccae472b 100644 --- a/src/coreclr/jit/lowerwasm.cpp +++ b/src/coreclr/jit/lowerwasm.cpp @@ -530,7 +530,6 @@ void Lowering::AfterLowerBlocks() Compiler* m_compiler; ArrayStack m_stack; unsigned m_minimumTempLclNum; - unsigned m_maximumTempLclNum; Temporary* m_availableTemps[TYP_COUNT] = {}; Temporary* m_unusedTempNodes = nullptr; bool m_anyChanges = false; @@ -541,7 +540,6 @@ void Lowering::AfterLowerBlocks() , m_compiler(lower->m_compiler) , m_stack(m_compiler->getAllocator(CMK_Lower)) , m_minimumTempLclNum(m_compiler->lvaCount) - , m_maximumTempLclNum(m_compiler->lvaCount) { } From a0dc9dce9ab6383b99c2e60d24f3a62d883e9e73 Mon Sep 17 00:00:00 2001 From: Adam Perlin Date: Tue, 28 Apr 2026 17:46:24 -0700 Subject: [PATCH 9/9] Update src/coreclr/jit/lowerwasm.cpp Co-authored-by: SingleAccretion <62474226+SingleAccretion@users.noreply.github.com> --- src/coreclr/jit/lowerwasm.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/coreclr/jit/lowerwasm.cpp b/src/coreclr/jit/lowerwasm.cpp index 1d7107ccae472b..904736bb2d87cf 100644 --- a/src/coreclr/jit/lowerwasm.cpp +++ b/src/coreclr/jit/lowerwasm.cpp @@ -552,9 +552,8 @@ void Lowering::AfterLowerBlocks() { assert(IsDataFlowRoot(node)); node = StackifyTree(node); - // We've finished processing the current root tree, so - // we can release any temps used in stackification of the tree, - // since there is no more risk of interference between tree operands. + // We don't track liveness of temporaries more precisely since introducing eairler uses + // may interfere with later (by that point already inserted and stackified) stores. ReleaseTemporaries(); } m_lower->m_block = nullptr;