Skip to content

Commit 564c1b8

Browse files
Fix a GC hole in LSSA with cached "GC_EXPOSED_NO" results
As the added test shows, we can't cache "NO" results as they depend on the current execution point.
1 parent 1cf4354 commit 564c1b8

File tree

1 file changed

+23
-42
lines changed

1 file changed

+23
-42
lines changed

src/coreclr/jit/llvmlssa.cpp

Lines changed: 23 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,6 @@ class ShadowStackAllocator
242242
class LssaDomTreeVisitor : public DomTreeVisitor<LssaDomTreeVisitor>
243243
{
244244
class NotExposedReason;
245-
struct NotExposedReasonLink;
246245

247246
// Cannot use raw node pointers as their values influence hash table iteration order.
248247
struct DeterministicNodeHashInfo : public HashTableInfo<DeterministicNodeHashInfo>
@@ -329,7 +328,7 @@ class ShadowStackAllocator
329328
ArrayStack<BitVec> m_candidateBitSetPool;
330329

331330
#ifdef DEBUG
332-
JitHashTable<LclSsaVarDsc*, JitPtrKeyFuncs<LclSsaVarDsc>, NotExposedReasonLink*> m_notExposedReasonMap;
331+
JitHashTable<LclSsaVarDsc*, JitPtrKeyFuncs<LclSsaVarDsc>, const char*> m_notExposedReasonMap;
333332
#endif // DEBUG
334333

335334
public:
@@ -748,9 +747,7 @@ class ShadowStackAllocator
748747
unsigned status = GetGcExposedStatus(ssaDsc);
749748
if (status == GC_EXPOSED_UNKNOWN)
750749
{
751-
bool isExposed = true;
752750
GenTreeLclVarCommon* defNode = ssaDsc->GetDefNode();
753-
INDEBUG(NotExposedReason defReason(this));
754751
if (defNode == nullptr)
755752
{
756753
assert(ssaNum == SsaConfig::FIRST_SSA_NUM);
@@ -760,19 +757,25 @@ class ShadowStackAllocator
760757
assert(initKind != ValueInitKind::None);
761758
if (initKind != ValueInitKind::Param)
762759
{
763-
INDEBUG(defReason.Add("implicit zero def"));
764-
isExposed = false;
760+
INDEBUG(pReason->Add("implicit zero def"));
761+
status = GC_EXPOSED_NO;
765762
}
766763
}
767764
else if (defNode->OperIs(GT_STORE_LCL_VAR) &&
768-
!IsGcExposedValue(defNode->Data(), DefStatus::Unknown DEBUGARG(&defReason)))
765+
!IsGcExposedValue(defNode->Data(), DefStatus::Unknown DEBUGARG(pReason)))
769766
{
770-
isExposed = false;
767+
status = GC_EXPOSED_NO;
771768
}
772769

773-
// This caching is designed to prevent quadratic behavior.
774-
status = isExposed ? GC_EXPOSED_YES : GC_EXPOSED_NO;
775-
SetGcExposedStatus(ssaDsc, status DEBUGARG(varDsc) DEBUGARG(&defReason));
770+
if (status != GC_EXPOSED_NO)
771+
{
772+
// This caching is designed to prevent quadratic behavior. However, we can't cache "NO" results as
773+
// they depend on the current execution point (different points may yield different results as
774+
// shadow slots get overwritten with new values), so this is not a complete solution...
775+
// TODO-LLVM-LSSA: fix the above with a new LSSA algorithm.
776+
status = GC_EXPOSED_YES;
777+
SetGcExposedStatus(ssaDsc, status DEBUGARG(varDsc));
778+
}
776779
}
777780
// Take care not to depend on potentially stale information. Consider:
778781
//
@@ -1112,7 +1115,7 @@ class ShadowStackAllocator
11121115
}
11131116

11141117
void SetGcExposedStatus(
1115-
LclSsaVarDsc* ssaDsc, unsigned status DEBUGARG(LclVarDsc* varDsc) DEBUGARG(NotExposedReason* pReason))
1118+
LclSsaVarDsc* ssaDsc, unsigned status DEBUGARG(LclVarDsc* varDsc) DEBUGARG(NotExposedReason* pReason = nullptr))
11161119
{
11171120
unsigned oldStatus = GetGcExposedStatus(ssaDsc);
11181121
assert((oldStatus == GC_EXPOSED_UNKNOWN) || ((oldStatus == GC_EXPOSED_YES) && (status == GC_EXPOSED_SPILL)));
@@ -1123,7 +1126,7 @@ class ShadowStackAllocator
11231126

11241127
JITDUMP("V%02u/%u: %s -> %s\n", m_compiler->lvaGetLclNum(varDsc),
11251128
varDsc->GetSsaNumForSsaDef(ssaDsc), GcStatusToString(oldStatus), GcStatusToString(status));
1126-
INDEBUG(pReason->SaveForDef(ssaDsc));
1129+
DBEXEC(pReason != nullptr, pReason->SaveForDef(ssaDsc));
11271130
}
11281131

11291132
unsigned GetGcExposedStatus(LclSsaVarDsc* ssaDsc)
@@ -1353,12 +1356,6 @@ class ShadowStackAllocator
13531356
}
13541357
}
13551358

1356-
struct NotExposedReasonLink
1357-
{
1358-
const char* Reason;
1359-
NotExposedReasonLink* Next;
1360-
};
1361-
13621359
class NotExposedReason
13631360
{
13641361
LssaDomTreeVisitor* m_visitor;
@@ -1393,12 +1390,11 @@ class ShadowStackAllocator
13931390
{
13941391
if (m_enabled)
13951392
{
1393+
const char* reason;
13961394
LclSsaVarDsc* ssaDsc = varDsc->GetPerSsaData(ssaNum);
1397-
NotExposedReasonLink* link = m_visitor->m_notExposedReasonMap[ssaDsc];
1398-
while (link != nullptr)
1395+
if (m_visitor->m_notExposedReasonMap.Lookup(ssaDsc, &reason))
13991396
{
1400-
Add(link->Reason);
1401-
link = link->Next;
1397+
Add(reason);
14021398
}
14031399

14041400
unsigned lclNum = m_visitor->m_compiler->lvaGetLclNum(varDsc);
@@ -1429,26 +1425,11 @@ class ShadowStackAllocator
14291425

14301426
void SaveForDef(LclSsaVarDsc* ssaDsc)
14311427
{
1432-
CompAllocator alloc = m_visitor->m_compiler->getAllocator(CMK_DebugOnly);
1433-
NotExposedReasonLink* links = nullptr;
1434-
NotExposedReasonLink* last = nullptr;
1435-
for (int i = 0; i < m_chain.Height(); i++)
1436-
{
1437-
NotExposedReasonLink* link = new (alloc) NotExposedReasonLink();
1438-
link->Reason = m_chain.Bottom(i);
1439-
if (last == nullptr)
1440-
{
1441-
links = link;
1442-
}
1443-
else
1444-
{
1445-
last->Next = link;
1446-
}
1447-
last = link;
1448-
}
1449-
if (links != nullptr)
1428+
if (m_enabled)
14501429
{
1451-
m_visitor->m_notExposedReasonMap.Set(ssaDsc, links);
1430+
assert(m_chain.Height() == 1); // Full support currently not needed.
1431+
const char* reason = m_chain.Top();
1432+
m_visitor->m_notExposedReasonMap.Set(ssaDsc, reason);
14521433
}
14531434
}
14541435

0 commit comments

Comments
 (0)