@@ -3583,12 +3583,40 @@ void Compiler::fgDebugCheckNodesUniqueness()
3583
3583
}
3584
3584
}
3585
3585
3586
- // SsaCheckWalker keeps data that is necessary to check if
3587
- // we are properly updating SSA uses and defs.
3586
+ // ------------------------------------------------------------------------------
3587
+ // SsaCheckVisitor: build and maintain state about SSA uses in the IR
3588
+ //
3589
+ // Expects to be invoked on each root expression in each basic block that
3590
+ // SSA renames (note SSA will not rename defs and uses in unreachable blocks)
3591
+ // and all blocks created after SSA was built (identified by bbID).
3592
+ //
3593
+ // Maintains a hash table keyed by (lclNum, ssaNum) that tracks information
3594
+ // about that SSA lifetime. This information is updated by each SSA use and
3595
+ // def seen in the trees via ProcessUses and ProcessDefs.
3596
+ //
3597
+ // We can spot certain errors during collection, if local occurrences either
3598
+ // unexpectedy lack or have SSA numbers.
3599
+ //
3600
+ // Once collection is done, DoChecks() verifies that the collected information
3601
+ // is soundly approximated by the data stored in the LclSsaVarDsc entries.
3602
+ //
3603
+ // In particular the properties claimed for an SSA lifetime via its
3604
+ // LclSsaVarDsc must be accurate or an over-estimate. We tolerate over-estimates
3605
+ // as there is no good mechanism in the jit for keeping track when bits of IR
3606
+ // are deleted, so over time the number and kind of uses indicated in the
3607
+ // LclSsaVarDsc may show more uses and more different kinds of uses then actually
3608
+ // remain in the IR.
3609
+ //
3610
+ // One important caveat is that for promoted locals there may be implicit uses
3611
+ // (via the parent var) that do not get numbered by SSA. Neither the LclSsaVarDsc
3612
+ // nor the IR will track these implicit uses. So the checking done below will
3613
+ // only catch anomalies in the defs or in the explicit uses.
3588
3614
//
3589
3615
class SsaCheckVisitor : public GenTreeVisitor <SsaCheckVisitor>
3590
3616
{
3591
3617
private:
3618
+ // Hash key for tracking per-SSA lifetime info
3619
+ //
3592
3620
struct SsaKey
3593
3621
{
3594
3622
private:
@@ -3624,6 +3652,8 @@ class SsaCheckVisitor : public GenTreeVisitor<SsaCheckVisitor>
3624
3652
}
3625
3653
};
3626
3654
3655
+ // Per-SSA lifetime info
3656
+ //
3627
3657
struct SsaInfo
3628
3658
{
3629
3659
private:
@@ -3949,30 +3979,39 @@ class SsaCheckVisitor : public GenTreeVisitor<SsaCheckVisitor>
3949
3979
{
3950
3980
for (unsigned lclNum = 0 ; lclNum < m_compiler->lvaCount ; lclNum++)
3951
3981
{
3982
+ // Check each local in SSA
3983
+ //
3952
3984
LclVarDsc* const varDsc = m_compiler->lvaGetDesc (lclNum);
3953
3985
3954
3986
if (!varDsc->lvInSsa )
3955
3987
{
3956
3988
continue ;
3957
3989
}
3958
3990
3991
+ // Check each SSA lifetime of that local
3992
+ //
3959
3993
const SsaDefArray<LclSsaVarDsc>& ssaDefs = varDsc->lvPerSsaData ;
3960
3994
3961
3995
for (unsigned i = 0 ; i < ssaDefs.GetCount (); i++)
3962
3996
{
3963
3997
LclSsaVarDsc* const ssaVarDsc = ssaDefs.GetSsaDefByIndex (i);
3964
3998
const unsigned ssaNum = ssaDefs.GetSsaNum (ssaVarDsc);
3965
3999
4000
+ // Find the SSA info we gathered for this lifetime via the IR walk
4001
+ //
3966
4002
SsaKey key (lclNum, ssaNum);
3967
4003
SsaInfo* ssaInfo = nullptr ;
3968
4004
3969
4005
if (!m_infoMap.Lookup (key, &ssaInfo))
3970
4006
{
3971
- // Possibly there are no more references to this local.
4007
+ // IR has no information about this lifetime.
4008
+ // Possibly there are no more references.
4009
+ //
3972
4010
continue ;
3973
4011
}
3974
4012
3975
- // VarDsc block should be accurate.
4013
+ // Now cross-check the gathered ssaInfo vs the LclSsaVarDsc.
4014
+ // LclSsaVarDsc should have the correct def block
3976
4015
//
3977
4016
BasicBlock* const ssaInfoDefBlock = ssaInfo->GetDefBlock ();
3978
4017
BasicBlock* const ssaVarDscDefBlock = ssaVarDsc->GetBlock ();
@@ -3999,7 +4038,7 @@ class SsaCheckVisitor : public GenTreeVisitor<SsaCheckVisitor>
3999
4038
unsigned const ssaInfoUses = ssaInfo->GetNumUses ();
4000
4039
unsigned const ssaVarDscUses = ssaVarDsc->GetNumUses ();
4001
4040
4002
- // VarDsc use count must be accurate or an over-estimate
4041
+ // LclSsaVarDsc use count must be accurate or an over-estimate
4003
4042
//
4004
4043
if (ssaInfoUses > ssaVarDscUses)
4005
4044
{
@@ -4015,7 +4054,7 @@ class SsaCheckVisitor : public GenTreeVisitor<SsaCheckVisitor>
4015
4054
ssaVarDscUses);
4016
4055
}
4017
4056
4018
- // HasPhiUse use must be accurate or an over-estimate
4057
+ // LclSsaVarDsc HasPhiUse use must be accurate or an over-estimate
4019
4058
//
4020
4059
if (ssaInfo->HasPhiUse () && !ssaVarDsc->HasPhiUse ())
4021
4060
{
@@ -4027,7 +4066,7 @@ class SsaCheckVisitor : public GenTreeVisitor<SsaCheckVisitor>
4027
4066
JITDUMP (" [info] HasPhiUse overestimated for V%02u.%u\n " , lclNum, ssaNum);
4028
4067
}
4029
4068
4030
- // HasGlobalUse use must be accurate or an over-estimate
4069
+ // LclSsaVarDsc HasGlobalUse use must be accurate or an over-estimate
4031
4070
//
4032
4071
if (ssaInfo->HasGlobalUse () && !ssaVarDsc->HasGlobalUse ())
4033
4072
{
@@ -4058,9 +4097,9 @@ class SsaCheckVisitor : public GenTreeVisitor<SsaCheckVisitor>
4058
4097
// * There is at most one SSA def for a given SSA num, and it is in the expected block.
4059
4098
// * Operands that should have SSA numbers have them
4060
4099
// * Operands that should not have SSA numbers do not have them
4061
- // * The number of SSA uses is accurate or an over-estimate
4062
- // * The local/global bit is properly set or an over-estimate
4063
- // * The has phi use bit is properly set or an over-estimate
4100
+ // * GetNumUses is accurate or an over-estimate
4101
+ // * HasGlobalUse is properly set or an over-estimate
4102
+ // * HasPhiUse is properly set or an over-estimate
4064
4103
//
4065
4104
// Todo:
4066
4105
// * Try and sanity check PHIs
@@ -4077,6 +4116,7 @@ void Compiler::fgDebugCheckSsa()
4077
4116
assert (fgDomsComputed);
4078
4117
4079
4118
// This class visits the flow graph the same way the SSA builder does.
4119
+ // In particular it may skip over blocks that SSA did not rename.
4080
4120
//
4081
4121
class SsaCheckDomTreeVisitor : public DomTreeVisitor <SsaCheckDomTreeVisitor>
4082
4122
{
@@ -4099,13 +4139,13 @@ void Compiler::fgDebugCheckSsa()
4099
4139
}
4100
4140
};
4101
4141
4102
- // Visit the blocks SSA modified
4142
+ // Visit the blocks that SSA intially renamed
4103
4143
//
4104
4144
SsaCheckVisitor scv (this );
4105
4145
SsaCheckDomTreeVisitor visitor (this , scv);
4106
4146
visitor.WalkTree ();
4107
4147
4108
- // Also visit any blocks added afterwards
4148
+ // Also visit any blocks added after SSA was built
4109
4149
//
4110
4150
for (BasicBlock* const block : Blocks ())
4111
4151
{
@@ -4115,7 +4155,8 @@ void Compiler::fgDebugCheckSsa()
4115
4155
}
4116
4156
}
4117
4157
4118
- // Cross-check the information gathered from IR against the info in the SSA table
4158
+ // Cross-check the information gathered from IR against the info
4159
+ // in the LclSsaVarDscs.
4119
4160
//
4120
4161
scv.DoChecks ();
4121
4162
0 commit comments