@@ -241,6 +241,9 @@ class ShadowStackAllocator
241
241
242
242
class LssaDomTreeVisitor : public DomTreeVisitor <LssaDomTreeVisitor>
243
243
{
244
+ class NotExposedReason ;
245
+ struct NotExposedReasonLink ;
246
+
244
247
// Cannot use raw node pointers as their values influence hash table iteration order.
245
248
struct DeterministicNodeHashInfo : public HashTableInfo <DeterministicNodeHashInfo>
246
249
{
@@ -326,7 +329,7 @@ class ShadowStackAllocator
326
329
ArrayStack<BitVec> m_candidateBitSetPool;
327
330
328
331
#ifdef DEBUG
329
- JitHashTable<LclSsaVarDsc*, JitPtrKeyFuncs<LclSsaVarDsc>, const char *> m_gcExposedStatusReasonMap ;
332
+ JitHashTable<LclSsaVarDsc*, JitPtrKeyFuncs<LclSsaVarDsc>, NotExposedReasonLink *> m_notExposedReasonMap ;
330
333
#endif // DEBUG
331
334
332
335
public:
@@ -345,7 +348,7 @@ class ShadowStackAllocator
345
348
, m_candidateBitSetTraits(lssa->m_largestCandidateVarIndexPlusOne, m_compiler)
346
349
, m_candidateBitSetPool(m_compiler->getAllocator (CMK_LSRA))
347
350
#ifdef DEBUG
348
- , m_gcExposedStatusReasonMap (m_compiler->getAllocator (CMK_DebugOnly))
351
+ , m_notExposedReasonMap (m_compiler->getAllocator (CMK_DebugOnly))
349
352
#endif // DEBUG
350
353
{
351
354
}
@@ -550,7 +553,7 @@ class ShadowStackAllocator
550
553
if (IsCandidateLocal (varDsc))
551
554
{
552
555
unsigned ssaNum = m_activeDefs.Top (lclVarIndex);
553
- SpillLocalValue (lclNum, ssaNum DEBUGARG (" is live " ));
556
+ SpillLocalValue (lclNum, ssaNum DEBUGARG (node ));
554
557
}
555
558
}
556
559
}
@@ -616,6 +619,7 @@ class ShadowStackAllocator
616
619
if (*pSpillLclNum != BAD_VAR_NUM)
617
620
{
618
621
// We may have already spilled this def live across multiple safe points.
622
+ JITDUMP (" [%06u] is live, but not exposed: already spilled\n " , Compiler::dspTreeID (defNode));
619
623
return ;
620
624
}
621
625
@@ -629,15 +633,15 @@ class ShadowStackAllocator
629
633
*pSpillLclNum = spillLclNum;
630
634
}
631
635
632
- void SpillLocalValue (unsigned lclNum, unsigned ssaNum DEBUGARG (const char * spillReason ))
636
+ void SpillLocalValue (unsigned lclNum, unsigned ssaNum DEBUGARG (GenTree* safePoint ))
633
637
{
634
638
LclVarDsc* varDsc = m_compiler->lvaGetDesc (lclNum);
635
639
LclSsaVarDsc* ssaDsc = varDsc->GetPerSsaData (ssaNum);
636
640
637
- INDEBUG (const char * reason);
641
+ INDEBUG (NotExposedReason reason ( this ) );
638
642
if (!IsGcExposedLocalValue (varDsc, ssaNum, DefStatus::Active DEBUGARG (&reason)))
639
643
{
640
- JITDUMP ( " V%02u/%d %s , but did not insert a spill: %s \n " , lclNum, ssaNum, spillReason, reason );
644
+ JITDUMPEXEC (reason. Print ( " V%02u/%d is live , but not exposed: " , " \n " , lclNum, ssaNum) );
641
645
return ;
642
646
}
643
647
@@ -667,28 +671,30 @@ class ShadowStackAllocator
667
671
defBlockRange.InsertAfter (defNode, value, store);
668
672
}
669
673
670
- JITDUMP (" V%02u/%d %s , inserted a spill in " FMT_BB " :\n " , lclNum, ssaNum, spillReason , block->bbNum );
674
+ JITDUMP (" V%02u/%d is live , inserted a spill in " FMT_BB " :\n " , lclNum, ssaNum, block->bbNum );
671
675
DISPTREERANGE (defBlockRange, store);
672
676
677
+ INDEBUG (NotExposedReason spillReason (this ));
678
+ INDEBUG (spillReason.AddSpill (safePoint));
673
679
varDsc->SetRegNum (REG_STK_CANDIDATE_COMMITED);
674
- SetGcExposedStatus (ssaDsc, GC_EXPOSED_SPILL DEBUGARG (" already spilled " ));
680
+ SetGcExposedStatus (ssaDsc, GC_EXPOSED_SPILL DEBUGARG (varDsc) DEBUGARG (&spillReason ));
675
681
UpdateShadowSlotsStateForSpilledDef (varDsc, ssaDsc);
676
682
}
677
683
678
- bool IsGcExposedValue (GenTree* node, DefStatus defStatus)
684
+ bool IsGcExposedValue (GenTree* node, DefStatus defStatus DEBUGARG (NotExposedReason* pReason) )
679
685
{
680
686
// TODO-LLVM-LSSA-CQ: add handling for PHIs here. Take note to handle cycles properly.
681
687
assert (node->IsValue ());
682
688
LclVarDsc* varDsc;
683
689
if (IsCandidateLocalNode (node, &varDsc))
684
690
{
685
- return IsGcExposedLocalValue (varDsc, node->AsLclVarCommon ()->GetSsaNum (), defStatus);
691
+ return IsGcExposedLocalValue (varDsc, node->AsLclVarCommon ()->GetSsaNum (), defStatus DEBUGARG (pReason) );
686
692
}
687
693
688
- return IsGcExposedSdsuValue (node, defStatus);
694
+ return IsGcExposedSdsuValue (node, defStatus DEBUGARG (pReason) );
689
695
}
690
696
691
- bool IsGcExposedSdsuValue (GenTree* node, DefStatus defStatus)
697
+ bool IsGcExposedSdsuValue (GenTree* node, DefStatus defStatus DEBUGARG (NotExposedReason* pReason) )
692
698
{
693
699
assert (node->IsValue () && !IsCandidateLocalNode (node));
694
700
@@ -697,10 +703,13 @@ class ShadowStackAllocator
697
703
// of the underlying object, or be performed on pinned values only.
698
704
if (node->OperIs (GT_ADD, GT_SUB) && node->gtGetOp2 ()->TypeIs (TYP_I_IMPL))
699
705
{
700
- return IsGcExposedValue (node->gtGetOp1 (), DefStatus::Unknown);
706
+ if (!IsGcExposedValue (node->gtGetOp1 (), DefStatus::Unknown DEBUGARG (pReason)))
707
+ {
708
+ INDEBUG (pReason->AddOp (node));
709
+ return false ;
710
+ }
701
711
}
702
-
703
- if (node->OperIsLocalRead ())
712
+ else if (node->OperIsLocalRead ())
704
713
{
705
714
// For non-candidate locals, all definitions are spilled to the shadow stack. Thus, the only
706
715
// way for a value to get exposed is if something modifies the backing location. This can
@@ -712,35 +721,36 @@ class ShadowStackAllocator
712
721
// even a 'normal' untracked local may be redefined via a local store; we handle this via
713
722
// the check on 'defStatus'.
714
723
//
715
- if ((defStatus == DefStatus::Unknown) ||
716
- m_compiler->lvaGetDesc (node->AsLclVarCommon ())->IsAddressExposed ())
724
+ if ((defStatus == DefStatus::Active) &&
725
+ ! m_compiler->lvaGetDesc (node->AsLclVarCommon ())->IsAddressExposed ())
717
726
{
718
- return true ;
727
+ INDEBUG (pReason->AddNonCandidate (node->AsLclVarCommon ()));
728
+ return false ;
719
729
}
720
-
721
- return false ;
722
730
}
723
-
724
731
// Local address nodes always point to the stack (native or shadow). Constant handles will
725
732
// only point to immortal and immovable (frozen) objects. TODO-LLVM-LSSA-CQ: add LCLHEAP here.
726
- if (node->OperIs (GT_LCL_ADDR) || node->IsIconHandle () || node->IsIntegralConst (0 ))
733
+ else if (node->OperIs (GT_LCL_ADDR) || node->IsIconHandle () || node->IsIntegralConst (0 ))
727
734
{
735
+ INDEBUG (pReason->AddOp (node));
728
736
return false ;
729
737
}
730
738
731
739
return true ;
732
740
}
733
741
734
742
bool IsGcExposedLocalValue (
735
- LclVarDsc* varDsc, unsigned ssaNum, DefStatus defStatus DEBUGARG (const char ** pReason = nullptr ))
743
+ LclVarDsc* varDsc, unsigned ssaNum, DefStatus defStatus DEBUGARG (NotExposedReason* pReason))
736
744
{
745
+ assert (IsCandidateLocal (varDsc));
746
+
737
747
LclSsaVarDsc* ssaDsc = varDsc->GetPerSsaData (ssaNum);
738
- unsigned status = GetGcExposedStatus (ssaDsc DEBUGARG (pReason) );
748
+ unsigned status = GetGcExposedStatus (ssaDsc);
739
749
if (status == GC_EXPOSED_UNKNOWN)
740
750
{
741
751
bool isExposed = true ;
742
- INDEBUG (const char * reason = nullptr );
743
752
GenTreeLclVarCommon* defNode = ssaDsc->GetDefNode ();
753
+ INDEBUG (NotExposedReason defReason (this ));
744
754
if (defNode == nullptr )
745
755
{
746
756
assert (ssaNum == SsaConfig::FIRST_SSA_NUM);
@@ -750,20 +760,19 @@ class ShadowStackAllocator
750
760
assert (initKind != ValueInitKind::None);
751
761
if (initKind != ValueInitKind::Param)
752
762
{
753
- INDEBUG (reason = " value is zero at implicit def" );
763
+ INDEBUG (defReason. Add ( " implicit zero def" ) );
754
764
isExposed = false ;
755
765
}
756
766
}
757
- else if (defNode->OperIs (GT_STORE_LCL_VAR) && !IsGcExposedValue (defNode->Data (), DefStatus::Unknown))
767
+ else if (defNode->OperIs (GT_STORE_LCL_VAR) &&
768
+ !IsGcExposedValue (defNode->Data (), DefStatus::Unknown DEBUGARG (&defReason)))
758
769
{
759
- INDEBUG (reason = " value not exposed" );
760
770
isExposed = false ;
761
771
}
762
772
763
773
// This caching is designed to prevent quadratic behavior.
764
774
status = isExposed ? GC_EXPOSED_YES : GC_EXPOSED_NO;
765
- SetGcExposedStatus (ssaDsc, status DEBUGARG (reason));
766
- DBEXEC (pReason != nullptr , *pReason = reason);
775
+ SetGcExposedStatus (ssaDsc, status DEBUGARG (varDsc) DEBUGARG (&defReason));
767
776
}
768
777
// Take care not to depend on potentially stale information. Consider:
769
778
//
@@ -787,6 +796,7 @@ class ShadowStackAllocator
787
796
}
788
797
789
798
assert (status != GC_EXPOSED_UNKNOWN);
799
+ DBEXEC (status != GC_EXPOSED_YES, pReason->AddDef (varDsc, ssaNum));
790
800
return status == GC_EXPOSED_YES;
791
801
}
792
802
@@ -799,6 +809,7 @@ class ShadowStackAllocator
799
809
}
800
810
801
811
LclVarDsc* varDsc;
812
+ INDEBUG (NotExposedReason reason (this ));
802
813
if (IsCandidateLocalNode (node, &varDsc))
803
814
{
804
815
JITDUMP (" -- Processing a candidate:\n " );
@@ -826,7 +837,7 @@ class ShadowStackAllocator
826
837
}
827
838
}
828
839
else if (node->IsValue () && !node->IsUnusedValue () && IsGcExposedType (node) &&
829
- IsGcExposedSdsuValue (node, DefStatus::Active))
840
+ IsGcExposedSdsuValue (node, DefStatus::Active DEBUGARG (&reason) ))
830
841
{
831
842
node->gtLIRFlags |= LIR::Flags::Mark;
832
843
m_liveSdsuGcDefs.AddOrUpdate (node, BAD_VAR_NUM);
@@ -1100,20 +1111,23 @@ class ShadowStackAllocator
1100
1111
m_candidateBitSetPool.Push (bitSet);
1101
1112
}
1102
1113
1103
- void SetGcExposedStatus (LclSsaVarDsc* ssaDsc, unsigned status DEBUGARG (const char * reason))
1114
+ void SetGcExposedStatus (
1115
+ LclSsaVarDsc* ssaDsc, unsigned status DEBUGARG (LclVarDsc* varDsc) DEBUGARG(NotExposedReason* pReason))
1104
1116
{
1105
1117
unsigned oldStatus = GetGcExposedStatus (ssaDsc);
1106
1118
assert ((oldStatus == GC_EXPOSED_UNKNOWN) || ((oldStatus == GC_EXPOSED_YES) && (status == GC_EXPOSED_SPILL)));
1107
1119
assert (status != GC_EXPOSED_UNKNOWN);
1108
1120
1109
1121
unsigned * pStatus = GetRawDefStatusRef (ssaDsc);
1110
- DBEXEC (reason != nullptr , m_gcExposedStatusReasonMap.Set (ssaDsc, reason));
1111
1122
*pStatus = (*pStatus & ~GC_EXPOSED_MASK) | status;
1123
+
1124
+ JITDUMP (" V%02u/%u: %s -> %s\n " , m_compiler->lvaGetLclNum (varDsc),
1125
+ varDsc->GetSsaNumForSsaDef (ssaDsc), GcStatusToString (oldStatus), GcStatusToString (status));
1126
+ INDEBUG (pReason->SaveForDef (ssaDsc));
1112
1127
}
1113
1128
1114
- unsigned GetGcExposedStatus (LclSsaVarDsc* ssaDsc DEBUGARG ( const char ** pReason = nullptr ) )
1129
+ unsigned GetGcExposedStatus (LclSsaVarDsc* ssaDsc)
1115
1130
{
1116
- INDEBUG (m_gcExposedStatusReasonMap.Lookup (ssaDsc, pReason));
1117
1131
return *GetRawDefStatusRef (ssaDsc) & GC_EXPOSED_MASK;
1118
1132
}
1119
1133
@@ -1321,6 +1335,147 @@ class ShadowStackAllocator
1321
1335
}
1322
1336
// printf("}\n");
1323
1337
}
1338
+
1339
+ const char * GcStatusToString (unsigned status)
1340
+ {
1341
+ switch (status)
1342
+ {
1343
+ case GC_EXPOSED_NO:
1344
+ return " GC_EXPOSED_NO" ;
1345
+ case GC_EXPOSED_SPILL:
1346
+ return " GC_EXPOSED_SPILL" ;
1347
+ case GC_EXPOSED_YES:
1348
+ return " GC_EXPOSED_YES" ;
1349
+ case GC_EXPOSED_UNKNOWN:
1350
+ return " GC_EXPOSED_UNKNOWN" ;
1351
+ default :
1352
+ unreached ();
1353
+ }
1354
+ }
1355
+
1356
+ struct NotExposedReasonLink
1357
+ {
1358
+ const char * Reason;
1359
+ NotExposedReasonLink* Next;
1360
+ };
1361
+
1362
+ class NotExposedReason
1363
+ {
1364
+ LssaDomTreeVisitor* m_visitor;
1365
+ ArrayStack<const char *> m_chain;
1366
+ bool m_enabled;
1367
+
1368
+ public:
1369
+ NotExposedReason (LssaDomTreeVisitor* visitor)
1370
+ : m_visitor(visitor)
1371
+ , m_chain(visitor->m_compiler->getAllocator (CMK_DebugOnly))
1372
+ , m_enabled(visitor->m_compiler->verbose)
1373
+ {
1374
+ }
1375
+
1376
+ void AddOp (GenTree* node)
1377
+ {
1378
+ if (m_enabled)
1379
+ {
1380
+ Add (" %s [%06u]" , GenTree::OpName (node->OperGet ()), Compiler::dspTreeID (node));
1381
+ }
1382
+ }
1383
+
1384
+ void AddNonCandidate (GenTreeLclVarCommon* node)
1385
+ {
1386
+ if (m_enabled)
1387
+ {
1388
+ Add (" V%02u [%06u]" , node->GetLclNum (), Compiler::dspTreeID (node));
1389
+ }
1390
+ }
1391
+
1392
+ void AddDef (LclVarDsc* varDsc, unsigned ssaNum)
1393
+ {
1394
+ if (m_enabled)
1395
+ {
1396
+ LclSsaVarDsc* ssaDsc = varDsc->GetPerSsaData (ssaNum);
1397
+ NotExposedReasonLink* link = m_visitor->m_notExposedReasonMap [ssaDsc];
1398
+ while (link != nullptr )
1399
+ {
1400
+ Add (link->Reason );
1401
+ link = link->Next ;
1402
+ }
1403
+
1404
+ unsigned lclNum = m_visitor->m_compiler ->lvaGetLclNum (varDsc);
1405
+ Add (" V%02u/%u def" , lclNum, ssaNum);
1406
+ }
1407
+ }
1408
+
1409
+ void AddSpill (GenTree* safePoint)
1410
+ {
1411
+ if (m_enabled)
1412
+ {
1413
+ Add (" spilled due to [%06u]" , Compiler::dspTreeID (safePoint));
1414
+ }
1415
+ }
1416
+
1417
+ template <typename ... TArgs>
1418
+ void Add (const char * linkFmt, TArgs... args)
1419
+ {
1420
+ const char * link = m_visitor->m_compiler ->printfAlloc (linkFmt, args...);
1421
+ Add (link);
1422
+ }
1423
+
1424
+ void Add (const char * link)
1425
+ {
1426
+ assert (m_enabled);
1427
+ m_chain.Push (link);
1428
+ }
1429
+
1430
+ void SaveForDef (LclSsaVarDsc* ssaDsc)
1431
+ {
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 )
1450
+ {
1451
+ m_visitor->m_notExposedReasonMap .Set (ssaDsc, links);
1452
+ }
1453
+ }
1454
+
1455
+ template <typename ... TArgs>
1456
+ void Print (const char * prefix, const char * postfix, TArgs... args)
1457
+ {
1458
+ printf (prefix, args...);
1459
+ PrintLinks ();
1460
+ printf (" %s" , postfix);
1461
+ }
1462
+
1463
+ private:
1464
+ void PrintLinks ()
1465
+ {
1466
+ for (int i = m_chain.Height () - 1 ; i >= 0 ; i--)
1467
+ {
1468
+ const char * link = m_chain.Bottom (i);
1469
+ assert (link != nullptr );
1470
+ printf (" %s" , link);
1471
+
1472
+ if (i != 0 )
1473
+ {
1474
+ printf (" <= " );
1475
+ }
1476
+ }
1477
+ }
1478
+ };
1324
1479
#endif // DEBUG
1325
1480
};
1326
1481
0 commit comments