noway_assert(lclNum < lvaCount);
LclVarDsc* lclVar = &lvaTable[lclNum];
- // If the local variable has its address exposed then bail
- if (fgExcludeFromSsa(lclNum))
+ // If the local variable is not in SSA then bail
+ if (!lvaInSsa(lclNum))
{
goto DONE_ASSERTION;
}
unsigned char lvFieldAccessed : 1; // The var is a struct local, and a field of the variable is accessed. Affects
// struct promotion.
+ unsigned char lvInSsa : 1; // The variable is in SSA form (set by SsaBuilder)
+
#ifdef DEBUG
// These further document the reasons for setting "lvDoNotEnregister". (Note that "lvAddrExposed" is one of the
// reasons;
unsigned lvaGSSecurityCookie; // LclVar number
bool lvaTempsHaveLargerOffsetThanVars();
+ // Returns "true" iff local variable "lclNum" is in SSA form.
+ bool lvaInSsa(unsigned lclNum)
+ {
+ assert(lclNum < lvaCount);
+ return lvaTable[lclNum].lvInSsa;
+ }
+
unsigned lvaSecurityObject; // variable representing the security object on the stack
unsigned lvaStubArgumentVar; // variable representing the secret stub argument coming in EAX
unsigned fgSsaPassesCompleted; // Number of times fgSsaBuild has been run.
- // Returns "true" iff lcl "lclNum" should be excluded from SSA.
- inline bool fgExcludeFromSsa(unsigned lclNum);
-
// Returns "true" if a struct temp of the given type requires needs zero init in this block
inline bool fgStructTempNeedsExplicitZeroInit(LclVarDsc* varDsc, BasicBlock* block);
return (!containsGCPtr || !info.compInitMem || ((block->bbFlags & BBF_BACKWARD_JUMP) != 0));
}
-/*****************************************************************************/
-bool Compiler::fgExcludeFromSsa(unsigned lclNum)
-{
- if (opts.MinOpts())
- {
- return true; // If we're doing MinOpts, no SSA vars.
- }
-
- LclVarDsc* varDsc = &lvaTable[lclNum];
-
- if (varDsc->lvAddrExposed)
- {
- return true; // We exclude address-exposed variables.
- }
- if (!varDsc->lvTracked)
- {
- return true; // SSA is only done for tracked variables
- }
- // lvPromoted structs are never tracked...
- assert(!varDsc->lvPromoted);
-
- if (varDsc->lvOverlappingFields)
- {
- return true; // Don't use SSA on structs that have overlapping fields
- }
-
- if (varDsc->lvIsStructField && (lvaGetParentPromotionType(lclNum) != PROMOTION_TYPE_INDEPENDENT))
- {
- // SSA must exclude struct fields that are not independent
- // - because we don't model the struct assignment properly when multiple fields can be assigned by one struct
- // assignment.
- // - SSA doesn't allow a single node to contain multiple SSA definitions.
- // - and PROMOTION_TYPE_DEPENDEDNT fields are never candidates for a register.
- //
- // Example mscorlib method: CompatibilitySwitches:IsCompatibilitySwitchSet
- //
- return true;
- }
- // otherwise this variable is *not* excluded for SSA
- return false;
-}
-
/*****************************************************************************/
ValueNum Compiler::GetUseAsgDefVNOrTreeVN(GenTree* op)
{
unsigned Compiler::GetSsaNumForLocalVarDef(GenTree* lcl)
{
// Address-taken variables don't have SSA numbers.
- if (fgExcludeFromSsa(lcl->AsLclVarCommon()->gtLclNum))
+ if (!lvaInSsa(lcl->AsLclVarCommon()->gtLclNum))
{
return SsaConfig::RESERVED_SSA_NUM;
}
continue;
}
unsigned lclNum = tree->gtLclVarCommon.gtLclNum;
- if (fgExcludeFromSsa(lclNum))
+ if (!lvaInSsa(lclNum))
{
continue;
}
}
unsigned lclNum = tree->AsLclVarCommon()->GetLclNum();
- // Skip address exposed variables.
- if (fgExcludeFromSsa(lclNum))
+ // Skip non-SSA variables.
+ if (!lvaInSsa(lclNum))
{
return;
}
*/
bool Compiler::optIsSsaLocal(GenTree* tree)
{
- return tree->IsLocal() && !fgExcludeFromSsa(tree->AsLclVarCommon()->GetLclNum());
+ return tree->IsLocal() && lvaInSsa(tree->AsLclVarCommon()->GetLclNum());
}
//------------------------------------------------------------------------------
return nullptr;
}
- if (!objectRefPtr->OperIsScalarLocal() || fgExcludeFromSsa(objectRefPtr->AsLclVarCommon()->GetLclNum()))
+ if (!objectRefPtr->OperIsScalarLocal() || !lvaInSsa(objectRefPtr->AsLclVarCommon()->GetLclNum()))
{
return nullptr;
assert(treelhs == treeDefParent->gtGetOp1());
GenTree* treeRhs = treeDefParent->gtGetOp2();
- if (treeRhs->OperIsScalarLocal() && !fgExcludeFromSsa(treeRhs->AsLclVarCommon()->GetLclNum()))
+ if (treeRhs->OperIsScalarLocal() && lvaInSsa(treeRhs->AsLclVarCommon()->GetLclNum()))
{
// Recursively track the Rhs
unsigned rhsLclNum = treeRhs->AsLclVarCommon()->GetLclNum();
// Since this previously was a TYP_STRUCT and we have changed it to a TYP_BYREF
// make sure that the following flag is not set as these will force SSA to
- // exclude tracking/enregistering these LclVars. (see fgExcludeFromSsa)
+ // exclude tracking/enregistering these LclVars. (see SsaBuilder::IncludeInSsa)
//
varDsc->lvOverlappingFields = 0; // This flag could have been set, clear it.
unsigned lclNum = lclVar->gtLclNum;
// The lvlVar must be have an Ssa tracked lifetime
- if (fgExcludeFromSsa(lclNum))
+ if (!lvaInSsa(lclNum))
{
return false;
}
{
// If it's a local byref for which we recorded a value number, use that...
GenTreeLclVar* argLcl = arg->AsLclVar();
- if (!fgExcludeFromSsa(argLcl->GetLclNum()))
+ if (lvaInSsa(argLcl->GetLclNum()))
{
ValueNum argVN =
lvaTable[argLcl->GetLclNum()].GetPerSsaData(argLcl->GetSsaNum())->m_vnPair.GetLiberal();
if (rhsVN != ValueNumStore::NoVN)
{
rhsVN = vnStore->VNNormVal(rhsVN);
- if (!fgExcludeFromSsa(lhsLcl->GetLclNum()))
+ if (lvaInSsa(lhsLcl->GetLclNum()))
{
lvaTable[lhsLcl->GetLclNum()]
.GetPerSsaData(lhsLcl->GetSsaNum())
{
JITDUMP("*************** In SsaBuilder::InsertPhiFunctions()\n");
- // Compute liveness on the graph.
- m_pCompiler->fgLocalVarLiveness();
- EndPhase(PHASE_BUILD_SSA_LIVENESS);
-
// Compute dominance frontier.
BlkToBlkVectorMap mapDF(m_allocator);
ComputeDominanceFrontiers(postOrder, count, &mapDF);
unsigned lclNum = m_pCompiler->lvaTrackedToVarNum[varIndex];
DBG_SSA_JITDUMP(" Considering local var V%02u:\n", lclNum);
- if (m_pCompiler->fgExcludeFromSsa(lclNum))
+ if (!m_pCompiler->lvaInSsa(lclNum))
{
DBG_SSA_JITDUMP(" Skipping because it is excluded.\n");
continue;
unsigned lclNum = tree->gtLclVarCommon.gtLclNum;
// Is this a variable we exclude from SSA?
- if (m_pCompiler->fgExcludeFromSsa(lclNum))
+ if (!m_pCompiler->lvaInSsa(lclNum))
{
tree->gtLclVarCommon.SetSsaNum(SsaConfig::RESERVED_SSA_NUM);
return;
// The first thing we do is treat parameters and must-init variables as if they have a
// virtual definition before entry -- they start out at SSA name 1.
- for (unsigned i = 0; i < m_pCompiler->lvaCount; i++)
+ for (unsigned lclNum = 0; lclNum < m_pCompiler->lvaCount; lclNum++)
{
- LclVarDsc* varDsc = &m_pCompiler->lvaTable[i];
+ if (!m_pCompiler->lvaInSsa(lclNum))
+ {
+ continue;
+ }
+
+ LclVarDsc* varDsc = &m_pCompiler->lvaTable[lclNum];
+ assert(varDsc->lvTracked);
if (varDsc->lvIsParam || m_pCompiler->info.compInitMem || varDsc->lvMustInit ||
- (varDsc->lvTracked &&
- VarSetOps::IsMember(m_pCompiler, m_pCompiler->fgFirstBB->bbLiveIn, varDsc->lvVarIndex)))
+ VarSetOps::IsMember(m_pCompiler, m_pCompiler->fgFirstBB->bbLiveIn, varDsc->lvVarIndex))
{
unsigned ssaNum = varDsc->lvPerSsaData.AllocSsaNum(m_allocator);
// In ValueNum we'd assume un-inited variables get FIRST_SSA_NUM.
assert(ssaNum == SsaConfig::FIRST_SSA_NUM);
- pRenameState->Push(nullptr, i, ssaNum);
+ pRenameState->Push(nullptr, lclNum, ssaNum);
}
}
ComputeDominators(postOrder, count, domTree);
EndPhase(PHASE_BUILD_SSA_DOMS);
+ // Compute liveness on the graph.
+ m_pCompiler->fgLocalVarLiveness();
+ EndPhase(PHASE_BUILD_SSA_LIVENESS);
+
+ // Mark all variables that will be tracked by SSA
+ for (unsigned lclNum = 0; lclNum < m_pCompiler->lvaCount; lclNum++)
+ {
+ m_pCompiler->lvaTable[lclNum].lvInSsa = IncludeInSsa(lclNum);
+ }
+
// Insert phi functions.
InsertPhiFunctions(postOrder, count);
}
}
+//------------------------------------------------------------------------
+// IncludeInSsa: Check if the specified variable can be included in SSA.
+//
+// Arguments:
+// lclNum - the variable number
+//
+// Return Value:
+// true if the variable is included in SSA
+//
+bool SsaBuilder::IncludeInSsa(unsigned lclNum)
+{
+ LclVarDsc* varDsc = &m_pCompiler->lvaTable[lclNum];
+
+ if (varDsc->lvAddrExposed)
+ {
+ return false; // We exclude address-exposed variables.
+ }
+ if (!varDsc->lvTracked)
+ {
+ return false; // SSA is only done for tracked variables
+ }
+ // lvPromoted structs are never tracked...
+ assert(!varDsc->lvPromoted);
+
+ if (varDsc->lvOverlappingFields)
+ {
+ return false; // Don't use SSA on structs that have overlapping fields
+ }
+
+ if (varDsc->lvIsStructField &&
+ (m_pCompiler->lvaGetParentPromotionType(lclNum) != Compiler::PROMOTION_TYPE_INDEPENDENT))
+ {
+ // SSA must exclude struct fields that are not independent
+ // - because we don't model the struct assignment properly when multiple fields can be assigned by one struct
+ // assignment.
+ // - SSA doesn't allow a single node to contain multiple SSA definitions.
+ // - and PROMOTION_TYPE_DEPENDEDNT fields are never candidates for a register.
+ //
+ // Example mscorlib method: CompatibilitySwitches:IsCompatibilitySwitchSet
+ //
+ return false;
+ }
+ // otherwise this variable is included in SSA
+ return true;
+}
+
#ifdef DEBUG
// This method asserts that SSA name constraints specified are satisfied.
void Compiler::JitTestCheckSSA()
m_pCompiler->EndPhase(phase);
}
+ bool IncludeInSsa(unsigned lclNum);
+
public:
// Constructor
SsaBuilder(Compiler* pCompiler);
// Start by giving incoming arguments value numbers.
// Also give must-init vars a zero of their type.
- for (unsigned i = 0; i < lvaCount; i++)
+ for (unsigned lclNum = 0; lclNum < lvaCount; lclNum++)
{
- LclVarDsc* varDsc = &lvaTable[i];
+ if (!lvaInSsa(lclNum))
+ {
+ continue;
+ }
+
+ LclVarDsc* varDsc = &lvaTable[lclNum];
+ assert(varDsc->lvTracked);
+
if (varDsc->lvIsParam)
{
// We assume that code equivalent to this variable initialization loop
// SSA numbers always start from FIRST_SSA_NUM, and we give the value number to SSA name FIRST_SSA_NUM.
// We use the VNF_InitVal(i) from here so we know that this value is loop-invariant
// in all loops.
- ValueNum initVal = vnStore->VNForFunc(varDsc->TypeGet(), VNF_InitVal, vnStore->VNForIntCon(i));
+ ValueNum initVal = vnStore->VNForFunc(varDsc->TypeGet(), VNF_InitVal, vnStore->VNForIntCon(lclNum));
LclSsaVarDsc* ssaDef = varDsc->GetPerSsaData(SsaConfig::FIRST_SSA_NUM);
ssaDef->m_vnPair.SetBoth(initVal);
ssaDef->m_defLoc.m_blk = fgFirstBB;
}
else if (info.compInitMem || varDsc->lvMustInit ||
- (varDsc->lvTracked && VarSetOps::IsMember(this, fgFirstBB->bbLiveIn, varDsc->lvVarIndex)))
+ VarSetOps::IsMember(this, fgFirstBB->bbLiveIn, varDsc->lvVarIndex))
{
// The last clause covers the use-before-def variables (the ones that are live-in to the the first block),
// these are variables that are read before being initialized (at least on some control flow paths)
else
{
// Here we have uninitialized TYP_BYREF
- initVal = vnStore->VNForFunc(typ, VNF_InitVal, vnStore->VNForIntCon(i));
+ initVal = vnStore->VNForFunc(typ, VNF_InitVal, vnStore->VNForIntCon(lclNum));
}
break;
}
else
{
- initVal = vnStore->VNForFunc(typ, VNF_InitVal, vnStore->VNForIntCon(i));
+ initVal = vnStore->VNForFunc(typ, VNF_InitVal, vnStore->VNForIntCon(lclNum));
}
break;
}
#ifdef _TARGET_X86_
- bool isVarargParam = (i == lvaVarargsBaseOfStkArgs || i == lvaVarargsHandleArg);
+ bool isVarargParam = (lclNum == lvaVarargsBaseOfStkArgs || lclNum == lvaVarargsHandleArg);
if (isVarargParam)
initVal = vnStore->VNForExpr(fgFirstBB); // a new, unique VN.
#endif
// Ignore vars that we excluded from SSA (for example, because they're address-exposed). They don't have
// SSA names in which to store VN's on defs. We'll yield unique VN's when we read from them.
- if (!fgExcludeFromSsa(lclNum))
+ if (lvaInSsa(lclNum))
{
// Should not have been recorded as updating ByrefExposed.
assert(!GetMemorySsaMap(ByrefExposed)->Lookup(tree, &memorySsaNum));
unsigned lhsLclNum = lclVarTree->GetLclNum();
FieldSeqNode* lhsFldSeq = nullptr;
// If it's excluded from SSA, don't need to do anything.
- if (!fgExcludeFromSsa(lhsLclNum))
+ if (lvaInSsa(lhsLclNum))
{
// Should not have been recorded as updating ByrefExposed.
assert(!GetMemorySsaMap(ByrefExposed)->Lookup(tree, &memorySsaNum));
{
unsigned rhsLclNum = rhsLclVarTree->GetLclNum();
rhsVarDsc = &lvaTable[rhsLclNum];
- if (fgExcludeFromSsa(rhsLclNum) || rhsFldSeq == FieldSeqStore::NotAField())
+ if (!lvaInSsa(rhsLclNum) || rhsFldSeq == FieldSeqStore::NotAField())
{
rhsVNPair.SetBoth(vnStore->VNForExpr(compCurBB, rhsLclVarTree->TypeGet()));
isNewUniq = true;
{
unsigned rhsLclNum = rhsLclVarTree->GetLclNum();
rhsVarDsc = &lvaTable[rhsLclNum];
- if (fgExcludeFromSsa(rhsLclNum) || rhsFldSeq == FieldSeqStore::NotAField())
+ if (!lvaInSsa(rhsLclNum) || rhsFldSeq == FieldSeqStore::NotAField())
{
isNewUniq = true;
}
case GT_LCL_FLD:
{
GenTreeLclFld* lclFld = tree->AsLclFld();
- assert(fgExcludeFromSsa(lclFld->GetLclNum()) || lclFld->gtFieldSeq != nullptr);
+ assert(!lvaInSsa(lclFld->GetLclNum()) || lclFld->gtFieldSeq != nullptr);
// If this is a (full) def, then the variable will be labeled with the new SSA number,
// which will not have a value. We skip; it will be handled by one of the assignment-like
// forms (assignment, or initBlk or copyBlk).
LclVarDsc* varDsc = &lvaTable[lclNum];
var_types indType = tree->TypeGet();
- if (lclFld->gtFieldSeq == FieldSeqStore::NotAField() || fgExcludeFromSsa(lclFld->GetLclNum()))
+ if (lclFld->gtFieldSeq == FieldSeqStore::NotAField() || !lvaInSsa(lclFld->GetLclNum()))
{
// This doesn't represent a proper field access or it's a struct
// with overlapping fields that is hard to reason about; return a new unique VN.
wasLocal = true;
- if (!fgExcludeFromSsa(lclNum))
+ if (lvaInSsa(lclNum))
{
FieldSeqNode* fieldSeq = vnStore->FieldSeqVNToFieldSeq(funcApp.m_args[1]);
{
FieldSeqNode* fieldSeq = nullptr;
ValueNum newVN = ValueNumStore::NoVN;
- if (fgExcludeFromSsa(arg->gtLclVarCommon.GetLclNum()))
+ if (!lvaInSsa(arg->gtLclVarCommon.GetLclNum()))
{
newVN = vnStore->VNForExpr(compCurBB, TYP_BYREF);
}
VNFuncApp funcApp;
// Is it a local or a heap address?
- if (addr->IsLocalAddrExpr(this, &lclVarTree, &localFldSeq) &&
- !fgExcludeFromSsa(lclVarTree->GetLclNum()))
+ if (addr->IsLocalAddrExpr(this, &lclVarTree, &localFldSeq) && lvaInSsa(lclVarTree->GetLclNum()))
{
unsigned lclNum = lclVarTree->GetLclNum();
unsigned ssaNum = lclVarTree->GetSsaNum();