@@ -242,7 +242,6 @@ class ShadowStackAllocator
242
242
class LssaDomTreeVisitor : public DomTreeVisitor <LssaDomTreeVisitor>
243
243
{
244
244
class NotExposedReason ;
245
- struct NotExposedReasonLink ;
246
245
247
246
// Cannot use raw node pointers as their values influence hash table iteration order.
248
247
struct DeterministicNodeHashInfo : public HashTableInfo <DeterministicNodeHashInfo>
@@ -329,7 +328,7 @@ class ShadowStackAllocator
329
328
ArrayStack<BitVec> m_candidateBitSetPool;
330
329
331
330
#ifdef DEBUG
332
- JitHashTable<LclSsaVarDsc*, JitPtrKeyFuncs<LclSsaVarDsc>, NotExposedReasonLink *> m_notExposedReasonMap;
331
+ JitHashTable<LclSsaVarDsc*, JitPtrKeyFuncs<LclSsaVarDsc>, const char *> m_notExposedReasonMap;
333
332
#endif // DEBUG
334
333
335
334
public:
@@ -616,6 +615,16 @@ class ShadowStackAllocator
616
615
617
616
void SpillSdsuValue (LIR::Range& blockRange, GenTree* defNode, unsigned * pSpillLclNum)
618
617
{
618
+ // Like with candidates, the GC status of an SDSU can change during its lifetime, so we need to check it
619
+ // at every safepoint, in the general case. We could tighten this by filtering out more SDSUs early (like
620
+ // 'null' and such), but live-across-a-safepoint SDSUs are not that common, so we currently don't.
621
+ INDEBUG (NotExposedReason reason (this ));
622
+ if (!IsGcExposedSdsuValue (defNode, DefStatus::Active DEBUGARG (&reason)))
623
+ {
624
+ JITDUMPEXEC (reason.Print (" [%06u] is live, but not exposed: " , " \n " , Compiler::dspTreeID (defNode)));
625
+ return ;
626
+ }
627
+
619
628
if (*pSpillLclNum != BAD_VAR_NUM)
620
629
{
621
630
// We may have already spilled this def live across multiple safe points.
@@ -748,9 +757,7 @@ class ShadowStackAllocator
748
757
unsigned status = GetGcExposedStatus (ssaDsc);
749
758
if (status == GC_EXPOSED_UNKNOWN)
750
759
{
751
- bool isExposed = true ;
752
760
GenTreeLclVarCommon* defNode = ssaDsc->GetDefNode ();
753
- INDEBUG (NotExposedReason defReason (this ));
754
761
if (defNode == nullptr )
755
762
{
756
763
assert (ssaNum == SsaConfig::FIRST_SSA_NUM);
@@ -760,19 +767,25 @@ class ShadowStackAllocator
760
767
assert (initKind != ValueInitKind::None);
761
768
if (initKind != ValueInitKind::Param)
762
769
{
763
- INDEBUG (defReason. Add (" implicit zero def" ));
764
- isExposed = false ;
770
+ INDEBUG (pReason-> Add (" implicit zero def" ));
771
+ status = GC_EXPOSED_NO ;
765
772
}
766
773
}
767
774
else if (defNode->OperIs (GT_STORE_LCL_VAR) &&
768
- !IsGcExposedValue (defNode->Data (), DefStatus::Unknown DEBUGARG (&defReason )))
775
+ !IsGcExposedValue (defNode->Data (), DefStatus::Unknown DEBUGARG (pReason )))
769
776
{
770
- isExposed = false ;
777
+ status = GC_EXPOSED_NO ;
771
778
}
772
779
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));
780
+ if (status != GC_EXPOSED_NO)
781
+ {
782
+ // This caching is designed to prevent quadratic behavior. However, we can't cache "NO" results as
783
+ // they depend on the current execution point (different points may yield different results as
784
+ // shadow slots get overwritten with new values), so this is not a complete solution...
785
+ // TODO-LLVM-LSSA: fix the above with a new LSSA algorithm.
786
+ status = GC_EXPOSED_YES;
787
+ SetGcExposedStatus (ssaDsc, status DEBUGARG (varDsc));
788
+ }
776
789
}
777
790
// Take care not to depend on potentially stale information. Consider:
778
791
//
@@ -809,7 +822,6 @@ class ShadowStackAllocator
809
822
}
810
823
811
824
LclVarDsc* varDsc;
812
- INDEBUG (NotExposedReason reason (this ));
813
825
if (IsCandidateLocalNode (node, &varDsc))
814
826
{
815
827
JITDUMP (" -- Processing a candidate:\n " );
@@ -836,8 +848,7 @@ class ShadowStackAllocator
836
848
IncrementActiveUseCount (varDsc->GetPerSsaData (ssaNum));
837
849
}
838
850
}
839
- else if (node->IsValue () && !node->IsUnusedValue () && IsGcExposedType (node) &&
840
- IsGcExposedSdsuValue (node, DefStatus::Active DEBUGARG (&reason)))
851
+ else if (node->IsValue () && !node->IsUnusedValue () && IsGcExposedType (node))
841
852
{
842
853
node->gtLIRFlags |= LIR::Flags::Mark;
843
854
m_liveSdsuGcDefs.AddOrUpdate (node, BAD_VAR_NUM);
@@ -852,17 +863,7 @@ class ShadowStackAllocator
852
863
}
853
864
if (node->TypeIs (TYP_STRUCT))
854
865
{
855
- // TODO-LLVM: delete this once we're up to date with upstream deleting "STORE_DYN_BLK".
856
- if (node->OperIs (GT_IND))
857
- {
858
- return false ;
859
- }
860
- if (!node->GetLayout (m_compiler)->HasGCPtr ())
861
- {
862
- return false ;
863
- }
864
-
865
- return true ;
866
+ return node->GetLayout (m_compiler)->HasGCPtr ();
866
867
}
867
868
868
869
return false ;
@@ -1112,7 +1113,7 @@ class ShadowStackAllocator
1112
1113
}
1113
1114
1114
1115
void SetGcExposedStatus (
1115
- LclSsaVarDsc* ssaDsc, unsigned status DEBUGARG (LclVarDsc* varDsc) DEBUGARG(NotExposedReason* pReason))
1116
+ LclSsaVarDsc* ssaDsc, unsigned status DEBUGARG (LclVarDsc* varDsc) DEBUGARG(NotExposedReason* pReason = nullptr ))
1116
1117
{
1117
1118
unsigned oldStatus = GetGcExposedStatus (ssaDsc);
1118
1119
assert ((oldStatus == GC_EXPOSED_UNKNOWN) || ((oldStatus == GC_EXPOSED_YES) && (status == GC_EXPOSED_SPILL)));
@@ -1123,7 +1124,7 @@ class ShadowStackAllocator
1123
1124
1124
1125
JITDUMP (" V%02u/%u: %s -> %s\n " , m_compiler->lvaGetLclNum (varDsc),
1125
1126
varDsc->GetSsaNumForSsaDef (ssaDsc), GcStatusToString (oldStatus), GcStatusToString (status));
1126
- INDEBUG ( pReason->SaveForDef (ssaDsc));
1127
+ DBEXEC (pReason != nullptr , pReason->SaveForDef (ssaDsc));
1127
1128
}
1128
1129
1129
1130
unsigned GetGcExposedStatus (LclSsaVarDsc* ssaDsc)
@@ -1353,12 +1354,6 @@ class ShadowStackAllocator
1353
1354
}
1354
1355
}
1355
1356
1356
- struct NotExposedReasonLink
1357
- {
1358
- const char * Reason;
1359
- NotExposedReasonLink* Next;
1360
- };
1361
-
1362
1357
class NotExposedReason
1363
1358
{
1364
1359
LssaDomTreeVisitor* m_visitor;
@@ -1393,12 +1388,11 @@ class ShadowStackAllocator
1393
1388
{
1394
1389
if (m_enabled)
1395
1390
{
1391
+ const char * reason;
1396
1392
LclSsaVarDsc* ssaDsc = varDsc->GetPerSsaData (ssaNum);
1397
- NotExposedReasonLink* link = m_visitor->m_notExposedReasonMap [ssaDsc];
1398
- while (link != nullptr )
1393
+ if (m_visitor->m_notExposedReasonMap .Lookup (ssaDsc, &reason))
1399
1394
{
1400
- Add (link->Reason );
1401
- link = link->Next ;
1395
+ Add (reason);
1402
1396
}
1403
1397
1404
1398
unsigned lclNum = m_visitor->m_compiler ->lvaGetLclNum (varDsc);
@@ -1429,26 +1423,11 @@ class ShadowStackAllocator
1429
1423
1430
1424
void SaveForDef (LclSsaVarDsc* ssaDsc)
1431
1425
{
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 )
1426
+ if (m_enabled)
1450
1427
{
1451
- m_visitor->m_notExposedReasonMap .Set (ssaDsc, links);
1428
+ assert (m_chain.Height () == 1 ); // Full support currently not needed.
1429
+ const char * reason = m_chain.Top ();
1430
+ m_visitor->m_notExposedReasonMap .Set (ssaDsc, reason);
1452
1431
}
1453
1432
}
1454
1433
0 commit comments